roojs-all.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.boostrap.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     var fireDocReady = function(){
6052         if(!docReadyState){
6053             docReadyState = true;
6054             Roo.isReady = true;
6055             if(docReadyProcId){
6056                 clearInterval(docReadyProcId);
6057             }
6058             if(Roo.isGecko || Roo.isOpera) {
6059                 document.removeEventListener("DOMContentLoaded", fireDocReady, false);
6060             }
6061             if(Roo.isIE){
6062                 var defer = document.getElementById("ie-deferred-loader");
6063                 if(defer){
6064                     defer.onreadystatechange = null;
6065                     defer.parentNode.removeChild(defer);
6066                 }
6067             }
6068             if(docReadyEvent){
6069                 docReadyEvent.fire();
6070                 docReadyEvent.clearListeners();
6071             }
6072         }
6073     };
6074     
6075     var initDocReady = function(){
6076         docReadyEvent = new Roo.util.Event();
6077         if(Roo.isGecko || Roo.isOpera) {
6078             document.addEventListener("DOMContentLoaded", fireDocReady, false);
6079         }else if(Roo.isIE){
6080             document.write("<s"+'cript id="ie-deferred-loader" defer="defer" src="/'+'/:"></s'+"cript>");
6081             var defer = document.getElementById("ie-deferred-loader");
6082             defer.onreadystatechange = function(){
6083                 if(this.readyState == "complete"){
6084                     fireDocReady();
6085                 }
6086             };
6087         }else if(Roo.isSafari){ 
6088             docReadyProcId = setInterval(function(){
6089                 var rs = document.readyState;
6090                 if(rs == "complete") {
6091                     fireDocReady();     
6092                  }
6093             }, 10);
6094         }
6095         // no matter what, make sure it fires on load
6096         E.on(window, "load", fireDocReady);
6097     };
6098
6099     var createBuffered = function(h, o){
6100         var task = new Roo.util.DelayedTask(h);
6101         return function(e){
6102             // create new event object impl so new events don't wipe out properties
6103             e = new Roo.EventObjectImpl(e);
6104             task.delay(o.buffer, h, null, [e]);
6105         };
6106     };
6107
6108     var createSingle = function(h, el, ename, fn){
6109         return function(e){
6110             Roo.EventManager.removeListener(el, ename, fn);
6111             h(e);
6112         };
6113     };
6114
6115     var createDelayed = function(h, o){
6116         return function(e){
6117             // create new event object impl so new events don't wipe out properties
6118             e = new Roo.EventObjectImpl(e);
6119             setTimeout(function(){
6120                 h(e);
6121             }, o.delay || 10);
6122         };
6123     };
6124
6125     var listen = function(element, ename, opt, fn, scope){
6126         var o = (!opt || typeof opt == "boolean") ? {} : opt;
6127         fn = fn || o.fn; scope = scope || o.scope;
6128         var el = Roo.getDom(element);
6129         if(!el){
6130             throw "Error listening for \"" + ename + '\". Element "' + element + '" doesn\'t exist.';
6131         }
6132         var h = function(e){
6133             e = Roo.EventObject.setEvent(e);
6134             var t;
6135             if(o.delegate){
6136                 t = e.getTarget(o.delegate, el);
6137                 if(!t){
6138                     return;
6139                 }
6140             }else{
6141                 t = e.target;
6142             }
6143             if(o.stopEvent === true){
6144                 e.stopEvent();
6145             }
6146             if(o.preventDefault === true){
6147                e.preventDefault();
6148             }
6149             if(o.stopPropagation === true){
6150                 e.stopPropagation();
6151             }
6152
6153             if(o.normalized === false){
6154                 e = e.browserEvent;
6155             }
6156
6157             fn.call(scope || el, e, t, o);
6158         };
6159         if(o.delay){
6160             h = createDelayed(h, o);
6161         }
6162         if(o.single){
6163             h = createSingle(h, el, ename, fn);
6164         }
6165         if(o.buffer){
6166             h = createBuffered(h, o);
6167         }
6168         fn._handlers = fn._handlers || [];
6169         fn._handlers.push([Roo.id(el), ename, h]);
6170
6171         E.on(el, ename, h);
6172         if(ename == "mousewheel" && el.addEventListener){ // workaround for jQuery
6173             el.addEventListener("DOMMouseScroll", h, false);
6174             E.on(window, 'unload', function(){
6175                 el.removeEventListener("DOMMouseScroll", h, false);
6176             });
6177         }
6178         if(ename == "mousedown" && el == document){ // fix stopped mousedowns on the document
6179             Roo.EventManager.stoppedMouseDownEvent.addListener(h);
6180         }
6181         return h;
6182     };
6183
6184     var stopListening = function(el, ename, fn){
6185         var id = Roo.id(el), hds = fn._handlers, hd = fn;
6186         if(hds){
6187             for(var i = 0, len = hds.length; i < len; i++){
6188                 var h = hds[i];
6189                 if(h[0] == id && h[1] == ename){
6190                     hd = h[2];
6191                     hds.splice(i, 1);
6192                     break;
6193                 }
6194             }
6195         }
6196         E.un(el, ename, hd);
6197         el = Roo.getDom(el);
6198         if(ename == "mousewheel" && el.addEventListener){
6199             el.removeEventListener("DOMMouseScroll", hd, false);
6200         }
6201         if(ename == "mousedown" && el == document){ // fix stopped mousedowns on the document
6202             Roo.EventManager.stoppedMouseDownEvent.removeListener(hd);
6203         }
6204     };
6205
6206     var propRe = /^(?:scope|delay|buffer|single|stopEvent|preventDefault|stopPropagation|normalized|args|delegate)$/;
6207     
6208     var pub = {
6209         
6210         
6211         /** 
6212          * Fix for doc tools
6213          * @scope Roo.EventManager
6214          */
6215         
6216         
6217         /** 
6218          * This is no longer needed and is deprecated. Places a simple wrapper around an event handler to override the browser event
6219          * object with a Roo.EventObject
6220          * @param {Function} fn        The method the event invokes
6221          * @param {Object}   scope    An object that becomes the scope of the handler
6222          * @param {boolean}  override If true, the obj passed in becomes
6223          *                             the execution scope of the listener
6224          * @return {Function} The wrapped function
6225          * @deprecated
6226          */
6227         wrap : function(fn, scope, override){
6228             return function(e){
6229                 Roo.EventObject.setEvent(e);
6230                 fn.call(override ? scope || window : window, Roo.EventObject, scope);
6231             };
6232         },
6233         
6234         /**
6235      * Appends an event handler to an element (shorthand for addListener)
6236      * @param {String/HTMLElement}   element        The html element or id to assign the
6237      * @param {String}   eventName The type of event to listen for
6238      * @param {Function} handler The method the event invokes
6239      * @param {Object}   scope (optional) The scope in which to execute the handler
6240      * function. The handler function's "this" context.
6241      * @param {Object}   options (optional) An object containing handler configuration
6242      * properties. This may contain any of the following properties:<ul>
6243      * <li>scope {Object} The scope in which to execute the handler function. The handler function's "this" context.</li>
6244      * <li>delegate {String} A simple selector to filter the target or look for a descendant of the target</li>
6245      * <li>stopEvent {Boolean} True to stop the event. That is stop propagation, and prevent the default action.</li>
6246      * <li>preventDefault {Boolean} True to prevent the default action</li>
6247      * <li>stopPropagation {Boolean} True to prevent event propagation</li>
6248      * <li>normalized {Boolean} False to pass a browser event to the handler function instead of an Roo.EventObject</li>
6249      * <li>delay {Number} The number of milliseconds to delay the invocation of the handler after te event fires.</li>
6250      * <li>single {Boolean} True to add a handler to handle just the next firing of the event, and then remove itself.</li>
6251      * <li>buffer {Number} Causes the handler to be scheduled to run in an {@link Roo.util.DelayedTask} delayed
6252      * by the specified number of milliseconds. If the event fires again within that time, the original
6253      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>
6254      * </ul><br>
6255      * <p>
6256      * <b>Combining Options</b><br>
6257      * Using the options argument, it is possible to combine different types of listeners:<br>
6258      * <br>
6259      * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)<div style="margin: 5px 20px 20px;">
6260      * Code:<pre><code>
6261 el.on('click', this.onClick, this, {
6262     single: true,
6263     delay: 100,
6264     stopEvent : true,
6265     forumId: 4
6266 });</code></pre>
6267      * <p>
6268      * <b>Attaching multiple handlers in 1 call</b><br>
6269       * The method also allows for a single argument to be passed which is a config object containing properties
6270      * which specify multiple handlers.
6271      * <p>
6272      * Code:<pre><code>
6273 el.on({
6274     'click' : {
6275         fn: this.onClick
6276         scope: this,
6277         delay: 100
6278     },
6279     'mouseover' : {
6280         fn: this.onMouseOver
6281         scope: this
6282     },
6283     'mouseout' : {
6284         fn: this.onMouseOut
6285         scope: this
6286     }
6287 });</code></pre>
6288      * <p>
6289      * Or a shorthand syntax:<br>
6290      * Code:<pre><code>
6291 el.on({
6292     'click' : this.onClick,
6293     'mouseover' : this.onMouseOver,
6294     'mouseout' : this.onMouseOut
6295     scope: this
6296 });</code></pre>
6297      */
6298         addListener : function(element, eventName, fn, scope, options){
6299             if(typeof eventName == "object"){
6300                 var o = eventName;
6301                 for(var e in o){
6302                     if(propRe.test(e)){
6303                         continue;
6304                     }
6305                     if(typeof o[e] == "function"){
6306                         // shared options
6307                         listen(element, e, o, o[e], o.scope);
6308                     }else{
6309                         // individual options
6310                         listen(element, e, o[e]);
6311                     }
6312                 }
6313                 return;
6314             }
6315             return listen(element, eventName, options, fn, scope);
6316         },
6317         
6318         /**
6319          * Removes an event handler
6320          *
6321          * @param {String/HTMLElement}   element        The id or html element to remove the 
6322          *                             event from
6323          * @param {String}   eventName     The type of event
6324          * @param {Function} fn
6325          * @return {Boolean} True if a listener was actually removed
6326          */
6327         removeListener : function(element, eventName, fn){
6328             return stopListening(element, eventName, fn);
6329         },
6330         
6331         /**
6332          * Fires when the document is ready (before onload and before images are loaded). Can be 
6333          * accessed shorthanded Roo.onReady().
6334          * @param {Function} fn        The method the event invokes
6335          * @param {Object}   scope    An  object that becomes the scope of the handler
6336          * @param {boolean}  options
6337          */
6338         onDocumentReady : function(fn, scope, options){
6339             if(docReadyState){ // if it already fired
6340                 docReadyEvent.addListener(fn, scope, options);
6341                 docReadyEvent.fire();
6342                 docReadyEvent.clearListeners();
6343                 return;
6344             }
6345             if(!docReadyEvent){
6346                 initDocReady();
6347             }
6348             docReadyEvent.addListener(fn, scope, options);
6349         },
6350         
6351         /**
6352          * Fires when the window is resized and provides resize event buffering (50 milliseconds), passes new viewport width and height to handlers.
6353          * @param {Function} fn        The method the event invokes
6354          * @param {Object}   scope    An object that becomes the scope of the handler
6355          * @param {boolean}  options
6356          */
6357         onWindowResize : function(fn, scope, options){
6358             if(!resizeEvent){
6359                 resizeEvent = new Roo.util.Event();
6360                 resizeTask = new Roo.util.DelayedTask(function(){
6361                     resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
6362                 });
6363                 E.on(window, "resize", function(){
6364                     if(Roo.isIE){
6365                         resizeTask.delay(50);
6366                     }else{
6367                         resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
6368                     }
6369                 });
6370             }
6371             resizeEvent.addListener(fn, scope, options);
6372         },
6373
6374         /**
6375          * Fires when the user changes the active text size. Handler gets called with 2 params, the old size and the new size.
6376          * @param {Function} fn        The method the event invokes
6377          * @param {Object}   scope    An object that becomes the scope of the handler
6378          * @param {boolean}  options
6379          */
6380         onTextResize : function(fn, scope, options){
6381             if(!textEvent){
6382                 textEvent = new Roo.util.Event();
6383                 var textEl = new Roo.Element(document.createElement('div'));
6384                 textEl.dom.className = 'x-text-resize';
6385                 textEl.dom.innerHTML = 'X';
6386                 textEl.appendTo(document.body);
6387                 textSize = textEl.dom.offsetHeight;
6388                 setInterval(function(){
6389                     if(textEl.dom.offsetHeight != textSize){
6390                         textEvent.fire(textSize, textSize = textEl.dom.offsetHeight);
6391                     }
6392                 }, this.textResizeInterval);
6393             }
6394             textEvent.addListener(fn, scope, options);
6395         },
6396
6397         /**
6398          * Removes the passed window resize listener.
6399          * @param {Function} fn        The method the event invokes
6400          * @param {Object}   scope    The scope of handler
6401          */
6402         removeResizeListener : function(fn, scope){
6403             if(resizeEvent){
6404                 resizeEvent.removeListener(fn, scope);
6405             }
6406         },
6407
6408         // private
6409         fireResize : function(){
6410             if(resizeEvent){
6411                 resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
6412             }   
6413         },
6414         /**
6415          * Url used for onDocumentReady with using SSL (defaults to Roo.SSL_SECURE_URL)
6416          */
6417         ieDeferSrc : false,
6418         /**
6419          * The frequency, in milliseconds, to check for text resize events (defaults to 50)
6420          */
6421         textResizeInterval : 50
6422     };
6423     
6424     /**
6425      * Fix for doc tools
6426      * @scopeAlias pub=Roo.EventManager
6427      */
6428     
6429      /**
6430      * Appends an event handler to an element (shorthand for addListener)
6431      * @param {String/HTMLElement}   element        The html element or id to assign the
6432      * @param {String}   eventName The type of event to listen for
6433      * @param {Function} handler The method the event invokes
6434      * @param {Object}   scope (optional) The scope in which to execute the handler
6435      * function. The handler function's "this" context.
6436      * @param {Object}   options (optional) An object containing handler configuration
6437      * properties. This may contain any of the following properties:<ul>
6438      * <li>scope {Object} The scope in which to execute the handler function. The handler function's "this" context.</li>
6439      * <li>delegate {String} A simple selector to filter the target or look for a descendant of the target</li>
6440      * <li>stopEvent {Boolean} True to stop the event. That is stop propagation, and prevent the default action.</li>
6441      * <li>preventDefault {Boolean} True to prevent the default action</li>
6442      * <li>stopPropagation {Boolean} True to prevent event propagation</li>
6443      * <li>normalized {Boolean} False to pass a browser event to the handler function instead of an Roo.EventObject</li>
6444      * <li>delay {Number} The number of milliseconds to delay the invocation of the handler after te event fires.</li>
6445      * <li>single {Boolean} True to add a handler to handle just the next firing of the event, and then remove itself.</li>
6446      * <li>buffer {Number} Causes the handler to be scheduled to run in an {@link Roo.util.DelayedTask} delayed
6447      * by the specified number of milliseconds. If the event fires again within that time, the original
6448      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>
6449      * </ul><br>
6450      * <p>
6451      * <b>Combining Options</b><br>
6452      * Using the options argument, it is possible to combine different types of listeners:<br>
6453      * <br>
6454      * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)<div style="margin: 5px 20px 20px;">
6455      * Code:<pre><code>
6456 el.on('click', this.onClick, this, {
6457     single: true,
6458     delay: 100,
6459     stopEvent : true,
6460     forumId: 4
6461 });</code></pre>
6462      * <p>
6463      * <b>Attaching multiple handlers in 1 call</b><br>
6464       * The method also allows for a single argument to be passed which is a config object containing properties
6465      * which specify multiple handlers.
6466      * <p>
6467      * Code:<pre><code>
6468 el.on({
6469     'click' : {
6470         fn: this.onClick
6471         scope: this,
6472         delay: 100
6473     },
6474     'mouseover' : {
6475         fn: this.onMouseOver
6476         scope: this
6477     },
6478     'mouseout' : {
6479         fn: this.onMouseOut
6480         scope: this
6481     }
6482 });</code></pre>
6483      * <p>
6484      * Or a shorthand syntax:<br>
6485      * Code:<pre><code>
6486 el.on({
6487     'click' : this.onClick,
6488     'mouseover' : this.onMouseOver,
6489     'mouseout' : this.onMouseOut
6490     scope: this
6491 });</code></pre>
6492      */
6493     pub.on = pub.addListener;
6494     pub.un = pub.removeListener;
6495
6496     pub.stoppedMouseDownEvent = new Roo.util.Event();
6497     return pub;
6498 }();
6499 /**
6500   * Fires when the document is ready (before onload and before images are loaded).  Shorthand of {@link Roo.EventManager#onDocumentReady}.
6501   * @param {Function} fn        The method the event invokes
6502   * @param {Object}   scope    An  object that becomes the scope of the handler
6503   * @param {boolean}  override If true, the obj passed in becomes
6504   *                             the execution scope of the listener
6505   * @member Roo
6506   * @method onReady
6507  */
6508 Roo.onReady = Roo.EventManager.onDocumentReady;
6509
6510 Roo.onReady(function(){
6511     var bd = Roo.get(document.body);
6512     if(!bd){ return; }
6513
6514     var cls = [
6515             Roo.isIE ? "roo-ie"
6516             : Roo.isGecko ? "roo-gecko"
6517             : Roo.isOpera ? "roo-opera"
6518             : Roo.isSafari ? "roo-safari" : ""];
6519
6520     if(Roo.isMac){
6521         cls.push("roo-mac");
6522     }
6523     if(Roo.isLinux){
6524         cls.push("roo-linux");
6525     }
6526     if(Roo.isBorderBox){
6527         cls.push('roo-border-box');
6528     }
6529     if(Roo.isStrict){ // add to the parent to allow for selectors like ".ext-strict .ext-ie"
6530         var p = bd.dom.parentNode;
6531         if(p){
6532             p.className += ' roo-strict';
6533         }
6534     }
6535     bd.addClass(cls.join(' '));
6536 });
6537
6538 /**
6539  * @class Roo.EventObject
6540  * EventObject exposes the Yahoo! UI Event functionality directly on the object
6541  * passed to your event handler. It exists mostly for convenience. It also fixes the annoying null checks automatically to cleanup your code 
6542  * Example:
6543  * <pre><code>
6544  function handleClick(e){ // e is not a standard event object, it is a Roo.EventObject
6545     e.preventDefault();
6546     var target = e.getTarget();
6547     ...
6548  }
6549  var myDiv = Roo.get("myDiv");
6550  myDiv.on("click", handleClick);
6551  //or
6552  Roo.EventManager.on("myDiv", 'click', handleClick);
6553  Roo.EventManager.addListener("myDiv", 'click', handleClick);
6554  </code></pre>
6555  * @singleton
6556  */
6557 Roo.EventObject = function(){
6558     
6559     var E = Roo.lib.Event;
6560     
6561     // safari keypress events for special keys return bad keycodes
6562     var safariKeys = {
6563         63234 : 37, // left
6564         63235 : 39, // right
6565         63232 : 38, // up
6566         63233 : 40, // down
6567         63276 : 33, // page up
6568         63277 : 34, // page down
6569         63272 : 46, // delete
6570         63273 : 36, // home
6571         63275 : 35  // end
6572     };
6573
6574     // normalize button clicks
6575     var btnMap = Roo.isIE ? {1:0,4:1,2:2} :
6576                 (Roo.isSafari ? {1:0,2:1,3:2} : {0:0,1:1,2:2});
6577
6578     Roo.EventObjectImpl = function(e){
6579         if(e){
6580             this.setEvent(e.browserEvent || e);
6581         }
6582     };
6583     Roo.EventObjectImpl.prototype = {
6584         /**
6585          * Used to fix doc tools.
6586          * @scope Roo.EventObject.prototype
6587          */
6588             
6589
6590         
6591         
6592         /** The normal browser event */
6593         browserEvent : null,
6594         /** The button pressed in a mouse event */
6595         button : -1,
6596         /** True if the shift key was down during the event */
6597         shiftKey : false,
6598         /** True if the control key was down during the event */
6599         ctrlKey : false,
6600         /** True if the alt key was down during the event */
6601         altKey : false,
6602
6603         /** Key constant 
6604         * @type Number */
6605         BACKSPACE : 8,
6606         /** Key constant 
6607         * @type Number */
6608         TAB : 9,
6609         /** Key constant 
6610         * @type Number */
6611         RETURN : 13,
6612         /** Key constant 
6613         * @type Number */
6614         ENTER : 13,
6615         /** Key constant 
6616         * @type Number */
6617         SHIFT : 16,
6618         /** Key constant 
6619         * @type Number */
6620         CONTROL : 17,
6621         /** Key constant 
6622         * @type Number */
6623         ESC : 27,
6624         /** Key constant 
6625         * @type Number */
6626         SPACE : 32,
6627         /** Key constant 
6628         * @type Number */
6629         PAGEUP : 33,
6630         /** Key constant 
6631         * @type Number */
6632         PAGEDOWN : 34,
6633         /** Key constant 
6634         * @type Number */
6635         END : 35,
6636         /** Key constant 
6637         * @type Number */
6638         HOME : 36,
6639         /** Key constant 
6640         * @type Number */
6641         LEFT : 37,
6642         /** Key constant 
6643         * @type Number */
6644         UP : 38,
6645         /** Key constant 
6646         * @type Number */
6647         RIGHT : 39,
6648         /** Key constant 
6649         * @type Number */
6650         DOWN : 40,
6651         /** Key constant 
6652         * @type Number */
6653         DELETE : 46,
6654         /** Key constant 
6655         * @type Number */
6656         F5 : 116,
6657
6658            /** @private */
6659         setEvent : function(e){
6660             if(e == this || (e && e.browserEvent)){ // already wrapped
6661                 return e;
6662             }
6663             this.browserEvent = e;
6664             if(e){
6665                 // normalize buttons
6666                 this.button = e.button ? btnMap[e.button] : (e.which ? e.which-1 : -1);
6667                 if(e.type == 'click' && this.button == -1){
6668                     this.button = 0;
6669                 }
6670                 this.type = e.type;
6671                 this.shiftKey = e.shiftKey;
6672                 // mac metaKey behaves like ctrlKey
6673                 this.ctrlKey = e.ctrlKey || e.metaKey;
6674                 this.altKey = e.altKey;
6675                 // in getKey these will be normalized for the mac
6676                 this.keyCode = e.keyCode;
6677                 // keyup warnings on firefox.
6678                 this.charCode = (e.type == 'keyup' || e.type == 'keydown') ? 0 : e.charCode;
6679                 // cache the target for the delayed and or buffered events
6680                 this.target = E.getTarget(e);
6681                 // same for XY
6682                 this.xy = E.getXY(e);
6683             }else{
6684                 this.button = -1;
6685                 this.shiftKey = false;
6686                 this.ctrlKey = false;
6687                 this.altKey = false;
6688                 this.keyCode = 0;
6689                 this.charCode =0;
6690                 this.target = null;
6691                 this.xy = [0, 0];
6692             }
6693             return this;
6694         },
6695
6696         /**
6697          * Stop the event (preventDefault and stopPropagation)
6698          */
6699         stopEvent : function(){
6700             if(this.browserEvent){
6701                 if(this.browserEvent.type == 'mousedown'){
6702                     Roo.EventManager.stoppedMouseDownEvent.fire(this);
6703                 }
6704                 E.stopEvent(this.browserEvent);
6705             }
6706         },
6707
6708         /**
6709          * Prevents the browsers default handling of the event.
6710          */
6711         preventDefault : function(){
6712             if(this.browserEvent){
6713                 E.preventDefault(this.browserEvent);
6714             }
6715         },
6716
6717         /** @private */
6718         isNavKeyPress : function(){
6719             var k = this.keyCode;
6720             k = Roo.isSafari ? (safariKeys[k] || k) : k;
6721             return (k >= 33 && k <= 40) || k == this.RETURN || k == this.TAB || k == this.ESC;
6722         },
6723
6724         isSpecialKey : function(){
6725             var k = this.keyCode;
6726             return (this.type == 'keypress' && this.ctrlKey) || k == 9 || k == 13  || k == 40 || k == 27 ||
6727             (k == 16) || (k == 17) ||
6728             (k >= 18 && k <= 20) ||
6729             (k >= 33 && k <= 35) ||
6730             (k >= 36 && k <= 39) ||
6731             (k >= 44 && k <= 45);
6732         },
6733         /**
6734          * Cancels bubbling of the event.
6735          */
6736         stopPropagation : function(){
6737             if(this.browserEvent){
6738                 if(this.type == 'mousedown'){
6739                     Roo.EventManager.stoppedMouseDownEvent.fire(this);
6740                 }
6741                 E.stopPropagation(this.browserEvent);
6742             }
6743         },
6744
6745         /**
6746          * Gets the key code for the event.
6747          * @return {Number}
6748          */
6749         getCharCode : function(){
6750             return this.charCode || this.keyCode;
6751         },
6752
6753         /**
6754          * Returns a normalized keyCode for the event.
6755          * @return {Number} The key code
6756          */
6757         getKey : function(){
6758             var k = this.keyCode || this.charCode;
6759             return Roo.isSafari ? (safariKeys[k] || k) : k;
6760         },
6761
6762         /**
6763          * Gets the x coordinate of the event.
6764          * @return {Number}
6765          */
6766         getPageX : function(){
6767             return this.xy[0];
6768         },
6769
6770         /**
6771          * Gets the y coordinate of the event.
6772          * @return {Number}
6773          */
6774         getPageY : function(){
6775             return this.xy[1];
6776         },
6777
6778         /**
6779          * Gets the time of the event.
6780          * @return {Number}
6781          */
6782         getTime : function(){
6783             if(this.browserEvent){
6784                 return E.getTime(this.browserEvent);
6785             }
6786             return null;
6787         },
6788
6789         /**
6790          * Gets the page coordinates of the event.
6791          * @return {Array} The xy values like [x, y]
6792          */
6793         getXY : function(){
6794             return this.xy;
6795         },
6796
6797         /**
6798          * Gets the target for the event.
6799          * @param {String} selector (optional) A simple selector to filter the target or look for an ancestor of the target
6800          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
6801                 search as a number or element (defaults to 10 || document.body)
6802          * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
6803          * @return {HTMLelement}
6804          */
6805         getTarget : function(selector, maxDepth, returnEl){
6806             return selector ? Roo.fly(this.target).findParent(selector, maxDepth, returnEl) : this.target;
6807         },
6808         /**
6809          * Gets the related target.
6810          * @return {HTMLElement}
6811          */
6812         getRelatedTarget : function(){
6813             if(this.browserEvent){
6814                 return E.getRelatedTarget(this.browserEvent);
6815             }
6816             return null;
6817         },
6818
6819         /**
6820          * Normalizes mouse wheel delta across browsers
6821          * @return {Number} The delta
6822          */
6823         getWheelDelta : function(){
6824             var e = this.browserEvent;
6825             var delta = 0;
6826             if(e.wheelDelta){ /* IE/Opera. */
6827                 delta = e.wheelDelta/120;
6828             }else if(e.detail){ /* Mozilla case. */
6829                 delta = -e.detail/3;
6830             }
6831             return delta;
6832         },
6833
6834         /**
6835          * Returns true if the control, meta, shift or alt key was pressed during this event.
6836          * @return {Boolean}
6837          */
6838         hasModifier : function(){
6839             return !!((this.ctrlKey || this.altKey) || this.shiftKey);
6840         },
6841
6842         /**
6843          * Returns true if the target of this event equals el or is a child of el
6844          * @param {String/HTMLElement/Element} el
6845          * @param {Boolean} related (optional) true to test if the related target is within el instead of the target
6846          * @return {Boolean}
6847          */
6848         within : function(el, related){
6849             var t = this[related ? "getRelatedTarget" : "getTarget"]();
6850             return t && Roo.fly(el).contains(t);
6851         },
6852
6853         getPoint : function(){
6854             return new Roo.lib.Point(this.xy[0], this.xy[1]);
6855         }
6856     };
6857
6858     return new Roo.EventObjectImpl();
6859 }();
6860             
6861     /*
6862  * Based on:
6863  * Ext JS Library 1.1.1
6864  * Copyright(c) 2006-2007, Ext JS, LLC.
6865  *
6866  * Originally Released Under LGPL - original licence link has changed is not relivant.
6867  *
6868  * Fork - LGPL
6869  * <script type="text/javascript">
6870  */
6871
6872  
6873 // was in Composite Element!??!?!
6874  
6875 (function(){
6876     var D = Roo.lib.Dom;
6877     var E = Roo.lib.Event;
6878     var A = Roo.lib.Anim;
6879
6880     // local style camelizing for speed
6881     var propCache = {};
6882     var camelRe = /(-[a-z])/gi;
6883     var camelFn = function(m, a){ return a.charAt(1).toUpperCase(); };
6884     var view = document.defaultView;
6885
6886 /**
6887  * @class Roo.Element
6888  * Represents an Element in the DOM.<br><br>
6889  * Usage:<br>
6890 <pre><code>
6891 var el = Roo.get("my-div");
6892
6893 // or with getEl
6894 var el = getEl("my-div");
6895
6896 // or with a DOM element
6897 var el = Roo.get(myDivElement);
6898 </code></pre>
6899  * Using Roo.get() or getEl() instead of calling the constructor directly ensures you get the same object
6900  * each call instead of constructing a new one.<br><br>
6901  * <b>Animations</b><br />
6902  * Many of the functions for manipulating an element have an optional "animate" parameter. The animate parameter
6903  * should either be a boolean (true) or an object literal with animation options. The animation options are:
6904 <pre>
6905 Option    Default   Description
6906 --------- --------  ---------------------------------------------
6907 duration  .35       The duration of the animation in seconds
6908 easing    easeOut   The YUI easing method
6909 callback  none      A function to execute when the anim completes
6910 scope     this      The scope (this) of the callback function
6911 </pre>
6912 * Also, the Anim object being used for the animation will be set on your options object as "anim", which allows you to stop or
6913 * manipulate the animation. Here's an example:
6914 <pre><code>
6915 var el = Roo.get("my-div");
6916
6917 // no animation
6918 el.setWidth(100);
6919
6920 // default animation
6921 el.setWidth(100, true);
6922
6923 // animation with some options set
6924 el.setWidth(100, {
6925     duration: 1,
6926     callback: this.foo,
6927     scope: this
6928 });
6929
6930 // using the "anim" property to get the Anim object
6931 var opt = {
6932     duration: 1,
6933     callback: this.foo,
6934     scope: this
6935 };
6936 el.setWidth(100, opt);
6937 ...
6938 if(opt.anim.isAnimated()){
6939     opt.anim.stop();
6940 }
6941 </code></pre>
6942 * <b> Composite (Collections of) Elements</b><br />
6943  * For working with collections of Elements, see <a href="Roo.CompositeElement.html">Roo.CompositeElement</a>
6944  * @constructor Create a new Element directly.
6945  * @param {String/HTMLElement} element
6946  * @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).
6947  */
6948     Roo.Element = function(element, forceNew){
6949         var dom = typeof element == "string" ?
6950                 document.getElementById(element) : element;
6951         if(!dom){ // invalid id/element
6952             return null;
6953         }
6954         var id = dom.id;
6955         if(forceNew !== true && id && Roo.Element.cache[id]){ // element object already exists
6956             return Roo.Element.cache[id];
6957         }
6958
6959         /**
6960          * The DOM element
6961          * @type HTMLElement
6962          */
6963         this.dom = dom;
6964
6965         /**
6966          * The DOM element ID
6967          * @type String
6968          */
6969         this.id = id || Roo.id(dom);
6970     };
6971
6972     var El = Roo.Element;
6973
6974     El.prototype = {
6975         /**
6976          * The element's default display mode  (defaults to "")
6977          * @type String
6978          */
6979         originalDisplay : "",
6980
6981         visibilityMode : 1,
6982         /**
6983          * The default unit to append to CSS values where a unit isn't provided (defaults to px).
6984          * @type String
6985          */
6986         defaultUnit : "px",
6987         /**
6988          * Sets the element's visibility mode. When setVisible() is called it
6989          * will use this to determine whether to set the visibility or the display property.
6990          * @param visMode Element.VISIBILITY or Element.DISPLAY
6991          * @return {Roo.Element} this
6992          */
6993         setVisibilityMode : function(visMode){
6994             this.visibilityMode = visMode;
6995             return this;
6996         },
6997         /**
6998          * Convenience method for setVisibilityMode(Element.DISPLAY)
6999          * @param {String} display (optional) What to set display to when visible
7000          * @return {Roo.Element} this
7001          */
7002         enableDisplayMode : function(display){
7003             this.setVisibilityMode(El.DISPLAY);
7004             if(typeof display != "undefined") this.originalDisplay = display;
7005             return this;
7006         },
7007
7008         /**
7009          * 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)
7010          * @param {String} selector The simple selector to test
7011          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
7012                 search as a number or element (defaults to 10 || document.body)
7013          * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
7014          * @return {HTMLElement} The matching DOM node (or null if no match was found)
7015          */
7016         findParent : function(simpleSelector, maxDepth, returnEl){
7017             var p = this.dom, b = document.body, depth = 0, dq = Roo.DomQuery, stopEl;
7018             maxDepth = maxDepth || 50;
7019             if(typeof maxDepth != "number"){
7020                 stopEl = Roo.getDom(maxDepth);
7021                 maxDepth = 10;
7022             }
7023             while(p && p.nodeType == 1 && depth < maxDepth && p != b && p != stopEl){
7024                 if(dq.is(p, simpleSelector)){
7025                     return returnEl ? Roo.get(p) : p;
7026                 }
7027                 depth++;
7028                 p = p.parentNode;
7029             }
7030             return null;
7031         },
7032
7033
7034         /**
7035          * Looks at parent nodes for a match of the passed simple selector (e.g. div.some-class or span:first-child)
7036          * @param {String} selector The simple selector to test
7037          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
7038                 search as a number or element (defaults to 10 || document.body)
7039          * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
7040          * @return {HTMLElement} The matching DOM node (or null if no match was found)
7041          */
7042         findParentNode : function(simpleSelector, maxDepth, returnEl){
7043             var p = Roo.fly(this.dom.parentNode, '_internal');
7044             return p ? p.findParent(simpleSelector, maxDepth, returnEl) : null;
7045         },
7046
7047         /**
7048          * Walks up the dom looking for a parent node that matches the passed simple selector (e.g. div.some-class or span:first-child).
7049          * This is a shortcut for findParentNode() that always returns an Roo.Element.
7050          * @param {String} selector The simple selector to test
7051          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
7052                 search as a number or element (defaults to 10 || document.body)
7053          * @return {Roo.Element} The matching DOM node (or null if no match was found)
7054          */
7055         up : function(simpleSelector, maxDepth){
7056             return this.findParentNode(simpleSelector, maxDepth, true);
7057         },
7058
7059
7060
7061         /**
7062          * Returns true if this element matches the passed simple selector (e.g. div.some-class or span:first-child)
7063          * @param {String} selector The simple selector to test
7064          * @return {Boolean} True if this element matches the selector, else false
7065          */
7066         is : function(simpleSelector){
7067             return Roo.DomQuery.is(this.dom, simpleSelector);
7068         },
7069
7070         /**
7071          * Perform animation on this element.
7072          * @param {Object} args The YUI animation control args
7073          * @param {Float} duration (optional) How long the animation lasts in seconds (defaults to .35)
7074          * @param {Function} onComplete (optional) Function to call when animation completes
7075          * @param {String} easing (optional) Easing method to use (defaults to 'easeOut')
7076          * @param {String} animType (optional) 'run' is the default. Can also be 'color', 'motion', or 'scroll'
7077          * @return {Roo.Element} this
7078          */
7079         animate : function(args, duration, onComplete, easing, animType){
7080             this.anim(args, {duration: duration, callback: onComplete, easing: easing}, animType);
7081             return this;
7082         },
7083
7084         /*
7085          * @private Internal animation call
7086          */
7087         anim : function(args, opt, animType, defaultDur, defaultEase, cb){
7088             animType = animType || 'run';
7089             opt = opt || {};
7090             var anim = Roo.lib.Anim[animType](
7091                 this.dom, args,
7092                 (opt.duration || defaultDur) || .35,
7093                 (opt.easing || defaultEase) || 'easeOut',
7094                 function(){
7095                     Roo.callback(cb, this);
7096                     Roo.callback(opt.callback, opt.scope || this, [this, opt]);
7097                 },
7098                 this
7099             );
7100             opt.anim = anim;
7101             return anim;
7102         },
7103
7104         // private legacy anim prep
7105         preanim : function(a, i){
7106             return !a[i] ? false : (typeof a[i] == "object" ? a[i]: {duration: a[i+1], callback: a[i+2], easing: a[i+3]});
7107         },
7108
7109         /**
7110          * Removes worthless text nodes
7111          * @param {Boolean} forceReclean (optional) By default the element
7112          * keeps track if it has been cleaned already so
7113          * you can call this over and over. However, if you update the element and
7114          * need to force a reclean, you can pass true.
7115          */
7116         clean : function(forceReclean){
7117             if(this.isCleaned && forceReclean !== true){
7118                 return this;
7119             }
7120             var ns = /\S/;
7121             var d = this.dom, n = d.firstChild, ni = -1;
7122             while(n){
7123                 var nx = n.nextSibling;
7124                 if(n.nodeType == 3 && !ns.test(n.nodeValue)){
7125                     d.removeChild(n);
7126                 }else{
7127                     n.nodeIndex = ++ni;
7128                 }
7129                 n = nx;
7130             }
7131             this.isCleaned = true;
7132             return this;
7133         },
7134
7135         // private
7136         calcOffsetsTo : function(el){
7137             el = Roo.get(el);
7138             var d = el.dom;
7139             var restorePos = false;
7140             if(el.getStyle('position') == 'static'){
7141                 el.position('relative');
7142                 restorePos = true;
7143             }
7144             var x = 0, y =0;
7145             var op = this.dom;
7146             while(op && op != d && op.tagName != 'HTML'){
7147                 x+= op.offsetLeft;
7148                 y+= op.offsetTop;
7149                 op = op.offsetParent;
7150             }
7151             if(restorePos){
7152                 el.position('static');
7153             }
7154             return [x, y];
7155         },
7156
7157         /**
7158          * Scrolls this element into view within the passed container.
7159          * @param {String/HTMLElement/Element} container (optional) The container element to scroll (defaults to document.body)
7160          * @param {Boolean} hscroll (optional) False to disable horizontal scroll (defaults to true)
7161          * @return {Roo.Element} this
7162          */
7163         scrollIntoView : function(container, hscroll){
7164             var c = Roo.getDom(container) || document.body;
7165             var el = this.dom;
7166
7167             var o = this.calcOffsetsTo(c),
7168                 l = o[0],
7169                 t = o[1],
7170                 b = t+el.offsetHeight,
7171                 r = l+el.offsetWidth;
7172
7173             var ch = c.clientHeight;
7174             var ct = parseInt(c.scrollTop, 10);
7175             var cl = parseInt(c.scrollLeft, 10);
7176             var cb = ct + ch;
7177             var cr = cl + c.clientWidth;
7178
7179             if(t < ct){
7180                 c.scrollTop = t;
7181             }else if(b > cb){
7182                 c.scrollTop = b-ch;
7183             }
7184
7185             if(hscroll !== false){
7186                 if(l < cl){
7187                     c.scrollLeft = l;
7188                 }else if(r > cr){
7189                     c.scrollLeft = r-c.clientWidth;
7190                 }
7191             }
7192             return this;
7193         },
7194
7195         // private
7196         scrollChildIntoView : function(child, hscroll){
7197             Roo.fly(child, '_scrollChildIntoView').scrollIntoView(this, hscroll);
7198         },
7199
7200         /**
7201          * Measures the element's content height and updates height to match. Note: this function uses setTimeout so
7202          * the new height may not be available immediately.
7203          * @param {Boolean} animate (optional) Animate the transition (defaults to false)
7204          * @param {Float} duration (optional) Length of the animation in seconds (defaults to .35)
7205          * @param {Function} onComplete (optional) Function to call when animation completes
7206          * @param {String} easing (optional) Easing method to use (defaults to easeOut)
7207          * @return {Roo.Element} this
7208          */
7209         autoHeight : function(animate, duration, onComplete, easing){
7210             var oldHeight = this.getHeight();
7211             this.clip();
7212             this.setHeight(1); // force clipping
7213             setTimeout(function(){
7214                 var height = parseInt(this.dom.scrollHeight, 10); // parseInt for Safari
7215                 if(!animate){
7216                     this.setHeight(height);
7217                     this.unclip();
7218                     if(typeof onComplete == "function"){
7219                         onComplete();
7220                     }
7221                 }else{
7222                     this.setHeight(oldHeight); // restore original height
7223                     this.setHeight(height, animate, duration, function(){
7224                         this.unclip();
7225                         if(typeof onComplete == "function") onComplete();
7226                     }.createDelegate(this), easing);
7227                 }
7228             }.createDelegate(this), 0);
7229             return this;
7230         },
7231
7232         /**
7233          * Returns true if this element is an ancestor of the passed element
7234          * @param {HTMLElement/String} el The element to check
7235          * @return {Boolean} True if this element is an ancestor of el, else false
7236          */
7237         contains : function(el){
7238             if(!el){return false;}
7239             return D.isAncestor(this.dom, el.dom ? el.dom : el);
7240         },
7241
7242         /**
7243          * Checks whether the element is currently visible using both visibility and display properties.
7244          * @param {Boolean} deep (optional) True to walk the dom and see if parent elements are hidden (defaults to false)
7245          * @return {Boolean} True if the element is currently visible, else false
7246          */
7247         isVisible : function(deep) {
7248             var vis = !(this.getStyle("visibility") == "hidden" || this.getStyle("display") == "none");
7249             if(deep !== true || !vis){
7250                 return vis;
7251             }
7252             var p = this.dom.parentNode;
7253             while(p && p.tagName.toLowerCase() != "body"){
7254                 if(!Roo.fly(p, '_isVisible').isVisible()){
7255                     return false;
7256                 }
7257                 p = p.parentNode;
7258             }
7259             return true;
7260         },
7261
7262         /**
7263          * Creates a {@link Roo.CompositeElement} for child nodes based on the passed CSS selector (the selector should not contain an id).
7264          * @param {String} selector The CSS selector
7265          * @param {Boolean} unique (optional) True to create a unique Roo.Element for each child (defaults to false, which creates a single shared flyweight object)
7266          * @return {CompositeElement/CompositeElementLite} The composite element
7267          */
7268         select : function(selector, unique){
7269             return El.select(selector, unique, this.dom);
7270         },
7271
7272         /**
7273          * Selects child nodes based on the passed CSS selector (the selector should not contain an id).
7274          * @param {String} selector The CSS selector
7275          * @return {Array} An array of the matched nodes
7276          */
7277         query : function(selector, unique){
7278             return Roo.DomQuery.select(selector, this.dom);
7279         },
7280
7281         /**
7282          * Selects a single child at any depth below this element based on the passed CSS selector (the selector should not contain an id).
7283          * @param {String} selector The CSS selector
7284          * @param {Boolean} returnDom (optional) True to return the DOM node instead of Roo.Element (defaults to false)
7285          * @return {HTMLElement/Roo.Element} The child Roo.Element (or DOM node if returnDom = true)
7286          */
7287         child : function(selector, returnDom){
7288             var n = Roo.DomQuery.selectNode(selector, this.dom);
7289             return returnDom ? n : Roo.get(n);
7290         },
7291
7292         /**
7293          * Selects a single *direct* child based on the passed CSS selector (the selector should not contain an id).
7294          * @param {String} selector The CSS selector
7295          * @param {Boolean} returnDom (optional) True to return the DOM node instead of Roo.Element (defaults to false)
7296          * @return {HTMLElement/Roo.Element} The child Roo.Element (or DOM node if returnDom = true)
7297          */
7298         down : function(selector, returnDom){
7299             var n = Roo.DomQuery.selectNode(" > " + selector, this.dom);
7300             return returnDom ? n : Roo.get(n);
7301         },
7302
7303         /**
7304          * Initializes a {@link Roo.dd.DD} drag drop object for this element.
7305          * @param {String} group The group the DD object is member of
7306          * @param {Object} config The DD config object
7307          * @param {Object} overrides An object containing methods to override/implement on the DD object
7308          * @return {Roo.dd.DD} The DD object
7309          */
7310         initDD : function(group, config, overrides){
7311             var dd = new Roo.dd.DD(Roo.id(this.dom), group, config);
7312             return Roo.apply(dd, overrides);
7313         },
7314
7315         /**
7316          * Initializes a {@link Roo.dd.DDProxy} object for this element.
7317          * @param {String} group The group the DDProxy object is member of
7318          * @param {Object} config The DDProxy config object
7319          * @param {Object} overrides An object containing methods to override/implement on the DDProxy object
7320          * @return {Roo.dd.DDProxy} The DDProxy object
7321          */
7322         initDDProxy : function(group, config, overrides){
7323             var dd = new Roo.dd.DDProxy(Roo.id(this.dom), group, config);
7324             return Roo.apply(dd, overrides);
7325         },
7326
7327         /**
7328          * Initializes a {@link Roo.dd.DDTarget} object for this element.
7329          * @param {String} group The group the DDTarget object is member of
7330          * @param {Object} config The DDTarget config object
7331          * @param {Object} overrides An object containing methods to override/implement on the DDTarget object
7332          * @return {Roo.dd.DDTarget} The DDTarget object
7333          */
7334         initDDTarget : function(group, config, overrides){
7335             var dd = new Roo.dd.DDTarget(Roo.id(this.dom), group, config);
7336             return Roo.apply(dd, overrides);
7337         },
7338
7339         /**
7340          * Sets the visibility of the element (see details). If the visibilityMode is set to Element.DISPLAY, it will use
7341          * the display property to hide the element, otherwise it uses visibility. The default is to hide and show using the visibility property.
7342          * @param {Boolean} visible Whether the element is visible
7343          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7344          * @return {Roo.Element} this
7345          */
7346          setVisible : function(visible, animate){
7347             if(!animate || !A){
7348                 if(this.visibilityMode == El.DISPLAY){
7349                     this.setDisplayed(visible);
7350                 }else{
7351                     this.fixDisplay();
7352                     this.dom.style.visibility = visible ? "visible" : "hidden";
7353                 }
7354             }else{
7355                 // closure for composites
7356                 var dom = this.dom;
7357                 var visMode = this.visibilityMode;
7358                 if(visible){
7359                     this.setOpacity(.01);
7360                     this.setVisible(true);
7361                 }
7362                 this.anim({opacity: { to: (visible?1:0) }},
7363                       this.preanim(arguments, 1),
7364                       null, .35, 'easeIn', function(){
7365                          if(!visible){
7366                              if(visMode == El.DISPLAY){
7367                                  dom.style.display = "none";
7368                              }else{
7369                                  dom.style.visibility = "hidden";
7370                              }
7371                              Roo.get(dom).setOpacity(1);
7372                          }
7373                      });
7374             }
7375             return this;
7376         },
7377
7378         /**
7379          * Returns true if display is not "none"
7380          * @return {Boolean}
7381          */
7382         isDisplayed : function() {
7383             return this.getStyle("display") != "none";
7384         },
7385
7386         /**
7387          * Toggles the element's visibility or display, depending on visibility mode.
7388          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7389          * @return {Roo.Element} this
7390          */
7391         toggle : function(animate){
7392             this.setVisible(!this.isVisible(), this.preanim(arguments, 0));
7393             return this;
7394         },
7395
7396         /**
7397          * Sets the CSS display property. Uses originalDisplay if the specified value is a boolean true.
7398          * @param {Boolean} value Boolean value to display the element using its default display, or a string to set the display directly
7399          * @return {Roo.Element} this
7400          */
7401         setDisplayed : function(value) {
7402             if(typeof value == "boolean"){
7403                value = value ? this.originalDisplay : "none";
7404             }
7405             this.setStyle("display", value);
7406             return this;
7407         },
7408
7409         /**
7410          * Tries to focus the element. Any exceptions are caught and ignored.
7411          * @return {Roo.Element} this
7412          */
7413         focus : function() {
7414             try{
7415                 this.dom.focus();
7416             }catch(e){}
7417             return this;
7418         },
7419
7420         /**
7421          * Tries to blur the element. Any exceptions are caught and ignored.
7422          * @return {Roo.Element} this
7423          */
7424         blur : function() {
7425             try{
7426                 this.dom.blur();
7427             }catch(e){}
7428             return this;
7429         },
7430
7431         /**
7432          * Adds one or more CSS classes to the element. Duplicate classes are automatically filtered out.
7433          * @param {String/Array} className The CSS class to add, or an array of classes
7434          * @return {Roo.Element} this
7435          */
7436         addClass : function(className){
7437             if(className instanceof Array){
7438                 for(var i = 0, len = className.length; i < len; i++) {
7439                     this.addClass(className[i]);
7440                 }
7441             }else{
7442                 if(className && !this.hasClass(className)){
7443                     this.dom.className = this.dom.className + " " + className;
7444                 }
7445             }
7446             return this;
7447         },
7448
7449         /**
7450          * Adds one or more CSS classes to this element and removes the same class(es) from all siblings.
7451          * @param {String/Array} className The CSS class to add, or an array of classes
7452          * @return {Roo.Element} this
7453          */
7454         radioClass : function(className){
7455             var siblings = this.dom.parentNode.childNodes;
7456             for(var i = 0; i < siblings.length; i++) {
7457                 var s = siblings[i];
7458                 if(s.nodeType == 1){
7459                     Roo.get(s).removeClass(className);
7460                 }
7461             }
7462             this.addClass(className);
7463             return this;
7464         },
7465
7466         /**
7467          * Removes one or more CSS classes from the element.
7468          * @param {String/Array} className The CSS class to remove, or an array of classes
7469          * @return {Roo.Element} this
7470          */
7471         removeClass : function(className){
7472             if(!className || !this.dom.className){
7473                 return this;
7474             }
7475             if(className instanceof Array){
7476                 for(var i = 0, len = className.length; i < len; i++) {
7477                     this.removeClass(className[i]);
7478                 }
7479             }else{
7480                 if(this.hasClass(className)){
7481                     var re = this.classReCache[className];
7482                     if (!re) {
7483                        re = new RegExp('(?:^|\\s+)' + className + '(?:\\s+|$)', "g");
7484                        this.classReCache[className] = re;
7485                     }
7486                     this.dom.className =
7487                         this.dom.className.replace(re, " ");
7488                 }
7489             }
7490             return this;
7491         },
7492
7493         // private
7494         classReCache: {},
7495
7496         /**
7497          * Toggles the specified CSS class on this element (removes it if it already exists, otherwise adds it).
7498          * @param {String} className The CSS class to toggle
7499          * @return {Roo.Element} this
7500          */
7501         toggleClass : function(className){
7502             if(this.hasClass(className)){
7503                 this.removeClass(className);
7504             }else{
7505                 this.addClass(className);
7506             }
7507             return this;
7508         },
7509
7510         /**
7511          * Checks if the specified CSS class exists on this element's DOM node.
7512          * @param {String} className The CSS class to check for
7513          * @return {Boolean} True if the class exists, else false
7514          */
7515         hasClass : function(className){
7516             return className && (' '+this.dom.className+' ').indexOf(' '+className+' ') != -1;
7517         },
7518
7519         /**
7520          * Replaces a CSS class on the element with another.  If the old name does not exist, the new name will simply be added.
7521          * @param {String} oldClassName The CSS class to replace
7522          * @param {String} newClassName The replacement CSS class
7523          * @return {Roo.Element} this
7524          */
7525         replaceClass : function(oldClassName, newClassName){
7526             this.removeClass(oldClassName);
7527             this.addClass(newClassName);
7528             return this;
7529         },
7530
7531         /**
7532          * Returns an object with properties matching the styles requested.
7533          * For example, el.getStyles('color', 'font-size', 'width') might return
7534          * {'color': '#FFFFFF', 'font-size': '13px', 'width': '100px'}.
7535          * @param {String} style1 A style name
7536          * @param {String} style2 A style name
7537          * @param {String} etc.
7538          * @return {Object} The style object
7539          */
7540         getStyles : function(){
7541             var a = arguments, len = a.length, r = {};
7542             for(var i = 0; i < len; i++){
7543                 r[a[i]] = this.getStyle(a[i]);
7544             }
7545             return r;
7546         },
7547
7548         /**
7549          * Normalizes currentStyle and computedStyle. This is not YUI getStyle, it is an optimised version.
7550          * @param {String} property The style property whose value is returned.
7551          * @return {String} The current value of the style property for this element.
7552          */
7553         getStyle : function(){
7554             return view && view.getComputedStyle ?
7555                 function(prop){
7556                     var el = this.dom, v, cs, camel;
7557                     if(prop == 'float'){
7558                         prop = "cssFloat";
7559                     }
7560                     if(el.style && (v = el.style[prop])){
7561                         return v;
7562                     }
7563                     if(cs = view.getComputedStyle(el, "")){
7564                         if(!(camel = propCache[prop])){
7565                             camel = propCache[prop] = prop.replace(camelRe, camelFn);
7566                         }
7567                         return cs[camel];
7568                     }
7569                     return null;
7570                 } :
7571                 function(prop){
7572                     var el = this.dom, v, cs, camel;
7573                     if(prop == 'opacity'){
7574                         if(typeof el.style.filter == 'string'){
7575                             var m = el.style.filter.match(/alpha\(opacity=(.*)\)/i);
7576                             if(m){
7577                                 var fv = parseFloat(m[1]);
7578                                 if(!isNaN(fv)){
7579                                     return fv ? fv / 100 : 0;
7580                                 }
7581                             }
7582                         }
7583                         return 1;
7584                     }else if(prop == 'float'){
7585                         prop = "styleFloat";
7586                     }
7587                     if(!(camel = propCache[prop])){
7588                         camel = propCache[prop] = prop.replace(camelRe, camelFn);
7589                     }
7590                     if(v = el.style[camel]){
7591                         return v;
7592                     }
7593                     if(cs = el.currentStyle){
7594                         return cs[camel];
7595                     }
7596                     return null;
7597                 };
7598         }(),
7599
7600         /**
7601          * Wrapper for setting style properties, also takes single object parameter of multiple styles.
7602          * @param {String/Object} property The style property to be set, or an object of multiple styles.
7603          * @param {String} value (optional) The value to apply to the given property, or null if an object was passed.
7604          * @return {Roo.Element} this
7605          */
7606         setStyle : function(prop, value){
7607             if(typeof prop == "string"){
7608                 
7609                 if (prop == 'float') {
7610                     this.setStyle(Roo.isIE ? 'styleFloat'  : 'cssFloat', value);
7611                     return this;
7612                 }
7613                 
7614                 var camel;
7615                 if(!(camel = propCache[prop])){
7616                     camel = propCache[prop] = prop.replace(camelRe, camelFn);
7617                 }
7618                 
7619                 if(camel == 'opacity') {
7620                     this.setOpacity(value);
7621                 }else{
7622                     this.dom.style[camel] = value;
7623                 }
7624             }else{
7625                 for(var style in prop){
7626                     if(typeof prop[style] != "function"){
7627                        this.setStyle(style, prop[style]);
7628                     }
7629                 }
7630             }
7631             return this;
7632         },
7633
7634         /**
7635          * More flexible version of {@link #setStyle} for setting style properties.
7636          * @param {String/Object/Function} styles A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
7637          * a function which returns such a specification.
7638          * @return {Roo.Element} this
7639          */
7640         applyStyles : function(style){
7641             Roo.DomHelper.applyStyles(this.dom, style);
7642             return this;
7643         },
7644
7645         /**
7646           * 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).
7647           * @return {Number} The X position of the element
7648           */
7649         getX : function(){
7650             return D.getX(this.dom);
7651         },
7652
7653         /**
7654           * 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).
7655           * @return {Number} The Y position of the element
7656           */
7657         getY : function(){
7658             return D.getY(this.dom);
7659         },
7660
7661         /**
7662           * 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).
7663           * @return {Array} The XY position of the element
7664           */
7665         getXY : function(){
7666             return D.getXY(this.dom);
7667         },
7668
7669         /**
7670          * 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).
7671          * @param {Number} The X position of the element
7672          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7673          * @return {Roo.Element} this
7674          */
7675         setX : function(x, animate){
7676             if(!animate || !A){
7677                 D.setX(this.dom, x);
7678             }else{
7679                 this.setXY([x, this.getY()], this.preanim(arguments, 1));
7680             }
7681             return this;
7682         },
7683
7684         /**
7685          * 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).
7686          * @param {Number} The Y position of the element
7687          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7688          * @return {Roo.Element} this
7689          */
7690         setY : function(y, animate){
7691             if(!animate || !A){
7692                 D.setY(this.dom, y);
7693             }else{
7694                 this.setXY([this.getX(), y], this.preanim(arguments, 1));
7695             }
7696             return this;
7697         },
7698
7699         /**
7700          * Sets the element's left position directly using CSS style (instead of {@link #setX}).
7701          * @param {String} left The left CSS property value
7702          * @return {Roo.Element} this
7703          */
7704         setLeft : function(left){
7705             this.setStyle("left", this.addUnits(left));
7706             return this;
7707         },
7708
7709         /**
7710          * Sets the element's top position directly using CSS style (instead of {@link #setY}).
7711          * @param {String} top The top CSS property value
7712          * @return {Roo.Element} this
7713          */
7714         setTop : function(top){
7715             this.setStyle("top", this.addUnits(top));
7716             return this;
7717         },
7718
7719         /**
7720          * Sets the element's CSS right style.
7721          * @param {String} right The right CSS property value
7722          * @return {Roo.Element} this
7723          */
7724         setRight : function(right){
7725             this.setStyle("right", this.addUnits(right));
7726             return this;
7727         },
7728
7729         /**
7730          * Sets the element's CSS bottom style.
7731          * @param {String} bottom The bottom CSS property value
7732          * @return {Roo.Element} this
7733          */
7734         setBottom : function(bottom){
7735             this.setStyle("bottom", this.addUnits(bottom));
7736             return this;
7737         },
7738
7739         /**
7740          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
7741          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7742          * @param {Array} pos Contains X & Y [x, y] values for new position (coordinates are page-based)
7743          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7744          * @return {Roo.Element} this
7745          */
7746         setXY : function(pos, animate){
7747             if(!animate || !A){
7748                 D.setXY(this.dom, pos);
7749             }else{
7750                 this.anim({points: {to: pos}}, this.preanim(arguments, 1), 'motion');
7751             }
7752             return this;
7753         },
7754
7755         /**
7756          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
7757          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7758          * @param {Number} x X value for new position (coordinates are page-based)
7759          * @param {Number} y Y value for new position (coordinates are page-based)
7760          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7761          * @return {Roo.Element} this
7762          */
7763         setLocation : function(x, y, animate){
7764             this.setXY([x, y], this.preanim(arguments, 2));
7765             return this;
7766         },
7767
7768         /**
7769          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
7770          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7771          * @param {Number} x X value for new position (coordinates are page-based)
7772          * @param {Number} y Y value for new position (coordinates are page-based)
7773          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7774          * @return {Roo.Element} this
7775          */
7776         moveTo : function(x, y, animate){
7777             this.setXY([x, y], this.preanim(arguments, 2));
7778             return this;
7779         },
7780
7781         /**
7782          * Returns the region of the given element.
7783          * The element must be part of the DOM tree to have a region (display:none or elements not appended return false).
7784          * @return {Region} A Roo.lib.Region containing "top, left, bottom, right" member data.
7785          */
7786         getRegion : function(){
7787             return D.getRegion(this.dom);
7788         },
7789
7790         /**
7791          * Returns the offset height of the element
7792          * @param {Boolean} contentHeight (optional) true to get the height minus borders and padding
7793          * @return {Number} The element's height
7794          */
7795         getHeight : function(contentHeight){
7796             var h = this.dom.offsetHeight || 0;
7797             return contentHeight !== true ? h : h-this.getBorderWidth("tb")-this.getPadding("tb");
7798         },
7799
7800         /**
7801          * Returns the offset width of the element
7802          * @param {Boolean} contentWidth (optional) true to get the width minus borders and padding
7803          * @return {Number} The element's width
7804          */
7805         getWidth : function(contentWidth){
7806             var w = this.dom.offsetWidth || 0;
7807             return contentWidth !== true ? w : w-this.getBorderWidth("lr")-this.getPadding("lr");
7808         },
7809
7810         /**
7811          * Returns either the offsetHeight or the height of this element based on CSS height adjusted by padding or borders
7812          * when needed to simulate offsetHeight when offsets aren't available. This may not work on display:none elements
7813          * if a height has not been set using CSS.
7814          * @return {Number}
7815          */
7816         getComputedHeight : function(){
7817             var h = Math.max(this.dom.offsetHeight, this.dom.clientHeight);
7818             if(!h){
7819                 h = parseInt(this.getStyle('height'), 10) || 0;
7820                 if(!this.isBorderBox()){
7821                     h += this.getFrameWidth('tb');
7822                 }
7823             }
7824             return h;
7825         },
7826
7827         /**
7828          * Returns either the offsetWidth or the width of this element based on CSS width adjusted by padding or borders
7829          * when needed to simulate offsetWidth when offsets aren't available. This may not work on display:none elements
7830          * if a width has not been set using CSS.
7831          * @return {Number}
7832          */
7833         getComputedWidth : function(){
7834             var w = Math.max(this.dom.offsetWidth, this.dom.clientWidth);
7835             if(!w){
7836                 w = parseInt(this.getStyle('width'), 10) || 0;
7837                 if(!this.isBorderBox()){
7838                     w += this.getFrameWidth('lr');
7839                 }
7840             }
7841             return w;
7842         },
7843
7844         /**
7845          * Returns the size of the element.
7846          * @param {Boolean} contentSize (optional) true to get the width/size minus borders and padding
7847          * @return {Object} An object containing the element's size {width: (element width), height: (element height)}
7848          */
7849         getSize : function(contentSize){
7850             return {width: this.getWidth(contentSize), height: this.getHeight(contentSize)};
7851         },
7852
7853         /**
7854          * Returns the width and height of the viewport.
7855          * @return {Object} An object containing the viewport's size {width: (viewport width), height: (viewport height)}
7856          */
7857         getViewSize : function(){
7858             var d = this.dom, doc = document, aw = 0, ah = 0;
7859             if(d == doc || d == doc.body){
7860                 return {width : D.getViewWidth(), height: D.getViewHeight()};
7861             }else{
7862                 return {
7863                     width : d.clientWidth,
7864                     height: d.clientHeight
7865                 };
7866             }
7867         },
7868
7869         /**
7870          * Returns the value of the "value" attribute
7871          * @param {Boolean} asNumber true to parse the value as a number
7872          * @return {String/Number}
7873          */
7874         getValue : function(asNumber){
7875             return asNumber ? parseInt(this.dom.value, 10) : this.dom.value;
7876         },
7877
7878         // private
7879         adjustWidth : function(width){
7880             if(typeof width == "number"){
7881                 if(this.autoBoxAdjust && !this.isBorderBox()){
7882                    width -= (this.getBorderWidth("lr") + this.getPadding("lr"));
7883                 }
7884                 if(width < 0){
7885                     width = 0;
7886                 }
7887             }
7888             return width;
7889         },
7890
7891         // private
7892         adjustHeight : function(height){
7893             if(typeof height == "number"){
7894                if(this.autoBoxAdjust && !this.isBorderBox()){
7895                    height -= (this.getBorderWidth("tb") + this.getPadding("tb"));
7896                }
7897                if(height < 0){
7898                    height = 0;
7899                }
7900             }
7901             return height;
7902         },
7903
7904         /**
7905          * Set the width of the element
7906          * @param {Number} width The new width
7907          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7908          * @return {Roo.Element} this
7909          */
7910         setWidth : function(width, animate){
7911             width = this.adjustWidth(width);
7912             if(!animate || !A){
7913                 this.dom.style.width = this.addUnits(width);
7914             }else{
7915                 this.anim({width: {to: width}}, this.preanim(arguments, 1));
7916             }
7917             return this;
7918         },
7919
7920         /**
7921          * Set the height of the element
7922          * @param {Number} height The new height
7923          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7924          * @return {Roo.Element} this
7925          */
7926          setHeight : function(height, animate){
7927             height = this.adjustHeight(height);
7928             if(!animate || !A){
7929                 this.dom.style.height = this.addUnits(height);
7930             }else{
7931                 this.anim({height: {to: height}}, this.preanim(arguments, 1));
7932             }
7933             return this;
7934         },
7935
7936         /**
7937          * Set the size of the element. If animation is true, both width an height will be animated concurrently.
7938          * @param {Number} width The new width
7939          * @param {Number} height The new height
7940          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7941          * @return {Roo.Element} this
7942          */
7943          setSize : function(width, height, animate){
7944             if(typeof width == "object"){ // in case of object from getSize()
7945                 height = width.height; width = width.width;
7946             }
7947             width = this.adjustWidth(width); height = this.adjustHeight(height);
7948             if(!animate || !A){
7949                 this.dom.style.width = this.addUnits(width);
7950                 this.dom.style.height = this.addUnits(height);
7951             }else{
7952                 this.anim({width: {to: width}, height: {to: height}}, this.preanim(arguments, 2));
7953             }
7954             return this;
7955         },
7956
7957         /**
7958          * Sets the element's position and size in one shot. If animation is true then width, height, x and y will be animated concurrently.
7959          * @param {Number} x X value for new position (coordinates are page-based)
7960          * @param {Number} y Y value for new position (coordinates are page-based)
7961          * @param {Number} width The new width
7962          * @param {Number} height The new height
7963          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7964          * @return {Roo.Element} this
7965          */
7966         setBounds : function(x, y, width, height, animate){
7967             if(!animate || !A){
7968                 this.setSize(width, height);
7969                 this.setLocation(x, y);
7970             }else{
7971                 width = this.adjustWidth(width); height = this.adjustHeight(height);
7972                 this.anim({points: {to: [x, y]}, width: {to: width}, height: {to: height}},
7973                               this.preanim(arguments, 4), 'motion');
7974             }
7975             return this;
7976         },
7977
7978         /**
7979          * 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.
7980          * @param {Roo.lib.Region} region The region to fill
7981          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7982          * @return {Roo.Element} this
7983          */
7984         setRegion : function(region, animate){
7985             this.setBounds(region.left, region.top, region.right-region.left, region.bottom-region.top, this.preanim(arguments, 1));
7986             return this;
7987         },
7988
7989         /**
7990          * Appends an event handler
7991          *
7992          * @param {String}   eventName     The type of event to append
7993          * @param {Function} fn        The method the event invokes
7994          * @param {Object} scope       (optional) The scope (this object) of the fn
7995          * @param {Object}   options   (optional)An object with standard {@link Roo.EventManager#addListener} options
7996          */
7997         addListener : function(eventName, fn, scope, options){
7998             if (this.dom) {
7999                 Roo.EventManager.on(this.dom,  eventName, fn, scope || this, options);
8000             }
8001         },
8002
8003         /**
8004          * Removes an event handler from this element
8005          * @param {String} eventName the type of event to remove
8006          * @param {Function} fn the method the event invokes
8007          * @return {Roo.Element} this
8008          */
8009         removeListener : function(eventName, fn){
8010             Roo.EventManager.removeListener(this.dom,  eventName, fn);
8011             return this;
8012         },
8013
8014         /**
8015          * Removes all previous added listeners from this element
8016          * @return {Roo.Element} this
8017          */
8018         removeAllListeners : function(){
8019             E.purgeElement(this.dom);
8020             return this;
8021         },
8022
8023         relayEvent : function(eventName, observable){
8024             this.on(eventName, function(e){
8025                 observable.fireEvent(eventName, e);
8026             });
8027         },
8028
8029         /**
8030          * Set the opacity of the element
8031          * @param {Float} opacity The new opacity. 0 = transparent, .5 = 50% visibile, 1 = fully visible, etc
8032          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8033          * @return {Roo.Element} this
8034          */
8035          setOpacity : function(opacity, animate){
8036             if(!animate || !A){
8037                 var s = this.dom.style;
8038                 if(Roo.isIE){
8039                     s.zoom = 1;
8040                     s.filter = (s.filter || '').replace(/alpha\([^\)]*\)/gi,"") +
8041                                (opacity == 1 ? "" : "alpha(opacity=" + opacity * 100 + ")");
8042                 }else{
8043                     s.opacity = opacity;
8044                 }
8045             }else{
8046                 this.anim({opacity: {to: opacity}}, this.preanim(arguments, 1), null, .35, 'easeIn');
8047             }
8048             return this;
8049         },
8050
8051         /**
8052          * Gets the left X coordinate
8053          * @param {Boolean} local True to get the local css position instead of page coordinate
8054          * @return {Number}
8055          */
8056         getLeft : function(local){
8057             if(!local){
8058                 return this.getX();
8059             }else{
8060                 return parseInt(this.getStyle("left"), 10) || 0;
8061             }
8062         },
8063
8064         /**
8065          * Gets the right X coordinate of the element (element X position + element width)
8066          * @param {Boolean} local True to get the local css position instead of page coordinate
8067          * @return {Number}
8068          */
8069         getRight : function(local){
8070             if(!local){
8071                 return this.getX() + this.getWidth();
8072             }else{
8073                 return (this.getLeft(true) + this.getWidth()) || 0;
8074             }
8075         },
8076
8077         /**
8078          * Gets the top Y coordinate
8079          * @param {Boolean} local True to get the local css position instead of page coordinate
8080          * @return {Number}
8081          */
8082         getTop : function(local) {
8083             if(!local){
8084                 return this.getY();
8085             }else{
8086                 return parseInt(this.getStyle("top"), 10) || 0;
8087             }
8088         },
8089
8090         /**
8091          * Gets the bottom Y coordinate of the element (element Y position + element height)
8092          * @param {Boolean} local True to get the local css position instead of page coordinate
8093          * @return {Number}
8094          */
8095         getBottom : function(local){
8096             if(!local){
8097                 return this.getY() + this.getHeight();
8098             }else{
8099                 return (this.getTop(true) + this.getHeight()) || 0;
8100             }
8101         },
8102
8103         /**
8104         * Initializes positioning on this element. If a desired position is not passed, it will make the
8105         * the element positioned relative IF it is not already positioned.
8106         * @param {String} pos (optional) Positioning to use "relative", "absolute" or "fixed"
8107         * @param {Number} zIndex (optional) The zIndex to apply
8108         * @param {Number} x (optional) Set the page X position
8109         * @param {Number} y (optional) Set the page Y position
8110         */
8111         position : function(pos, zIndex, x, y){
8112             if(!pos){
8113                if(this.getStyle('position') == 'static'){
8114                    this.setStyle('position', 'relative');
8115                }
8116             }else{
8117                 this.setStyle("position", pos);
8118             }
8119             if(zIndex){
8120                 this.setStyle("z-index", zIndex);
8121             }
8122             if(x !== undefined && y !== undefined){
8123                 this.setXY([x, y]);
8124             }else if(x !== undefined){
8125                 this.setX(x);
8126             }else if(y !== undefined){
8127                 this.setY(y);
8128             }
8129         },
8130
8131         /**
8132         * Clear positioning back to the default when the document was loaded
8133         * @param {String} value (optional) The value to use for the left,right,top,bottom, defaults to '' (empty string). You could use 'auto'.
8134         * @return {Roo.Element} this
8135          */
8136         clearPositioning : function(value){
8137             value = value ||'';
8138             this.setStyle({
8139                 "left": value,
8140                 "right": value,
8141                 "top": value,
8142                 "bottom": value,
8143                 "z-index": "",
8144                 "position" : "static"
8145             });
8146             return this;
8147         },
8148
8149         /**
8150         * Gets an object with all CSS positioning properties. Useful along with setPostioning to get
8151         * snapshot before performing an update and then restoring the element.
8152         * @return {Object}
8153         */
8154         getPositioning : function(){
8155             var l = this.getStyle("left");
8156             var t = this.getStyle("top");
8157             return {
8158                 "position" : this.getStyle("position"),
8159                 "left" : l,
8160                 "right" : l ? "" : this.getStyle("right"),
8161                 "top" : t,
8162                 "bottom" : t ? "" : this.getStyle("bottom"),
8163                 "z-index" : this.getStyle("z-index")
8164             };
8165         },
8166
8167         /**
8168          * Gets the width of the border(s) for the specified side(s)
8169          * @param {String} side Can be t, l, r, b or any combination of those to add multiple values. For example,
8170          * passing lr would get the border (l)eft width + the border (r)ight width.
8171          * @return {Number} The width of the sides passed added together
8172          */
8173         getBorderWidth : function(side){
8174             return this.addStyles(side, El.borders);
8175         },
8176
8177         /**
8178          * Gets the width of the padding(s) for the specified side(s)
8179          * @param {String} side Can be t, l, r, b or any combination of those to add multiple values. For example,
8180          * passing lr would get the padding (l)eft + the padding (r)ight.
8181          * @return {Number} The padding of the sides passed added together
8182          */
8183         getPadding : function(side){
8184             return this.addStyles(side, El.paddings);
8185         },
8186
8187         /**
8188         * Set positioning with an object returned by getPositioning().
8189         * @param {Object} posCfg
8190         * @return {Roo.Element} this
8191          */
8192         setPositioning : function(pc){
8193             this.applyStyles(pc);
8194             if(pc.right == "auto"){
8195                 this.dom.style.right = "";
8196             }
8197             if(pc.bottom == "auto"){
8198                 this.dom.style.bottom = "";
8199             }
8200             return this;
8201         },
8202
8203         // private
8204         fixDisplay : function(){
8205             if(this.getStyle("display") == "none"){
8206                 this.setStyle("visibility", "hidden");
8207                 this.setStyle("display", this.originalDisplay); // first try reverting to default
8208                 if(this.getStyle("display") == "none"){ // if that fails, default to block
8209                     this.setStyle("display", "block");
8210                 }
8211             }
8212         },
8213
8214         /**
8215          * Quick set left and top adding default units
8216          * @param {String} left The left CSS property value
8217          * @param {String} top The top CSS property value
8218          * @return {Roo.Element} this
8219          */
8220          setLeftTop : function(left, top){
8221             this.dom.style.left = this.addUnits(left);
8222             this.dom.style.top = this.addUnits(top);
8223             return this;
8224         },
8225
8226         /**
8227          * Move this element relative to its current position.
8228          * @param {String} direction Possible values are: "l","left" - "r","right" - "t","top","up" - "b","bottom","down".
8229          * @param {Number} distance How far to move the element in pixels
8230          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8231          * @return {Roo.Element} this
8232          */
8233          move : function(direction, distance, animate){
8234             var xy = this.getXY();
8235             direction = direction.toLowerCase();
8236             switch(direction){
8237                 case "l":
8238                 case "left":
8239                     this.moveTo(xy[0]-distance, xy[1], this.preanim(arguments, 2));
8240                     break;
8241                case "r":
8242                case "right":
8243                     this.moveTo(xy[0]+distance, xy[1], this.preanim(arguments, 2));
8244                     break;
8245                case "t":
8246                case "top":
8247                case "up":
8248                     this.moveTo(xy[0], xy[1]-distance, this.preanim(arguments, 2));
8249                     break;
8250                case "b":
8251                case "bottom":
8252                case "down":
8253                     this.moveTo(xy[0], xy[1]+distance, this.preanim(arguments, 2));
8254                     break;
8255             }
8256             return this;
8257         },
8258
8259         /**
8260          *  Store the current overflow setting and clip overflow on the element - use {@link #unclip} to remove
8261          * @return {Roo.Element} this
8262          */
8263         clip : function(){
8264             if(!this.isClipped){
8265                this.isClipped = true;
8266                this.originalClip = {
8267                    "o": this.getStyle("overflow"),
8268                    "x": this.getStyle("overflow-x"),
8269                    "y": this.getStyle("overflow-y")
8270                };
8271                this.setStyle("overflow", "hidden");
8272                this.setStyle("overflow-x", "hidden");
8273                this.setStyle("overflow-y", "hidden");
8274             }
8275             return this;
8276         },
8277
8278         /**
8279          *  Return clipping (overflow) to original clipping before clip() was called
8280          * @return {Roo.Element} this
8281          */
8282         unclip : function(){
8283             if(this.isClipped){
8284                 this.isClipped = false;
8285                 var o = this.originalClip;
8286                 if(o.o){this.setStyle("overflow", o.o);}
8287                 if(o.x){this.setStyle("overflow-x", o.x);}
8288                 if(o.y){this.setStyle("overflow-y", o.y);}
8289             }
8290             return this;
8291         },
8292
8293
8294         /**
8295          * Gets the x,y coordinates specified by the anchor position on the element.
8296          * @param {String} anchor (optional) The specified anchor position (defaults to "c").  See {@link #alignTo} for details on supported anchor positions.
8297          * @param {Object} size (optional) An object containing the size to use for calculating anchor position
8298          *                       {width: (target width), height: (target height)} (defaults to the element's current size)
8299          * @param {Boolean} local (optional) True to get the local (element top/left-relative) anchor position instead of page coordinates
8300          * @return {Array} [x, y] An array containing the element's x and y coordinates
8301          */
8302         getAnchorXY : function(anchor, local, s){
8303             //Passing a different size is useful for pre-calculating anchors,
8304             //especially for anchored animations that change the el size.
8305
8306             var w, h, vp = false;
8307             if(!s){
8308                 var d = this.dom;
8309                 if(d == document.body || d == document){
8310                     vp = true;
8311                     w = D.getViewWidth(); h = D.getViewHeight();
8312                 }else{
8313                     w = this.getWidth(); h = this.getHeight();
8314                 }
8315             }else{
8316                 w = s.width;  h = s.height;
8317             }
8318             var x = 0, y = 0, r = Math.round;
8319             switch((anchor || "tl").toLowerCase()){
8320                 case "c":
8321                     x = r(w*.5);
8322                     y = r(h*.5);
8323                 break;
8324                 case "t":
8325                     x = r(w*.5);
8326                     y = 0;
8327                 break;
8328                 case "l":
8329                     x = 0;
8330                     y = r(h*.5);
8331                 break;
8332                 case "r":
8333                     x = w;
8334                     y = r(h*.5);
8335                 break;
8336                 case "b":
8337                     x = r(w*.5);
8338                     y = h;
8339                 break;
8340                 case "tl":
8341                     x = 0;
8342                     y = 0;
8343                 break;
8344                 case "bl":
8345                     x = 0;
8346                     y = h;
8347                 break;
8348                 case "br":
8349                     x = w;
8350                     y = h;
8351                 break;
8352                 case "tr":
8353                     x = w;
8354                     y = 0;
8355                 break;
8356             }
8357             if(local === true){
8358                 return [x, y];
8359             }
8360             if(vp){
8361                 var sc = this.getScroll();
8362                 return [x + sc.left, y + sc.top];
8363             }
8364             //Add the element's offset xy
8365             var o = this.getXY();
8366             return [x+o[0], y+o[1]];
8367         },
8368
8369         /**
8370          * Gets the x,y coordinates to align this element with another element. See {@link #alignTo} for more info on the
8371          * supported position values.
8372          * @param {String/HTMLElement/Roo.Element} element The element to align to.
8373          * @param {String} position The position to align to.
8374          * @param {Array} offsets (optional) Offset the positioning by [x, y]
8375          * @return {Array} [x, y]
8376          */
8377         getAlignToXY : function(el, p, o){
8378             el = Roo.get(el);
8379             var d = this.dom;
8380             if(!el.dom){
8381                 throw "Element.alignTo with an element that doesn't exist";
8382             }
8383             var c = false; //constrain to viewport
8384             var p1 = "", p2 = "";
8385             o = o || [0,0];
8386
8387             if(!p){
8388                 p = "tl-bl";
8389             }else if(p == "?"){
8390                 p = "tl-bl?";
8391             }else if(p.indexOf("-") == -1){
8392                 p = "tl-" + p;
8393             }
8394             p = p.toLowerCase();
8395             var m = p.match(/^([a-z]+)-([a-z]+)(\?)?$/);
8396             if(!m){
8397                throw "Element.alignTo with an invalid alignment " + p;
8398             }
8399             p1 = m[1]; p2 = m[2]; c = !!m[3];
8400
8401             //Subtract the aligned el's internal xy from the target's offset xy
8402             //plus custom offset to get the aligned el's new offset xy
8403             var a1 = this.getAnchorXY(p1, true);
8404             var a2 = el.getAnchorXY(p2, false);
8405             var x = a2[0] - a1[0] + o[0];
8406             var y = a2[1] - a1[1] + o[1];
8407             if(c){
8408                 //constrain the aligned el to viewport if necessary
8409                 var w = this.getWidth(), h = this.getHeight(), r = el.getRegion();
8410                 // 5px of margin for ie
8411                 var dw = D.getViewWidth()-5, dh = D.getViewHeight()-5;
8412
8413                 //If we are at a viewport boundary and the aligned el is anchored on a target border that is
8414                 //perpendicular to the vp border, allow the aligned el to slide on that border,
8415                 //otherwise swap the aligned el to the opposite border of the target.
8416                 var p1y = p1.charAt(0), p1x = p1.charAt(p1.length-1);
8417                var p2y = p2.charAt(0), p2x = p2.charAt(p2.length-1);
8418                var swapY = ((p1y=="t" && p2y=="b") || (p1y=="b" && p2y=="t"));
8419                var swapX = ((p1x=="r" && p2x=="l") || (p1x=="l" && p2x=="r"));
8420
8421                var doc = document;
8422                var scrollX = (doc.documentElement.scrollLeft || doc.body.scrollLeft || 0)+5;
8423                var scrollY = (doc.documentElement.scrollTop || doc.body.scrollTop || 0)+5;
8424
8425                if((x+w) > dw + scrollX){
8426                     x = swapX ? r.left-w : dw+scrollX-w;
8427                 }
8428                if(x < scrollX){
8429                    x = swapX ? r.right : scrollX;
8430                }
8431                if((y+h) > dh + scrollY){
8432                     y = swapY ? r.top-h : dh+scrollY-h;
8433                 }
8434                if (y < scrollY){
8435                    y = swapY ? r.bottom : scrollY;
8436                }
8437             }
8438             return [x,y];
8439         },
8440
8441         // private
8442         getConstrainToXY : function(){
8443             var os = {top:0, left:0, bottom:0, right: 0};
8444
8445             return function(el, local, offsets, proposedXY){
8446                 el = Roo.get(el);
8447                 offsets = offsets ? Roo.applyIf(offsets, os) : os;
8448
8449                 var vw, vh, vx = 0, vy = 0;
8450                 if(el.dom == document.body || el.dom == document){
8451                     vw = Roo.lib.Dom.getViewWidth();
8452                     vh = Roo.lib.Dom.getViewHeight();
8453                 }else{
8454                     vw = el.dom.clientWidth;
8455                     vh = el.dom.clientHeight;
8456                     if(!local){
8457                         var vxy = el.getXY();
8458                         vx = vxy[0];
8459                         vy = vxy[1];
8460                     }
8461                 }
8462
8463                 var s = el.getScroll();
8464
8465                 vx += offsets.left + s.left;
8466                 vy += offsets.top + s.top;
8467
8468                 vw -= offsets.right;
8469                 vh -= offsets.bottom;
8470
8471                 var vr = vx+vw;
8472                 var vb = vy+vh;
8473
8474                 var xy = proposedXY || (!local ? this.getXY() : [this.getLeft(true), this.getTop(true)]);
8475                 var x = xy[0], y = xy[1];
8476                 var w = this.dom.offsetWidth, h = this.dom.offsetHeight;
8477
8478                 // only move it if it needs it
8479                 var moved = false;
8480
8481                 // first validate right/bottom
8482                 if((x + w) > vr){
8483                     x = vr - w;
8484                     moved = true;
8485                 }
8486                 if((y + h) > vb){
8487                     y = vb - h;
8488                     moved = true;
8489                 }
8490                 // then make sure top/left isn't negative
8491                 if(x < vx){
8492                     x = vx;
8493                     moved = true;
8494                 }
8495                 if(y < vy){
8496                     y = vy;
8497                     moved = true;
8498                 }
8499                 return moved ? [x, y] : false;
8500             };
8501         }(),
8502
8503         // private
8504         adjustForConstraints : function(xy, parent, offsets){
8505             return this.getConstrainToXY(parent || document, false, offsets, xy) ||  xy;
8506         },
8507
8508         /**
8509          * Aligns this element with another element relative to the specified anchor points. If the other element is the
8510          * document it aligns it to the viewport.
8511          * The position parameter is optional, and can be specified in any one of the following formats:
8512          * <ul>
8513          *   <li><b>Blank</b>: Defaults to aligning the element's top-left corner to the target's bottom-left corner ("tl-bl").</li>
8514          *   <li><b>One anchor (deprecated)</b>: The passed anchor position is used as the target element's anchor point.
8515          *       The element being aligned will position its top-left corner (tl) to that point.  <i>This method has been
8516          *       deprecated in favor of the newer two anchor syntax below</i>.</li>
8517          *   <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
8518          *       element's anchor point, and the second value is used as the target's anchor point.</li>
8519          * </ul>
8520          * In addition to the anchor points, the position parameter also supports the "?" character.  If "?" is passed at the end of
8521          * the position string, the element will attempt to align as specified, but the position will be adjusted to constrain to
8522          * the viewport if necessary.  Note that the element being aligned might be swapped to align to a different position than
8523          * that specified in order to enforce the viewport constraints.
8524          * Following are all of the supported anchor positions:
8525     <pre>
8526     Value  Description
8527     -----  -----------------------------
8528     tl     The top left corner (default)
8529     t      The center of the top edge
8530     tr     The top right corner
8531     l      The center of the left edge
8532     c      In the center of the element
8533     r      The center of the right edge
8534     bl     The bottom left corner
8535     b      The center of the bottom edge
8536     br     The bottom right corner
8537     </pre>
8538     Example Usage:
8539     <pre><code>
8540     // align el to other-el using the default positioning ("tl-bl", non-constrained)
8541     el.alignTo("other-el");
8542
8543     // align the top left corner of el with the top right corner of other-el (constrained to viewport)
8544     el.alignTo("other-el", "tr?");
8545
8546     // align the bottom right corner of el with the center left edge of other-el
8547     el.alignTo("other-el", "br-l?");
8548
8549     // align the center of el with the bottom left corner of other-el and
8550     // adjust the x position by -6 pixels (and the y position by 0)
8551     el.alignTo("other-el", "c-bl", [-6, 0]);
8552     </code></pre>
8553          * @param {String/HTMLElement/Roo.Element} element The element to align to.
8554          * @param {String} position The position to align to.
8555          * @param {Array} offsets (optional) Offset the positioning by [x, y]
8556          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8557          * @return {Roo.Element} this
8558          */
8559         alignTo : function(element, position, offsets, animate){
8560             var xy = this.getAlignToXY(element, position, offsets);
8561             this.setXY(xy, this.preanim(arguments, 3));
8562             return this;
8563         },
8564
8565         /**
8566          * Anchors an element to another element and realigns it when the window is resized.
8567          * @param {String/HTMLElement/Roo.Element} element The element to align to.
8568          * @param {String} position The position to align to.
8569          * @param {Array} offsets (optional) Offset the positioning by [x, y]
8570          * @param {Boolean/Object} animate (optional) True for the default animation or a standard Element animation config object
8571          * @param {Boolean/Number} monitorScroll (optional) True to monitor body scroll and reposition. If this parameter
8572          * is a number, it is used as the buffer delay (defaults to 50ms).
8573          * @param {Function} callback The function to call after the animation finishes
8574          * @return {Roo.Element} this
8575          */
8576         anchorTo : function(el, alignment, offsets, animate, monitorScroll, callback){
8577             var action = function(){
8578                 this.alignTo(el, alignment, offsets, animate);
8579                 Roo.callback(callback, this);
8580             };
8581             Roo.EventManager.onWindowResize(action, this);
8582             var tm = typeof monitorScroll;
8583             if(tm != 'undefined'){
8584                 Roo.EventManager.on(window, 'scroll', action, this,
8585                     {buffer: tm == 'number' ? monitorScroll : 50});
8586             }
8587             action.call(this); // align immediately
8588             return this;
8589         },
8590         /**
8591          * Clears any opacity settings from this element. Required in some cases for IE.
8592          * @return {Roo.Element} this
8593          */
8594         clearOpacity : function(){
8595             if (window.ActiveXObject) {
8596                 if(typeof this.dom.style.filter == 'string' && (/alpha/i).test(this.dom.style.filter)){
8597                     this.dom.style.filter = "";
8598                 }
8599             } else {
8600                 this.dom.style.opacity = "";
8601                 this.dom.style["-moz-opacity"] = "";
8602                 this.dom.style["-khtml-opacity"] = "";
8603             }
8604             return this;
8605         },
8606
8607         /**
8608          * Hide this element - Uses display mode to determine whether to use "display" or "visibility". See {@link #setVisible}.
8609          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8610          * @return {Roo.Element} this
8611          */
8612         hide : function(animate){
8613             this.setVisible(false, this.preanim(arguments, 0));
8614             return this;
8615         },
8616
8617         /**
8618         * Show this element - Uses display mode to determine whether to use "display" or "visibility". See {@link #setVisible}.
8619         * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8620          * @return {Roo.Element} this
8621          */
8622         show : function(animate){
8623             this.setVisible(true, this.preanim(arguments, 0));
8624             return this;
8625         },
8626
8627         /**
8628          * @private Test if size has a unit, otherwise appends the default
8629          */
8630         addUnits : function(size){
8631             return Roo.Element.addUnits(size, this.defaultUnit);
8632         },
8633
8634         /**
8635          * Temporarily enables offsets (width,height,x,y) for an element with display:none, use endMeasure() when done.
8636          * @return {Roo.Element} this
8637          */
8638         beginMeasure : function(){
8639             var el = this.dom;
8640             if(el.offsetWidth || el.offsetHeight){
8641                 return this; // offsets work already
8642             }
8643             var changed = [];
8644             var p = this.dom, b = document.body; // start with this element
8645             while((!el.offsetWidth && !el.offsetHeight) && p && p.tagName && p != b){
8646                 var pe = Roo.get(p);
8647                 if(pe.getStyle('display') == 'none'){
8648                     changed.push({el: p, visibility: pe.getStyle("visibility")});
8649                     p.style.visibility = "hidden";
8650                     p.style.display = "block";
8651                 }
8652                 p = p.parentNode;
8653             }
8654             this._measureChanged = changed;
8655             return this;
8656
8657         },
8658
8659         /**
8660          * Restores displays to before beginMeasure was called
8661          * @return {Roo.Element} this
8662          */
8663         endMeasure : function(){
8664             var changed = this._measureChanged;
8665             if(changed){
8666                 for(var i = 0, len = changed.length; i < len; i++) {
8667                     var r = changed[i];
8668                     r.el.style.visibility = r.visibility;
8669                     r.el.style.display = "none";
8670                 }
8671                 this._measureChanged = null;
8672             }
8673             return this;
8674         },
8675
8676         /**
8677         * Update the innerHTML of this element, optionally searching for and processing scripts
8678         * @param {String} html The new HTML
8679         * @param {Boolean} loadScripts (optional) true to look for and process scripts
8680         * @param {Function} callback For async script loading you can be noticed when the update completes
8681         * @return {Roo.Element} this
8682          */
8683         update : function(html, loadScripts, callback){
8684             if(typeof html == "undefined"){
8685                 html = "";
8686             }
8687             if(loadScripts !== true){
8688                 this.dom.innerHTML = html;
8689                 if(typeof callback == "function"){
8690                     callback();
8691                 }
8692                 return this;
8693             }
8694             var id = Roo.id();
8695             var dom = this.dom;
8696
8697             html += '<span id="' + id + '"></span>';
8698
8699             E.onAvailable(id, function(){
8700                 var hd = document.getElementsByTagName("head")[0];
8701                 var re = /(?:<script([^>]*)?>)((\n|\r|.)*?)(?:<\/script>)/ig;
8702                 var srcRe = /\ssrc=([\'\"])(.*?)\1/i;
8703                 var typeRe = /\stype=([\'\"])(.*?)\1/i;
8704
8705                 var match;
8706                 while(match = re.exec(html)){
8707                     var attrs = match[1];
8708                     var srcMatch = attrs ? attrs.match(srcRe) : false;
8709                     if(srcMatch && srcMatch[2]){
8710                        var s = document.createElement("script");
8711                        s.src = srcMatch[2];
8712                        var typeMatch = attrs.match(typeRe);
8713                        if(typeMatch && typeMatch[2]){
8714                            s.type = typeMatch[2];
8715                        }
8716                        hd.appendChild(s);
8717                     }else if(match[2] && match[2].length > 0){
8718                         if(window.execScript) {
8719                            window.execScript(match[2]);
8720                         } else {
8721                             /**
8722                              * eval:var:id
8723                              * eval:var:dom
8724                              * eval:var:html
8725                              * 
8726                              */
8727                            window.eval(match[2]);
8728                         }
8729                     }
8730                 }
8731                 var el = document.getElementById(id);
8732                 if(el){el.parentNode.removeChild(el);}
8733                 if(typeof callback == "function"){
8734                     callback();
8735                 }
8736             });
8737             dom.innerHTML = html.replace(/(?:<script.*?>)((\n|\r|.)*?)(?:<\/script>)/ig, "");
8738             return this;
8739         },
8740
8741         /**
8742          * Direct access to the UpdateManager update() method (takes the same parameters).
8743          * @param {String/Function} url The url for this request or a function to call to get the url
8744          * @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}
8745          * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
8746          * @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.
8747          * @return {Roo.Element} this
8748          */
8749         load : function(){
8750             var um = this.getUpdateManager();
8751             um.update.apply(um, arguments);
8752             return this;
8753         },
8754
8755         /**
8756         * Gets this element's UpdateManager
8757         * @return {Roo.UpdateManager} The UpdateManager
8758         */
8759         getUpdateManager : function(){
8760             if(!this.updateManager){
8761                 this.updateManager = new Roo.UpdateManager(this);
8762             }
8763             return this.updateManager;
8764         },
8765
8766         /**
8767          * Disables text selection for this element (normalized across browsers)
8768          * @return {Roo.Element} this
8769          */
8770         unselectable : function(){
8771             this.dom.unselectable = "on";
8772             this.swallowEvent("selectstart", true);
8773             this.applyStyles("-moz-user-select:none;-khtml-user-select:none;");
8774             this.addClass("x-unselectable");
8775             return this;
8776         },
8777
8778         /**
8779         * Calculates the x, y to center this element on the screen
8780         * @return {Array} The x, y values [x, y]
8781         */
8782         getCenterXY : function(){
8783             return this.getAlignToXY(document, 'c-c');
8784         },
8785
8786         /**
8787         * Centers the Element in either the viewport, or another Element.
8788         * @param {String/HTMLElement/Roo.Element} centerIn (optional) The element in which to center the element.
8789         */
8790         center : function(centerIn){
8791             this.alignTo(centerIn || document, 'c-c');
8792             return this;
8793         },
8794
8795         /**
8796          * Tests various css rules/browsers to determine if this element uses a border box
8797          * @return {Boolean}
8798          */
8799         isBorderBox : function(){
8800             return noBoxAdjust[this.dom.tagName.toLowerCase()] || Roo.isBorderBox;
8801         },
8802
8803         /**
8804          * Return a box {x, y, width, height} that can be used to set another elements
8805          * size/location to match this element.
8806          * @param {Boolean} contentBox (optional) If true a box for the content of the element is returned.
8807          * @param {Boolean} local (optional) If true the element's left and top are returned instead of page x/y.
8808          * @return {Object} box An object in the format {x, y, width, height}
8809          */
8810         getBox : function(contentBox, local){
8811             var xy;
8812             if(!local){
8813                 xy = this.getXY();
8814             }else{
8815                 var left = parseInt(this.getStyle("left"), 10) || 0;
8816                 var top = parseInt(this.getStyle("top"), 10) || 0;
8817                 xy = [left, top];
8818             }
8819             var el = this.dom, w = el.offsetWidth, h = el.offsetHeight, bx;
8820             if(!contentBox){
8821                 bx = {x: xy[0], y: xy[1], 0: xy[0], 1: xy[1], width: w, height: h};
8822             }else{
8823                 var l = this.getBorderWidth("l")+this.getPadding("l");
8824                 var r = this.getBorderWidth("r")+this.getPadding("r");
8825                 var t = this.getBorderWidth("t")+this.getPadding("t");
8826                 var b = this.getBorderWidth("b")+this.getPadding("b");
8827                 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)};
8828             }
8829             bx.right = bx.x + bx.width;
8830             bx.bottom = bx.y + bx.height;
8831             return bx;
8832         },
8833
8834         /**
8835          * Returns the sum width of the padding and borders for the passed "sides". See getBorderWidth()
8836          for more information about the sides.
8837          * @param {String} sides
8838          * @return {Number}
8839          */
8840         getFrameWidth : function(sides, onlyContentBox){
8841             return onlyContentBox && Roo.isBorderBox ? 0 : (this.getPadding(sides) + this.getBorderWidth(sides));
8842         },
8843
8844         /**
8845          * 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.
8846          * @param {Object} box The box to fill {x, y, width, height}
8847          * @param {Boolean} adjust (optional) Whether to adjust for box-model issues automatically
8848          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8849          * @return {Roo.Element} this
8850          */
8851         setBox : function(box, adjust, animate){
8852             var w = box.width, h = box.height;
8853             if((adjust && !this.autoBoxAdjust) && !this.isBorderBox()){
8854                w -= (this.getBorderWidth("lr") + this.getPadding("lr"));
8855                h -= (this.getBorderWidth("tb") + this.getPadding("tb"));
8856             }
8857             this.setBounds(box.x, box.y, w, h, this.preanim(arguments, 2));
8858             return this;
8859         },
8860
8861         /**
8862          * Forces the browser to repaint this element
8863          * @return {Roo.Element} this
8864          */
8865          repaint : function(){
8866             var dom = this.dom;
8867             this.addClass("x-repaint");
8868             setTimeout(function(){
8869                 Roo.get(dom).removeClass("x-repaint");
8870             }, 1);
8871             return this;
8872         },
8873
8874         /**
8875          * Returns an object with properties top, left, right and bottom representing the margins of this element unless sides is passed,
8876          * then it returns the calculated width of the sides (see getPadding)
8877          * @param {String} sides (optional) Any combination of l, r, t, b to get the sum of those sides
8878          * @return {Object/Number}
8879          */
8880         getMargins : function(side){
8881             if(!side){
8882                 return {
8883                     top: parseInt(this.getStyle("margin-top"), 10) || 0,
8884                     left: parseInt(this.getStyle("margin-left"), 10) || 0,
8885                     bottom: parseInt(this.getStyle("margin-bottom"), 10) || 0,
8886                     right: parseInt(this.getStyle("margin-right"), 10) || 0
8887                 };
8888             }else{
8889                 return this.addStyles(side, El.margins);
8890              }
8891         },
8892
8893         // private
8894         addStyles : function(sides, styles){
8895             var val = 0, v, w;
8896             for(var i = 0, len = sides.length; i < len; i++){
8897                 v = this.getStyle(styles[sides.charAt(i)]);
8898                 if(v){
8899                      w = parseInt(v, 10);
8900                      if(w){ val += w; }
8901                 }
8902             }
8903             return val;
8904         },
8905
8906         /**
8907          * Creates a proxy element of this element
8908          * @param {String/Object} config The class name of the proxy element or a DomHelper config object
8909          * @param {String/HTMLElement} renderTo (optional) The element or element id to render the proxy to (defaults to document.body)
8910          * @param {Boolean} matchBox (optional) True to align and size the proxy to this element now (defaults to false)
8911          * @return {Roo.Element} The new proxy element
8912          */
8913         createProxy : function(config, renderTo, matchBox){
8914             if(renderTo){
8915                 renderTo = Roo.getDom(renderTo);
8916             }else{
8917                 renderTo = document.body;
8918             }
8919             config = typeof config == "object" ?
8920                 config : {tag : "div", cls: config};
8921             var proxy = Roo.DomHelper.append(renderTo, config, true);
8922             if(matchBox){
8923                proxy.setBox(this.getBox());
8924             }
8925             return proxy;
8926         },
8927
8928         /**
8929          * Puts a mask over this element to disable user interaction. Requires core.css.
8930          * This method can only be applied to elements which accept child nodes.
8931          * @param {String} msg (optional) A message to display in the mask
8932          * @param {String} msgCls (optional) A css class to apply to the msg element
8933          * @return {Element} The mask  element
8934          */
8935         mask : function(msg, msgCls)
8936         {
8937             if(this.getStyle("position") == "static"){
8938                 this.setStyle("position", "relative");
8939             }
8940             if(!this._mask){
8941                 this._mask = Roo.DomHelper.append(this.dom, {cls:"roo-el-mask"}, true);
8942             }
8943             this.addClass("x-masked");
8944             this._mask.setDisplayed(true);
8945             
8946             // we wander
8947             var z = 0;
8948             var dom = this.dom
8949             while (dom && dom.style) {
8950                 if (!isNaN(parseInt(dom.style.zIndex))) {
8951                     z = Math.max(z, parseInt(dom.style.zIndex));
8952                 }
8953                 dom = dom.parentNode;
8954             }
8955             // if we are masking the body - then it hides everything..
8956             if (this.dom == document.body) {
8957                 z = 1000000;
8958                 this._mask.setWidth(Roo.lib.Dom.getDocumentWidth());
8959                 this._mask.setHeight(Roo.lib.Dom.getDocumentHeight());
8960             }
8961            
8962             if(typeof msg == 'string'){
8963                 if(!this._maskMsg){
8964                     this._maskMsg = Roo.DomHelper.append(this.dom, {cls:"roo-el-mask-msg", cn:{tag:'div'}}, true);
8965                 }
8966                 var mm = this._maskMsg;
8967                 mm.dom.className = msgCls ? "roo-el-mask-msg " + msgCls : "roo-el-mask-msg";
8968                 mm.dom.firstChild.innerHTML = msg;
8969                 mm.setDisplayed(true);
8970                 mm.center(this);
8971                 mm.setStyle('z-index', z + 102);
8972             }
8973             if(Roo.isIE && !(Roo.isIE7 && Roo.isStrict) && this.getStyle('height') == 'auto'){ // ie will not expand full height automatically
8974                 this._mask.setHeight(this.getHeight());
8975             }
8976             this._mask.setStyle('z-index', z + 100);
8977             
8978             return this._mask;
8979         },
8980
8981         /**
8982          * Removes a previously applied mask. If removeEl is true the mask overlay is destroyed, otherwise
8983          * it is cached for reuse.
8984          */
8985         unmask : function(removeEl){
8986             if(this._mask){
8987                 if(removeEl === true){
8988                     this._mask.remove();
8989                     delete this._mask;
8990                     if(this._maskMsg){
8991                         this._maskMsg.remove();
8992                         delete this._maskMsg;
8993                     }
8994                 }else{
8995                     this._mask.setDisplayed(false);
8996                     if(this._maskMsg){
8997                         this._maskMsg.setDisplayed(false);
8998                     }
8999                 }
9000             }
9001             this.removeClass("x-masked");
9002         },
9003
9004         /**
9005          * Returns true if this element is masked
9006          * @return {Boolean}
9007          */
9008         isMasked : function(){
9009             return this._mask && this._mask.isVisible();
9010         },
9011
9012         /**
9013          * Creates an iframe shim for this element to keep selects and other windowed objects from
9014          * showing through.
9015          * @return {Roo.Element} The new shim element
9016          */
9017         createShim : function(){
9018             var el = document.createElement('iframe');
9019             el.frameBorder = 'no';
9020             el.className = 'roo-shim';
9021             if(Roo.isIE && Roo.isSecure){
9022                 el.src = Roo.SSL_SECURE_URL;
9023             }
9024             var shim = Roo.get(this.dom.parentNode.insertBefore(el, this.dom));
9025             shim.autoBoxAdjust = false;
9026             return shim;
9027         },
9028
9029         /**
9030          * Removes this element from the DOM and deletes it from the cache
9031          */
9032         remove : function(){
9033             if(this.dom.parentNode){
9034                 this.dom.parentNode.removeChild(this.dom);
9035             }
9036             delete El.cache[this.dom.id];
9037         },
9038
9039         /**
9040          * Sets up event handlers to add and remove a css class when the mouse is over this element
9041          * @param {String} className
9042          * @param {Boolean} preventFlicker (optional) If set to true, it prevents flickering by filtering
9043          * mouseout events for children elements
9044          * @return {Roo.Element} this
9045          */
9046         addClassOnOver : function(className, preventFlicker){
9047             this.on("mouseover", function(){
9048                 Roo.fly(this, '_internal').addClass(className);
9049             }, this.dom);
9050             var removeFn = function(e){
9051                 if(preventFlicker !== true || !e.within(this, true)){
9052                     Roo.fly(this, '_internal').removeClass(className);
9053                 }
9054             };
9055             this.on("mouseout", removeFn, this.dom);
9056             return this;
9057         },
9058
9059         /**
9060          * Sets up event handlers to add and remove a css class when this element has the focus
9061          * @param {String} className
9062          * @return {Roo.Element} this
9063          */
9064         addClassOnFocus : function(className){
9065             this.on("focus", function(){
9066                 Roo.fly(this, '_internal').addClass(className);
9067             }, this.dom);
9068             this.on("blur", function(){
9069                 Roo.fly(this, '_internal').removeClass(className);
9070             }, this.dom);
9071             return this;
9072         },
9073         /**
9074          * 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)
9075          * @param {String} className
9076          * @return {Roo.Element} this
9077          */
9078         addClassOnClick : function(className){
9079             var dom = this.dom;
9080             this.on("mousedown", function(){
9081                 Roo.fly(dom, '_internal').addClass(className);
9082                 var d = Roo.get(document);
9083                 var fn = function(){
9084                     Roo.fly(dom, '_internal').removeClass(className);
9085                     d.removeListener("mouseup", fn);
9086                 };
9087                 d.on("mouseup", fn);
9088             });
9089             return this;
9090         },
9091
9092         /**
9093          * Stops the specified event from bubbling and optionally prevents the default action
9094          * @param {String} eventName
9095          * @param {Boolean} preventDefault (optional) true to prevent the default action too
9096          * @return {Roo.Element} this
9097          */
9098         swallowEvent : function(eventName, preventDefault){
9099             var fn = function(e){
9100                 e.stopPropagation();
9101                 if(preventDefault){
9102                     e.preventDefault();
9103                 }
9104             };
9105             if(eventName instanceof Array){
9106                 for(var i = 0, len = eventName.length; i < len; i++){
9107                      this.on(eventName[i], fn);
9108                 }
9109                 return this;
9110             }
9111             this.on(eventName, fn);
9112             return this;
9113         },
9114
9115         /**
9116          * @private
9117          */
9118       fitToParentDelegate : Roo.emptyFn, // keep a reference to the fitToParent delegate
9119
9120         /**
9121          * Sizes this element to its parent element's dimensions performing
9122          * neccessary box adjustments.
9123          * @param {Boolean} monitorResize (optional) If true maintains the fit when the browser window is resized.
9124          * @param {String/HTMLElment/Element} targetParent (optional) The target parent, default to the parentNode.
9125          * @return {Roo.Element} this
9126          */
9127         fitToParent : function(monitorResize, targetParent) {
9128           Roo.EventManager.removeResizeListener(this.fitToParentDelegate); // always remove previous fitToParent delegate from onWindowResize
9129           this.fitToParentDelegate = Roo.emptyFn; // remove reference to previous delegate
9130           if (monitorResize === true && !this.dom.parentNode) { // check if this Element still exists
9131             return;
9132           }
9133           var p = Roo.get(targetParent || this.dom.parentNode);
9134           this.setSize(p.getComputedWidth() - p.getFrameWidth('lr'), p.getComputedHeight() - p.getFrameWidth('tb'));
9135           if (monitorResize === true) {
9136             this.fitToParentDelegate = this.fitToParent.createDelegate(this, [true, targetParent]);
9137             Roo.EventManager.onWindowResize(this.fitToParentDelegate);
9138           }
9139           return this;
9140         },
9141
9142         /**
9143          * Gets the next sibling, skipping text nodes
9144          * @return {HTMLElement} The next sibling or null
9145          */
9146         getNextSibling : function(){
9147             var n = this.dom.nextSibling;
9148             while(n && n.nodeType != 1){
9149                 n = n.nextSibling;
9150             }
9151             return n;
9152         },
9153
9154         /**
9155          * Gets the previous sibling, skipping text nodes
9156          * @return {HTMLElement} The previous sibling or null
9157          */
9158         getPrevSibling : function(){
9159             var n = this.dom.previousSibling;
9160             while(n && n.nodeType != 1){
9161                 n = n.previousSibling;
9162             }
9163             return n;
9164         },
9165
9166
9167         /**
9168          * Appends the passed element(s) to this element
9169          * @param {String/HTMLElement/Array/Element/CompositeElement} el
9170          * @return {Roo.Element} this
9171          */
9172         appendChild: function(el){
9173             el = Roo.get(el);
9174             el.appendTo(this);
9175             return this;
9176         },
9177
9178         /**
9179          * Creates the passed DomHelper config and appends it to this element or optionally inserts it before the passed child element.
9180          * @param {Object} config DomHelper element config object.  If no tag is specified (e.g., {tag:'input'}) then a div will be
9181          * automatically generated with the specified attributes.
9182          * @param {HTMLElement} insertBefore (optional) a child element of this element
9183          * @param {Boolean} returnDom (optional) true to return the dom node instead of creating an Element
9184          * @return {Roo.Element} The new child element
9185          */
9186         createChild: function(config, insertBefore, returnDom){
9187             config = config || {tag:'div'};
9188             if(insertBefore){
9189                 return Roo.DomHelper.insertBefore(insertBefore, config, returnDom !== true);
9190             }
9191             return Roo.DomHelper[!this.dom.firstChild ? 'overwrite' : 'append'](this.dom, config,  returnDom !== true);
9192         },
9193
9194         /**
9195          * Appends this element to the passed element
9196          * @param {String/HTMLElement/Element} el The new parent element
9197          * @return {Roo.Element} this
9198          */
9199         appendTo: function(el){
9200             el = Roo.getDom(el);
9201             el.appendChild(this.dom);
9202             return this;
9203         },
9204
9205         /**
9206          * Inserts this element before the passed element in the DOM
9207          * @param {String/HTMLElement/Element} el The element to insert before
9208          * @return {Roo.Element} this
9209          */
9210         insertBefore: function(el){
9211             el = Roo.getDom(el);
9212             el.parentNode.insertBefore(this.dom, el);
9213             return this;
9214         },
9215
9216         /**
9217          * Inserts this element after the passed element in the DOM
9218          * @param {String/HTMLElement/Element} el The element to insert after
9219          * @return {Roo.Element} this
9220          */
9221         insertAfter: function(el){
9222             el = Roo.getDom(el);
9223             el.parentNode.insertBefore(this.dom, el.nextSibling);
9224             return this;
9225         },
9226
9227         /**
9228          * Inserts (or creates) an element (or DomHelper config) as the first child of the this element
9229          * @param {String/HTMLElement/Element/Object} el The id or element to insert or a DomHelper config to create and insert
9230          * @return {Roo.Element} The new child
9231          */
9232         insertFirst: function(el, returnDom){
9233             el = el || {};
9234             if(typeof el == 'object' && !el.nodeType){ // dh config
9235                 return this.createChild(el, this.dom.firstChild, returnDom);
9236             }else{
9237                 el = Roo.getDom(el);
9238                 this.dom.insertBefore(el, this.dom.firstChild);
9239                 return !returnDom ? Roo.get(el) : el;
9240             }
9241         },
9242
9243         /**
9244          * Inserts (or creates) the passed element (or DomHelper config) as a sibling of this element
9245          * @param {String/HTMLElement/Element/Object} el The id or element to insert or a DomHelper config to create and insert
9246          * @param {String} where (optional) 'before' or 'after' defaults to before
9247          * @param {Boolean} returnDom (optional) True to return the raw DOM element instead of Roo.Element
9248          * @return {Roo.Element} the inserted Element
9249          */
9250         insertSibling: function(el, where, returnDom){
9251             where = where ? where.toLowerCase() : 'before';
9252             el = el || {};
9253             var rt, refNode = where == 'before' ? this.dom : this.dom.nextSibling;
9254
9255             if(typeof el == 'object' && !el.nodeType){ // dh config
9256                 if(where == 'after' && !this.dom.nextSibling){
9257                     rt = Roo.DomHelper.append(this.dom.parentNode, el, !returnDom);
9258                 }else{
9259                     rt = Roo.DomHelper[where == 'after' ? 'insertAfter' : 'insertBefore'](this.dom, el, !returnDom);
9260                 }
9261
9262             }else{
9263                 rt = this.dom.parentNode.insertBefore(Roo.getDom(el),
9264                             where == 'before' ? this.dom : this.dom.nextSibling);
9265                 if(!returnDom){
9266                     rt = Roo.get(rt);
9267                 }
9268             }
9269             return rt;
9270         },
9271
9272         /**
9273          * Creates and wraps this element with another element
9274          * @param {Object} config (optional) DomHelper element config object for the wrapper element or null for an empty div
9275          * @param {Boolean} returnDom (optional) True to return the raw DOM element instead of Roo.Element
9276          * @return {HTMLElement/Element} The newly created wrapper element
9277          */
9278         wrap: function(config, returnDom){
9279             if(!config){
9280                 config = {tag: "div"};
9281             }
9282             var newEl = Roo.DomHelper.insertBefore(this.dom, config, !returnDom);
9283             newEl.dom ? newEl.dom.appendChild(this.dom) : newEl.appendChild(this.dom);
9284             return newEl;
9285         },
9286
9287         /**
9288          * Replaces the passed element with this element
9289          * @param {String/HTMLElement/Element} el The element to replace
9290          * @return {Roo.Element} this
9291          */
9292         replace: function(el){
9293             el = Roo.get(el);
9294             this.insertBefore(el);
9295             el.remove();
9296             return this;
9297         },
9298
9299         /**
9300          * Inserts an html fragment into this element
9301          * @param {String} where Where to insert the html in relation to the this element - beforeBegin, afterBegin, beforeEnd, afterEnd.
9302          * @param {String} html The HTML fragment
9303          * @param {Boolean} returnEl True to return an Roo.Element
9304          * @return {HTMLElement/Roo.Element} The inserted node (or nearest related if more than 1 inserted)
9305          */
9306         insertHtml : function(where, html, returnEl){
9307             var el = Roo.DomHelper.insertHtml(where, this.dom, html);
9308             return returnEl ? Roo.get(el) : el;
9309         },
9310
9311         /**
9312          * Sets the passed attributes as attributes of this element (a style attribute can be a string, object or function)
9313          * @param {Object} o The object with the attributes
9314          * @param {Boolean} useSet (optional) false to override the default setAttribute to use expandos.
9315          * @return {Roo.Element} this
9316          */
9317         set : function(o, useSet){
9318             var el = this.dom;
9319             useSet = typeof useSet == 'undefined' ? (el.setAttribute ? true : false) : useSet;
9320             for(var attr in o){
9321                 if(attr == "style" || typeof o[attr] == "function") continue;
9322                 if(attr=="cls"){
9323                     el.className = o["cls"];
9324                 }else{
9325                     if(useSet) el.setAttribute(attr, o[attr]);
9326                     else el[attr] = o[attr];
9327                 }
9328             }
9329             if(o.style){
9330                 Roo.DomHelper.applyStyles(el, o.style);
9331             }
9332             return this;
9333         },
9334
9335         /**
9336          * Convenience method for constructing a KeyMap
9337          * @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:
9338          *                                  {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
9339          * @param {Function} fn The function to call
9340          * @param {Object} scope (optional) The scope of the function
9341          * @return {Roo.KeyMap} The KeyMap created
9342          */
9343         addKeyListener : function(key, fn, scope){
9344             var config;
9345             if(typeof key != "object" || key instanceof Array){
9346                 config = {
9347                     key: key,
9348                     fn: fn,
9349                     scope: scope
9350                 };
9351             }else{
9352                 config = {
9353                     key : key.key,
9354                     shift : key.shift,
9355                     ctrl : key.ctrl,
9356                     alt : key.alt,
9357                     fn: fn,
9358                     scope: scope
9359                 };
9360             }
9361             return new Roo.KeyMap(this, config);
9362         },
9363
9364         /**
9365          * Creates a KeyMap for this element
9366          * @param {Object} config The KeyMap config. See {@link Roo.KeyMap} for more details
9367          * @return {Roo.KeyMap} The KeyMap created
9368          */
9369         addKeyMap : function(config){
9370             return new Roo.KeyMap(this, config);
9371         },
9372
9373         /**
9374          * Returns true if this element is scrollable.
9375          * @return {Boolean}
9376          */
9377          isScrollable : function(){
9378             var dom = this.dom;
9379             return dom.scrollHeight > dom.clientHeight || dom.scrollWidth > dom.clientWidth;
9380         },
9381
9382         /**
9383          * 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().
9384          * @param {String} side Either "left" for scrollLeft values or "top" for scrollTop values.
9385          * @param {Number} value The new scroll value
9386          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9387          * @return {Element} this
9388          */
9389
9390         scrollTo : function(side, value, animate){
9391             var prop = side.toLowerCase() == "left" ? "scrollLeft" : "scrollTop";
9392             if(!animate || !A){
9393                 this.dom[prop] = value;
9394             }else{
9395                 var to = prop == "scrollLeft" ? [value, this.dom.scrollTop] : [this.dom.scrollLeft, value];
9396                 this.anim({scroll: {"to": to}}, this.preanim(arguments, 2), 'scroll');
9397             }
9398             return this;
9399         },
9400
9401         /**
9402          * Scrolls this element the specified direction. Does bounds checking to make sure the scroll is
9403          * within this element's scrollable range.
9404          * @param {String} direction Possible values are: "l","left" - "r","right" - "t","top","up" - "b","bottom","down".
9405          * @param {Number} distance How far to scroll the element in pixels
9406          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9407          * @return {Boolean} Returns true if a scroll was triggered or false if the element
9408          * was scrolled as far as it could go.
9409          */
9410          scroll : function(direction, distance, animate){
9411              if(!this.isScrollable()){
9412                  return;
9413              }
9414              var el = this.dom;
9415              var l = el.scrollLeft, t = el.scrollTop;
9416              var w = el.scrollWidth, h = el.scrollHeight;
9417              var cw = el.clientWidth, ch = el.clientHeight;
9418              direction = direction.toLowerCase();
9419              var scrolled = false;
9420              var a = this.preanim(arguments, 2);
9421              switch(direction){
9422                  case "l":
9423                  case "left":
9424                      if(w - l > cw){
9425                          var v = Math.min(l + distance, w-cw);
9426                          this.scrollTo("left", v, a);
9427                          scrolled = true;
9428                      }
9429                      break;
9430                 case "r":
9431                 case "right":
9432                      if(l > 0){
9433                          var v = Math.max(l - distance, 0);
9434                          this.scrollTo("left", v, a);
9435                          scrolled = true;
9436                      }
9437                      break;
9438                 case "t":
9439                 case "top":
9440                 case "up":
9441                      if(t > 0){
9442                          var v = Math.max(t - distance, 0);
9443                          this.scrollTo("top", v, a);
9444                          scrolled = true;
9445                      }
9446                      break;
9447                 case "b":
9448                 case "bottom":
9449                 case "down":
9450                      if(h - t > ch){
9451                          var v = Math.min(t + distance, h-ch);
9452                          this.scrollTo("top", v, a);
9453                          scrolled = true;
9454                      }
9455                      break;
9456              }
9457              return scrolled;
9458         },
9459
9460         /**
9461          * Translates the passed page coordinates into left/top css values for this element
9462          * @param {Number/Array} x The page x or an array containing [x, y]
9463          * @param {Number} y The page y
9464          * @return {Object} An object with left and top properties. e.g. {left: (value), top: (value)}
9465          */
9466         translatePoints : function(x, y){
9467             if(typeof x == 'object' || x instanceof Array){
9468                 y = x[1]; x = x[0];
9469             }
9470             var p = this.getStyle('position');
9471             var o = this.getXY();
9472
9473             var l = parseInt(this.getStyle('left'), 10);
9474             var t = parseInt(this.getStyle('top'), 10);
9475
9476             if(isNaN(l)){
9477                 l = (p == "relative") ? 0 : this.dom.offsetLeft;
9478             }
9479             if(isNaN(t)){
9480                 t = (p == "relative") ? 0 : this.dom.offsetTop;
9481             }
9482
9483             return {left: (x - o[0] + l), top: (y - o[1] + t)};
9484         },
9485
9486         /**
9487          * Returns the current scroll position of the element.
9488          * @return {Object} An object containing the scroll position in the format {left: (scrollLeft), top: (scrollTop)}
9489          */
9490         getScroll : function(){
9491             var d = this.dom, doc = document;
9492             if(d == doc || d == doc.body){
9493                 var l = window.pageXOffset || doc.documentElement.scrollLeft || doc.body.scrollLeft || 0;
9494                 var t = window.pageYOffset || doc.documentElement.scrollTop || doc.body.scrollTop || 0;
9495                 return {left: l, top: t};
9496             }else{
9497                 return {left: d.scrollLeft, top: d.scrollTop};
9498             }
9499         },
9500
9501         /**
9502          * Return the CSS color for the specified CSS attribute. rgb, 3 digit (like #fff) and valid values
9503          * are convert to standard 6 digit hex color.
9504          * @param {String} attr The css attribute
9505          * @param {String} defaultValue The default value to use when a valid color isn't found
9506          * @param {String} prefix (optional) defaults to #. Use an empty string when working with
9507          * YUI color anims.
9508          */
9509         getColor : function(attr, defaultValue, prefix){
9510             var v = this.getStyle(attr);
9511             if(!v || v == "transparent" || v == "inherit") {
9512                 return defaultValue;
9513             }
9514             var color = typeof prefix == "undefined" ? "#" : prefix;
9515             if(v.substr(0, 4) == "rgb("){
9516                 var rvs = v.slice(4, v.length -1).split(",");
9517                 for(var i = 0; i < 3; i++){
9518                     var h = parseInt(rvs[i]).toString(16);
9519                     if(h < 16){
9520                         h = "0" + h;
9521                     }
9522                     color += h;
9523                 }
9524             } else {
9525                 if(v.substr(0, 1) == "#"){
9526                     if(v.length == 4) {
9527                         for(var i = 1; i < 4; i++){
9528                             var c = v.charAt(i);
9529                             color +=  c + c;
9530                         }
9531                     }else if(v.length == 7){
9532                         color += v.substr(1);
9533                     }
9534                 }
9535             }
9536             return(color.length > 5 ? color.toLowerCase() : defaultValue);
9537         },
9538
9539         /**
9540          * Wraps the specified element with a special markup/CSS block that renders by default as a gray container with a
9541          * gradient background, rounded corners and a 4-way shadow.
9542          * @param {String} class (optional) A base CSS class to apply to the containing wrapper element (defaults to 'x-box').
9543          * Note that there are a number of CSS rules that are dependent on this name to make the overall effect work,
9544          * so if you supply an alternate base class, make sure you also supply all of the necessary rules.
9545          * @return {Roo.Element} this
9546          */
9547         boxWrap : function(cls){
9548             cls = cls || 'x-box';
9549             var el = Roo.get(this.insertHtml('beforeBegin', String.format('<div class="{0}">'+El.boxMarkup+'</div>', cls)));
9550             el.child('.'+cls+'-mc').dom.appendChild(this.dom);
9551             return el;
9552         },
9553
9554         /**
9555          * Returns the value of a namespaced attribute from the element's underlying DOM node.
9556          * @param {String} namespace The namespace in which to look for the attribute
9557          * @param {String} name The attribute name
9558          * @return {String} The attribute value
9559          */
9560         getAttributeNS : Roo.isIE ? function(ns, name){
9561             var d = this.dom;
9562             var type = typeof d[ns+":"+name];
9563             if(type != 'undefined' && type != 'unknown'){
9564                 return d[ns+":"+name];
9565             }
9566             return d[name];
9567         } : function(ns, name){
9568             var d = this.dom;
9569             return d.getAttributeNS(ns, name) || d.getAttribute(ns+":"+name) || d.getAttribute(name) || d[name];
9570         },
9571         
9572         
9573         /**
9574          * Sets or Returns the value the dom attribute value
9575          * @param {String|Object} name The attribute name (or object to set multiple attributes)
9576          * @param {String} value (optional) The value to set the attribute to
9577          * @return {String} The attribute value
9578          */
9579         attr : function(name){
9580             if (arguments.length > 1) {
9581                 this.dom.setAttribute(name, arguments[1]);
9582                 return arguments[1];
9583             }
9584             if (typeof(name) == 'object') {
9585                 for(var i in name) {
9586                     this.attr(i, name[i]);
9587                 }
9588                 return name;
9589             }
9590             
9591             
9592             if (!this.dom.hasAttribute(name)) {
9593                 return undefined;
9594             }
9595             return this.dom.getAttribute(name);
9596         }
9597         
9598         
9599         
9600     };
9601
9602     var ep = El.prototype;
9603
9604     /**
9605      * Appends an event handler (Shorthand for addListener)
9606      * @param {String}   eventName     The type of event to append
9607      * @param {Function} fn        The method the event invokes
9608      * @param {Object} scope       (optional) The scope (this object) of the fn
9609      * @param {Object}   options   (optional)An object with standard {@link Roo.EventManager#addListener} options
9610      * @method
9611      */
9612     ep.on = ep.addListener;
9613         // backwards compat
9614     ep.mon = ep.addListener;
9615
9616     /**
9617      * Removes an event handler from this element (shorthand for removeListener)
9618      * @param {String} eventName the type of event to remove
9619      * @param {Function} fn the method the event invokes
9620      * @return {Roo.Element} this
9621      * @method
9622      */
9623     ep.un = ep.removeListener;
9624
9625     /**
9626      * true to automatically adjust width and height settings for box-model issues (default to true)
9627      */
9628     ep.autoBoxAdjust = true;
9629
9630     // private
9631     El.unitPattern = /\d+(px|em|%|en|ex|pt|in|cm|mm|pc)$/i;
9632
9633     // private
9634     El.addUnits = function(v, defaultUnit){
9635         if(v === "" || v == "auto"){
9636             return v;
9637         }
9638         if(v === undefined){
9639             return '';
9640         }
9641         if(typeof v == "number" || !El.unitPattern.test(v)){
9642             return v + (defaultUnit || 'px');
9643         }
9644         return v;
9645     };
9646
9647     // special markup used throughout Roo when box wrapping elements
9648     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>';
9649     /**
9650      * Visibility mode constant - Use visibility to hide element
9651      * @static
9652      * @type Number
9653      */
9654     El.VISIBILITY = 1;
9655     /**
9656      * Visibility mode constant - Use display to hide element
9657      * @static
9658      * @type Number
9659      */
9660     El.DISPLAY = 2;
9661
9662     El.borders = {l: "border-left-width", r: "border-right-width", t: "border-top-width", b: "border-bottom-width"};
9663     El.paddings = {l: "padding-left", r: "padding-right", t: "padding-top", b: "padding-bottom"};
9664     El.margins = {l: "margin-left", r: "margin-right", t: "margin-top", b: "margin-bottom"};
9665
9666
9667
9668     /**
9669      * @private
9670      */
9671     El.cache = {};
9672
9673     var docEl;
9674
9675     /**
9676      * Static method to retrieve Element objects. Uses simple caching to consistently return the same object.
9677      * Automatically fixes if an object was recreated with the same id via AJAX or DOM.
9678      * @param {String/HTMLElement/Element} el The id of the node, a DOM Node or an existing Element.
9679      * @return {Element} The Element object
9680      * @static
9681      */
9682     El.get = function(el){
9683         var ex, elm, id;
9684         if(!el){ return null; }
9685         if(typeof el == "string"){ // element id
9686             if(!(elm = document.getElementById(el))){
9687                 return null;
9688             }
9689             if(ex = El.cache[el]){
9690                 ex.dom = elm;
9691             }else{
9692                 ex = El.cache[el] = new El(elm);
9693             }
9694             return ex;
9695         }else if(el.tagName){ // dom element
9696             if(!(id = el.id)){
9697                 id = Roo.id(el);
9698             }
9699             if(ex = El.cache[id]){
9700                 ex.dom = el;
9701             }else{
9702                 ex = El.cache[id] = new El(el);
9703             }
9704             return ex;
9705         }else if(el instanceof El){
9706             if(el != docEl){
9707                 el.dom = document.getElementById(el.id) || el.dom; // refresh dom element in case no longer valid,
9708                                                               // catch case where it hasn't been appended
9709                 El.cache[el.id] = el; // in case it was created directly with Element(), let's cache it
9710             }
9711             return el;
9712         }else if(el.isComposite){
9713             return el;
9714         }else if(el instanceof Array){
9715             return El.select(el);
9716         }else if(el == document){
9717             // create a bogus element object representing the document object
9718             if(!docEl){
9719                 var f = function(){};
9720                 f.prototype = El.prototype;
9721                 docEl = new f();
9722                 docEl.dom = document;
9723             }
9724             return docEl;
9725         }
9726         return null;
9727     };
9728
9729     // private
9730     El.uncache = function(el){
9731         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
9732             if(a[i]){
9733                 delete El.cache[a[i].id || a[i]];
9734             }
9735         }
9736     };
9737
9738     // private
9739     // Garbage collection - uncache elements/purge listeners on orphaned elements
9740     // so we don't hold a reference and cause the browser to retain them
9741     El.garbageCollect = function(){
9742         if(!Roo.enableGarbageCollector){
9743             clearInterval(El.collectorThread);
9744             return;
9745         }
9746         for(var eid in El.cache){
9747             var el = El.cache[eid], d = el.dom;
9748             // -------------------------------------------------------
9749             // Determining what is garbage:
9750             // -------------------------------------------------------
9751             // !d
9752             // dom node is null, definitely garbage
9753             // -------------------------------------------------------
9754             // !d.parentNode
9755             // no parentNode == direct orphan, definitely garbage
9756             // -------------------------------------------------------
9757             // !d.offsetParent && !document.getElementById(eid)
9758             // display none elements have no offsetParent so we will
9759             // also try to look it up by it's id. However, check
9760             // offsetParent first so we don't do unneeded lookups.
9761             // This enables collection of elements that are not orphans
9762             // directly, but somewhere up the line they have an orphan
9763             // parent.
9764             // -------------------------------------------------------
9765             if(!d || !d.parentNode || (!d.offsetParent && !document.getElementById(eid))){
9766                 delete El.cache[eid];
9767                 if(d && Roo.enableListenerCollection){
9768                     E.purgeElement(d);
9769                 }
9770             }
9771         }
9772     }
9773     El.collectorThreadId = setInterval(El.garbageCollect, 30000);
9774
9775
9776     // dom is optional
9777     El.Flyweight = function(dom){
9778         this.dom = dom;
9779     };
9780     El.Flyweight.prototype = El.prototype;
9781
9782     El._flyweights = {};
9783     /**
9784      * Gets the globally shared flyweight Element, with the passed node as the active element. Do not store a reference to this element -
9785      * the dom node can be overwritten by other code.
9786      * @param {String/HTMLElement} el The dom node or id
9787      * @param {String} named (optional) Allows for creation of named reusable flyweights to
9788      *                                  prevent conflicts (e.g. internally Roo uses "_internal")
9789      * @static
9790      * @return {Element} The shared Element object
9791      */
9792     El.fly = function(el, named){
9793         named = named || '_global';
9794         el = Roo.getDom(el);
9795         if(!el){
9796             return null;
9797         }
9798         if(!El._flyweights[named]){
9799             El._flyweights[named] = new El.Flyweight();
9800         }
9801         El._flyweights[named].dom = el;
9802         return El._flyweights[named];
9803     };
9804
9805     /**
9806      * Static method to retrieve Element objects. Uses simple caching to consistently return the same object.
9807      * Automatically fixes if an object was recreated with the same id via AJAX or DOM.
9808      * Shorthand of {@link Roo.Element#get}
9809      * @param {String/HTMLElement/Element} el The id of the node, a DOM Node or an existing Element.
9810      * @return {Element} The Element object
9811      * @member Roo
9812      * @method get
9813      */
9814     Roo.get = El.get;
9815     /**
9816      * Gets the globally shared flyweight Element, with the passed node as the active element. Do not store a reference to this element -
9817      * the dom node can be overwritten by other code.
9818      * Shorthand of {@link Roo.Element#fly}
9819      * @param {String/HTMLElement} el The dom node or id
9820      * @param {String} named (optional) Allows for creation of named reusable flyweights to
9821      *                                  prevent conflicts (e.g. internally Roo uses "_internal")
9822      * @static
9823      * @return {Element} The shared Element object
9824      * @member Roo
9825      * @method fly
9826      */
9827     Roo.fly = El.fly;
9828
9829     // speedy lookup for elements never to box adjust
9830     var noBoxAdjust = Roo.isStrict ? {
9831         select:1
9832     } : {
9833         input:1, select:1, textarea:1
9834     };
9835     if(Roo.isIE || Roo.isGecko){
9836         noBoxAdjust['button'] = 1;
9837     }
9838
9839
9840     Roo.EventManager.on(window, 'unload', function(){
9841         delete El.cache;
9842         delete El._flyweights;
9843     });
9844 })();
9845
9846
9847
9848
9849 if(Roo.DomQuery){
9850     Roo.Element.selectorFunction = Roo.DomQuery.select;
9851 }
9852
9853 Roo.Element.select = function(selector, unique, root){
9854     var els;
9855     if(typeof selector == "string"){
9856         els = Roo.Element.selectorFunction(selector, root);
9857     }else if(selector.length !== undefined){
9858         els = selector;
9859     }else{
9860         throw "Invalid selector";
9861     }
9862     if(unique === true){
9863         return new Roo.CompositeElement(els);
9864     }else{
9865         return new Roo.CompositeElementLite(els);
9866     }
9867 };
9868 /**
9869  * Selects elements based on the passed CSS selector to enable working on them as 1.
9870  * @param {String/Array} selector The CSS selector or an array of elements
9871  * @param {Boolean} unique (optional) true to create a unique Roo.Element for each element (defaults to a shared flyweight object)
9872  * @param {HTMLElement/String} root (optional) The root element of the query or id of the root
9873  * @return {CompositeElementLite/CompositeElement}
9874  * @member Roo
9875  * @method select
9876  */
9877 Roo.select = Roo.Element.select;
9878
9879
9880
9881
9882
9883
9884
9885
9886
9887
9888
9889
9890
9891
9892 /*
9893  * Based on:
9894  * Ext JS Library 1.1.1
9895  * Copyright(c) 2006-2007, Ext JS, LLC.
9896  *
9897  * Originally Released Under LGPL - original licence link has changed is not relivant.
9898  *
9899  * Fork - LGPL
9900  * <script type="text/javascript">
9901  */
9902
9903
9904
9905 //Notifies Element that fx methods are available
9906 Roo.enableFx = true;
9907
9908 /**
9909  * @class Roo.Fx
9910  * <p>A class to provide basic animation and visual effects support.  <b>Note:</b> This class is automatically applied
9911  * to the {@link Roo.Element} interface when included, so all effects calls should be performed via Element.
9912  * Conversely, since the effects are not actually defined in Element, Roo.Fx <b>must</b> be included in order for the 
9913  * Element effects to work.</p><br/>
9914  *
9915  * <p>It is important to note that although the Fx methods and many non-Fx Element methods support "method chaining" in that
9916  * they return the Element object itself as the method return value, it is not always possible to mix the two in a single
9917  * method chain.  The Fx methods use an internal effects queue so that each effect can be properly timed and sequenced.
9918  * Non-Fx methods, on the other hand, have no such internal queueing and will always execute immediately.  For this reason,
9919  * while it may be possible to mix certain Fx and non-Fx method calls in a single chain, it may not always provide the
9920  * expected results and should be done with care.</p><br/>
9921  *
9922  * <p>Motion effects support 8-way anchoring, meaning that you can choose one of 8 different anchor points on the Element
9923  * that will serve as either the start or end point of the animation.  Following are all of the supported anchor positions:</p>
9924 <pre>
9925 Value  Description
9926 -----  -----------------------------
9927 tl     The top left corner
9928 t      The center of the top edge
9929 tr     The top right corner
9930 l      The center of the left edge
9931 r      The center of the right edge
9932 bl     The bottom left corner
9933 b      The center of the bottom edge
9934 br     The bottom right corner
9935 </pre>
9936  * <b>Although some Fx methods accept specific custom config parameters, the ones shown in the Config Options section
9937  * below are common options that can be passed to any Fx method.</b>
9938  * @cfg {Function} callback A function called when the effect is finished
9939  * @cfg {Object} scope The scope of the effect function
9940  * @cfg {String} easing A valid Easing value for the effect
9941  * @cfg {String} afterCls A css class to apply after the effect
9942  * @cfg {Number} duration The length of time (in seconds) that the effect should last
9943  * @cfg {Boolean} remove Whether the Element should be removed from the DOM and destroyed after the effect finishes
9944  * @cfg {Boolean} useDisplay Whether to use the <i>display</i> CSS property instead of <i>visibility</i> when hiding Elements (only applies to 
9945  * effects that end with the element being visually hidden, ignored otherwise)
9946  * @cfg {String/Object/Function} afterStyle A style specification string, e.g. "width:100px", or an object in the form {width:"100px"}, or
9947  * a function which returns such a specification that will be applied to the Element after the effect finishes
9948  * @cfg {Boolean} block Whether the effect should block other effects from queueing while it runs
9949  * @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
9950  * @cfg {Boolean} stopFx Whether subsequent effects should be stopped and removed after the current effect finishes
9951  */
9952 Roo.Fx = {
9953         /**
9954          * Slides the element into view.  An anchor point can be optionally passed to set the point of
9955          * origin for the slide effect.  This function automatically handles wrapping the element with
9956          * a fixed-size container if needed.  See the Fx class overview for valid anchor point options.
9957          * Usage:
9958          *<pre><code>
9959 // default: slide the element in from the top
9960 el.slideIn();
9961
9962 // custom: slide the element in from the right with a 2-second duration
9963 el.slideIn('r', { duration: 2 });
9964
9965 // common config options shown with default values
9966 el.slideIn('t', {
9967     easing: 'easeOut',
9968     duration: .5
9969 });
9970 </code></pre>
9971          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to top: 't')
9972          * @param {Object} options (optional) Object literal with any of the Fx config options
9973          * @return {Roo.Element} The Element
9974          */
9975     slideIn : function(anchor, o){
9976         var el = this.getFxEl();
9977         o = o || {};
9978
9979         el.queueFx(o, function(){
9980
9981             anchor = anchor || "t";
9982
9983             // fix display to visibility
9984             this.fixDisplay();
9985
9986             // restore values after effect
9987             var r = this.getFxRestore();
9988             var b = this.getBox();
9989             // fixed size for slide
9990             this.setSize(b);
9991
9992             // wrap if needed
9993             var wrap = this.fxWrap(r.pos, o, "hidden");
9994
9995             var st = this.dom.style;
9996             st.visibility = "visible";
9997             st.position = "absolute";
9998
9999             // clear out temp styles after slide and unwrap
10000             var after = function(){
10001                 el.fxUnwrap(wrap, r.pos, o);
10002                 st.width = r.width;
10003                 st.height = r.height;
10004                 el.afterFx(o);
10005             };
10006             // time to calc the positions
10007             var a, pt = {to: [b.x, b.y]}, bw = {to: b.width}, bh = {to: b.height};
10008
10009             switch(anchor.toLowerCase()){
10010                 case "t":
10011                     wrap.setSize(b.width, 0);
10012                     st.left = st.bottom = "0";
10013                     a = {height: bh};
10014                 break;
10015                 case "l":
10016                     wrap.setSize(0, b.height);
10017                     st.right = st.top = "0";
10018                     a = {width: bw};
10019                 break;
10020                 case "r":
10021                     wrap.setSize(0, b.height);
10022                     wrap.setX(b.right);
10023                     st.left = st.top = "0";
10024                     a = {width: bw, points: pt};
10025                 break;
10026                 case "b":
10027                     wrap.setSize(b.width, 0);
10028                     wrap.setY(b.bottom);
10029                     st.left = st.top = "0";
10030                     a = {height: bh, points: pt};
10031                 break;
10032                 case "tl":
10033                     wrap.setSize(0, 0);
10034                     st.right = st.bottom = "0";
10035                     a = {width: bw, height: bh};
10036                 break;
10037                 case "bl":
10038                     wrap.setSize(0, 0);
10039                     wrap.setY(b.y+b.height);
10040                     st.right = st.top = "0";
10041                     a = {width: bw, height: bh, points: pt};
10042                 break;
10043                 case "br":
10044                     wrap.setSize(0, 0);
10045                     wrap.setXY([b.right, b.bottom]);
10046                     st.left = st.top = "0";
10047                     a = {width: bw, height: bh, points: pt};
10048                 break;
10049                 case "tr":
10050                     wrap.setSize(0, 0);
10051                     wrap.setX(b.x+b.width);
10052                     st.left = st.bottom = "0";
10053                     a = {width: bw, height: bh, points: pt};
10054                 break;
10055             }
10056             this.dom.style.visibility = "visible";
10057             wrap.show();
10058
10059             arguments.callee.anim = wrap.fxanim(a,
10060                 o,
10061                 'motion',
10062                 .5,
10063                 'easeOut', after);
10064         });
10065         return this;
10066     },
10067     
10068         /**
10069          * Slides the element out of view.  An anchor point can be optionally passed to set the end point
10070          * for the slide effect.  When the effect is completed, the element will be hidden (visibility = 
10071          * 'hidden') but block elements will still take up space in the document.  The element must be removed
10072          * from the DOM using the 'remove' config option if desired.  This function automatically handles 
10073          * wrapping the element with a fixed-size container if needed.  See the Fx class overview for valid anchor point options.
10074          * Usage:
10075          *<pre><code>
10076 // default: slide the element out to the top
10077 el.slideOut();
10078
10079 // custom: slide the element out to the right with a 2-second duration
10080 el.slideOut('r', { duration: 2 });
10081
10082 // common config options shown with default values
10083 el.slideOut('t', {
10084     easing: 'easeOut',
10085     duration: .5,
10086     remove: false,
10087     useDisplay: false
10088 });
10089 </code></pre>
10090          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to top: 't')
10091          * @param {Object} options (optional) Object literal with any of the Fx config options
10092          * @return {Roo.Element} The Element
10093          */
10094     slideOut : function(anchor, o){
10095         var el = this.getFxEl();
10096         o = o || {};
10097
10098         el.queueFx(o, function(){
10099
10100             anchor = anchor || "t";
10101
10102             // restore values after effect
10103             var r = this.getFxRestore();
10104             
10105             var b = this.getBox();
10106             // fixed size for slide
10107             this.setSize(b);
10108
10109             // wrap if needed
10110             var wrap = this.fxWrap(r.pos, o, "visible");
10111
10112             var st = this.dom.style;
10113             st.visibility = "visible";
10114             st.position = "absolute";
10115
10116             wrap.setSize(b);
10117
10118             var after = function(){
10119                 if(o.useDisplay){
10120                     el.setDisplayed(false);
10121                 }else{
10122                     el.hide();
10123                 }
10124
10125                 el.fxUnwrap(wrap, r.pos, o);
10126
10127                 st.width = r.width;
10128                 st.height = r.height;
10129
10130                 el.afterFx(o);
10131             };
10132
10133             var a, zero = {to: 0};
10134             switch(anchor.toLowerCase()){
10135                 case "t":
10136                     st.left = st.bottom = "0";
10137                     a = {height: zero};
10138                 break;
10139                 case "l":
10140                     st.right = st.top = "0";
10141                     a = {width: zero};
10142                 break;
10143                 case "r":
10144                     st.left = st.top = "0";
10145                     a = {width: zero, points: {to:[b.right, b.y]}};
10146                 break;
10147                 case "b":
10148                     st.left = st.top = "0";
10149                     a = {height: zero, points: {to:[b.x, b.bottom]}};
10150                 break;
10151                 case "tl":
10152                     st.right = st.bottom = "0";
10153                     a = {width: zero, height: zero};
10154                 break;
10155                 case "bl":
10156                     st.right = st.top = "0";
10157                     a = {width: zero, height: zero, points: {to:[b.x, b.bottom]}};
10158                 break;
10159                 case "br":
10160                     st.left = st.top = "0";
10161                     a = {width: zero, height: zero, points: {to:[b.x+b.width, b.bottom]}};
10162                 break;
10163                 case "tr":
10164                     st.left = st.bottom = "0";
10165                     a = {width: zero, height: zero, points: {to:[b.right, b.y]}};
10166                 break;
10167             }
10168
10169             arguments.callee.anim = wrap.fxanim(a,
10170                 o,
10171                 'motion',
10172                 .5,
10173                 "easeOut", after);
10174         });
10175         return this;
10176     },
10177
10178         /**
10179          * Fades the element out while slowly expanding it in all directions.  When the effect is completed, the 
10180          * element will be hidden (visibility = 'hidden') but block elements will still take up space in the document. 
10181          * The element must be removed from the DOM using the 'remove' config option if desired.
10182          * Usage:
10183          *<pre><code>
10184 // default
10185 el.puff();
10186
10187 // common config options shown with default values
10188 el.puff({
10189     easing: 'easeOut',
10190     duration: .5,
10191     remove: false,
10192     useDisplay: false
10193 });
10194 </code></pre>
10195          * @param {Object} options (optional) Object literal with any of the Fx config options
10196          * @return {Roo.Element} The Element
10197          */
10198     puff : function(o){
10199         var el = this.getFxEl();
10200         o = o || {};
10201
10202         el.queueFx(o, function(){
10203             this.clearOpacity();
10204             this.show();
10205
10206             // restore values after effect
10207             var r = this.getFxRestore();
10208             var st = this.dom.style;
10209
10210             var after = function(){
10211                 if(o.useDisplay){
10212                     el.setDisplayed(false);
10213                 }else{
10214                     el.hide();
10215                 }
10216
10217                 el.clearOpacity();
10218
10219                 el.setPositioning(r.pos);
10220                 st.width = r.width;
10221                 st.height = r.height;
10222                 st.fontSize = '';
10223                 el.afterFx(o);
10224             };
10225
10226             var width = this.getWidth();
10227             var height = this.getHeight();
10228
10229             arguments.callee.anim = this.fxanim({
10230                     width : {to: this.adjustWidth(width * 2)},
10231                     height : {to: this.adjustHeight(height * 2)},
10232                     points : {by: [-(width * .5), -(height * .5)]},
10233                     opacity : {to: 0},
10234                     fontSize: {to:200, unit: "%"}
10235                 },
10236                 o,
10237                 'motion',
10238                 .5,
10239                 "easeOut", after);
10240         });
10241         return this;
10242     },
10243
10244         /**
10245          * Blinks the element as if it was clicked and then collapses on its center (similar to switching off a television).
10246          * When the effect is completed, the element will be hidden (visibility = 'hidden') but block elements will still 
10247          * take up space in the document. The element must be removed from the DOM using the 'remove' config option if desired.
10248          * Usage:
10249          *<pre><code>
10250 // default
10251 el.switchOff();
10252
10253 // all config options shown with default values
10254 el.switchOff({
10255     easing: 'easeIn',
10256     duration: .3,
10257     remove: false,
10258     useDisplay: false
10259 });
10260 </code></pre>
10261          * @param {Object} options (optional) Object literal with any of the Fx config options
10262          * @return {Roo.Element} The Element
10263          */
10264     switchOff : function(o){
10265         var el = this.getFxEl();
10266         o = o || {};
10267
10268         el.queueFx(o, function(){
10269             this.clearOpacity();
10270             this.clip();
10271
10272             // restore values after effect
10273             var r = this.getFxRestore();
10274             var st = this.dom.style;
10275
10276             var after = function(){
10277                 if(o.useDisplay){
10278                     el.setDisplayed(false);
10279                 }else{
10280                     el.hide();
10281                 }
10282
10283                 el.clearOpacity();
10284                 el.setPositioning(r.pos);
10285                 st.width = r.width;
10286                 st.height = r.height;
10287
10288                 el.afterFx(o);
10289             };
10290
10291             this.fxanim({opacity:{to:0.3}}, null, null, .1, null, function(){
10292                 this.clearOpacity();
10293                 (function(){
10294                     this.fxanim({
10295                         height:{to:1},
10296                         points:{by:[0, this.getHeight() * .5]}
10297                     }, o, 'motion', 0.3, 'easeIn', after);
10298                 }).defer(100, this);
10299             });
10300         });
10301         return this;
10302     },
10303
10304     /**
10305      * Highlights the Element by setting a color (applies to the background-color by default, but can be
10306      * changed using the "attr" config option) and then fading back to the original color. If no original
10307      * color is available, you should provide the "endColor" config option which will be cleared after the animation.
10308      * Usage:
10309 <pre><code>
10310 // default: highlight background to yellow
10311 el.highlight();
10312
10313 // custom: highlight foreground text to blue for 2 seconds
10314 el.highlight("0000ff", { attr: 'color', duration: 2 });
10315
10316 // common config options shown with default values
10317 el.highlight("ffff9c", {
10318     attr: "background-color", //can be any valid CSS property (attribute) that supports a color value
10319     endColor: (current color) or "ffffff",
10320     easing: 'easeIn',
10321     duration: 1
10322 });
10323 </code></pre>
10324      * @param {String} color (optional) The highlight color. Should be a 6 char hex color without the leading # (defaults to yellow: 'ffff9c')
10325      * @param {Object} options (optional) Object literal with any of the Fx config options
10326      * @return {Roo.Element} The Element
10327      */ 
10328     highlight : function(color, o){
10329         var el = this.getFxEl();
10330         o = o || {};
10331
10332         el.queueFx(o, function(){
10333             color = color || "ffff9c";
10334             attr = o.attr || "backgroundColor";
10335
10336             this.clearOpacity();
10337             this.show();
10338
10339             var origColor = this.getColor(attr);
10340             var restoreColor = this.dom.style[attr];
10341             endColor = (o.endColor || origColor) || "ffffff";
10342
10343             var after = function(){
10344                 el.dom.style[attr] = restoreColor;
10345                 el.afterFx(o);
10346             };
10347
10348             var a = {};
10349             a[attr] = {from: color, to: endColor};
10350             arguments.callee.anim = this.fxanim(a,
10351                 o,
10352                 'color',
10353                 1,
10354                 'easeIn', after);
10355         });
10356         return this;
10357     },
10358
10359    /**
10360     * Shows a ripple of exploding, attenuating borders to draw attention to an Element.
10361     * Usage:
10362 <pre><code>
10363 // default: a single light blue ripple
10364 el.frame();
10365
10366 // custom: 3 red ripples lasting 3 seconds total
10367 el.frame("ff0000", 3, { duration: 3 });
10368
10369 // common config options shown with default values
10370 el.frame("C3DAF9", 1, {
10371     duration: 1 //duration of entire animation (not each individual ripple)
10372     // Note: Easing is not configurable and will be ignored if included
10373 });
10374 </code></pre>
10375     * @param {String} color (optional) The color of the border.  Should be a 6 char hex color without the leading # (defaults to light blue: 'C3DAF9').
10376     * @param {Number} count (optional) The number of ripples to display (defaults to 1)
10377     * @param {Object} options (optional) Object literal with any of the Fx config options
10378     * @return {Roo.Element} The Element
10379     */
10380     frame : function(color, count, o){
10381         var el = this.getFxEl();
10382         o = o || {};
10383
10384         el.queueFx(o, function(){
10385             color = color || "#C3DAF9";
10386             if(color.length == 6){
10387                 color = "#" + color;
10388             }
10389             count = count || 1;
10390             duration = o.duration || 1;
10391             this.show();
10392
10393             var b = this.getBox();
10394             var animFn = function(){
10395                 var proxy = this.createProxy({
10396
10397                      style:{
10398                         visbility:"hidden",
10399                         position:"absolute",
10400                         "z-index":"35000", // yee haw
10401                         border:"0px solid " + color
10402                      }
10403                   });
10404                 var scale = Roo.isBorderBox ? 2 : 1;
10405                 proxy.animate({
10406                     top:{from:b.y, to:b.y - 20},
10407                     left:{from:b.x, to:b.x - 20},
10408                     borderWidth:{from:0, to:10},
10409                     opacity:{from:1, to:0},
10410                     height:{from:b.height, to:(b.height + (20*scale))},
10411                     width:{from:b.width, to:(b.width + (20*scale))}
10412                 }, duration, function(){
10413                     proxy.remove();
10414                 });
10415                 if(--count > 0){
10416                      animFn.defer((duration/2)*1000, this);
10417                 }else{
10418                     el.afterFx(o);
10419                 }
10420             };
10421             animFn.call(this);
10422         });
10423         return this;
10424     },
10425
10426    /**
10427     * Creates a pause before any subsequent queued effects begin.  If there are
10428     * no effects queued after the pause it will have no effect.
10429     * Usage:
10430 <pre><code>
10431 el.pause(1);
10432 </code></pre>
10433     * @param {Number} seconds The length of time to pause (in seconds)
10434     * @return {Roo.Element} The Element
10435     */
10436     pause : function(seconds){
10437         var el = this.getFxEl();
10438         var o = {};
10439
10440         el.queueFx(o, function(){
10441             setTimeout(function(){
10442                 el.afterFx(o);
10443             }, seconds * 1000);
10444         });
10445         return this;
10446     },
10447
10448    /**
10449     * Fade an element in (from transparent to opaque).  The ending opacity can be specified
10450     * using the "endOpacity" config option.
10451     * Usage:
10452 <pre><code>
10453 // default: fade in from opacity 0 to 100%
10454 el.fadeIn();
10455
10456 // custom: fade in from opacity 0 to 75% over 2 seconds
10457 el.fadeIn({ endOpacity: .75, duration: 2});
10458
10459 // common config options shown with default values
10460 el.fadeIn({
10461     endOpacity: 1, //can be any value between 0 and 1 (e.g. .5)
10462     easing: 'easeOut',
10463     duration: .5
10464 });
10465 </code></pre>
10466     * @param {Object} options (optional) Object literal with any of the Fx config options
10467     * @return {Roo.Element} The Element
10468     */
10469     fadeIn : function(o){
10470         var el = this.getFxEl();
10471         o = o || {};
10472         el.queueFx(o, function(){
10473             this.setOpacity(0);
10474             this.fixDisplay();
10475             this.dom.style.visibility = 'visible';
10476             var to = o.endOpacity || 1;
10477             arguments.callee.anim = this.fxanim({opacity:{to:to}},
10478                 o, null, .5, "easeOut", function(){
10479                 if(to == 1){
10480                     this.clearOpacity();
10481                 }
10482                 el.afterFx(o);
10483             });
10484         });
10485         return this;
10486     },
10487
10488    /**
10489     * Fade an element out (from opaque to transparent).  The ending opacity can be specified
10490     * using the "endOpacity" config option.
10491     * Usage:
10492 <pre><code>
10493 // default: fade out from the element's current opacity to 0
10494 el.fadeOut();
10495
10496 // custom: fade out from the element's current opacity to 25% over 2 seconds
10497 el.fadeOut({ endOpacity: .25, duration: 2});
10498
10499 // common config options shown with default values
10500 el.fadeOut({
10501     endOpacity: 0, //can be any value between 0 and 1 (e.g. .5)
10502     easing: 'easeOut',
10503     duration: .5
10504     remove: false,
10505     useDisplay: false
10506 });
10507 </code></pre>
10508     * @param {Object} options (optional) Object literal with any of the Fx config options
10509     * @return {Roo.Element} The Element
10510     */
10511     fadeOut : function(o){
10512         var el = this.getFxEl();
10513         o = o || {};
10514         el.queueFx(o, function(){
10515             arguments.callee.anim = this.fxanim({opacity:{to:o.endOpacity || 0}},
10516                 o, null, .5, "easeOut", function(){
10517                 if(this.visibilityMode == Roo.Element.DISPLAY || o.useDisplay){
10518                      this.dom.style.display = "none";
10519                 }else{
10520                      this.dom.style.visibility = "hidden";
10521                 }
10522                 this.clearOpacity();
10523                 el.afterFx(o);
10524             });
10525         });
10526         return this;
10527     },
10528
10529    /**
10530     * Animates the transition of an element's dimensions from a starting height/width
10531     * to an ending height/width.
10532     * Usage:
10533 <pre><code>
10534 // change height and width to 100x100 pixels
10535 el.scale(100, 100);
10536
10537 // common config options shown with default values.  The height and width will default to
10538 // the element's existing values if passed as null.
10539 el.scale(
10540     [element's width],
10541     [element's height], {
10542     easing: 'easeOut',
10543     duration: .35
10544 });
10545 </code></pre>
10546     * @param {Number} width  The new width (pass undefined to keep the original width)
10547     * @param {Number} height  The new height (pass undefined to keep the original height)
10548     * @param {Object} options (optional) Object literal with any of the Fx config options
10549     * @return {Roo.Element} The Element
10550     */
10551     scale : function(w, h, o){
10552         this.shift(Roo.apply({}, o, {
10553             width: w,
10554             height: h
10555         }));
10556         return this;
10557     },
10558
10559    /**
10560     * Animates the transition of any combination of an element's dimensions, xy position and/or opacity.
10561     * Any of these properties not specified in the config object will not be changed.  This effect 
10562     * requires that at least one new dimension, position or opacity setting must be passed in on
10563     * the config object in order for the function to have any effect.
10564     * Usage:
10565 <pre><code>
10566 // slide the element horizontally to x position 200 while changing the height and opacity
10567 el.shift({ x: 200, height: 50, opacity: .8 });
10568
10569 // common config options shown with default values.
10570 el.shift({
10571     width: [element's width],
10572     height: [element's height],
10573     x: [element's x position],
10574     y: [element's y position],
10575     opacity: [element's opacity],
10576     easing: 'easeOut',
10577     duration: .35
10578 });
10579 </code></pre>
10580     * @param {Object} options  Object literal with any of the Fx config options
10581     * @return {Roo.Element} The Element
10582     */
10583     shift : function(o){
10584         var el = this.getFxEl();
10585         o = o || {};
10586         el.queueFx(o, function(){
10587             var a = {}, w = o.width, h = o.height, x = o.x, y = o.y,  op = o.opacity;
10588             if(w !== undefined){
10589                 a.width = {to: this.adjustWidth(w)};
10590             }
10591             if(h !== undefined){
10592                 a.height = {to: this.adjustHeight(h)};
10593             }
10594             if(x !== undefined || y !== undefined){
10595                 a.points = {to: [
10596                     x !== undefined ? x : this.getX(),
10597                     y !== undefined ? y : this.getY()
10598                 ]};
10599             }
10600             if(op !== undefined){
10601                 a.opacity = {to: op};
10602             }
10603             if(o.xy !== undefined){
10604                 a.points = {to: o.xy};
10605             }
10606             arguments.callee.anim = this.fxanim(a,
10607                 o, 'motion', .35, "easeOut", function(){
10608                 el.afterFx(o);
10609             });
10610         });
10611         return this;
10612     },
10613
10614         /**
10615          * Slides the element while fading it out of view.  An anchor point can be optionally passed to set the 
10616          * ending point of the effect.
10617          * Usage:
10618          *<pre><code>
10619 // default: slide the element downward while fading out
10620 el.ghost();
10621
10622 // custom: slide the element out to the right with a 2-second duration
10623 el.ghost('r', { duration: 2 });
10624
10625 // common config options shown with default values
10626 el.ghost('b', {
10627     easing: 'easeOut',
10628     duration: .5
10629     remove: false,
10630     useDisplay: false
10631 });
10632 </code></pre>
10633          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to bottom: 'b')
10634          * @param {Object} options (optional) Object literal with any of the Fx config options
10635          * @return {Roo.Element} The Element
10636          */
10637     ghost : function(anchor, o){
10638         var el = this.getFxEl();
10639         o = o || {};
10640
10641         el.queueFx(o, function(){
10642             anchor = anchor || "b";
10643
10644             // restore values after effect
10645             var r = this.getFxRestore();
10646             var w = this.getWidth(),
10647                 h = this.getHeight();
10648
10649             var st = this.dom.style;
10650
10651             var after = function(){
10652                 if(o.useDisplay){
10653                     el.setDisplayed(false);
10654                 }else{
10655                     el.hide();
10656                 }
10657
10658                 el.clearOpacity();
10659                 el.setPositioning(r.pos);
10660                 st.width = r.width;
10661                 st.height = r.height;
10662
10663                 el.afterFx(o);
10664             };
10665
10666             var a = {opacity: {to: 0}, points: {}}, pt = a.points;
10667             switch(anchor.toLowerCase()){
10668                 case "t":
10669                     pt.by = [0, -h];
10670                 break;
10671                 case "l":
10672                     pt.by = [-w, 0];
10673                 break;
10674                 case "r":
10675                     pt.by = [w, 0];
10676                 break;
10677                 case "b":
10678                     pt.by = [0, h];
10679                 break;
10680                 case "tl":
10681                     pt.by = [-w, -h];
10682                 break;
10683                 case "bl":
10684                     pt.by = [-w, h];
10685                 break;
10686                 case "br":
10687                     pt.by = [w, h];
10688                 break;
10689                 case "tr":
10690                     pt.by = [w, -h];
10691                 break;
10692             }
10693
10694             arguments.callee.anim = this.fxanim(a,
10695                 o,
10696                 'motion',
10697                 .5,
10698                 "easeOut", after);
10699         });
10700         return this;
10701     },
10702
10703         /**
10704          * Ensures that all effects queued after syncFx is called on the element are
10705          * run concurrently.  This is the opposite of {@link #sequenceFx}.
10706          * @return {Roo.Element} The Element
10707          */
10708     syncFx : function(){
10709         this.fxDefaults = Roo.apply(this.fxDefaults || {}, {
10710             block : false,
10711             concurrent : true,
10712             stopFx : false
10713         });
10714         return this;
10715     },
10716
10717         /**
10718          * Ensures that all effects queued after sequenceFx is called on the element are
10719          * run in sequence.  This is the opposite of {@link #syncFx}.
10720          * @return {Roo.Element} The Element
10721          */
10722     sequenceFx : function(){
10723         this.fxDefaults = Roo.apply(this.fxDefaults || {}, {
10724             block : false,
10725             concurrent : false,
10726             stopFx : false
10727         });
10728         return this;
10729     },
10730
10731         /* @private */
10732     nextFx : function(){
10733         var ef = this.fxQueue[0];
10734         if(ef){
10735             ef.call(this);
10736         }
10737     },
10738
10739         /**
10740          * Returns true if the element has any effects actively running or queued, else returns false.
10741          * @return {Boolean} True if element has active effects, else false
10742          */
10743     hasActiveFx : function(){
10744         return this.fxQueue && this.fxQueue[0];
10745     },
10746
10747         /**
10748          * Stops any running effects and clears the element's internal effects queue if it contains
10749          * any additional effects that haven't started yet.
10750          * @return {Roo.Element} The Element
10751          */
10752     stopFx : function(){
10753         if(this.hasActiveFx()){
10754             var cur = this.fxQueue[0];
10755             if(cur && cur.anim && cur.anim.isAnimated()){
10756                 this.fxQueue = [cur]; // clear out others
10757                 cur.anim.stop(true);
10758             }
10759         }
10760         return this;
10761     },
10762
10763         /* @private */
10764     beforeFx : function(o){
10765         if(this.hasActiveFx() && !o.concurrent){
10766            if(o.stopFx){
10767                this.stopFx();
10768                return true;
10769            }
10770            return false;
10771         }
10772         return true;
10773     },
10774
10775         /**
10776          * Returns true if the element is currently blocking so that no other effect can be queued
10777          * until this effect is finished, else returns false if blocking is not set.  This is commonly
10778          * used to ensure that an effect initiated by a user action runs to completion prior to the
10779          * same effect being restarted (e.g., firing only one effect even if the user clicks several times).
10780          * @return {Boolean} True if blocking, else false
10781          */
10782     hasFxBlock : function(){
10783         var q = this.fxQueue;
10784         return q && q[0] && q[0].block;
10785     },
10786
10787         /* @private */
10788     queueFx : function(o, fn){
10789         if(!this.fxQueue){
10790             this.fxQueue = [];
10791         }
10792         if(!this.hasFxBlock()){
10793             Roo.applyIf(o, this.fxDefaults);
10794             if(!o.concurrent){
10795                 var run = this.beforeFx(o);
10796                 fn.block = o.block;
10797                 this.fxQueue.push(fn);
10798                 if(run){
10799                     this.nextFx();
10800                 }
10801             }else{
10802                 fn.call(this);
10803             }
10804         }
10805         return this;
10806     },
10807
10808         /* @private */
10809     fxWrap : function(pos, o, vis){
10810         var wrap;
10811         if(!o.wrap || !(wrap = Roo.get(o.wrap))){
10812             var wrapXY;
10813             if(o.fixPosition){
10814                 wrapXY = this.getXY();
10815             }
10816             var div = document.createElement("div");
10817             div.style.visibility = vis;
10818             wrap = Roo.get(this.dom.parentNode.insertBefore(div, this.dom));
10819             wrap.setPositioning(pos);
10820             if(wrap.getStyle("position") == "static"){
10821                 wrap.position("relative");
10822             }
10823             this.clearPositioning('auto');
10824             wrap.clip();
10825             wrap.dom.appendChild(this.dom);
10826             if(wrapXY){
10827                 wrap.setXY(wrapXY);
10828             }
10829         }
10830         return wrap;
10831     },
10832
10833         /* @private */
10834     fxUnwrap : function(wrap, pos, o){
10835         this.clearPositioning();
10836         this.setPositioning(pos);
10837         if(!o.wrap){
10838             wrap.dom.parentNode.insertBefore(this.dom, wrap.dom);
10839             wrap.remove();
10840         }
10841     },
10842
10843         /* @private */
10844     getFxRestore : function(){
10845         var st = this.dom.style;
10846         return {pos: this.getPositioning(), width: st.width, height : st.height};
10847     },
10848
10849         /* @private */
10850     afterFx : function(o){
10851         if(o.afterStyle){
10852             this.applyStyles(o.afterStyle);
10853         }
10854         if(o.afterCls){
10855             this.addClass(o.afterCls);
10856         }
10857         if(o.remove === true){
10858             this.remove();
10859         }
10860         Roo.callback(o.callback, o.scope, [this]);
10861         if(!o.concurrent){
10862             this.fxQueue.shift();
10863             this.nextFx();
10864         }
10865     },
10866
10867         /* @private */
10868     getFxEl : function(){ // support for composite element fx
10869         return Roo.get(this.dom);
10870     },
10871
10872         /* @private */
10873     fxanim : function(args, opt, animType, defaultDur, defaultEase, cb){
10874         animType = animType || 'run';
10875         opt = opt || {};
10876         var anim = Roo.lib.Anim[animType](
10877             this.dom, args,
10878             (opt.duration || defaultDur) || .35,
10879             (opt.easing || defaultEase) || 'easeOut',
10880             function(){
10881                 Roo.callback(cb, this);
10882             },
10883             this
10884         );
10885         opt.anim = anim;
10886         return anim;
10887     }
10888 };
10889
10890 // backwords compat
10891 Roo.Fx.resize = Roo.Fx.scale;
10892
10893 //When included, Roo.Fx is automatically applied to Element so that all basic
10894 //effects are available directly via the Element API
10895 Roo.apply(Roo.Element.prototype, Roo.Fx);/*
10896  * Based on:
10897  * Ext JS Library 1.1.1
10898  * Copyright(c) 2006-2007, Ext JS, LLC.
10899  *
10900  * Originally Released Under LGPL - original licence link has changed is not relivant.
10901  *
10902  * Fork - LGPL
10903  * <script type="text/javascript">
10904  */
10905
10906
10907 /**
10908  * @class Roo.CompositeElement
10909  * Standard composite class. Creates a Roo.Element for every element in the collection.
10910  * <br><br>
10911  * <b>NOTE: Although they are not listed, this class supports all of the set/update methods of Roo.Element. All Roo.Element
10912  * actions will be performed on all the elements in this collection.</b>
10913  * <br><br>
10914  * All methods return <i>this</i> and can be chained.
10915  <pre><code>
10916  var els = Roo.select("#some-el div.some-class", true);
10917  // or select directly from an existing element
10918  var el = Roo.get('some-el');
10919  el.select('div.some-class', true);
10920
10921  els.setWidth(100); // all elements become 100 width
10922  els.hide(true); // all elements fade out and hide
10923  // or
10924  els.setWidth(100).hide(true);
10925  </code></pre>
10926  */
10927 Roo.CompositeElement = function(els){
10928     this.elements = [];
10929     this.addElements(els);
10930 };
10931 Roo.CompositeElement.prototype = {
10932     isComposite: true,
10933     addElements : function(els){
10934         if(!els) return this;
10935         if(typeof els == "string"){
10936             els = Roo.Element.selectorFunction(els);
10937         }
10938         var yels = this.elements;
10939         var index = yels.length-1;
10940         for(var i = 0, len = els.length; i < len; i++) {
10941                 yels[++index] = Roo.get(els[i]);
10942         }
10943         return this;
10944     },
10945
10946     /**
10947     * Clears this composite and adds the elements returned by the passed selector.
10948     * @param {String/Array} els A string CSS selector, an array of elements or an element
10949     * @return {CompositeElement} this
10950     */
10951     fill : function(els){
10952         this.elements = [];
10953         this.add(els);
10954         return this;
10955     },
10956
10957     /**
10958     * Filters this composite to only elements that match the passed selector.
10959     * @param {String} selector A string CSS selector
10960     * @param {Boolean} inverse return inverse filter (not matches)
10961     * @return {CompositeElement} this
10962     */
10963     filter : function(selector, inverse){
10964         var els = [];
10965         inverse = inverse || false;
10966         this.each(function(el){
10967             var match = inverse ? !el.is(selector) : el.is(selector);
10968             if(match){
10969                 els[els.length] = el.dom;
10970             }
10971         });
10972         this.fill(els);
10973         return this;
10974     },
10975
10976     invoke : function(fn, args){
10977         var els = this.elements;
10978         for(var i = 0, len = els.length; i < len; i++) {
10979                 Roo.Element.prototype[fn].apply(els[i], args);
10980         }
10981         return this;
10982     },
10983     /**
10984     * Adds elements to this composite.
10985     * @param {String/Array} els A string CSS selector, an array of elements or an element
10986     * @return {CompositeElement} this
10987     */
10988     add : function(els){
10989         if(typeof els == "string"){
10990             this.addElements(Roo.Element.selectorFunction(els));
10991         }else if(els.length !== undefined){
10992             this.addElements(els);
10993         }else{
10994             this.addElements([els]);
10995         }
10996         return this;
10997     },
10998     /**
10999     * Calls the passed function passing (el, this, index) for each element in this composite.
11000     * @param {Function} fn The function to call
11001     * @param {Object} scope (optional) The <i>this</i> object (defaults to the element)
11002     * @return {CompositeElement} this
11003     */
11004     each : function(fn, scope){
11005         var els = this.elements;
11006         for(var i = 0, len = els.length; i < len; i++){
11007             if(fn.call(scope || els[i], els[i], this, i) === false) {
11008                 break;
11009             }
11010         }
11011         return this;
11012     },
11013
11014     /**
11015      * Returns the Element object at the specified index
11016      * @param {Number} index
11017      * @return {Roo.Element}
11018      */
11019     item : function(index){
11020         return this.elements[index] || null;
11021     },
11022
11023     /**
11024      * Returns the first Element
11025      * @return {Roo.Element}
11026      */
11027     first : function(){
11028         return this.item(0);
11029     },
11030
11031     /**
11032      * Returns the last Element
11033      * @return {Roo.Element}
11034      */
11035     last : function(){
11036         return this.item(this.elements.length-1);
11037     },
11038
11039     /**
11040      * Returns the number of elements in this composite
11041      * @return Number
11042      */
11043     getCount : function(){
11044         return this.elements.length;
11045     },
11046
11047     /**
11048      * Returns true if this composite contains the passed element
11049      * @return Boolean
11050      */
11051     contains : function(el){
11052         return this.indexOf(el) !== -1;
11053     },
11054
11055     /**
11056      * Returns true if this composite contains the passed element
11057      * @return Boolean
11058      */
11059     indexOf : function(el){
11060         return this.elements.indexOf(Roo.get(el));
11061     },
11062
11063
11064     /**
11065     * Removes the specified element(s).
11066     * @param {Mixed} el The id of an element, the Element itself, the index of the element in this composite
11067     * or an array of any of those.
11068     * @param {Boolean} removeDom (optional) True to also remove the element from the document
11069     * @return {CompositeElement} this
11070     */
11071     removeElement : function(el, removeDom){
11072         if(el instanceof Array){
11073             for(var i = 0, len = el.length; i < len; i++){
11074                 this.removeElement(el[i]);
11075             }
11076             return this;
11077         }
11078         var index = typeof el == 'number' ? el : this.indexOf(el);
11079         if(index !== -1){
11080             if(removeDom){
11081                 var d = this.elements[index];
11082                 if(d.dom){
11083                     d.remove();
11084                 }else{
11085                     d.parentNode.removeChild(d);
11086                 }
11087             }
11088             this.elements.splice(index, 1);
11089         }
11090         return this;
11091     },
11092
11093     /**
11094     * Replaces the specified element with the passed element.
11095     * @param {String/HTMLElement/Element/Number} el The id of an element, the Element itself, the index of the element in this composite
11096     * to replace.
11097     * @param {String/HTMLElement/Element} replacement The id of an element or the Element itself.
11098     * @param {Boolean} domReplace (Optional) True to remove and replace the element in the document too.
11099     * @return {CompositeElement} this
11100     */
11101     replaceElement : function(el, replacement, domReplace){
11102         var index = typeof el == 'number' ? el : this.indexOf(el);
11103         if(index !== -1){
11104             if(domReplace){
11105                 this.elements[index].replaceWith(replacement);
11106             }else{
11107                 this.elements.splice(index, 1, Roo.get(replacement))
11108             }
11109         }
11110         return this;
11111     },
11112
11113     /**
11114      * Removes all elements.
11115      */
11116     clear : function(){
11117         this.elements = [];
11118     }
11119 };
11120 (function(){
11121     Roo.CompositeElement.createCall = function(proto, fnName){
11122         if(!proto[fnName]){
11123             proto[fnName] = function(){
11124                 return this.invoke(fnName, arguments);
11125             };
11126         }
11127     };
11128     for(var fnName in Roo.Element.prototype){
11129         if(typeof Roo.Element.prototype[fnName] == "function"){
11130             Roo.CompositeElement.createCall(Roo.CompositeElement.prototype, fnName);
11131         }
11132     };
11133 })();
11134 /*
11135  * Based on:
11136  * Ext JS Library 1.1.1
11137  * Copyright(c) 2006-2007, Ext JS, LLC.
11138  *
11139  * Originally Released Under LGPL - original licence link has changed is not relivant.
11140  *
11141  * Fork - LGPL
11142  * <script type="text/javascript">
11143  */
11144
11145 /**
11146  * @class Roo.CompositeElementLite
11147  * @extends Roo.CompositeElement
11148  * Flyweight composite class. Reuses the same Roo.Element for element operations.
11149  <pre><code>
11150  var els = Roo.select("#some-el div.some-class");
11151  // or select directly from an existing element
11152  var el = Roo.get('some-el');
11153  el.select('div.some-class');
11154
11155  els.setWidth(100); // all elements become 100 width
11156  els.hide(true); // all elements fade out and hide
11157  // or
11158  els.setWidth(100).hide(true);
11159  </code></pre><br><br>
11160  * <b>NOTE: Although they are not listed, this class supports all of the set/update methods of Roo.Element. All Roo.Element
11161  * actions will be performed on all the elements in this collection.</b>
11162  */
11163 Roo.CompositeElementLite = function(els){
11164     Roo.CompositeElementLite.superclass.constructor.call(this, els);
11165     this.el = new Roo.Element.Flyweight();
11166 };
11167 Roo.extend(Roo.CompositeElementLite, Roo.CompositeElement, {
11168     addElements : function(els){
11169         if(els){
11170             if(els instanceof Array){
11171                 this.elements = this.elements.concat(els);
11172             }else{
11173                 var yels = this.elements;
11174                 var index = yels.length-1;
11175                 for(var i = 0, len = els.length; i < len; i++) {
11176                     yels[++index] = els[i];
11177                 }
11178             }
11179         }
11180         return this;
11181     },
11182     invoke : function(fn, args){
11183         var els = this.elements;
11184         var el = this.el;
11185         for(var i = 0, len = els.length; i < len; i++) {
11186             el.dom = els[i];
11187                 Roo.Element.prototype[fn].apply(el, args);
11188         }
11189         return this;
11190     },
11191     /**
11192      * Returns a flyweight Element of the dom element object at the specified index
11193      * @param {Number} index
11194      * @return {Roo.Element}
11195      */
11196     item : function(index){
11197         if(!this.elements[index]){
11198             return null;
11199         }
11200         this.el.dom = this.elements[index];
11201         return this.el;
11202     },
11203
11204     // fixes scope with flyweight
11205     addListener : function(eventName, handler, scope, opt){
11206         var els = this.elements;
11207         for(var i = 0, len = els.length; i < len; i++) {
11208             Roo.EventManager.on(els[i], eventName, handler, scope || els[i], opt);
11209         }
11210         return this;
11211     },
11212
11213     /**
11214     * Calls the passed function passing (el, this, index) for each element in this composite. <b>The element
11215     * passed is the flyweight (shared) Roo.Element instance, so if you require a
11216     * a reference to the dom node, use el.dom.</b>
11217     * @param {Function} fn The function to call
11218     * @param {Object} scope (optional) The <i>this</i> object (defaults to the element)
11219     * @return {CompositeElement} this
11220     */
11221     each : function(fn, scope){
11222         var els = this.elements;
11223         var el = this.el;
11224         for(var i = 0, len = els.length; i < len; i++){
11225             el.dom = els[i];
11226                 if(fn.call(scope || el, el, this, i) === false){
11227                 break;
11228             }
11229         }
11230         return this;
11231     },
11232
11233     indexOf : function(el){
11234         return this.elements.indexOf(Roo.getDom(el));
11235     },
11236
11237     replaceElement : function(el, replacement, domReplace){
11238         var index = typeof el == 'number' ? el : this.indexOf(el);
11239         if(index !== -1){
11240             replacement = Roo.getDom(replacement);
11241             if(domReplace){
11242                 var d = this.elements[index];
11243                 d.parentNode.insertBefore(replacement, d);
11244                 d.parentNode.removeChild(d);
11245             }
11246             this.elements.splice(index, 1, replacement);
11247         }
11248         return this;
11249     }
11250 });
11251 Roo.CompositeElementLite.prototype.on = Roo.CompositeElementLite.prototype.addListener;
11252
11253 /*
11254  * Based on:
11255  * Ext JS Library 1.1.1
11256  * Copyright(c) 2006-2007, Ext JS, LLC.
11257  *
11258  * Originally Released Under LGPL - original licence link has changed is not relivant.
11259  *
11260  * Fork - LGPL
11261  * <script type="text/javascript">
11262  */
11263
11264  
11265
11266 /**
11267  * @class Roo.data.Connection
11268  * @extends Roo.util.Observable
11269  * The class encapsulates a connection to the page's originating domain, allowing requests to be made
11270  * either to a configured URL, or to a URL specified at request time.<br><br>
11271  * <p>
11272  * Requests made by this class are asynchronous, and will return immediately. No data from
11273  * the server will be available to the statement immediately following the {@link #request} call.
11274  * To process returned data, use a callback in the request options object, or an event listener.</p><br>
11275  * <p>
11276  * Note: If you are doing a file upload, you will not get a normal response object sent back to
11277  * your callback or event handler.  Since the upload is handled via in IFRAME, there is no XMLHttpRequest.
11278  * The response object is created using the innerHTML of the IFRAME's document as the responseText
11279  * property and, if present, the IFRAME's XML document as the responseXML property.</p><br>
11280  * This means that a valid XML or HTML document must be returned. If JSON data is required, it is suggested
11281  * that it be placed either inside a &lt;textarea> in an HTML document and retrieved from the responseText
11282  * using a regex, or inside a CDATA section in an XML document and retrieved from the responseXML using
11283  * standard DOM methods.
11284  * @constructor
11285  * @param {Object} config a configuration object.
11286  */
11287 Roo.data.Connection = function(config){
11288     Roo.apply(this, config);
11289     this.addEvents({
11290         /**
11291          * @event beforerequest
11292          * Fires before a network request is made to retrieve a data object.
11293          * @param {Connection} conn This Connection object.
11294          * @param {Object} options The options config object passed to the {@link #request} method.
11295          */
11296         "beforerequest" : true,
11297         /**
11298          * @event requestcomplete
11299          * Fires if the request was successfully completed.
11300          * @param {Connection} conn This Connection object.
11301          * @param {Object} response The XHR object containing the response data.
11302          * See {@link http://www.w3.org/TR/XMLHttpRequest/} for details.
11303          * @param {Object} options The options config object passed to the {@link #request} method.
11304          */
11305         "requestcomplete" : true,
11306         /**
11307          * @event requestexception
11308          * Fires if an error HTTP status was returned from the server.
11309          * See {@link http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html} for details of HTTP status codes.
11310          * @param {Connection} conn This Connection object.
11311          * @param {Object} response The XHR object containing the response data.
11312          * See {@link http://www.w3.org/TR/XMLHttpRequest/} for details.
11313          * @param {Object} options The options config object passed to the {@link #request} method.
11314          */
11315         "requestexception" : true
11316     });
11317     Roo.data.Connection.superclass.constructor.call(this);
11318 };
11319
11320 Roo.extend(Roo.data.Connection, Roo.util.Observable, {
11321     /**
11322      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
11323      */
11324     /**
11325      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
11326      * extra parameters to each request made by this object. (defaults to undefined)
11327      */
11328     /**
11329      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
11330      *  to each request made by this object. (defaults to undefined)
11331      */
11332     /**
11333      * @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)
11334      */
11335     /**
11336      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
11337      */
11338     timeout : 30000,
11339     /**
11340      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
11341      * @type Boolean
11342      */
11343     autoAbort:false,
11344
11345     /**
11346      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
11347      * @type Boolean
11348      */
11349     disableCaching: true,
11350
11351     /**
11352      * Sends an HTTP request to a remote server.
11353      * @param {Object} options An object which may contain the following properties:<ul>
11354      * <li><b>url</b> {String} (Optional) The URL to which to send the request. Defaults to configured URL</li>
11355      * <li><b>params</b> {Object/String/Function} (Optional) An object containing properties which are used as parameters to the
11356      * request, a url encoded string or a function to call to get either.</li>
11357      * <li><b>method</b> {String} (Optional) The HTTP method to use for the request. Defaults to the configured method, or
11358      * if no method was configured, "GET" if no parameters are being sent, and "POST" if parameters are being sent.</li>
11359      * <li><b>callback</b> {Function} (Optional) The function to be called upon receipt of the HTTP response.
11360      * The callback is called regardless of success or failure and is passed the following parameters:<ul>
11361      * <li>options {Object} The parameter to the request call.</li>
11362      * <li>success {Boolean} True if the request succeeded.</li>
11363      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
11364      * </ul></li>
11365      * <li><b>success</b> {Function} (Optional) The function to be called upon success of the request.
11366      * The callback is passed the following parameters:<ul>
11367      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
11368      * <li>options {Object} The parameter to the request call.</li>
11369      * </ul></li>
11370      * <li><b>failure</b> {Function} (Optional) The function to be called upon failure of the request.
11371      * The callback is passed the following parameters:<ul>
11372      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
11373      * <li>options {Object} The parameter to the request call.</li>
11374      * </ul></li>
11375      * <li><b>scope</b> {Object} (Optional) The scope in which to execute the callbacks: The "this" object
11376      * for the callback function. Defaults to the browser window.</li>
11377      * <li><b>form</b> {Object/String} (Optional) A form object or id to pull parameters from.</li>
11378      * <li><b>isUpload</b> {Boolean} (Optional) True if the form object is a file upload (will usually be automatically detected).</li>
11379      * <li><b>headers</b> {Object} (Optional) Request headers to set for the request.</li>
11380      * <li><b>xmlData</b> {Object} (Optional) XML document to use for the post. Note: This will be used instead of
11381      * params for the post data. Any params will be appended to the URL.</li>
11382      * <li><b>disableCaching</b> {Boolean} (Optional) True to add a unique cache-buster param to GET requests.</li>
11383      * </ul>
11384      * @return {Number} transactionId
11385      */
11386     request : function(o){
11387         if(this.fireEvent("beforerequest", this, o) !== false){
11388             var p = o.params;
11389
11390             if(typeof p == "function"){
11391                 p = p.call(o.scope||window, o);
11392             }
11393             if(typeof p == "object"){
11394                 p = Roo.urlEncode(o.params);
11395             }
11396             if(this.extraParams){
11397                 var extras = Roo.urlEncode(this.extraParams);
11398                 p = p ? (p + '&' + extras) : extras;
11399             }
11400
11401             var url = o.url || this.url;
11402             if(typeof url == 'function'){
11403                 url = url.call(o.scope||window, o);
11404             }
11405
11406             if(o.form){
11407                 var form = Roo.getDom(o.form);
11408                 url = url || form.action;
11409
11410                 var enctype = form.getAttribute("enctype");
11411                 if(o.isUpload || (enctype && enctype.toLowerCase() == 'multipart/form-data')){
11412                     return this.doFormUpload(o, p, url);
11413                 }
11414                 var f = Roo.lib.Ajax.serializeForm(form);
11415                 p = p ? (p + '&' + f) : f;
11416             }
11417
11418             var hs = o.headers;
11419             if(this.defaultHeaders){
11420                 hs = Roo.apply(hs || {}, this.defaultHeaders);
11421                 if(!o.headers){
11422                     o.headers = hs;
11423                 }
11424             }
11425
11426             var cb = {
11427                 success: this.handleResponse,
11428                 failure: this.handleFailure,
11429                 scope: this,
11430                 argument: {options: o},
11431                 timeout : o.timeout || this.timeout
11432             };
11433
11434             var method = o.method||this.method||(p ? "POST" : "GET");
11435
11436             if(method == 'GET' && (this.disableCaching && o.disableCaching !== false) || o.disableCaching === true){
11437                 url += (url.indexOf('?') != -1 ? '&' : '?') + '_dc=' + (new Date().getTime());
11438             }
11439
11440             if(typeof o.autoAbort == 'boolean'){ // options gets top priority
11441                 if(o.autoAbort){
11442                     this.abort();
11443                 }
11444             }else if(this.autoAbort !== false){
11445                 this.abort();
11446             }
11447
11448             if((method == 'GET' && p) || o.xmlData){
11449                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
11450                 p = '';
11451             }
11452             this.transId = Roo.lib.Ajax.request(method, url, cb, p, o);
11453             return this.transId;
11454         }else{
11455             Roo.callback(o.callback, o.scope, [o, null, null]);
11456             return null;
11457         }
11458     },
11459
11460     /**
11461      * Determine whether this object has a request outstanding.
11462      * @param {Number} transactionId (Optional) defaults to the last transaction
11463      * @return {Boolean} True if there is an outstanding request.
11464      */
11465     isLoading : function(transId){
11466         if(transId){
11467             return Roo.lib.Ajax.isCallInProgress(transId);
11468         }else{
11469             return this.transId ? true : false;
11470         }
11471     },
11472
11473     /**
11474      * Aborts any outstanding request.
11475      * @param {Number} transactionId (Optional) defaults to the last transaction
11476      */
11477     abort : function(transId){
11478         if(transId || this.isLoading()){
11479             Roo.lib.Ajax.abort(transId || this.transId);
11480         }
11481     },
11482
11483     // private
11484     handleResponse : function(response){
11485         this.transId = false;
11486         var options = response.argument.options;
11487         response.argument = options ? options.argument : null;
11488         this.fireEvent("requestcomplete", this, response, options);
11489         Roo.callback(options.success, options.scope, [response, options]);
11490         Roo.callback(options.callback, options.scope, [options, true, response]);
11491     },
11492
11493     // private
11494     handleFailure : function(response, e){
11495         this.transId = false;
11496         var options = response.argument.options;
11497         response.argument = options ? options.argument : null;
11498         this.fireEvent("requestexception", this, response, options, e);
11499         Roo.callback(options.failure, options.scope, [response, options]);
11500         Roo.callback(options.callback, options.scope, [options, false, response]);
11501     },
11502
11503     // private
11504     doFormUpload : function(o, ps, url){
11505         var id = Roo.id();
11506         var frame = document.createElement('iframe');
11507         frame.id = id;
11508         frame.name = id;
11509         frame.className = 'x-hidden';
11510         if(Roo.isIE){
11511             frame.src = Roo.SSL_SECURE_URL;
11512         }
11513         document.body.appendChild(frame);
11514
11515         if(Roo.isIE){
11516            document.frames[id].name = id;
11517         }
11518
11519         var form = Roo.getDom(o.form);
11520         form.target = id;
11521         form.method = 'POST';
11522         form.enctype = form.encoding = 'multipart/form-data';
11523         if(url){
11524             form.action = url;
11525         }
11526
11527         var hiddens, hd;
11528         if(ps){ // add dynamic params
11529             hiddens = [];
11530             ps = Roo.urlDecode(ps, false);
11531             for(var k in ps){
11532                 if(ps.hasOwnProperty(k)){
11533                     hd = document.createElement('input');
11534                     hd.type = 'hidden';
11535                     hd.name = k;
11536                     hd.value = ps[k];
11537                     form.appendChild(hd);
11538                     hiddens.push(hd);
11539                 }
11540             }
11541         }
11542
11543         function cb(){
11544             var r = {  // bogus response object
11545                 responseText : '',
11546                 responseXML : null
11547             };
11548
11549             r.argument = o ? o.argument : null;
11550
11551             try { //
11552                 var doc;
11553                 if(Roo.isIE){
11554                     doc = frame.contentWindow.document;
11555                 }else {
11556                     doc = (frame.contentDocument || window.frames[id].document);
11557                 }
11558                 if(doc && doc.body){
11559                     r.responseText = doc.body.innerHTML;
11560                 }
11561                 if(doc && doc.XMLDocument){
11562                     r.responseXML = doc.XMLDocument;
11563                 }else {
11564                     r.responseXML = doc;
11565                 }
11566             }
11567             catch(e) {
11568                 // ignore
11569             }
11570
11571             Roo.EventManager.removeListener(frame, 'load', cb, this);
11572
11573             this.fireEvent("requestcomplete", this, r, o);
11574             Roo.callback(o.success, o.scope, [r, o]);
11575             Roo.callback(o.callback, o.scope, [o, true, r]);
11576
11577             setTimeout(function(){document.body.removeChild(frame);}, 100);
11578         }
11579
11580         Roo.EventManager.on(frame, 'load', cb, this);
11581         form.submit();
11582
11583         if(hiddens){ // remove dynamic params
11584             for(var i = 0, len = hiddens.length; i < len; i++){
11585                 form.removeChild(hiddens[i]);
11586             }
11587         }
11588     }
11589 });
11590 /*
11591  * Based on:
11592  * Ext JS Library 1.1.1
11593  * Copyright(c) 2006-2007, Ext JS, LLC.
11594  *
11595  * Originally Released Under LGPL - original licence link has changed is not relivant.
11596  *
11597  * Fork - LGPL
11598  * <script type="text/javascript">
11599  */
11600  
11601 /**
11602  * Global Ajax request class.
11603  * 
11604  * @class Roo.Ajax
11605  * @extends Roo.data.Connection
11606  * @static
11607  * 
11608  * @cfg {String} url  The default URL to be used for requests to the server. (defaults to undefined)
11609  * @cfg {Object} extraParams  An object containing properties which are used as extra parameters to each request made by this object. (defaults to undefined)
11610  * @cfg {Object} defaultHeaders  An object containing request headers which are added to each request made by this object. (defaults to undefined)
11611  * @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)
11612  * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
11613  * @cfg {Boolean} autoAbort (Optional) Whether a new request should abort any pending requests. (defaults to false)
11614  * @cfg {Boolean} disableCaching (Optional)   True to add a unique cache-buster param to GET requests. (defaults to true)
11615  */
11616 Roo.Ajax = new Roo.data.Connection({
11617     // fix up the docs
11618     /**
11619      * @scope Roo.Ajax
11620      * @type {Boolear} 
11621      */
11622     autoAbort : false,
11623
11624     /**
11625      * Serialize the passed form into a url encoded string
11626      * @scope Roo.Ajax
11627      * @param {String/HTMLElement} form
11628      * @return {String}
11629      */
11630     serializeForm : function(form){
11631         return Roo.lib.Ajax.serializeForm(form);
11632     }
11633 });/*
11634  * Based on:
11635  * Ext JS Library 1.1.1
11636  * Copyright(c) 2006-2007, Ext JS, LLC.
11637  *
11638  * Originally Released Under LGPL - original licence link has changed is not relivant.
11639  *
11640  * Fork - LGPL
11641  * <script type="text/javascript">
11642  */
11643
11644  
11645 /**
11646  * @class Roo.UpdateManager
11647  * @extends Roo.util.Observable
11648  * Provides AJAX-style update for Element object.<br><br>
11649  * Usage:<br>
11650  * <pre><code>
11651  * // Get it from a Roo.Element object
11652  * var el = Roo.get("foo");
11653  * var mgr = el.getUpdateManager();
11654  * mgr.update("http://myserver.com/index.php", "param1=1&amp;param2=2");
11655  * ...
11656  * mgr.formUpdate("myFormId", "http://myserver.com/index.php");
11657  * <br>
11658  * // or directly (returns the same UpdateManager instance)
11659  * var mgr = new Roo.UpdateManager("myElementId");
11660  * mgr.startAutoRefresh(60, "http://myserver.com/index.php");
11661  * mgr.on("update", myFcnNeedsToKnow);
11662  * <br>
11663    // short handed call directly from the element object
11664    Roo.get("foo").load({
11665         url: "bar.php",
11666         scripts:true,
11667         params: "for=bar",
11668         text: "Loading Foo..."
11669    });
11670  * </code></pre>
11671  * @constructor
11672  * Create new UpdateManager directly.
11673  * @param {String/HTMLElement/Roo.Element} el The element to update
11674  * @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).
11675  */
11676 Roo.UpdateManager = function(el, forceNew){
11677     el = Roo.get(el);
11678     if(!forceNew && el.updateManager){
11679         return el.updateManager;
11680     }
11681     /**
11682      * The Element object
11683      * @type Roo.Element
11684      */
11685     this.el = el;
11686     /**
11687      * Cached url to use for refreshes. Overwritten every time update() is called unless "discardUrl" param is set to true.
11688      * @type String
11689      */
11690     this.defaultUrl = null;
11691
11692     this.addEvents({
11693         /**
11694          * @event beforeupdate
11695          * Fired before an update is made, return false from your handler and the update is cancelled.
11696          * @param {Roo.Element} el
11697          * @param {String/Object/Function} url
11698          * @param {String/Object} params
11699          */
11700         "beforeupdate": true,
11701         /**
11702          * @event update
11703          * Fired after successful update is made.
11704          * @param {Roo.Element} el
11705          * @param {Object} oResponseObject The response Object
11706          */
11707         "update": true,
11708         /**
11709          * @event failure
11710          * Fired on update failure.
11711          * @param {Roo.Element} el
11712          * @param {Object} oResponseObject The response Object
11713          */
11714         "failure": true
11715     });
11716     var d = Roo.UpdateManager.defaults;
11717     /**
11718      * Blank page URL to use with SSL file uploads (Defaults to Roo.UpdateManager.defaults.sslBlankUrl or "about:blank").
11719      * @type String
11720      */
11721     this.sslBlankUrl = d.sslBlankUrl;
11722     /**
11723      * Whether to append unique parameter on get request to disable caching (Defaults to Roo.UpdateManager.defaults.disableCaching or false).
11724      * @type Boolean
11725      */
11726     this.disableCaching = d.disableCaching;
11727     /**
11728      * Text for loading indicator (Defaults to Roo.UpdateManager.defaults.indicatorText or '&lt;div class="loading-indicator"&gt;Loading...&lt;/div&gt;').
11729      * @type String
11730      */
11731     this.indicatorText = d.indicatorText;
11732     /**
11733      * Whether to show indicatorText when loading (Defaults to Roo.UpdateManager.defaults.showLoadIndicator or true).
11734      * @type String
11735      */
11736     this.showLoadIndicator = d.showLoadIndicator;
11737     /**
11738      * Timeout for requests or form posts in seconds (Defaults to Roo.UpdateManager.defaults.timeout or 30 seconds).
11739      * @type Number
11740      */
11741     this.timeout = d.timeout;
11742
11743     /**
11744      * True to process scripts in the output (Defaults to Roo.UpdateManager.defaults.loadScripts (false)).
11745      * @type Boolean
11746      */
11747     this.loadScripts = d.loadScripts;
11748
11749     /**
11750      * Transaction object of current executing transaction
11751      */
11752     this.transaction = null;
11753
11754     /**
11755      * @private
11756      */
11757     this.autoRefreshProcId = null;
11758     /**
11759      * Delegate for refresh() prebound to "this", use myUpdater.refreshDelegate.createCallback(arg1, arg2) to bind arguments
11760      * @type Function
11761      */
11762     this.refreshDelegate = this.refresh.createDelegate(this);
11763     /**
11764      * Delegate for update() prebound to "this", use myUpdater.updateDelegate.createCallback(arg1, arg2) to bind arguments
11765      * @type Function
11766      */
11767     this.updateDelegate = this.update.createDelegate(this);
11768     /**
11769      * Delegate for formUpdate() prebound to "this", use myUpdater.formUpdateDelegate.createCallback(arg1, arg2) to bind arguments
11770      * @type Function
11771      */
11772     this.formUpdateDelegate = this.formUpdate.createDelegate(this);
11773     /**
11774      * @private
11775      */
11776     this.successDelegate = this.processSuccess.createDelegate(this);
11777     /**
11778      * @private
11779      */
11780     this.failureDelegate = this.processFailure.createDelegate(this);
11781
11782     if(!this.renderer){
11783      /**
11784       * The renderer for this UpdateManager. Defaults to {@link Roo.UpdateManager.BasicRenderer}.
11785       */
11786     this.renderer = new Roo.UpdateManager.BasicRenderer();
11787     }
11788     
11789     Roo.UpdateManager.superclass.constructor.call(this);
11790 };
11791
11792 Roo.extend(Roo.UpdateManager, Roo.util.Observable, {
11793     /**
11794      * Get the Element this UpdateManager is bound to
11795      * @return {Roo.Element} The element
11796      */
11797     getEl : function(){
11798         return this.el;
11799     },
11800     /**
11801      * Performs an async request, updating this element with the response. If params are specified it uses POST, otherwise it uses GET.
11802      * @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:
11803 <pre><code>
11804 um.update({<br/>
11805     url: "your-url.php",<br/>
11806     params: {param1: "foo", param2: "bar"}, // or a URL encoded string<br/>
11807     callback: yourFunction,<br/>
11808     scope: yourObject, //(optional scope)  <br/>
11809     discardUrl: false, <br/>
11810     nocache: false,<br/>
11811     text: "Loading...",<br/>
11812     timeout: 30,<br/>
11813     scripts: false<br/>
11814 });
11815 </code></pre>
11816      * The only required property is url. The optional properties nocache, text and scripts
11817      * are shorthand for disableCaching, indicatorText and loadScripts and are used to set their associated property on this UpdateManager instance.
11818      * @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}
11819      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess, oResponse)
11820      * @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.
11821      */
11822     update : function(url, params, callback, discardUrl){
11823         if(this.fireEvent("beforeupdate", this.el, url, params) !== false){
11824             var method = this.method,
11825                 cfg;
11826             if(typeof url == "object"){ // must be config object
11827                 cfg = url;
11828                 url = cfg.url;
11829                 params = params || cfg.params;
11830                 callback = callback || cfg.callback;
11831                 discardUrl = discardUrl || cfg.discardUrl;
11832                 if(callback && cfg.scope){
11833                     callback = callback.createDelegate(cfg.scope);
11834                 }
11835                 if(typeof cfg.method != "undefined"){method = cfg.method;};
11836                 if(typeof cfg.nocache != "undefined"){this.disableCaching = cfg.nocache;};
11837                 if(typeof cfg.text != "undefined"){this.indicatorText = '<div class="loading-indicator">'+cfg.text+"</div>";};
11838                 if(typeof cfg.scripts != "undefined"){this.loadScripts = cfg.scripts;};
11839                 if(typeof cfg.timeout != "undefined"){this.timeout = cfg.timeout;};
11840             }
11841             this.showLoading();
11842             if(!discardUrl){
11843                 this.defaultUrl = url;
11844             }
11845             if(typeof url == "function"){
11846                 url = url.call(this);
11847             }
11848
11849             method = method || (params ? "POST" : "GET");
11850             if(method == "GET"){
11851                 url = this.prepareUrl(url);
11852             }
11853
11854             var o = Roo.apply(cfg ||{}, {
11855                 url : url,
11856                 params: params,
11857                 success: this.successDelegate,
11858                 failure: this.failureDelegate,
11859                 callback: undefined,
11860                 timeout: (this.timeout*1000),
11861                 argument: {"url": url, "form": null, "callback": callback, "params": params}
11862             });
11863             Roo.log("updated manager called with timeout of " + o.timeout);
11864             this.transaction = Roo.Ajax.request(o);
11865         }
11866     },
11867
11868     /**
11869      * 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.
11870      * Uses this.sslBlankUrl for SSL file uploads to prevent IE security warning.
11871      * @param {String/HTMLElement} form The form Id or form element
11872      * @param {String} url (optional) The url to pass the form to. If omitted the action attribute on the form will be used.
11873      * @param {Boolean} reset (optional) Whether to try to reset the form after the update
11874      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess, oResponse)
11875      */
11876     formUpdate : function(form, url, reset, callback){
11877         if(this.fireEvent("beforeupdate", this.el, form, url) !== false){
11878             if(typeof url == "function"){
11879                 url = url.call(this);
11880             }
11881             form = Roo.getDom(form);
11882             this.transaction = Roo.Ajax.request({
11883                 form: form,
11884                 url:url,
11885                 success: this.successDelegate,
11886                 failure: this.failureDelegate,
11887                 timeout: (this.timeout*1000),
11888                 argument: {"url": url, "form": form, "callback": callback, "reset": reset}
11889             });
11890             this.showLoading.defer(1, this);
11891         }
11892     },
11893
11894     /**
11895      * Refresh the element with the last used url or defaultUrl. If there is no url, it returns immediately
11896      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
11897      */
11898     refresh : function(callback){
11899         if(this.defaultUrl == null){
11900             return;
11901         }
11902         this.update(this.defaultUrl, null, callback, true);
11903     },
11904
11905     /**
11906      * Set this element to auto refresh.
11907      * @param {Number} interval How often to update (in seconds).
11908      * @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)
11909      * @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}
11910      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
11911      * @param {Boolean} refreshNow (optional) Whether to execute the refresh now, or wait the interval
11912      */
11913     startAutoRefresh : function(interval, url, params, callback, refreshNow){
11914         if(refreshNow){
11915             this.update(url || this.defaultUrl, params, callback, true);
11916         }
11917         if(this.autoRefreshProcId){
11918             clearInterval(this.autoRefreshProcId);
11919         }
11920         this.autoRefreshProcId = setInterval(this.update.createDelegate(this, [url || this.defaultUrl, params, callback, true]), interval*1000);
11921     },
11922
11923     /**
11924      * Stop auto refresh on this element.
11925      */
11926      stopAutoRefresh : function(){
11927         if(this.autoRefreshProcId){
11928             clearInterval(this.autoRefreshProcId);
11929             delete this.autoRefreshProcId;
11930         }
11931     },
11932
11933     isAutoRefreshing : function(){
11934        return this.autoRefreshProcId ? true : false;
11935     },
11936     /**
11937      * Called to update the element to "Loading" state. Override to perform custom action.
11938      */
11939     showLoading : function(){
11940         if(this.showLoadIndicator){
11941             this.el.update(this.indicatorText);
11942         }
11943     },
11944
11945     /**
11946      * Adds unique parameter to query string if disableCaching = true
11947      * @private
11948      */
11949     prepareUrl : function(url){
11950         if(this.disableCaching){
11951             var append = "_dc=" + (new Date().getTime());
11952             if(url.indexOf("?") !== -1){
11953                 url += "&" + append;
11954             }else{
11955                 url += "?" + append;
11956             }
11957         }
11958         return url;
11959     },
11960
11961     /**
11962      * @private
11963      */
11964     processSuccess : function(response){
11965         this.transaction = null;
11966         if(response.argument.form && response.argument.reset){
11967             try{ // put in try/catch since some older FF releases had problems with this
11968                 response.argument.form.reset();
11969             }catch(e){}
11970         }
11971         if(this.loadScripts){
11972             this.renderer.render(this.el, response, this,
11973                 this.updateComplete.createDelegate(this, [response]));
11974         }else{
11975             this.renderer.render(this.el, response, this);
11976             this.updateComplete(response);
11977         }
11978     },
11979
11980     updateComplete : function(response){
11981         this.fireEvent("update", this.el, response);
11982         if(typeof response.argument.callback == "function"){
11983             response.argument.callback(this.el, true, response);
11984         }
11985     },
11986
11987     /**
11988      * @private
11989      */
11990     processFailure : function(response){
11991         this.transaction = null;
11992         this.fireEvent("failure", this.el, response);
11993         if(typeof response.argument.callback == "function"){
11994             response.argument.callback(this.el, false, response);
11995         }
11996     },
11997
11998     /**
11999      * Set the content renderer for this UpdateManager. See {@link Roo.UpdateManager.BasicRenderer#render} for more details.
12000      * @param {Object} renderer The object implementing the render() method
12001      */
12002     setRenderer : function(renderer){
12003         this.renderer = renderer;
12004     },
12005
12006     getRenderer : function(){
12007        return this.renderer;
12008     },
12009
12010     /**
12011      * Set the defaultUrl used for updates
12012      * @param {String/Function} defaultUrl The url or a function to call to get the url
12013      */
12014     setDefaultUrl : function(defaultUrl){
12015         this.defaultUrl = defaultUrl;
12016     },
12017
12018     /**
12019      * Aborts the executing transaction
12020      */
12021     abort : function(){
12022         if(this.transaction){
12023             Roo.Ajax.abort(this.transaction);
12024         }
12025     },
12026
12027     /**
12028      * Returns true if an update is in progress
12029      * @return {Boolean}
12030      */
12031     isUpdating : function(){
12032         if(this.transaction){
12033             return Roo.Ajax.isLoading(this.transaction);
12034         }
12035         return false;
12036     }
12037 });
12038
12039 /**
12040  * @class Roo.UpdateManager.defaults
12041  * @static (not really - but it helps the doc tool)
12042  * The defaults collection enables customizing the default properties of UpdateManager
12043  */
12044    Roo.UpdateManager.defaults = {
12045        /**
12046          * Timeout for requests or form posts in seconds (Defaults 30 seconds).
12047          * @type Number
12048          */
12049          timeout : 30,
12050
12051          /**
12052          * True to process scripts by default (Defaults to false).
12053          * @type Boolean
12054          */
12055         loadScripts : false,
12056
12057         /**
12058         * Blank page URL to use with SSL file uploads (Defaults to "javascript:false").
12059         * @type String
12060         */
12061         sslBlankUrl : (Roo.SSL_SECURE_URL || "javascript:false"),
12062         /**
12063          * Whether to append unique parameter on get request to disable caching (Defaults to false).
12064          * @type Boolean
12065          */
12066         disableCaching : false,
12067         /**
12068          * Whether to show indicatorText when loading (Defaults to true).
12069          * @type Boolean
12070          */
12071         showLoadIndicator : true,
12072         /**
12073          * Text for loading indicator (Defaults to '&lt;div class="loading-indicator"&gt;Loading...&lt;/div&gt;').
12074          * @type String
12075          */
12076         indicatorText : '<div class="loading-indicator">Loading...</div>'
12077    };
12078
12079 /**
12080  * Static convenience method. This method is deprecated in favor of el.load({url:'foo.php', ...}).
12081  *Usage:
12082  * <pre><code>Roo.UpdateManager.updateElement("my-div", "stuff.php");</code></pre>
12083  * @param {String/HTMLElement/Roo.Element} el The element to update
12084  * @param {String} url The url
12085  * @param {String/Object} params (optional) Url encoded param string or an object of name/value pairs
12086  * @param {Object} options (optional) A config object with any of the UpdateManager properties you want to set - for example: {disableCaching:true, indicatorText: "Loading data..."}
12087  * @static
12088  * @deprecated
12089  * @member Roo.UpdateManager
12090  */
12091 Roo.UpdateManager.updateElement = function(el, url, params, options){
12092     var um = Roo.get(el, true).getUpdateManager();
12093     Roo.apply(um, options);
12094     um.update(url, params, options ? options.callback : null);
12095 };
12096 // alias for backwards compat
12097 Roo.UpdateManager.update = Roo.UpdateManager.updateElement;
12098 /**
12099  * @class Roo.UpdateManager.BasicRenderer
12100  * Default Content renderer. Updates the elements innerHTML with the responseText.
12101  */
12102 Roo.UpdateManager.BasicRenderer = function(){};
12103
12104 Roo.UpdateManager.BasicRenderer.prototype = {
12105     /**
12106      * This is called when the transaction is completed and it's time to update the element - The BasicRenderer
12107      * updates the elements innerHTML with the responseText - To perform a custom render (i.e. XML or JSON processing),
12108      * create an object with a "render(el, response)" method and pass it to setRenderer on the UpdateManager.
12109      * @param {Roo.Element} el The element being rendered
12110      * @param {Object} response The YUI Connect response object
12111      * @param {UpdateManager} updateManager The calling update manager
12112      * @param {Function} callback A callback that will need to be called if loadScripts is true on the UpdateManager
12113      */
12114      render : function(el, response, updateManager, callback){
12115         el.update(response.responseText, updateManager.loadScripts, callback);
12116     }
12117 };
12118 /*
12119  * Based on:
12120  * Roo JS
12121  * (c)) Alan Knowles
12122  * Licence : LGPL
12123  */
12124
12125
12126 /**
12127  * @class Roo.DomTemplate
12128  * @extends Roo.Template
12129  * An effort at a dom based template engine..
12130  *
12131  * Similar to XTemplate, except it uses dom parsing to create the template..
12132  *
12133  * Supported features:
12134  *
12135  *  Tags:
12136
12137 <pre><code>
12138       {a_variable} - output encoded.
12139       {a_variable.format:("Y-m-d")} - call a method on the variable
12140       {a_variable:raw} - unencoded output
12141       {a_variable:toFixed(1,2)} - Roo.util.Format."toFixed"
12142       {a_variable:this.method_on_template(...)} - call a method on the template object.
12143  
12144 </code></pre>
12145  *  The tpl tag:
12146 <pre><code>
12147         &lt;div roo-for="a_variable or condition.."&gt;&lt;/div&gt;
12148         &lt;div roo-if="a_variable or condition"&gt;&lt;/div&gt;
12149         &lt;div roo-exec="some javascript"&gt;&lt;/div&gt;
12150         &lt;div roo-name="named_template"&gt;&lt;/div&gt; 
12151   
12152 </code></pre>
12153  *      
12154  */
12155 Roo.DomTemplate = function()
12156 {
12157      Roo.DomTemplate.superclass.constructor.apply(this, arguments);
12158      if (this.html) {
12159         this.compile();
12160      }
12161 };
12162
12163
12164 Roo.extend(Roo.DomTemplate, Roo.Template, {
12165     /**
12166      * id counter for sub templates.
12167      */
12168     id : 0,
12169     /**
12170      * flag to indicate if dom parser is inside a pre,
12171      * it will strip whitespace if not.
12172      */
12173     inPre : false,
12174     
12175     /**
12176      * The various sub templates
12177      */
12178     tpls : false,
12179     
12180     
12181     
12182     /**
12183      *
12184      * basic tag replacing syntax
12185      * WORD:WORD()
12186      *
12187      * // you can fake an object call by doing this
12188      *  x.t:(test,tesT) 
12189      * 
12190      */
12191     re : /(\{|\%7B)([\w-\.]+)(?:\:([\w\.]*)(?:\(([^)]*?)?\))?)?(\}|\%7D)/g,
12192     //re : /\{([\w-\.]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
12193     
12194     iterChild : function (node, method) {
12195         
12196         var oldPre = this.inPre;
12197         if (node.tagName == 'PRE') {
12198             this.inPre = true;
12199         }
12200         for( var i = 0; i < node.childNodes.length; i++) {
12201             method.call(this, node.childNodes[i]);
12202         }
12203         this.inPre = oldPre;
12204     },
12205     
12206     
12207     
12208     /**
12209      * compile the template
12210      *
12211      * This is not recursive, so I'm not sure how nested templates are really going to be handled..
12212      *
12213      */
12214     compile: function()
12215     {
12216         var s = this.html;
12217         
12218         // covert the html into DOM...
12219         var doc = false;
12220         var div =false;
12221         try {
12222             doc = document.implementation.createHTMLDocument("");
12223             doc.documentElement.innerHTML =   this.html  ;
12224             div = doc.documentElement;
12225         } catch (e) {
12226             // old IE... - nasty -- it causes all sorts of issues.. with
12227             // images getting pulled from server..
12228             div = document.createElement('div');
12229             div.innerHTML = this.html;
12230         }
12231         //doc.documentElement.innerHTML = htmlBody
12232          
12233         
12234         
12235         this.tpls = [];
12236         var _t = this;
12237         this.iterChild(div, function(n) {_t.compileNode(n, true); });
12238         
12239         var tpls = this.tpls;
12240         
12241         // create a top level template from the snippet..
12242         
12243         //Roo.log(div.innerHTML);
12244         
12245         var tpl = {
12246             uid : 'master',
12247             id : this.id++,
12248             attr : false,
12249             value : false,
12250             body : div.innerHTML,
12251             
12252             forCall : false,
12253             execCall : false,
12254             dom : div,
12255             isTop : true
12256             
12257         };
12258         tpls.unshift(tpl);
12259         
12260         
12261         // compile them...
12262         this.tpls = [];
12263         Roo.each(tpls, function(tp){
12264             this.compileTpl(tp);
12265             this.tpls[tp.id] = tp;
12266         }, this);
12267         
12268         this.master = tpls[0];
12269         return this;
12270         
12271         
12272     },
12273     
12274     compileNode : function(node, istop) {
12275         // test for
12276         //Roo.log(node);
12277         
12278         
12279         // skip anything not a tag..
12280         if (node.nodeType != 1) {
12281             if (node.nodeType == 3 && !this.inPre) {
12282                 // reduce white space..
12283                 node.nodeValue = node.nodeValue.replace(/\s+/g, ' '); 
12284                 
12285             }
12286             return;
12287         }
12288         
12289         var tpl = {
12290             uid : false,
12291             id : false,
12292             attr : false,
12293             value : false,
12294             body : '',
12295             
12296             forCall : false,
12297             execCall : false,
12298             dom : false,
12299             isTop : istop
12300             
12301             
12302         };
12303         
12304         
12305         switch(true) {
12306             case (node.hasAttribute('roo-for')): tpl.attr = 'for'; break;
12307             case (node.hasAttribute('roo-if')): tpl.attr = 'if'; break;
12308             case (node.hasAttribute('roo-name')): tpl.attr = 'name'; break;
12309             case (node.hasAttribute('roo-exec')): tpl.attr = 'exec'; break;
12310             // no default..
12311         }
12312         
12313         
12314         if (!tpl.attr) {
12315             // just itterate children..
12316             this.iterChild(node,this.compileNode);
12317             return;
12318         }
12319         tpl.uid = this.id++;
12320         tpl.value = node.getAttribute('roo-' +  tpl.attr);
12321         node.removeAttribute('roo-'+ tpl.attr);
12322         if (tpl.attr != 'name') {
12323             var placeholder = document.createTextNode('{domtpl' + tpl.uid + '}');
12324             node.parentNode.replaceChild(placeholder,  node);
12325         } else {
12326             
12327             var placeholder =  document.createElement('span');
12328             placeholder.className = 'roo-tpl-' + tpl.value;
12329             node.parentNode.replaceChild(placeholder,  node);
12330         }
12331         
12332         // parent now sees '{domtplXXXX}
12333         this.iterChild(node,this.compileNode);
12334         
12335         // we should now have node body...
12336         var div = document.createElement('div');
12337         div.appendChild(node);
12338         tpl.dom = node;
12339         // this has the unfortunate side effect of converting tagged attributes
12340         // eg. href="{...}" into %7C...%7D
12341         // this has been fixed by searching for those combo's although it's a bit hacky..
12342         
12343         
12344         tpl.body = div.innerHTML;
12345         
12346         
12347          
12348         tpl.id = tpl.uid;
12349         switch(tpl.attr) {
12350             case 'for' :
12351                 switch (tpl.value) {
12352                     case '.':  tpl.forCall = new Function('values', 'parent', 'with(values){ return values; }'); break;
12353                     case '..': tpl.forCall= new Function('values', 'parent', 'with(values){ return parent; }'); break;
12354                     default:   tpl.forCall= new Function('values', 'parent', 'with(values){ return '+tpl.value+'; }');
12355                 }
12356                 break;
12357             
12358             case 'exec':
12359                 tpl.execCall = new Function('values', 'parent', 'with(values){ '+(Roo.util.Format.htmlDecode(tpl.value))+'; }');
12360                 break;
12361             
12362             case 'if':     
12363                 tpl.ifCall = new Function('values', 'parent', 'with(values){ return '+(Roo.util.Format.htmlDecode(tpl.value))+'; }');
12364                 break;
12365             
12366             case 'name':
12367                 tpl.id  = tpl.value; // replace non characters???
12368                 break;
12369             
12370         }
12371         
12372         
12373         this.tpls.push(tpl);
12374         
12375         
12376         
12377     },
12378     
12379     
12380     
12381     
12382     /**
12383      * Compile a segment of the template into a 'sub-template'
12384      *
12385      * 
12386      * 
12387      *
12388      */
12389     compileTpl : function(tpl)
12390     {
12391         var fm = Roo.util.Format;
12392         var useF = this.disableFormats !== true;
12393         
12394         var sep = Roo.isGecko ? "+\n" : ",\n";
12395         
12396         var undef = function(str) {
12397             Roo.debug && Roo.log("Property not found :"  + str);
12398             return '';
12399         };
12400           
12401         //Roo.log(tpl.body);
12402         
12403         
12404         
12405         var fn = function(m, lbrace, name, format, args)
12406         {
12407             //Roo.log("ARGS");
12408             //Roo.log(arguments);
12409             args = args ? args.replace(/\\'/g,"'") : args;
12410             //["{TEST:(a,b,c)}", "TEST", "", "a,b,c", 0, "{TEST:(a,b,c)}"]
12411             if (typeof(format) == 'undefined') {
12412                 format =  'htmlEncode'; 
12413             }
12414             if (format == 'raw' ) {
12415                 format = false;
12416             }
12417             
12418             if(name.substr(0, 6) == 'domtpl'){
12419                 return "'"+ sep +'this.applySubTemplate('+name.substr(6)+', values, parent)'+sep+"'";
12420             }
12421             
12422             // build an array of options to determine if value is undefined..
12423             
12424             // basically get 'xxxx.yyyy' then do
12425             // (typeof(xxxx) == 'undefined' || typeof(xxx.yyyy) == 'undefined') ?
12426             //    (function () { Roo.log("Property not found"); return ''; })() :
12427             //    ......
12428             
12429             var udef_ar = [];
12430             var lookfor = '';
12431             Roo.each(name.split('.'), function(st) {
12432                 lookfor += (lookfor.length ? '.': '') + st;
12433                 udef_ar.push(  "(typeof(" + lookfor + ") == 'undefined')"  );
12434             });
12435             
12436             var udef_st = '((' + udef_ar.join(" || ") +") ? undef('" + name + "') : "; // .. needs )
12437             
12438             
12439             if(format && useF){
12440                 
12441                 args = args ? ',' + args : "";
12442                  
12443                 if(format.substr(0, 5) != "this."){
12444                     format = "fm." + format + '(';
12445                 }else{
12446                     format = 'this.call("'+ format.substr(5) + '", ';
12447                     args = ", values";
12448                 }
12449                 
12450                 return "'"+ sep +   udef_st   +    format + name + args + "))"+sep+"'";
12451             }
12452              
12453             if (args && args.length) {
12454                 // called with xxyx.yuu:(test,test)
12455                 // change to ()
12456                 return "'"+ sep + udef_st  + name + '(' +  args + "))"+sep+"'";
12457             }
12458             // raw.. - :raw modifier..
12459             return "'"+ sep + udef_st  + name + ")"+sep+"'";
12460             
12461         };
12462         var body;
12463         // branched to use + in gecko and [].join() in others
12464         if(Roo.isGecko){
12465             body = "tpl.compiled = function(values, parent){  with(values) { return '" +
12466                    tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
12467                     "';};};";
12468         }else{
12469             body = ["tpl.compiled = function(values, parent){  with (values) { return ['"];
12470             body.push(tpl.body.replace(/(\r\n|\n)/g,
12471                             '\\n').replace(/'/g, "\\'").replace(this.re, fn));
12472             body.push("'].join('');};};");
12473             body = body.join('');
12474         }
12475         
12476         Roo.debug && Roo.log(body.replace(/\\n/,'\n'));
12477        
12478         /** eval:var:tpl eval:var:fm eval:var:useF eval:var:undef  */
12479         eval(body);
12480         
12481         return this;
12482     },
12483      
12484     /**
12485      * same as applyTemplate, except it's done to one of the subTemplates
12486      * when using named templates, you can do:
12487      *
12488      * var str = pl.applySubTemplate('your-name', values);
12489      *
12490      * 
12491      * @param {Number} id of the template
12492      * @param {Object} values to apply to template
12493      * @param {Object} parent (normaly the instance of this object)
12494      */
12495     applySubTemplate : function(id, values, parent)
12496     {
12497         
12498         
12499         var t = this.tpls[id];
12500         
12501         
12502         try { 
12503             if(t.ifCall && !t.ifCall.call(this, values, parent)){
12504                 Roo.debug && Roo.log('if call on ' + t.value + ' return false');
12505                 return '';
12506             }
12507         } catch(e) {
12508             Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on roo-if="' + t.value + '" - ' + e.toString());
12509             Roo.log(values);
12510           
12511             return '';
12512         }
12513         try { 
12514             
12515             if(t.execCall && t.execCall.call(this, values, parent)){
12516                 return '';
12517             }
12518         } catch(e) {
12519             Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on roo-for="' + t.value + '" - ' + e.toString());
12520             Roo.log(values);
12521             return '';
12522         }
12523         
12524         try {
12525             var vs = t.forCall ? t.forCall.call(this, values, parent) : values;
12526             parent = t.target ? values : parent;
12527             if(t.forCall && vs instanceof Array){
12528                 var buf = [];
12529                 for(var i = 0, len = vs.length; i < len; i++){
12530                     try {
12531                         buf[buf.length] = t.compiled.call(this, vs[i], parent);
12532                     } catch (e) {
12533                         Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on body="' + t.value + '" - ' + e.toString());
12534                         Roo.log(e.body);
12535                         //Roo.log(t.compiled);
12536                         Roo.log(vs[i]);
12537                     }   
12538                 }
12539                 return buf.join('');
12540             }
12541         } catch (e) {
12542             Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on roo-for="' + t.value + '" - ' + e.toString());
12543             Roo.log(values);
12544             return '';
12545         }
12546         try {
12547             return t.compiled.call(this, vs, parent);
12548         } catch (e) {
12549             Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on body="' + t.value + '" - ' + e.toString());
12550             Roo.log(e.body);
12551             //Roo.log(t.compiled);
12552             Roo.log(values);
12553             return '';
12554         }
12555     },
12556
12557    
12558
12559     applyTemplate : function(values){
12560         return this.master.compiled.call(this, values, {});
12561         //var s = this.subs;
12562     },
12563
12564     apply : function(){
12565         return this.applyTemplate.apply(this, arguments);
12566     }
12567
12568  });
12569
12570 Roo.DomTemplate.from = function(el){
12571     el = Roo.getDom(el);
12572     return new Roo.Domtemplate(el.value || el.innerHTML);
12573 };/*
12574  * Based on:
12575  * Ext JS Library 1.1.1
12576  * Copyright(c) 2006-2007, Ext JS, LLC.
12577  *
12578  * Originally Released Under LGPL - original licence link has changed is not relivant.
12579  *
12580  * Fork - LGPL
12581  * <script type="text/javascript">
12582  */
12583
12584 /**
12585  * @class Roo.util.DelayedTask
12586  * Provides a convenient method of performing setTimeout where a new
12587  * timeout cancels the old timeout. An example would be performing validation on a keypress.
12588  * You can use this class to buffer
12589  * the keypress events for a certain number of milliseconds, and perform only if they stop
12590  * for that amount of time.
12591  * @constructor The parameters to this constructor serve as defaults and are not required.
12592  * @param {Function} fn (optional) The default function to timeout
12593  * @param {Object} scope (optional) The default scope of that timeout
12594  * @param {Array} args (optional) The default Array of arguments
12595  */
12596 Roo.util.DelayedTask = function(fn, scope, args){
12597     var id = null, d, t;
12598
12599     var call = function(){
12600         var now = new Date().getTime();
12601         if(now - t >= d){
12602             clearInterval(id);
12603             id = null;
12604             fn.apply(scope, args || []);
12605         }
12606     };
12607     /**
12608      * Cancels any pending timeout and queues a new one
12609      * @param {Number} delay The milliseconds to delay
12610      * @param {Function} newFn (optional) Overrides function passed to constructor
12611      * @param {Object} newScope (optional) Overrides scope passed to constructor
12612      * @param {Array} newArgs (optional) Overrides args passed to constructor
12613      */
12614     this.delay = function(delay, newFn, newScope, newArgs){
12615         if(id && delay != d){
12616             this.cancel();
12617         }
12618         d = delay;
12619         t = new Date().getTime();
12620         fn = newFn || fn;
12621         scope = newScope || scope;
12622         args = newArgs || args;
12623         if(!id){
12624             id = setInterval(call, d);
12625         }
12626     };
12627
12628     /**
12629      * Cancel the last queued timeout
12630      */
12631     this.cancel = function(){
12632         if(id){
12633             clearInterval(id);
12634             id = null;
12635         }
12636     };
12637 };/*
12638  * Based on:
12639  * Ext JS Library 1.1.1
12640  * Copyright(c) 2006-2007, Ext JS, LLC.
12641  *
12642  * Originally Released Under LGPL - original licence link has changed is not relivant.
12643  *
12644  * Fork - LGPL
12645  * <script type="text/javascript">
12646  */
12647  
12648  
12649 Roo.util.TaskRunner = function(interval){
12650     interval = interval || 10;
12651     var tasks = [], removeQueue = [];
12652     var id = 0;
12653     var running = false;
12654
12655     var stopThread = function(){
12656         running = false;
12657         clearInterval(id);
12658         id = 0;
12659     };
12660
12661     var startThread = function(){
12662         if(!running){
12663             running = true;
12664             id = setInterval(runTasks, interval);
12665         }
12666     };
12667
12668     var removeTask = function(task){
12669         removeQueue.push(task);
12670         if(task.onStop){
12671             task.onStop();
12672         }
12673     };
12674
12675     var runTasks = function(){
12676         if(removeQueue.length > 0){
12677             for(var i = 0, len = removeQueue.length; i < len; i++){
12678                 tasks.remove(removeQueue[i]);
12679             }
12680             removeQueue = [];
12681             if(tasks.length < 1){
12682                 stopThread();
12683                 return;
12684             }
12685         }
12686         var now = new Date().getTime();
12687         for(var i = 0, len = tasks.length; i < len; ++i){
12688             var t = tasks[i];
12689             var itime = now - t.taskRunTime;
12690             if(t.interval <= itime){
12691                 var rt = t.run.apply(t.scope || t, t.args || [++t.taskRunCount]);
12692                 t.taskRunTime = now;
12693                 if(rt === false || t.taskRunCount === t.repeat){
12694                     removeTask(t);
12695                     return;
12696                 }
12697             }
12698             if(t.duration && t.duration <= (now - t.taskStartTime)){
12699                 removeTask(t);
12700             }
12701         }
12702     };
12703
12704     /**
12705      * Queues a new task.
12706      * @param {Object} task
12707      */
12708     this.start = function(task){
12709         tasks.push(task);
12710         task.taskStartTime = new Date().getTime();
12711         task.taskRunTime = 0;
12712         task.taskRunCount = 0;
12713         startThread();
12714         return task;
12715     };
12716
12717     this.stop = function(task){
12718         removeTask(task);
12719         return task;
12720     };
12721
12722     this.stopAll = function(){
12723         stopThread();
12724         for(var i = 0, len = tasks.length; i < len; i++){
12725             if(tasks[i].onStop){
12726                 tasks[i].onStop();
12727             }
12728         }
12729         tasks = [];
12730         removeQueue = [];
12731     };
12732 };
12733
12734 Roo.TaskMgr = new Roo.util.TaskRunner();/*
12735  * Based on:
12736  * Ext JS Library 1.1.1
12737  * Copyright(c) 2006-2007, Ext JS, LLC.
12738  *
12739  * Originally Released Under LGPL - original licence link has changed is not relivant.
12740  *
12741  * Fork - LGPL
12742  * <script type="text/javascript">
12743  */
12744
12745  
12746 /**
12747  * @class Roo.util.MixedCollection
12748  * @extends Roo.util.Observable
12749  * A Collection class that maintains both numeric indexes and keys and exposes events.
12750  * @constructor
12751  * @param {Boolean} allowFunctions True if the addAll function should add function references to the
12752  * collection (defaults to false)
12753  * @param {Function} keyFn A function that can accept an item of the type(s) stored in this MixedCollection
12754  * and return the key value for that item.  This is used when available to look up the key on items that
12755  * were passed without an explicit key parameter to a MixedCollection method.  Passing this parameter is
12756  * equivalent to providing an implementation for the {@link #getKey} method.
12757  */
12758 Roo.util.MixedCollection = function(allowFunctions, keyFn){
12759     this.items = [];
12760     this.map = {};
12761     this.keys = [];
12762     this.length = 0;
12763     this.addEvents({
12764         /**
12765          * @event clear
12766          * Fires when the collection is cleared.
12767          */
12768         "clear" : true,
12769         /**
12770          * @event add
12771          * Fires when an item is added to the collection.
12772          * @param {Number} index The index at which the item was added.
12773          * @param {Object} o The item added.
12774          * @param {String} key The key associated with the added item.
12775          */
12776         "add" : true,
12777         /**
12778          * @event replace
12779          * Fires when an item is replaced in the collection.
12780          * @param {String} key he key associated with the new added.
12781          * @param {Object} old The item being replaced.
12782          * @param {Object} new The new item.
12783          */
12784         "replace" : true,
12785         /**
12786          * @event remove
12787          * Fires when an item is removed from the collection.
12788          * @param {Object} o The item being removed.
12789          * @param {String} key (optional) The key associated with the removed item.
12790          */
12791         "remove" : true,
12792         "sort" : true
12793     });
12794     this.allowFunctions = allowFunctions === true;
12795     if(keyFn){
12796         this.getKey = keyFn;
12797     }
12798     Roo.util.MixedCollection.superclass.constructor.call(this);
12799 };
12800
12801 Roo.extend(Roo.util.MixedCollection, Roo.util.Observable, {
12802     allowFunctions : false,
12803     
12804 /**
12805  * Adds an item to the collection.
12806  * @param {String} key The key to associate with the item
12807  * @param {Object} o The item to add.
12808  * @return {Object} The item added.
12809  */
12810     add : function(key, o){
12811         if(arguments.length == 1){
12812             o = arguments[0];
12813             key = this.getKey(o);
12814         }
12815         if(typeof key == "undefined" || key === null){
12816             this.length++;
12817             this.items.push(o);
12818             this.keys.push(null);
12819         }else{
12820             var old = this.map[key];
12821             if(old){
12822                 return this.replace(key, o);
12823             }
12824             this.length++;
12825             this.items.push(o);
12826             this.map[key] = o;
12827             this.keys.push(key);
12828         }
12829         this.fireEvent("add", this.length-1, o, key);
12830         return o;
12831     },
12832        
12833 /**
12834   * MixedCollection has a generic way to fetch keys if you implement getKey.
12835 <pre><code>
12836 // normal way
12837 var mc = new Roo.util.MixedCollection();
12838 mc.add(someEl.dom.id, someEl);
12839 mc.add(otherEl.dom.id, otherEl);
12840 //and so on
12841
12842 // using getKey
12843 var mc = new Roo.util.MixedCollection();
12844 mc.getKey = function(el){
12845    return el.dom.id;
12846 };
12847 mc.add(someEl);
12848 mc.add(otherEl);
12849
12850 // or via the constructor
12851 var mc = new Roo.util.MixedCollection(false, function(el){
12852    return el.dom.id;
12853 });
12854 mc.add(someEl);
12855 mc.add(otherEl);
12856 </code></pre>
12857  * @param o {Object} The item for which to find the key.
12858  * @return {Object} The key for the passed item.
12859  */
12860     getKey : function(o){
12861          return o.id; 
12862     },
12863    
12864 /**
12865  * Replaces an item in the collection.
12866  * @param {String} key The key associated with the item to replace, or the item to replace.
12867  * @param o {Object} o (optional) If the first parameter passed was a key, the item to associate with that key.
12868  * @return {Object}  The new item.
12869  */
12870     replace : function(key, o){
12871         if(arguments.length == 1){
12872             o = arguments[0];
12873             key = this.getKey(o);
12874         }
12875         var old = this.item(key);
12876         if(typeof key == "undefined" || key === null || typeof old == "undefined"){
12877              return this.add(key, o);
12878         }
12879         var index = this.indexOfKey(key);
12880         this.items[index] = o;
12881         this.map[key] = o;
12882         this.fireEvent("replace", key, old, o);
12883         return o;
12884     },
12885    
12886 /**
12887  * Adds all elements of an Array or an Object to the collection.
12888  * @param {Object/Array} objs An Object containing properties which will be added to the collection, or
12889  * an Array of values, each of which are added to the collection.
12890  */
12891     addAll : function(objs){
12892         if(arguments.length > 1 || objs instanceof Array){
12893             var args = arguments.length > 1 ? arguments : objs;
12894             for(var i = 0, len = args.length; i < len; i++){
12895                 this.add(args[i]);
12896             }
12897         }else{
12898             for(var key in objs){
12899                 if(this.allowFunctions || typeof objs[key] != "function"){
12900                     this.add(key, objs[key]);
12901                 }
12902             }
12903         }
12904     },
12905    
12906 /**
12907  * Executes the specified function once for every item in the collection, passing each
12908  * item as the first and only parameter. returning false from the function will stop the iteration.
12909  * @param {Function} fn The function to execute for each item.
12910  * @param {Object} scope (optional) The scope in which to execute the function.
12911  */
12912     each : function(fn, scope){
12913         var items = [].concat(this.items); // each safe for removal
12914         for(var i = 0, len = items.length; i < len; i++){
12915             if(fn.call(scope || items[i], items[i], i, len) === false){
12916                 break;
12917             }
12918         }
12919     },
12920    
12921 /**
12922  * Executes the specified function once for every key in the collection, passing each
12923  * key, and its associated item as the first two parameters.
12924  * @param {Function} fn The function to execute for each item.
12925  * @param {Object} scope (optional) The scope in which to execute the function.
12926  */
12927     eachKey : function(fn, scope){
12928         for(var i = 0, len = this.keys.length; i < len; i++){
12929             fn.call(scope || window, this.keys[i], this.items[i], i, len);
12930         }
12931     },
12932    
12933 /**
12934  * Returns the first item in the collection which elicits a true return value from the
12935  * passed selection function.
12936  * @param {Function} fn The selection function to execute for each item.
12937  * @param {Object} scope (optional) The scope in which to execute the function.
12938  * @return {Object} The first item in the collection which returned true from the selection function.
12939  */
12940     find : function(fn, scope){
12941         for(var i = 0, len = this.items.length; i < len; i++){
12942             if(fn.call(scope || window, this.items[i], this.keys[i])){
12943                 return this.items[i];
12944             }
12945         }
12946         return null;
12947     },
12948    
12949 /**
12950  * Inserts an item at the specified index in the collection.
12951  * @param {Number} index The index to insert the item at.
12952  * @param {String} key The key to associate with the new item, or the item itself.
12953  * @param {Object} o  (optional) If the second parameter was a key, the new item.
12954  * @return {Object} The item inserted.
12955  */
12956     insert : function(index, key, o){
12957         if(arguments.length == 2){
12958             o = arguments[1];
12959             key = this.getKey(o);
12960         }
12961         if(index >= this.length){
12962             return this.add(key, o);
12963         }
12964         this.length++;
12965         this.items.splice(index, 0, o);
12966         if(typeof key != "undefined" && key != null){
12967             this.map[key] = o;
12968         }
12969         this.keys.splice(index, 0, key);
12970         this.fireEvent("add", index, o, key);
12971         return o;
12972     },
12973    
12974 /**
12975  * Removed an item from the collection.
12976  * @param {Object} o The item to remove.
12977  * @return {Object} The item removed.
12978  */
12979     remove : function(o){
12980         return this.removeAt(this.indexOf(o));
12981     },
12982    
12983 /**
12984  * Remove an item from a specified index in the collection.
12985  * @param {Number} index The index within the collection of the item to remove.
12986  */
12987     removeAt : function(index){
12988         if(index < this.length && index >= 0){
12989             this.length--;
12990             var o = this.items[index];
12991             this.items.splice(index, 1);
12992             var key = this.keys[index];
12993             if(typeof key != "undefined"){
12994                 delete this.map[key];
12995             }
12996             this.keys.splice(index, 1);
12997             this.fireEvent("remove", o, key);
12998         }
12999     },
13000    
13001 /**
13002  * Removed an item associated with the passed key fom the collection.
13003  * @param {String} key The key of the item to remove.
13004  */
13005     removeKey : function(key){
13006         return this.removeAt(this.indexOfKey(key));
13007     },
13008    
13009 /**
13010  * Returns the number of items in the collection.
13011  * @return {Number} the number of items in the collection.
13012  */
13013     getCount : function(){
13014         return this.length; 
13015     },
13016    
13017 /**
13018  * Returns index within the collection of the passed Object.
13019  * @param {Object} o The item to find the index of.
13020  * @return {Number} index of the item.
13021  */
13022     indexOf : function(o){
13023         if(!this.items.indexOf){
13024             for(var i = 0, len = this.items.length; i < len; i++){
13025                 if(this.items[i] == o) return i;
13026             }
13027             return -1;
13028         }else{
13029             return this.items.indexOf(o);
13030         }
13031     },
13032    
13033 /**
13034  * Returns index within the collection of the passed key.
13035  * @param {String} key The key to find the index of.
13036  * @return {Number} index of the key.
13037  */
13038     indexOfKey : function(key){
13039         if(!this.keys.indexOf){
13040             for(var i = 0, len = this.keys.length; i < len; i++){
13041                 if(this.keys[i] == key) return i;
13042             }
13043             return -1;
13044         }else{
13045             return this.keys.indexOf(key);
13046         }
13047     },
13048    
13049 /**
13050  * Returns the item associated with the passed key OR index. Key has priority over index.
13051  * @param {String/Number} key The key or index of the item.
13052  * @return {Object} The item associated with the passed key.
13053  */
13054     item : function(key){
13055         var item = typeof this.map[key] != "undefined" ? this.map[key] : this.items[key];
13056         return typeof item != 'function' || this.allowFunctions ? item : null; // for prototype!
13057     },
13058     
13059 /**
13060  * Returns the item at the specified index.
13061  * @param {Number} index The index of the item.
13062  * @return {Object}
13063  */
13064     itemAt : function(index){
13065         return this.items[index];
13066     },
13067     
13068 /**
13069  * Returns the item associated with the passed key.
13070  * @param {String/Number} key The key of the item.
13071  * @return {Object} The item associated with the passed key.
13072  */
13073     key : function(key){
13074         return this.map[key];
13075     },
13076    
13077 /**
13078  * Returns true if the collection contains the passed Object as an item.
13079  * @param {Object} o  The Object to look for in the collection.
13080  * @return {Boolean} True if the collection contains the Object as an item.
13081  */
13082     contains : function(o){
13083         return this.indexOf(o) != -1;
13084     },
13085    
13086 /**
13087  * Returns true if the collection contains the passed Object as a key.
13088  * @param {String} key The key to look for in the collection.
13089  * @return {Boolean} True if the collection contains the Object as a key.
13090  */
13091     containsKey : function(key){
13092         return typeof this.map[key] != "undefined";
13093     },
13094    
13095 /**
13096  * Removes all items from the collection.
13097  */
13098     clear : function(){
13099         this.length = 0;
13100         this.items = [];
13101         this.keys = [];
13102         this.map = {};
13103         this.fireEvent("clear");
13104     },
13105    
13106 /**
13107  * Returns the first item in the collection.
13108  * @return {Object} the first item in the collection..
13109  */
13110     first : function(){
13111         return this.items[0]; 
13112     },
13113    
13114 /**
13115  * Returns the last item in the collection.
13116  * @return {Object} the last item in the collection..
13117  */
13118     last : function(){
13119         return this.items[this.length-1];   
13120     },
13121     
13122     _sort : function(property, dir, fn){
13123         var dsc = String(dir).toUpperCase() == "DESC" ? -1 : 1;
13124         fn = fn || function(a, b){
13125             return a-b;
13126         };
13127         var c = [], k = this.keys, items = this.items;
13128         for(var i = 0, len = items.length; i < len; i++){
13129             c[c.length] = {key: k[i], value: items[i], index: i};
13130         }
13131         c.sort(function(a, b){
13132             var v = fn(a[property], b[property]) * dsc;
13133             if(v == 0){
13134                 v = (a.index < b.index ? -1 : 1);
13135             }
13136             return v;
13137         });
13138         for(var i = 0, len = c.length; i < len; i++){
13139             items[i] = c[i].value;
13140             k[i] = c[i].key;
13141         }
13142         this.fireEvent("sort", this);
13143     },
13144     
13145     /**
13146      * Sorts this collection with the passed comparison function
13147      * @param {String} direction (optional) "ASC" or "DESC"
13148      * @param {Function} fn (optional) comparison function
13149      */
13150     sort : function(dir, fn){
13151         this._sort("value", dir, fn);
13152     },
13153     
13154     /**
13155      * Sorts this collection by keys
13156      * @param {String} direction (optional) "ASC" or "DESC"
13157      * @param {Function} fn (optional) a comparison function (defaults to case insensitive string)
13158      */
13159     keySort : function(dir, fn){
13160         this._sort("key", dir, fn || function(a, b){
13161             return String(a).toUpperCase()-String(b).toUpperCase();
13162         });
13163     },
13164     
13165     /**
13166      * Returns a range of items in this collection
13167      * @param {Number} startIndex (optional) defaults to 0
13168      * @param {Number} endIndex (optional) default to the last item
13169      * @return {Array} An array of items
13170      */
13171     getRange : function(start, end){
13172         var items = this.items;
13173         if(items.length < 1){
13174             return [];
13175         }
13176         start = start || 0;
13177         end = Math.min(typeof end == "undefined" ? this.length-1 : end, this.length-1);
13178         var r = [];
13179         if(start <= end){
13180             for(var i = start; i <= end; i++) {
13181                     r[r.length] = items[i];
13182             }
13183         }else{
13184             for(var i = start; i >= end; i--) {
13185                     r[r.length] = items[i];
13186             }
13187         }
13188         return r;
13189     },
13190         
13191     /**
13192      * Filter the <i>objects</i> in this collection by a specific property. 
13193      * Returns a new collection that has been filtered.
13194      * @param {String} property A property on your objects
13195      * @param {String/RegExp} value Either string that the property values 
13196      * should start with or a RegExp to test against the property
13197      * @return {MixedCollection} The new filtered collection
13198      */
13199     filter : function(property, value){
13200         if(!value.exec){ // not a regex
13201             value = String(value);
13202             if(value.length == 0){
13203                 return this.clone();
13204             }
13205             value = new RegExp("^" + Roo.escapeRe(value), "i");
13206         }
13207         return this.filterBy(function(o){
13208             return o && value.test(o[property]);
13209         });
13210         },
13211     
13212     /**
13213      * Filter by a function. * Returns a new collection that has been filtered.
13214      * The passed function will be called with each 
13215      * object in the collection. If the function returns true, the value is included 
13216      * otherwise it is filtered.
13217      * @param {Function} fn The function to be called, it will receive the args o (the object), k (the key)
13218      * @param {Object} scope (optional) The scope of the function (defaults to this) 
13219      * @return {MixedCollection} The new filtered collection
13220      */
13221     filterBy : function(fn, scope){
13222         var r = new Roo.util.MixedCollection();
13223         r.getKey = this.getKey;
13224         var k = this.keys, it = this.items;
13225         for(var i = 0, len = it.length; i < len; i++){
13226             if(fn.call(scope||this, it[i], k[i])){
13227                                 r.add(k[i], it[i]);
13228                         }
13229         }
13230         return r;
13231     },
13232     
13233     /**
13234      * Creates a duplicate of this collection
13235      * @return {MixedCollection}
13236      */
13237     clone : function(){
13238         var r = new Roo.util.MixedCollection();
13239         var k = this.keys, it = this.items;
13240         for(var i = 0, len = it.length; i < len; i++){
13241             r.add(k[i], it[i]);
13242         }
13243         r.getKey = this.getKey;
13244         return r;
13245     }
13246 });
13247 /**
13248  * Returns the item associated with the passed key or index.
13249  * @method
13250  * @param {String/Number} key The key or index of the item.
13251  * @return {Object} The item associated with the passed key.
13252  */
13253 Roo.util.MixedCollection.prototype.get = Roo.util.MixedCollection.prototype.item;/*
13254  * Based on:
13255  * Ext JS Library 1.1.1
13256  * Copyright(c) 2006-2007, Ext JS, LLC.
13257  *
13258  * Originally Released Under LGPL - original licence link has changed is not relivant.
13259  *
13260  * Fork - LGPL
13261  * <script type="text/javascript">
13262  */
13263 /**
13264  * @class Roo.util.JSON
13265  * Modified version of Douglas Crockford"s json.js that doesn"t
13266  * mess with the Object prototype 
13267  * http://www.json.org/js.html
13268  * @singleton
13269  */
13270 Roo.util.JSON = new (function(){
13271     var useHasOwn = {}.hasOwnProperty ? true : false;
13272     
13273     // crashes Safari in some instances
13274     //var validRE = /^("(\\.|[^"\\\n\r])*?"|[,:{}\[\]0-9.\-+Eaeflnr-u \n\r\t])+?$/;
13275     
13276     var pad = function(n) {
13277         return n < 10 ? "0" + n : n;
13278     };
13279     
13280     var m = {
13281         "\b": '\\b',
13282         "\t": '\\t',
13283         "\n": '\\n',
13284         "\f": '\\f',
13285         "\r": '\\r',
13286         '"' : '\\"',
13287         "\\": '\\\\'
13288     };
13289
13290     var encodeString = function(s){
13291         if (/["\\\x00-\x1f]/.test(s)) {
13292             return '"' + s.replace(/([\x00-\x1f\\"])/g, function(a, b) {
13293                 var c = m[b];
13294                 if(c){
13295                     return c;
13296                 }
13297                 c = b.charCodeAt();
13298                 return "\\u00" +
13299                     Math.floor(c / 16).toString(16) +
13300                     (c % 16).toString(16);
13301             }) + '"';
13302         }
13303         return '"' + s + '"';
13304     };
13305     
13306     var encodeArray = function(o){
13307         var a = ["["], b, i, l = o.length, v;
13308             for (i = 0; i < l; i += 1) {
13309                 v = o[i];
13310                 switch (typeof v) {
13311                     case "undefined":
13312                     case "function":
13313                     case "unknown":
13314                         break;
13315                     default:
13316                         if (b) {
13317                             a.push(',');
13318                         }
13319                         a.push(v === null ? "null" : Roo.util.JSON.encode(v));
13320                         b = true;
13321                 }
13322             }
13323             a.push("]");
13324             return a.join("");
13325     };
13326     
13327     var encodeDate = function(o){
13328         return '"' + o.getFullYear() + "-" +
13329                 pad(o.getMonth() + 1) + "-" +
13330                 pad(o.getDate()) + "T" +
13331                 pad(o.getHours()) + ":" +
13332                 pad(o.getMinutes()) + ":" +
13333                 pad(o.getSeconds()) + '"';
13334     };
13335     
13336     /**
13337      * Encodes an Object, Array or other value
13338      * @param {Mixed} o The variable to encode
13339      * @return {String} The JSON string
13340      */
13341     this.encode = function(o)
13342     {
13343         // should this be extended to fully wrap stringify..
13344         
13345         if(typeof o == "undefined" || o === null){
13346             return "null";
13347         }else if(o instanceof Array){
13348             return encodeArray(o);
13349         }else if(o instanceof Date){
13350             return encodeDate(o);
13351         }else if(typeof o == "string"){
13352             return encodeString(o);
13353         }else if(typeof o == "number"){
13354             return isFinite(o) ? String(o) : "null";
13355         }else if(typeof o == "boolean"){
13356             return String(o);
13357         }else {
13358             var a = ["{"], b, i, v;
13359             for (i in o) {
13360                 if(!useHasOwn || o.hasOwnProperty(i)) {
13361                     v = o[i];
13362                     switch (typeof v) {
13363                     case "undefined":
13364                     case "function":
13365                     case "unknown":
13366                         break;
13367                     default:
13368                         if(b){
13369                             a.push(',');
13370                         }
13371                         a.push(this.encode(i), ":",
13372                                 v === null ? "null" : this.encode(v));
13373                         b = true;
13374                     }
13375                 }
13376             }
13377             a.push("}");
13378             return a.join("");
13379         }
13380     };
13381     
13382     /**
13383      * Decodes (parses) a JSON string to an object. If the JSON is invalid, this function throws a SyntaxError.
13384      * @param {String} json The JSON string
13385      * @return {Object} The resulting object
13386      */
13387     this.decode = function(json){
13388         
13389         return  /** eval:var:json */ eval("(" + json + ')');
13390     };
13391 })();
13392 /** 
13393  * Shorthand for {@link Roo.util.JSON#encode}
13394  * @member Roo encode 
13395  * @method */
13396 Roo.encode = typeof(JSON) != 'undefined' && JSON.stringify ? JSON.stringify : Roo.util.JSON.encode;
13397 /** 
13398  * Shorthand for {@link Roo.util.JSON#decode}
13399  * @member Roo decode 
13400  * @method */
13401 Roo.decode = typeof(JSON) != 'undefined' && JSON.parse ? JSON.parse : Roo.util.JSON.decode;
13402 /*
13403  * Based on:
13404  * Ext JS Library 1.1.1
13405  * Copyright(c) 2006-2007, Ext JS, LLC.
13406  *
13407  * Originally Released Under LGPL - original licence link has changed is not relivant.
13408  *
13409  * Fork - LGPL
13410  * <script type="text/javascript">
13411  */
13412  
13413 /**
13414  * @class Roo.util.Format
13415  * Reusable data formatting functions
13416  * @singleton
13417  */
13418 Roo.util.Format = function(){
13419     var trimRe = /^\s+|\s+$/g;
13420     return {
13421         /**
13422          * Truncate a string and add an ellipsis ('...') to the end if it exceeds the specified length
13423          * @param {String} value The string to truncate
13424          * @param {Number} length The maximum length to allow before truncating
13425          * @return {String} The converted text
13426          */
13427         ellipsis : function(value, len){
13428             if(value && value.length > len){
13429                 return value.substr(0, len-3)+"...";
13430             }
13431             return value;
13432         },
13433
13434         /**
13435          * Checks a reference and converts it to empty string if it is undefined
13436          * @param {Mixed} value Reference to check
13437          * @return {Mixed} Empty string if converted, otherwise the original value
13438          */
13439         undef : function(value){
13440             return typeof value != "undefined" ? value : "";
13441         },
13442
13443         /**
13444          * Convert certain characters (&, <, >, and ') to their HTML character equivalents for literal display in web pages.
13445          * @param {String} value The string to encode
13446          * @return {String} The encoded text
13447          */
13448         htmlEncode : function(value){
13449             return !value ? value : String(value).replace(/&/g, "&amp;").replace(/>/g, "&gt;").replace(/</g, "&lt;").replace(/"/g, "&quot;");
13450         },
13451
13452         /**
13453          * Convert certain characters (&, <, >, and ') from their HTML character equivalents.
13454          * @param {String} value The string to decode
13455          * @return {String} The decoded text
13456          */
13457         htmlDecode : function(value){
13458             return !value ? value : String(value).replace(/&amp;/g, "&").replace(/&gt;/g, ">").replace(/&lt;/g, "<").replace(/&quot;/g, '"');
13459         },
13460
13461         /**
13462          * Trims any whitespace from either side of a string
13463          * @param {String} value The text to trim
13464          * @return {String} The trimmed text
13465          */
13466         trim : function(value){
13467             return String(value).replace(trimRe, "");
13468         },
13469
13470         /**
13471          * Returns a substring from within an original string
13472          * @param {String} value The original text
13473          * @param {Number} start The start index of the substring
13474          * @param {Number} length The length of the substring
13475          * @return {String} The substring
13476          */
13477         substr : function(value, start, length){
13478             return String(value).substr(start, length);
13479         },
13480
13481         /**
13482          * Converts a string to all lower case letters
13483          * @param {String} value The text to convert
13484          * @return {String} The converted text
13485          */
13486         lowercase : function(value){
13487             return String(value).toLowerCase();
13488         },
13489
13490         /**
13491          * Converts a string to all upper case letters
13492          * @param {String} value The text to convert
13493          * @return {String} The converted text
13494          */
13495         uppercase : function(value){
13496             return String(value).toUpperCase();
13497         },
13498
13499         /**
13500          * Converts the first character only of a string to upper case
13501          * @param {String} value The text to convert
13502          * @return {String} The converted text
13503          */
13504         capitalize : function(value){
13505             return !value ? value : value.charAt(0).toUpperCase() + value.substr(1).toLowerCase();
13506         },
13507
13508         // private
13509         call : function(value, fn){
13510             if(arguments.length > 2){
13511                 var args = Array.prototype.slice.call(arguments, 2);
13512                 args.unshift(value);
13513                  
13514                 return /** eval:var:value */  eval(fn).apply(window, args);
13515             }else{
13516                 /** eval:var:value */
13517                 return /** eval:var:value */ eval(fn).call(window, value);
13518             }
13519         },
13520
13521        
13522         /**
13523          * safer version of Math.toFixed..??/
13524          * @param {Number/String} value The numeric value to format
13525          * @param {Number/String} value Decimal places 
13526          * @return {String} The formatted currency string
13527          */
13528         toFixed : function(v, n)
13529         {
13530             // why not use to fixed - precision is buggered???
13531             if (!n) {
13532                 return Math.round(v-0);
13533             }
13534             var fact = Math.pow(10,n+1);
13535             v = (Math.round((v-0)*fact))/fact;
13536             var z = (''+fact).substring(2);
13537             if (v == Math.floor(v)) {
13538                 return Math.floor(v) + '.' + z;
13539             }
13540             
13541             // now just padd decimals..
13542             var ps = String(v).split('.');
13543             var fd = (ps[1] + z);
13544             var r = fd.substring(0,n); 
13545             var rm = fd.substring(n); 
13546             if (rm < 5) {
13547                 return ps[0] + '.' + r;
13548             }
13549             r*=1; // turn it into a number;
13550             r++;
13551             if (String(r).length != n) {
13552                 ps[0]*=1;
13553                 ps[0]++;
13554                 r = String(r).substring(1); // chop the end off.
13555             }
13556             
13557             return ps[0] + '.' + r;
13558              
13559         },
13560         
13561         /**
13562          * Format a number as US currency
13563          * @param {Number/String} value The numeric value to format
13564          * @return {String} The formatted currency string
13565          */
13566         usMoney : function(v){
13567             return '$' + Roo.util.Format.number(v);
13568         },
13569         
13570         /**
13571          * Format a number
13572          * eventually this should probably emulate php's number_format
13573          * @param {Number/String} value The numeric value to format
13574          * @param {Number} decimals number of decimal places
13575          * @return {String} The formatted currency string
13576          */
13577         number : function(v,decimals)
13578         {
13579             // multiply and round.
13580             decimals = typeof(decimals) == 'undefined' ? 2 : decimals;
13581             var mul = Math.pow(10, decimals);
13582             var zero = String(mul).substring(1);
13583             v = (Math.round((v-0)*mul))/mul;
13584             
13585             // if it's '0' number.. then
13586             
13587             //v = (v == Math.floor(v)) ? v + "." + zero : ((v*10 == Math.floor(v*10)) ? v + "0" : v);
13588             v = String(v);
13589             var ps = v.split('.');
13590             var whole = ps[0];
13591             
13592             
13593             var r = /(\d+)(\d{3})/;
13594             // add comma's
13595             while (r.test(whole)) {
13596                 whole = whole.replace(r, '$1' + ',' + '$2');
13597             }
13598             
13599             
13600             var sub = ps[1] ?
13601                     // has decimals..
13602                     (decimals ?  ('.'+ ps[1] + zero.substring(ps[1].length)) : '') :
13603                     // does not have decimals
13604                     (decimals ? ('.' + zero) : '');
13605             
13606             
13607             return whole + sub ;
13608         },
13609         
13610         /**
13611          * Parse a value into a formatted date using the specified format pattern.
13612          * @param {Mixed} value The value to format
13613          * @param {String} format (optional) Any valid date format string (defaults to 'm/d/Y')
13614          * @return {String} The formatted date string
13615          */
13616         date : function(v, format){
13617             if(!v){
13618                 return "";
13619             }
13620             if(!(v instanceof Date)){
13621                 v = new Date(Date.parse(v));
13622             }
13623             return v.dateFormat(format || Roo.util.Format.defaults.date);
13624         },
13625
13626         /**
13627          * Returns a date rendering function that can be reused to apply a date format multiple times efficiently
13628          * @param {String} format Any valid date format string
13629          * @return {Function} The date formatting function
13630          */
13631         dateRenderer : function(format){
13632             return function(v){
13633                 return Roo.util.Format.date(v, format);  
13634             };
13635         },
13636
13637         // private
13638         stripTagsRE : /<\/?[^>]+>/gi,
13639         
13640         /**
13641          * Strips all HTML tags
13642          * @param {Mixed} value The text from which to strip tags
13643          * @return {String} The stripped text
13644          */
13645         stripTags : function(v){
13646             return !v ? v : String(v).replace(this.stripTagsRE, "");
13647         }
13648     };
13649 }();
13650 Roo.util.Format.defaults = {
13651     date : 'd/M/Y'
13652 };/*
13653  * Based on:
13654  * Ext JS Library 1.1.1
13655  * Copyright(c) 2006-2007, Ext JS, LLC.
13656  *
13657  * Originally Released Under LGPL - original licence link has changed is not relivant.
13658  *
13659  * Fork - LGPL
13660  * <script type="text/javascript">
13661  */
13662
13663
13664  
13665
13666 /**
13667  * @class Roo.MasterTemplate
13668  * @extends Roo.Template
13669  * Provides a template that can have child templates. The syntax is:
13670 <pre><code>
13671 var t = new Roo.MasterTemplate(
13672         '&lt;select name="{name}"&gt;',
13673                 '&lt;tpl name="options"&gt;&lt;option value="{value:trim}"&gt;{text:ellipsis(10)}&lt;/option&gt;&lt;/tpl&gt;',
13674         '&lt;/select&gt;'
13675 );
13676 t.add('options', {value: 'foo', text: 'bar'});
13677 // or you can add multiple child elements in one shot
13678 t.addAll('options', [
13679     {value: 'foo', text: 'bar'},
13680     {value: 'foo2', text: 'bar2'},
13681     {value: 'foo3', text: 'bar3'}
13682 ]);
13683 // then append, applying the master template values
13684 t.append('my-form', {name: 'my-select'});
13685 </code></pre>
13686 * A name attribute for the child template is not required if you have only one child
13687 * template or you want to refer to them by index.
13688  */
13689 Roo.MasterTemplate = function(){
13690     Roo.MasterTemplate.superclass.constructor.apply(this, arguments);
13691     this.originalHtml = this.html;
13692     var st = {};
13693     var m, re = this.subTemplateRe;
13694     re.lastIndex = 0;
13695     var subIndex = 0;
13696     while(m = re.exec(this.html)){
13697         var name = m[1], content = m[2];
13698         st[subIndex] = {
13699             name: name,
13700             index: subIndex,
13701             buffer: [],
13702             tpl : new Roo.Template(content)
13703         };
13704         if(name){
13705             st[name] = st[subIndex];
13706         }
13707         st[subIndex].tpl.compile();
13708         st[subIndex].tpl.call = this.call.createDelegate(this);
13709         subIndex++;
13710     }
13711     this.subCount = subIndex;
13712     this.subs = st;
13713 };
13714 Roo.extend(Roo.MasterTemplate, Roo.Template, {
13715     /**
13716     * The regular expression used to match sub templates
13717     * @type RegExp
13718     * @property
13719     */
13720     subTemplateRe : /<tpl(?:\sname="([\w-]+)")?>((?:.|\n)*?)<\/tpl>/gi,
13721
13722     /**
13723      * Applies the passed values to a child template.
13724      * @param {String/Number} name (optional) The name or index of the child template
13725      * @param {Array/Object} values The values to be applied to the template
13726      * @return {MasterTemplate} this
13727      */
13728      add : function(name, values){
13729         if(arguments.length == 1){
13730             values = arguments[0];
13731             name = 0;
13732         }
13733         var s = this.subs[name];
13734         s.buffer[s.buffer.length] = s.tpl.apply(values);
13735         return this;
13736     },
13737
13738     /**
13739      * Applies all the passed values to a child template.
13740      * @param {String/Number} name (optional) The name or index of the child template
13741      * @param {Array} values The values to be applied to the template, this should be an array of objects.
13742      * @param {Boolean} reset (optional) True to reset the template first
13743      * @return {MasterTemplate} this
13744      */
13745     fill : function(name, values, reset){
13746         var a = arguments;
13747         if(a.length == 1 || (a.length == 2 && typeof a[1] == "boolean")){
13748             values = a[0];
13749             name = 0;
13750             reset = a[1];
13751         }
13752         if(reset){
13753             this.reset();
13754         }
13755         for(var i = 0, len = values.length; i < len; i++){
13756             this.add(name, values[i]);
13757         }
13758         return this;
13759     },
13760
13761     /**
13762      * Resets the template for reuse
13763      * @return {MasterTemplate} this
13764      */
13765      reset : function(){
13766         var s = this.subs;
13767         for(var i = 0; i < this.subCount; i++){
13768             s[i].buffer = [];
13769         }
13770         return this;
13771     },
13772
13773     applyTemplate : function(values){
13774         var s = this.subs;
13775         var replaceIndex = -1;
13776         this.html = this.originalHtml.replace(this.subTemplateRe, function(m, name){
13777             return s[++replaceIndex].buffer.join("");
13778         });
13779         return Roo.MasterTemplate.superclass.applyTemplate.call(this, values);
13780     },
13781
13782     apply : function(){
13783         return this.applyTemplate.apply(this, arguments);
13784     },
13785
13786     compile : function(){return this;}
13787 });
13788
13789 /**
13790  * Alias for fill().
13791  * @method
13792  */
13793 Roo.MasterTemplate.prototype.addAll = Roo.MasterTemplate.prototype.fill;
13794  /**
13795  * Creates a template from the passed element's value (display:none textarea, preferred) or innerHTML. e.g.
13796  * var tpl = Roo.MasterTemplate.from('element-id');
13797  * @param {String/HTMLElement} el
13798  * @param {Object} config
13799  * @static
13800  */
13801 Roo.MasterTemplate.from = function(el, config){
13802     el = Roo.getDom(el);
13803     return new Roo.MasterTemplate(el.value || el.innerHTML, config || '');
13804 };/*
13805  * Based on:
13806  * Ext JS Library 1.1.1
13807  * Copyright(c) 2006-2007, Ext JS, LLC.
13808  *
13809  * Originally Released Under LGPL - original licence link has changed is not relivant.
13810  *
13811  * Fork - LGPL
13812  * <script type="text/javascript">
13813  */
13814
13815  
13816 /**
13817  * @class Roo.util.CSS
13818  * Utility class for manipulating CSS rules
13819  * @singleton
13820  */
13821 Roo.util.CSS = function(){
13822         var rules = null;
13823         var doc = document;
13824
13825     var camelRe = /(-[a-z])/gi;
13826     var camelFn = function(m, a){ return a.charAt(1).toUpperCase(); };
13827
13828    return {
13829    /**
13830     * Very simple dynamic creation of stylesheets from a text blob of rules.  The text will wrapped in a style
13831     * tag and appended to the HEAD of the document.
13832     * @param {String|Object} cssText The text containing the css rules
13833     * @param {String} id An id to add to the stylesheet for later removal
13834     * @return {StyleSheet}
13835     */
13836     createStyleSheet : function(cssText, id){
13837         var ss;
13838         var head = doc.getElementsByTagName("head")[0];
13839         var nrules = doc.createElement("style");
13840         nrules.setAttribute("type", "text/css");
13841         if(id){
13842             nrules.setAttribute("id", id);
13843         }
13844         if (typeof(cssText) != 'string') {
13845             // support object maps..
13846             // not sure if this a good idea.. 
13847             // perhaps it should be merged with the general css handling
13848             // and handle js style props.
13849             var cssTextNew = [];
13850             for(var n in cssText) {
13851                 var citems = [];
13852                 for(var k in cssText[n]) {
13853                     citems.push( k + ' : ' +cssText[n][k] + ';' );
13854                 }
13855                 cssTextNew.push( n + ' { ' + citems.join(' ') + '} ');
13856                 
13857             }
13858             cssText = cssTextNew.join("\n");
13859             
13860         }
13861        
13862        
13863        if(Roo.isIE){
13864            head.appendChild(nrules);
13865            ss = nrules.styleSheet;
13866            ss.cssText = cssText;
13867        }else{
13868            try{
13869                 nrules.appendChild(doc.createTextNode(cssText));
13870            }catch(e){
13871                nrules.cssText = cssText; 
13872            }
13873            head.appendChild(nrules);
13874            ss = nrules.styleSheet ? nrules.styleSheet : (nrules.sheet || doc.styleSheets[doc.styleSheets.length-1]);
13875        }
13876        this.cacheStyleSheet(ss);
13877        return ss;
13878    },
13879
13880    /**
13881     * Removes a style or link tag by id
13882     * @param {String} id The id of the tag
13883     */
13884    removeStyleSheet : function(id){
13885        var existing = doc.getElementById(id);
13886        if(existing){
13887            existing.parentNode.removeChild(existing);
13888        }
13889    },
13890
13891    /**
13892     * Dynamically swaps an existing stylesheet reference for a new one
13893     * @param {String} id The id of an existing link tag to remove
13894     * @param {String} url The href of the new stylesheet to include
13895     */
13896    swapStyleSheet : function(id, url){
13897        this.removeStyleSheet(id);
13898        var ss = doc.createElement("link");
13899        ss.setAttribute("rel", "stylesheet");
13900        ss.setAttribute("type", "text/css");
13901        ss.setAttribute("id", id);
13902        ss.setAttribute("href", url);
13903        doc.getElementsByTagName("head")[0].appendChild(ss);
13904    },
13905    
13906    /**
13907     * Refresh the rule cache if you have dynamically added stylesheets
13908     * @return {Object} An object (hash) of rules indexed by selector
13909     */
13910    refreshCache : function(){
13911        return this.getRules(true);
13912    },
13913
13914    // private
13915    cacheStyleSheet : function(stylesheet){
13916        if(!rules){
13917            rules = {};
13918        }
13919        try{// try catch for cross domain access issue
13920            var ssRules = stylesheet.cssRules || stylesheet.rules;
13921            for(var j = ssRules.length-1; j >= 0; --j){
13922                rules[ssRules[j].selectorText] = ssRules[j];
13923            }
13924        }catch(e){}
13925    },
13926    
13927    /**
13928     * Gets all css rules for the document
13929     * @param {Boolean} refreshCache true to refresh the internal cache
13930     * @return {Object} An object (hash) of rules indexed by selector
13931     */
13932    getRules : function(refreshCache){
13933                 if(rules == null || refreshCache){
13934                         rules = {};
13935                         var ds = doc.styleSheets;
13936                         for(var i =0, len = ds.length; i < len; i++){
13937                             try{
13938                         this.cacheStyleSheet(ds[i]);
13939                     }catch(e){} 
13940                 }
13941                 }
13942                 return rules;
13943         },
13944         
13945         /**
13946     * Gets an an individual CSS rule by selector(s)
13947     * @param {String/Array} selector The CSS selector or an array of selectors to try. The first selector that is found is returned.
13948     * @param {Boolean} refreshCache true to refresh the internal cache if you have recently updated any rules or added styles dynamically
13949     * @return {CSSRule} The CSS rule or null if one is not found
13950     */
13951    getRule : function(selector, refreshCache){
13952                 var rs = this.getRules(refreshCache);
13953                 if(!(selector instanceof Array)){
13954                     return rs[selector];
13955                 }
13956                 for(var i = 0; i < selector.length; i++){
13957                         if(rs[selector[i]]){
13958                                 return rs[selector[i]];
13959                         }
13960                 }
13961                 return null;
13962         },
13963         
13964         
13965         /**
13966     * Updates a rule property
13967     * @param {String/Array} selector If it's an array it tries each selector until it finds one. Stops immediately once one is found.
13968     * @param {String} property The css property
13969     * @param {String} value The new value for the property
13970     * @return {Boolean} true If a rule was found and updated
13971     */
13972    updateRule : function(selector, property, value){
13973                 if(!(selector instanceof Array)){
13974                         var rule = this.getRule(selector);
13975                         if(rule){
13976                                 rule.style[property.replace(camelRe, camelFn)] = value;
13977                                 return true;
13978                         }
13979                 }else{
13980                         for(var i = 0; i < selector.length; i++){
13981                                 if(this.updateRule(selector[i], property, value)){
13982                                         return true;
13983                                 }
13984                         }
13985                 }
13986                 return false;
13987         }
13988    };   
13989 }();/*
13990  * Based on:
13991  * Ext JS Library 1.1.1
13992  * Copyright(c) 2006-2007, Ext JS, LLC.
13993  *
13994  * Originally Released Under LGPL - original licence link has changed is not relivant.
13995  *
13996  * Fork - LGPL
13997  * <script type="text/javascript">
13998  */
13999
14000  
14001
14002 /**
14003  * @class Roo.util.ClickRepeater
14004  * @extends Roo.util.Observable
14005  * 
14006  * A wrapper class which can be applied to any element. Fires a "click" event while the
14007  * mouse is pressed. The interval between firings may be specified in the config but
14008  * defaults to 10 milliseconds.
14009  * 
14010  * Optionally, a CSS class may be applied to the element during the time it is pressed.
14011  * 
14012  * @cfg {String/HTMLElement/Element} el The element to act as a button.
14013  * @cfg {Number} delay The initial delay before the repeating event begins firing.
14014  * Similar to an autorepeat key delay.
14015  * @cfg {Number} interval The interval between firings of the "click" event. Default 10 ms.
14016  * @cfg {String} pressClass A CSS class name to be applied to the element while pressed.
14017  * @cfg {Boolean} accelerate True if autorepeating should start slowly and accelerate.
14018  *           "interval" and "delay" are ignored. "immediate" is honored.
14019  * @cfg {Boolean} preventDefault True to prevent the default click event
14020  * @cfg {Boolean} stopDefault True to stop the default click event
14021  * 
14022  * @history
14023  *     2007-02-02 jvs Original code contributed by Nige "Animal" White
14024  *     2007-02-02 jvs Renamed to ClickRepeater
14025  *   2007-02-03 jvs Modifications for FF Mac and Safari 
14026  *
14027  *  @constructor
14028  * @param {String/HTMLElement/Element} el The element to listen on
14029  * @param {Object} config
14030  **/
14031 Roo.util.ClickRepeater = function(el, config)
14032 {
14033     this.el = Roo.get(el);
14034     this.el.unselectable();
14035
14036     Roo.apply(this, config);
14037
14038     this.addEvents({
14039     /**
14040      * @event mousedown
14041      * Fires when the mouse button is depressed.
14042      * @param {Roo.util.ClickRepeater} this
14043      */
14044         "mousedown" : true,
14045     /**
14046      * @event click
14047      * Fires on a specified interval during the time the element is pressed.
14048      * @param {Roo.util.ClickRepeater} this
14049      */
14050         "click" : true,
14051     /**
14052      * @event mouseup
14053      * Fires when the mouse key is released.
14054      * @param {Roo.util.ClickRepeater} this
14055      */
14056         "mouseup" : true
14057     });
14058
14059     this.el.on("mousedown", this.handleMouseDown, this);
14060     if(this.preventDefault || this.stopDefault){
14061         this.el.on("click", function(e){
14062             if(this.preventDefault){
14063                 e.preventDefault();
14064             }
14065             if(this.stopDefault){
14066                 e.stopEvent();
14067             }
14068         }, this);
14069     }
14070
14071     // allow inline handler
14072     if(this.handler){
14073         this.on("click", this.handler,  this.scope || this);
14074     }
14075
14076     Roo.util.ClickRepeater.superclass.constructor.call(this);
14077 };
14078
14079 Roo.extend(Roo.util.ClickRepeater, Roo.util.Observable, {
14080     interval : 20,
14081     delay: 250,
14082     preventDefault : true,
14083     stopDefault : false,
14084     timer : 0,
14085
14086     // private
14087     handleMouseDown : function(){
14088         clearTimeout(this.timer);
14089         this.el.blur();
14090         if(this.pressClass){
14091             this.el.addClass(this.pressClass);
14092         }
14093         this.mousedownTime = new Date();
14094
14095         Roo.get(document).on("mouseup", this.handleMouseUp, this);
14096         this.el.on("mouseout", this.handleMouseOut, this);
14097
14098         this.fireEvent("mousedown", this);
14099         this.fireEvent("click", this);
14100         
14101         this.timer = this.click.defer(this.delay || this.interval, this);
14102     },
14103
14104     // private
14105     click : function(){
14106         this.fireEvent("click", this);
14107         this.timer = this.click.defer(this.getInterval(), this);
14108     },
14109
14110     // private
14111     getInterval: function(){
14112         if(!this.accelerate){
14113             return this.interval;
14114         }
14115         var pressTime = this.mousedownTime.getElapsed();
14116         if(pressTime < 500){
14117             return 400;
14118         }else if(pressTime < 1700){
14119             return 320;
14120         }else if(pressTime < 2600){
14121             return 250;
14122         }else if(pressTime < 3500){
14123             return 180;
14124         }else if(pressTime < 4400){
14125             return 140;
14126         }else if(pressTime < 5300){
14127             return 80;
14128         }else if(pressTime < 6200){
14129             return 50;
14130         }else{
14131             return 10;
14132         }
14133     },
14134
14135     // private
14136     handleMouseOut : function(){
14137         clearTimeout(this.timer);
14138         if(this.pressClass){
14139             this.el.removeClass(this.pressClass);
14140         }
14141         this.el.on("mouseover", this.handleMouseReturn, this);
14142     },
14143
14144     // private
14145     handleMouseReturn : function(){
14146         this.el.un("mouseover", this.handleMouseReturn);
14147         if(this.pressClass){
14148             this.el.addClass(this.pressClass);
14149         }
14150         this.click();
14151     },
14152
14153     // private
14154     handleMouseUp : function(){
14155         clearTimeout(this.timer);
14156         this.el.un("mouseover", this.handleMouseReturn);
14157         this.el.un("mouseout", this.handleMouseOut);
14158         Roo.get(document).un("mouseup", this.handleMouseUp);
14159         this.el.removeClass(this.pressClass);
14160         this.fireEvent("mouseup", this);
14161     }
14162 });/*
14163  * Based on:
14164  * Ext JS Library 1.1.1
14165  * Copyright(c) 2006-2007, Ext JS, LLC.
14166  *
14167  * Originally Released Under LGPL - original licence link has changed is not relivant.
14168  *
14169  * Fork - LGPL
14170  * <script type="text/javascript">
14171  */
14172
14173  
14174 /**
14175  * @class Roo.KeyNav
14176  * <p>Provides a convenient wrapper for normalized keyboard navigation.  KeyNav allows you to bind
14177  * navigation keys to function calls that will get called when the keys are pressed, providing an easy
14178  * way to implement custom navigation schemes for any UI component.</p>
14179  * <p>The following are all of the possible keys that can be implemented: enter, left, right, up, down, tab, esc,
14180  * pageUp, pageDown, del, home, end.  Usage:</p>
14181  <pre><code>
14182 var nav = new Roo.KeyNav("my-element", {
14183     "left" : function(e){
14184         this.moveLeft(e.ctrlKey);
14185     },
14186     "right" : function(e){
14187         this.moveRight(e.ctrlKey);
14188     },
14189     "enter" : function(e){
14190         this.save();
14191     },
14192     scope : this
14193 });
14194 </code></pre>
14195  * @constructor
14196  * @param {String/HTMLElement/Roo.Element} el The element to bind to
14197  * @param {Object} config The config
14198  */
14199 Roo.KeyNav = function(el, config){
14200     this.el = Roo.get(el);
14201     Roo.apply(this, config);
14202     if(!this.disabled){
14203         this.disabled = true;
14204         this.enable();
14205     }
14206 };
14207
14208 Roo.KeyNav.prototype = {
14209     /**
14210      * @cfg {Boolean} disabled
14211      * True to disable this KeyNav instance (defaults to false)
14212      */
14213     disabled : false,
14214     /**
14215      * @cfg {String} defaultEventAction
14216      * The method to call on the {@link Roo.EventObject} after this KeyNav intercepts a key.  Valid values are
14217      * {@link Roo.EventObject#stopEvent}, {@link Roo.EventObject#preventDefault} and
14218      * {@link Roo.EventObject#stopPropagation} (defaults to 'stopEvent')
14219      */
14220     defaultEventAction: "stopEvent",
14221     /**
14222      * @cfg {Boolean} forceKeyDown
14223      * Handle the keydown event instead of keypress (defaults to false).  KeyNav automatically does this for IE since
14224      * IE does not propagate special keys on keypress, but setting this to true will force other browsers to also
14225      * handle keydown instead of keypress.
14226      */
14227     forceKeyDown : false,
14228
14229     // private
14230     prepareEvent : function(e){
14231         var k = e.getKey();
14232         var h = this.keyToHandler[k];
14233         //if(h && this[h]){
14234         //    e.stopPropagation();
14235         //}
14236         if(Roo.isSafari && h && k >= 37 && k <= 40){
14237             e.stopEvent();
14238         }
14239     },
14240
14241     // private
14242     relay : function(e){
14243         var k = e.getKey();
14244         var h = this.keyToHandler[k];
14245         if(h && this[h]){
14246             if(this.doRelay(e, this[h], h) !== true){
14247                 e[this.defaultEventAction]();
14248             }
14249         }
14250     },
14251
14252     // private
14253     doRelay : function(e, h, hname){
14254         return h.call(this.scope || this, e);
14255     },
14256
14257     // possible handlers
14258     enter : false,
14259     left : false,
14260     right : false,
14261     up : false,
14262     down : false,
14263     tab : false,
14264     esc : false,
14265     pageUp : false,
14266     pageDown : false,
14267     del : false,
14268     home : false,
14269     end : false,
14270
14271     // quick lookup hash
14272     keyToHandler : {
14273         37 : "left",
14274         39 : "right",
14275         38 : "up",
14276         40 : "down",
14277         33 : "pageUp",
14278         34 : "pageDown",
14279         46 : "del",
14280         36 : "home",
14281         35 : "end",
14282         13 : "enter",
14283         27 : "esc",
14284         9  : "tab"
14285     },
14286
14287         /**
14288          * Enable this KeyNav
14289          */
14290         enable: function(){
14291                 if(this.disabled){
14292             // ie won't do special keys on keypress, no one else will repeat keys with keydown
14293             // the EventObject will normalize Safari automatically
14294             if(this.forceKeyDown || Roo.isIE || Roo.isAir){
14295                 this.el.on("keydown", this.relay,  this);
14296             }else{
14297                 this.el.on("keydown", this.prepareEvent,  this);
14298                 this.el.on("keypress", this.relay,  this);
14299             }
14300                     this.disabled = false;
14301                 }
14302         },
14303
14304         /**
14305          * Disable this KeyNav
14306          */
14307         disable: function(){
14308                 if(!this.disabled){
14309                     if(this.forceKeyDown || Roo.isIE || Roo.isAir){
14310                 this.el.un("keydown", this.relay);
14311             }else{
14312                 this.el.un("keydown", this.prepareEvent);
14313                 this.el.un("keypress", this.relay);
14314             }
14315                     this.disabled = true;
14316                 }
14317         }
14318 };/*
14319  * Based on:
14320  * Ext JS Library 1.1.1
14321  * Copyright(c) 2006-2007, Ext JS, LLC.
14322  *
14323  * Originally Released Under LGPL - original licence link has changed is not relivant.
14324  *
14325  * Fork - LGPL
14326  * <script type="text/javascript">
14327  */
14328
14329  
14330 /**
14331  * @class Roo.KeyMap
14332  * Handles mapping keys to actions for an element. One key map can be used for multiple actions.
14333  * The constructor accepts the same config object as defined by {@link #addBinding}.
14334  * If you bind a callback function to a KeyMap, anytime the KeyMap handles an expected key
14335  * combination it will call the function with this signature (if the match is a multi-key
14336  * combination the callback will still be called only once): (String key, Roo.EventObject e)
14337  * A KeyMap can also handle a string representation of keys.<br />
14338  * Usage:
14339  <pre><code>
14340 // map one key by key code
14341 var map = new Roo.KeyMap("my-element", {
14342     key: 13, // or Roo.EventObject.ENTER
14343     fn: myHandler,
14344     scope: myObject
14345 });
14346
14347 // map multiple keys to one action by string
14348 var map = new Roo.KeyMap("my-element", {
14349     key: "a\r\n\t",
14350     fn: myHandler,
14351     scope: myObject
14352 });
14353
14354 // map multiple keys to multiple actions by strings and array of codes
14355 var map = new Roo.KeyMap("my-element", [
14356     {
14357         key: [10,13],
14358         fn: function(){ alert("Return was pressed"); }
14359     }, {
14360         key: "abc",
14361         fn: function(){ alert('a, b or c was pressed'); }
14362     }, {
14363         key: "\t",
14364         ctrl:true,
14365         shift:true,
14366         fn: function(){ alert('Control + shift + tab was pressed.'); }
14367     }
14368 ]);
14369 </code></pre>
14370  * <b>Note: A KeyMap starts enabled</b>
14371  * @constructor
14372  * @param {String/HTMLElement/Roo.Element} el The element to bind to
14373  * @param {Object} config The config (see {@link #addBinding})
14374  * @param {String} eventName (optional) The event to bind to (defaults to "keydown")
14375  */
14376 Roo.KeyMap = function(el, config, eventName){
14377     this.el  = Roo.get(el);
14378     this.eventName = eventName || "keydown";
14379     this.bindings = [];
14380     if(config){
14381         this.addBinding(config);
14382     }
14383     this.enable();
14384 };
14385
14386 Roo.KeyMap.prototype = {
14387     /**
14388      * True to stop the event from bubbling and prevent the default browser action if the
14389      * key was handled by the KeyMap (defaults to false)
14390      * @type Boolean
14391      */
14392     stopEvent : false,
14393
14394     /**
14395      * Add a new binding to this KeyMap. The following config object properties are supported:
14396      * <pre>
14397 Property    Type             Description
14398 ----------  ---------------  ----------------------------------------------------------------------
14399 key         String/Array     A single keycode or an array of keycodes to handle
14400 shift       Boolean          True to handle key only when shift is pressed (defaults to false)
14401 ctrl        Boolean          True to handle key only when ctrl is pressed (defaults to false)
14402 alt         Boolean          True to handle key only when alt is pressed (defaults to false)
14403 fn          Function         The function to call when KeyMap finds the expected key combination
14404 scope       Object           The scope of the callback function
14405 </pre>
14406      *
14407      * Usage:
14408      * <pre><code>
14409 // Create a KeyMap
14410 var map = new Roo.KeyMap(document, {
14411     key: Roo.EventObject.ENTER,
14412     fn: handleKey,
14413     scope: this
14414 });
14415
14416 //Add a new binding to the existing KeyMap later
14417 map.addBinding({
14418     key: 'abc',
14419     shift: true,
14420     fn: handleKey,
14421     scope: this
14422 });
14423 </code></pre>
14424      * @param {Object/Array} config A single KeyMap config or an array of configs
14425      */
14426         addBinding : function(config){
14427         if(config instanceof Array){
14428             for(var i = 0, len = config.length; i < len; i++){
14429                 this.addBinding(config[i]);
14430             }
14431             return;
14432         }
14433         var keyCode = config.key,
14434             shift = config.shift, 
14435             ctrl = config.ctrl, 
14436             alt = config.alt,
14437             fn = config.fn,
14438             scope = config.scope;
14439         if(typeof keyCode == "string"){
14440             var ks = [];
14441             var keyString = keyCode.toUpperCase();
14442             for(var j = 0, len = keyString.length; j < len; j++){
14443                 ks.push(keyString.charCodeAt(j));
14444             }
14445             keyCode = ks;
14446         }
14447         var keyArray = keyCode instanceof Array;
14448         var handler = function(e){
14449             if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) &&  (!alt || e.altKey)){
14450                 var k = e.getKey();
14451                 if(keyArray){
14452                     for(var i = 0, len = keyCode.length; i < len; i++){
14453                         if(keyCode[i] == k){
14454                           if(this.stopEvent){
14455                               e.stopEvent();
14456                           }
14457                           fn.call(scope || window, k, e);
14458                           return;
14459                         }
14460                     }
14461                 }else{
14462                     if(k == keyCode){
14463                         if(this.stopEvent){
14464                            e.stopEvent();
14465                         }
14466                         fn.call(scope || window, k, e);
14467                     }
14468                 }
14469             }
14470         };
14471         this.bindings.push(handler);  
14472         },
14473
14474     /**
14475      * Shorthand for adding a single key listener
14476      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the
14477      * following options:
14478      * {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
14479      * @param {Function} fn The function to call
14480      * @param {Object} scope (optional) The scope of the function
14481      */
14482     on : function(key, fn, scope){
14483         var keyCode, shift, ctrl, alt;
14484         if(typeof key == "object" && !(key instanceof Array)){
14485             keyCode = key.key;
14486             shift = key.shift;
14487             ctrl = key.ctrl;
14488             alt = key.alt;
14489         }else{
14490             keyCode = key;
14491         }
14492         this.addBinding({
14493             key: keyCode,
14494             shift: shift,
14495             ctrl: ctrl,
14496             alt: alt,
14497             fn: fn,
14498             scope: scope
14499         })
14500     },
14501
14502     // private
14503     handleKeyDown : function(e){
14504             if(this.enabled){ //just in case
14505             var b = this.bindings;
14506             for(var i = 0, len = b.length; i < len; i++){
14507                 b[i].call(this, e);
14508             }
14509             }
14510         },
14511         
14512         /**
14513          * Returns true if this KeyMap is enabled
14514          * @return {Boolean} 
14515          */
14516         isEnabled : function(){
14517             return this.enabled;  
14518         },
14519         
14520         /**
14521          * Enables this KeyMap
14522          */
14523         enable: function(){
14524                 if(!this.enabled){
14525                     this.el.on(this.eventName, this.handleKeyDown, this);
14526                     this.enabled = true;
14527                 }
14528         },
14529
14530         /**
14531          * Disable this KeyMap
14532          */
14533         disable: function(){
14534                 if(this.enabled){
14535                     this.el.removeListener(this.eventName, this.handleKeyDown, this);
14536                     this.enabled = false;
14537                 }
14538         }
14539 };/*
14540  * Based on:
14541  * Ext JS Library 1.1.1
14542  * Copyright(c) 2006-2007, Ext JS, LLC.
14543  *
14544  * Originally Released Under LGPL - original licence link has changed is not relivant.
14545  *
14546  * Fork - LGPL
14547  * <script type="text/javascript">
14548  */
14549
14550  
14551 /**
14552  * @class Roo.util.TextMetrics
14553  * Provides precise pixel measurements for blocks of text so that you can determine exactly how high and
14554  * wide, in pixels, a given block of text will be.
14555  * @singleton
14556  */
14557 Roo.util.TextMetrics = function(){
14558     var shared;
14559     return {
14560         /**
14561          * Measures the size of the specified text
14562          * @param {String/HTMLElement} el The element, dom node or id from which to copy existing CSS styles
14563          * that can affect the size of the rendered text
14564          * @param {String} text The text to measure
14565          * @param {Number} fixedWidth (optional) If the text will be multiline, you have to set a fixed width
14566          * in order to accurately measure the text height
14567          * @return {Object} An object containing the text's size {width: (width), height: (height)}
14568          */
14569         measure : function(el, text, fixedWidth){
14570             if(!shared){
14571                 shared = Roo.util.TextMetrics.Instance(el, fixedWidth);
14572             }
14573             shared.bind(el);
14574             shared.setFixedWidth(fixedWidth || 'auto');
14575             return shared.getSize(text);
14576         },
14577
14578         /**
14579          * Return a unique TextMetrics instance that can be bound directly to an element and reused.  This reduces
14580          * the overhead of multiple calls to initialize the style properties on each measurement.
14581          * @param {String/HTMLElement} el The element, dom node or id that the instance will be bound to
14582          * @param {Number} fixedWidth (optional) If the text will be multiline, you have to set a fixed width
14583          * in order to accurately measure the text height
14584          * @return {Roo.util.TextMetrics.Instance} instance The new instance
14585          */
14586         createInstance : function(el, fixedWidth){
14587             return Roo.util.TextMetrics.Instance(el, fixedWidth);
14588         }
14589     };
14590 }();
14591
14592  
14593
14594 Roo.util.TextMetrics.Instance = function(bindTo, fixedWidth){
14595     var ml = new Roo.Element(document.createElement('div'));
14596     document.body.appendChild(ml.dom);
14597     ml.position('absolute');
14598     ml.setLeftTop(-1000, -1000);
14599     ml.hide();
14600
14601     if(fixedWidth){
14602         ml.setWidth(fixedWidth);
14603     }
14604      
14605     var instance = {
14606         /**
14607          * Returns the size of the specified text based on the internal element's style and width properties
14608          * @memberOf Roo.util.TextMetrics.Instance#
14609          * @param {String} text The text to measure
14610          * @return {Object} An object containing the text's size {width: (width), height: (height)}
14611          */
14612         getSize : function(text){
14613             ml.update(text);
14614             var s = ml.getSize();
14615             ml.update('');
14616             return s;
14617         },
14618
14619         /**
14620          * Binds this TextMetrics instance to an element from which to copy existing CSS styles
14621          * that can affect the size of the rendered text
14622          * @memberOf Roo.util.TextMetrics.Instance#
14623          * @param {String/HTMLElement} el The element, dom node or id
14624          */
14625         bind : function(el){
14626             ml.setStyle(
14627                 Roo.fly(el).getStyles('font-size','font-style', 'font-weight', 'font-family','line-height')
14628             );
14629         },
14630
14631         /**
14632          * Sets a fixed width on the internal measurement element.  If the text will be multiline, you have
14633          * to set a fixed width in order to accurately measure the text height.
14634          * @memberOf Roo.util.TextMetrics.Instance#
14635          * @param {Number} width The width to set on the element
14636          */
14637         setFixedWidth : function(width){
14638             ml.setWidth(width);
14639         },
14640
14641         /**
14642          * Returns the measured width of the specified text
14643          * @memberOf Roo.util.TextMetrics.Instance#
14644          * @param {String} text The text to measure
14645          * @return {Number} width The width in pixels
14646          */
14647         getWidth : function(text){
14648             ml.dom.style.width = 'auto';
14649             return this.getSize(text).width;
14650         },
14651
14652         /**
14653          * Returns the measured height of the specified text.  For multiline text, be sure to call
14654          * {@link #setFixedWidth} if necessary.
14655          * @memberOf Roo.util.TextMetrics.Instance#
14656          * @param {String} text The text to measure
14657          * @return {Number} height The height in pixels
14658          */
14659         getHeight : function(text){
14660             return this.getSize(text).height;
14661         }
14662     };
14663
14664     instance.bind(bindTo);
14665
14666     return instance;
14667 };
14668
14669 // backwards compat
14670 Roo.Element.measureText = Roo.util.TextMetrics.measure;/*
14671  * Based on:
14672  * Ext JS Library 1.1.1
14673  * Copyright(c) 2006-2007, Ext JS, LLC.
14674  *
14675  * Originally Released Under LGPL - original licence link has changed is not relivant.
14676  *
14677  * Fork - LGPL
14678  * <script type="text/javascript">
14679  */
14680
14681 /**
14682  * @class Roo.state.Provider
14683  * Abstract base class for state provider implementations. This class provides methods
14684  * for encoding and decoding <b>typed</b> variables including dates and defines the 
14685  * Provider interface.
14686  */
14687 Roo.state.Provider = function(){
14688     /**
14689      * @event statechange
14690      * Fires when a state change occurs.
14691      * @param {Provider} this This state provider
14692      * @param {String} key The state key which was changed
14693      * @param {String} value The encoded value for the state
14694      */
14695     this.addEvents({
14696         "statechange": true
14697     });
14698     this.state = {};
14699     Roo.state.Provider.superclass.constructor.call(this);
14700 };
14701 Roo.extend(Roo.state.Provider, Roo.util.Observable, {
14702     /**
14703      * Returns the current value for a key
14704      * @param {String} name The key name
14705      * @param {Mixed} defaultValue A default value to return if the key's value is not found
14706      * @return {Mixed} The state data
14707      */
14708     get : function(name, defaultValue){
14709         return typeof this.state[name] == "undefined" ?
14710             defaultValue : this.state[name];
14711     },
14712     
14713     /**
14714      * Clears a value from the state
14715      * @param {String} name The key name
14716      */
14717     clear : function(name){
14718         delete this.state[name];
14719         this.fireEvent("statechange", this, name, null);
14720     },
14721     
14722     /**
14723      * Sets the value for a key
14724      * @param {String} name The key name
14725      * @param {Mixed} value The value to set
14726      */
14727     set : function(name, value){
14728         this.state[name] = value;
14729         this.fireEvent("statechange", this, name, value);
14730     },
14731     
14732     /**
14733      * Decodes a string previously encoded with {@link #encodeValue}.
14734      * @param {String} value The value to decode
14735      * @return {Mixed} The decoded value
14736      */
14737     decodeValue : function(cookie){
14738         var re = /^(a|n|d|b|s|o)\:(.*)$/;
14739         var matches = re.exec(unescape(cookie));
14740         if(!matches || !matches[1]) return; // non state cookie
14741         var type = matches[1];
14742         var v = matches[2];
14743         switch(type){
14744             case "n":
14745                 return parseFloat(v);
14746             case "d":
14747                 return new Date(Date.parse(v));
14748             case "b":
14749                 return (v == "1");
14750             case "a":
14751                 var all = [];
14752                 var values = v.split("^");
14753                 for(var i = 0, len = values.length; i < len; i++){
14754                     all.push(this.decodeValue(values[i]));
14755                 }
14756                 return all;
14757            case "o":
14758                 var all = {};
14759                 var values = v.split("^");
14760                 for(var i = 0, len = values.length; i < len; i++){
14761                     var kv = values[i].split("=");
14762                     all[kv[0]] = this.decodeValue(kv[1]);
14763                 }
14764                 return all;
14765            default:
14766                 return v;
14767         }
14768     },
14769     
14770     /**
14771      * Encodes a value including type information.  Decode with {@link #decodeValue}.
14772      * @param {Mixed} value The value to encode
14773      * @return {String} The encoded value
14774      */
14775     encodeValue : function(v){
14776         var enc;
14777         if(typeof v == "number"){
14778             enc = "n:" + v;
14779         }else if(typeof v == "boolean"){
14780             enc = "b:" + (v ? "1" : "0");
14781         }else if(v instanceof Date){
14782             enc = "d:" + v.toGMTString();
14783         }else if(v instanceof Array){
14784             var flat = "";
14785             for(var i = 0, len = v.length; i < len; i++){
14786                 flat += this.encodeValue(v[i]);
14787                 if(i != len-1) flat += "^";
14788             }
14789             enc = "a:" + flat;
14790         }else if(typeof v == "object"){
14791             var flat = "";
14792             for(var key in v){
14793                 if(typeof v[key] != "function"){
14794                     flat += key + "=" + this.encodeValue(v[key]) + "^";
14795                 }
14796             }
14797             enc = "o:" + flat.substring(0, flat.length-1);
14798         }else{
14799             enc = "s:" + v;
14800         }
14801         return escape(enc);        
14802     }
14803 });
14804
14805 /*
14806  * Based on:
14807  * Ext JS Library 1.1.1
14808  * Copyright(c) 2006-2007, Ext JS, LLC.
14809  *
14810  * Originally Released Under LGPL - original licence link has changed is not relivant.
14811  *
14812  * Fork - LGPL
14813  * <script type="text/javascript">
14814  */
14815 /**
14816  * @class Roo.state.Manager
14817  * This is the global state manager. By default all components that are "state aware" check this class
14818  * for state information if you don't pass them a custom state provider. In order for this class
14819  * to be useful, it must be initialized with a provider when your application initializes.
14820  <pre><code>
14821 // in your initialization function
14822 init : function(){
14823    Roo.state.Manager.setProvider(new Roo.state.CookieProvider());
14824    ...
14825    // supposed you have a {@link Roo.BorderLayout}
14826    var layout = new Roo.BorderLayout(...);
14827    layout.restoreState();
14828    // or a {Roo.BasicDialog}
14829    var dialog = new Roo.BasicDialog(...);
14830    dialog.restoreState();
14831  </code></pre>
14832  * @singleton
14833  */
14834 Roo.state.Manager = function(){
14835     var provider = new Roo.state.Provider();
14836     
14837     return {
14838         /**
14839          * Configures the default state provider for your application
14840          * @param {Provider} stateProvider The state provider to set
14841          */
14842         setProvider : function(stateProvider){
14843             provider = stateProvider;
14844         },
14845         
14846         /**
14847          * Returns the current value for a key
14848          * @param {String} name The key name
14849          * @param {Mixed} defaultValue The default value to return if the key lookup does not match
14850          * @return {Mixed} The state data
14851          */
14852         get : function(key, defaultValue){
14853             return provider.get(key, defaultValue);
14854         },
14855         
14856         /**
14857          * Sets the value for a key
14858          * @param {String} name The key name
14859          * @param {Mixed} value The state data
14860          */
14861          set : function(key, value){
14862             provider.set(key, value);
14863         },
14864         
14865         /**
14866          * Clears a value from the state
14867          * @param {String} name The key name
14868          */
14869         clear : function(key){
14870             provider.clear(key);
14871         },
14872         
14873         /**
14874          * Gets the currently configured state provider
14875          * @return {Provider} The state provider
14876          */
14877         getProvider : function(){
14878             return provider;
14879         }
14880     };
14881 }();
14882 /*
14883  * Based on:
14884  * Ext JS Library 1.1.1
14885  * Copyright(c) 2006-2007, Ext JS, LLC.
14886  *
14887  * Originally Released Under LGPL - original licence link has changed is not relivant.
14888  *
14889  * Fork - LGPL
14890  * <script type="text/javascript">
14891  */
14892 /**
14893  * @class Roo.state.CookieProvider
14894  * @extends Roo.state.Provider
14895  * The default Provider implementation which saves state via cookies.
14896  * <br />Usage:
14897  <pre><code>
14898    var cp = new Roo.state.CookieProvider({
14899        path: "/cgi-bin/",
14900        expires: new Date(new Date().getTime()+(1000*60*60*24*30)); //30 days
14901        domain: "roojs.com"
14902    })
14903    Roo.state.Manager.setProvider(cp);
14904  </code></pre>
14905  * @cfg {String} path The path for which the cookie is active (defaults to root '/' which makes it active for all pages in the site)
14906  * @cfg {Date} expires The cookie expiration date (defaults to 7 days from now)
14907  * @cfg {String} domain The domain to save the cookie for.  Note that you cannot specify a different domain than
14908  * your page is on, but you can specify a sub-domain, or simply the domain itself like 'roojs.com' to include
14909  * all sub-domains if you need to access cookies across different sub-domains (defaults to null which uses the same
14910  * domain the page is running on including the 'www' like 'www.roojs.com')
14911  * @cfg {Boolean} secure True if the site is using SSL (defaults to false)
14912  * @constructor
14913  * Create a new CookieProvider
14914  * @param {Object} config The configuration object
14915  */
14916 Roo.state.CookieProvider = function(config){
14917     Roo.state.CookieProvider.superclass.constructor.call(this);
14918     this.path = "/";
14919     this.expires = new Date(new Date().getTime()+(1000*60*60*24*7)); //7 days
14920     this.domain = null;
14921     this.secure = false;
14922     Roo.apply(this, config);
14923     this.state = this.readCookies();
14924 };
14925
14926 Roo.extend(Roo.state.CookieProvider, Roo.state.Provider, {
14927     // private
14928     set : function(name, value){
14929         if(typeof value == "undefined" || value === null){
14930             this.clear(name);
14931             return;
14932         }
14933         this.setCookie(name, value);
14934         Roo.state.CookieProvider.superclass.set.call(this, name, value);
14935     },
14936
14937     // private
14938     clear : function(name){
14939         this.clearCookie(name);
14940         Roo.state.CookieProvider.superclass.clear.call(this, name);
14941     },
14942
14943     // private
14944     readCookies : function(){
14945         var cookies = {};
14946         var c = document.cookie + ";";
14947         var re = /\s?(.*?)=(.*?);/g;
14948         var matches;
14949         while((matches = re.exec(c)) != null){
14950             var name = matches[1];
14951             var value = matches[2];
14952             if(name && name.substring(0,3) == "ys-"){
14953                 cookies[name.substr(3)] = this.decodeValue(value);
14954             }
14955         }
14956         return cookies;
14957     },
14958
14959     // private
14960     setCookie : function(name, value){
14961         document.cookie = "ys-"+ name + "=" + this.encodeValue(value) +
14962            ((this.expires == null) ? "" : ("; expires=" + this.expires.toGMTString())) +
14963            ((this.path == null) ? "" : ("; path=" + this.path)) +
14964            ((this.domain == null) ? "" : ("; domain=" + this.domain)) +
14965            ((this.secure == true) ? "; secure" : "");
14966     },
14967
14968     // private
14969     clearCookie : function(name){
14970         document.cookie = "ys-" + name + "=null; expires=Thu, 01-Jan-70 00:00:01 GMT" +
14971            ((this.path == null) ? "" : ("; path=" + this.path)) +
14972            ((this.domain == null) ? "" : ("; domain=" + this.domain)) +
14973            ((this.secure == true) ? "; secure" : "");
14974     }
14975 });/*
14976  * Based on:
14977  * Ext JS Library 1.1.1
14978  * Copyright(c) 2006-2007, Ext JS, LLC.
14979  *
14980  * Originally Released Under LGPL - original licence link has changed is not relivant.
14981  *
14982  * Fork - LGPL
14983  * <script type="text/javascript">
14984  */
14985  
14986
14987 /**
14988  * @class Roo.ComponentMgr
14989  * Provides a common registry of all components on a page so that they can be easily accessed by component id (see {@link Roo.getCmp}).
14990  * @singleton
14991  */
14992 Roo.ComponentMgr = function(){
14993     var all = new Roo.util.MixedCollection();
14994
14995     return {
14996         /**
14997          * Registers a component.
14998          * @param {Roo.Component} c The component
14999          */
15000         register : function(c){
15001             all.add(c);
15002         },
15003
15004         /**
15005          * Unregisters a component.
15006          * @param {Roo.Component} c The component
15007          */
15008         unregister : function(c){
15009             all.remove(c);
15010         },
15011
15012         /**
15013          * Returns a component by id
15014          * @param {String} id The component id
15015          */
15016         get : function(id){
15017             return all.get(id);
15018         },
15019
15020         /**
15021          * Registers a function that will be called when a specified component is added to ComponentMgr
15022          * @param {String} id The component id
15023          * @param {Funtction} fn The callback function
15024          * @param {Object} scope The scope of the callback
15025          */
15026         onAvailable : function(id, fn, scope){
15027             all.on("add", function(index, o){
15028                 if(o.id == id){
15029                     fn.call(scope || o, o);
15030                     all.un("add", fn, scope);
15031                 }
15032             });
15033         }
15034     };
15035 }();/*
15036  * Based on:
15037  * Ext JS Library 1.1.1
15038  * Copyright(c) 2006-2007, Ext JS, LLC.
15039  *
15040  * Originally Released Under LGPL - original licence link has changed is not relivant.
15041  *
15042  * Fork - LGPL
15043  * <script type="text/javascript">
15044  */
15045  
15046 /**
15047  * @class Roo.Component
15048  * @extends Roo.util.Observable
15049  * Base class for all major Roo components.  All subclasses of Component can automatically participate in the standard
15050  * Roo component lifecycle of creation, rendering and destruction.  They also have automatic support for basic hide/show
15051  * and enable/disable behavior.  Component allows any subclass to be lazy-rendered into any {@link Roo.Container} and
15052  * to be automatically registered with the {@link Roo.ComponentMgr} so that it can be referenced at any time via {@link Roo.getCmp}.
15053  * All visual components (widgets) that require rendering into a layout should subclass Component.
15054  * @constructor
15055  * @param {Roo.Element/String/Object} config The configuration options.  If an element is passed, it is set as the internal
15056  * 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
15057  * and is used as the component id.  Otherwise, it is assumed to be a standard config object and is applied to the component.
15058  */
15059 Roo.Component = function(config){
15060     config = config || {};
15061     if(config.tagName || config.dom || typeof config == "string"){ // element object
15062         config = {el: config, id: config.id || config};
15063     }
15064     this.initialConfig = config;
15065
15066     Roo.apply(this, config);
15067     this.addEvents({
15068         /**
15069          * @event disable
15070          * Fires after the component is disabled.
15071              * @param {Roo.Component} this
15072              */
15073         disable : true,
15074         /**
15075          * @event enable
15076          * Fires after the component is enabled.
15077              * @param {Roo.Component} this
15078              */
15079         enable : true,
15080         /**
15081          * @event beforeshow
15082          * Fires before the component is shown.  Return false to stop the show.
15083              * @param {Roo.Component} this
15084              */
15085         beforeshow : true,
15086         /**
15087          * @event show
15088          * Fires after the component is shown.
15089              * @param {Roo.Component} this
15090              */
15091         show : true,
15092         /**
15093          * @event beforehide
15094          * Fires before the component is hidden. Return false to stop the hide.
15095              * @param {Roo.Component} this
15096              */
15097         beforehide : true,
15098         /**
15099          * @event hide
15100          * Fires after the component is hidden.
15101              * @param {Roo.Component} this
15102              */
15103         hide : true,
15104         /**
15105          * @event beforerender
15106          * Fires before the component is rendered. Return false to stop the render.
15107              * @param {Roo.Component} this
15108              */
15109         beforerender : true,
15110         /**
15111          * @event render
15112          * Fires after the component is rendered.
15113              * @param {Roo.Component} this
15114              */
15115         render : true,
15116         /**
15117          * @event beforedestroy
15118          * Fires before the component is destroyed. Return false to stop the destroy.
15119              * @param {Roo.Component} this
15120              */
15121         beforedestroy : true,
15122         /**
15123          * @event destroy
15124          * Fires after the component is destroyed.
15125              * @param {Roo.Component} this
15126              */
15127         destroy : true
15128     });
15129     if(!this.id){
15130         this.id = "ext-comp-" + (++Roo.Component.AUTO_ID);
15131     }
15132     Roo.ComponentMgr.register(this);
15133     Roo.Component.superclass.constructor.call(this);
15134     this.initComponent();
15135     if(this.renderTo){ // not supported by all components yet. use at your own risk!
15136         this.render(this.renderTo);
15137         delete this.renderTo;
15138     }
15139 };
15140
15141 /** @private */
15142 Roo.Component.AUTO_ID = 1000;
15143
15144 Roo.extend(Roo.Component, Roo.util.Observable, {
15145     /**
15146      * @scope Roo.Component.prototype
15147      * @type {Boolean}
15148      * true if this component is hidden. Read-only.
15149      */
15150     hidden : false,
15151     /**
15152      * @type {Boolean}
15153      * true if this component is disabled. Read-only.
15154      */
15155     disabled : false,
15156     /**
15157      * @type {Boolean}
15158      * true if this component has been rendered. Read-only.
15159      */
15160     rendered : false,
15161     
15162     /** @cfg {String} disableClass
15163      * CSS class added to the component when it is disabled (defaults to "x-item-disabled").
15164      */
15165     disabledClass : "x-item-disabled",
15166         /** @cfg {Boolean} allowDomMove
15167          * Whether the component can move the Dom node when rendering (defaults to true).
15168          */
15169     allowDomMove : true,
15170     /** @cfg {String} hideMode
15171      * How this component should hidden. Supported values are
15172      * "visibility" (css visibility), "offsets" (negative offset position) and
15173      * "display" (css display) - defaults to "display".
15174      */
15175     hideMode: 'display',
15176
15177     /** @private */
15178     ctype : "Roo.Component",
15179
15180     /**
15181      * @cfg {String} actionMode 
15182      * which property holds the element that used for  hide() / show() / disable() / enable()
15183      * default is 'el' 
15184      */
15185     actionMode : "el",
15186
15187     /** @private */
15188     getActionEl : function(){
15189         return this[this.actionMode];
15190     },
15191
15192     initComponent : Roo.emptyFn,
15193     /**
15194      * If this is a lazy rendering component, render it to its container element.
15195      * @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.
15196      */
15197     render : function(container, position){
15198         if(!this.rendered && this.fireEvent("beforerender", this) !== false){
15199             if(!container && this.el){
15200                 this.el = Roo.get(this.el);
15201                 container = this.el.dom.parentNode;
15202                 this.allowDomMove = false;
15203             }
15204             this.container = Roo.get(container);
15205             this.rendered = true;
15206             if(position !== undefined){
15207                 if(typeof position == 'number'){
15208                     position = this.container.dom.childNodes[position];
15209                 }else{
15210                     position = Roo.getDom(position);
15211                 }
15212             }
15213             this.onRender(this.container, position || null);
15214             if(this.cls){
15215                 this.el.addClass(this.cls);
15216                 delete this.cls;
15217             }
15218             if(this.style){
15219                 this.el.applyStyles(this.style);
15220                 delete this.style;
15221             }
15222             this.fireEvent("render", this);
15223             this.afterRender(this.container);
15224             if(this.hidden){
15225                 this.hide();
15226             }
15227             if(this.disabled){
15228                 this.disable();
15229             }
15230         }
15231         return this;
15232     },
15233
15234     /** @private */
15235     // default function is not really useful
15236     onRender : function(ct, position){
15237         if(this.el){
15238             this.el = Roo.get(this.el);
15239             if(this.allowDomMove !== false){
15240                 ct.dom.insertBefore(this.el.dom, position);
15241             }
15242         }
15243     },
15244
15245     /** @private */
15246     getAutoCreate : function(){
15247         var cfg = typeof this.autoCreate == "object" ?
15248                       this.autoCreate : Roo.apply({}, this.defaultAutoCreate);
15249         if(this.id && !cfg.id){
15250             cfg.id = this.id;
15251         }
15252         return cfg;
15253     },
15254
15255     /** @private */
15256     afterRender : Roo.emptyFn,
15257
15258     /**
15259      * Destroys this component by purging any event listeners, removing the component's element from the DOM,
15260      * removing the component from its {@link Roo.Container} (if applicable) and unregistering it from {@link Roo.ComponentMgr}.
15261      */
15262     destroy : function(){
15263         if(this.fireEvent("beforedestroy", this) !== false){
15264             this.purgeListeners();
15265             this.beforeDestroy();
15266             if(this.rendered){
15267                 this.el.removeAllListeners();
15268                 this.el.remove();
15269                 if(this.actionMode == "container"){
15270                     this.container.remove();
15271                 }
15272             }
15273             this.onDestroy();
15274             Roo.ComponentMgr.unregister(this);
15275             this.fireEvent("destroy", this);
15276         }
15277     },
15278
15279         /** @private */
15280     beforeDestroy : function(){
15281
15282     },
15283
15284         /** @private */
15285         onDestroy : function(){
15286
15287     },
15288
15289     /**
15290      * Returns the underlying {@link Roo.Element}.
15291      * @return {Roo.Element} The element
15292      */
15293     getEl : function(){
15294         return this.el;
15295     },
15296
15297     /**
15298      * Returns the id of this component.
15299      * @return {String}
15300      */
15301     getId : function(){
15302         return this.id;
15303     },
15304
15305     /**
15306      * Try to focus this component.
15307      * @param {Boolean} selectText True to also select the text in this component (if applicable)
15308      * @return {Roo.Component} this
15309      */
15310     focus : function(selectText){
15311         if(this.rendered){
15312             this.el.focus();
15313             if(selectText === true){
15314                 this.el.dom.select();
15315             }
15316         }
15317         return this;
15318     },
15319
15320     /** @private */
15321     blur : function(){
15322         if(this.rendered){
15323             this.el.blur();
15324         }
15325         return this;
15326     },
15327
15328     /**
15329      * Disable this component.
15330      * @return {Roo.Component} this
15331      */
15332     disable : function(){
15333         if(this.rendered){
15334             this.onDisable();
15335         }
15336         this.disabled = true;
15337         this.fireEvent("disable", this);
15338         return this;
15339     },
15340
15341         // private
15342     onDisable : function(){
15343         this.getActionEl().addClass(this.disabledClass);
15344         this.el.dom.disabled = true;
15345     },
15346
15347     /**
15348      * Enable this component.
15349      * @return {Roo.Component} this
15350      */
15351     enable : function(){
15352         if(this.rendered){
15353             this.onEnable();
15354         }
15355         this.disabled = false;
15356         this.fireEvent("enable", this);
15357         return this;
15358     },
15359
15360         // private
15361     onEnable : function(){
15362         this.getActionEl().removeClass(this.disabledClass);
15363         this.el.dom.disabled = false;
15364     },
15365
15366     /**
15367      * Convenience function for setting disabled/enabled by boolean.
15368      * @param {Boolean} disabled
15369      */
15370     setDisabled : function(disabled){
15371         this[disabled ? "disable" : "enable"]();
15372     },
15373
15374     /**
15375      * Show this component.
15376      * @return {Roo.Component} this
15377      */
15378     show: function(){
15379         if(this.fireEvent("beforeshow", this) !== false){
15380             this.hidden = false;
15381             if(this.rendered){
15382                 this.onShow();
15383             }
15384             this.fireEvent("show", this);
15385         }
15386         return this;
15387     },
15388
15389     // private
15390     onShow : function(){
15391         var ae = this.getActionEl();
15392         if(this.hideMode == 'visibility'){
15393             ae.dom.style.visibility = "visible";
15394         }else if(this.hideMode == 'offsets'){
15395             ae.removeClass('x-hidden');
15396         }else{
15397             ae.dom.style.display = "";
15398         }
15399     },
15400
15401     /**
15402      * Hide this component.
15403      * @return {Roo.Component} this
15404      */
15405     hide: function(){
15406         if(this.fireEvent("beforehide", this) !== false){
15407             this.hidden = true;
15408             if(this.rendered){
15409                 this.onHide();
15410             }
15411             this.fireEvent("hide", this);
15412         }
15413         return this;
15414     },
15415
15416     // private
15417     onHide : function(){
15418         var ae = this.getActionEl();
15419         if(this.hideMode == 'visibility'){
15420             ae.dom.style.visibility = "hidden";
15421         }else if(this.hideMode == 'offsets'){
15422             ae.addClass('x-hidden');
15423         }else{
15424             ae.dom.style.display = "none";
15425         }
15426     },
15427
15428     /**
15429      * Convenience function to hide or show this component by boolean.
15430      * @param {Boolean} visible True to show, false to hide
15431      * @return {Roo.Component} this
15432      */
15433     setVisible: function(visible){
15434         if(visible) {
15435             this.show();
15436         }else{
15437             this.hide();
15438         }
15439         return this;
15440     },
15441
15442     /**
15443      * Returns true if this component is visible.
15444      */
15445     isVisible : function(){
15446         return this.getActionEl().isVisible();
15447     },
15448
15449     cloneConfig : function(overrides){
15450         overrides = overrides || {};
15451         var id = overrides.id || Roo.id();
15452         var cfg = Roo.applyIf(overrides, this.initialConfig);
15453         cfg.id = id; // prevent dup id
15454         return new this.constructor(cfg);
15455     }
15456 });/*
15457  * Based on:
15458  * Ext JS Library 1.1.1
15459  * Copyright(c) 2006-2007, Ext JS, LLC.
15460  *
15461  * Originally Released Under LGPL - original licence link has changed is not relivant.
15462  *
15463  * Fork - LGPL
15464  * <script type="text/javascript">
15465  */
15466
15467 /**
15468  * @class Roo.BoxComponent
15469  * @extends Roo.Component
15470  * Base class for any visual {@link Roo.Component} that uses a box container.  BoxComponent provides automatic box
15471  * model adjustments for sizing and positioning and will work correctly withnin the Component rendering model.  All
15472  * container classes should subclass BoxComponent so that they will work consistently when nested within other Ext
15473  * layout containers.
15474  * @constructor
15475  * @param {Roo.Element/String/Object} config The configuration options.
15476  */
15477 Roo.BoxComponent = function(config){
15478     Roo.Component.call(this, config);
15479     this.addEvents({
15480         /**
15481          * @event resize
15482          * Fires after the component is resized.
15483              * @param {Roo.Component} this
15484              * @param {Number} adjWidth The box-adjusted width that was set
15485              * @param {Number} adjHeight The box-adjusted height that was set
15486              * @param {Number} rawWidth The width that was originally specified
15487              * @param {Number} rawHeight The height that was originally specified
15488              */
15489         resize : true,
15490         /**
15491          * @event move
15492          * Fires after the component is moved.
15493              * @param {Roo.Component} this
15494              * @param {Number} x The new x position
15495              * @param {Number} y The new y position
15496              */
15497         move : true
15498     });
15499 };
15500
15501 Roo.extend(Roo.BoxComponent, Roo.Component, {
15502     // private, set in afterRender to signify that the component has been rendered
15503     boxReady : false,
15504     // private, used to defer height settings to subclasses
15505     deferHeight: false,
15506     /** @cfg {Number} width
15507      * width (optional) size of component
15508      */
15509      /** @cfg {Number} height
15510      * height (optional) size of component
15511      */
15512      
15513     /**
15514      * Sets the width and height of the component.  This method fires the resize event.  This method can accept
15515      * either width and height as separate numeric arguments, or you can pass a size object like {width:10, height:20}.
15516      * @param {Number/Object} width The new width to set, or a size object in the format {width, height}
15517      * @param {Number} height The new height to set (not required if a size object is passed as the first arg)
15518      * @return {Roo.BoxComponent} this
15519      */
15520     setSize : function(w, h){
15521         // support for standard size objects
15522         if(typeof w == 'object'){
15523             h = w.height;
15524             w = w.width;
15525         }
15526         // not rendered
15527         if(!this.boxReady){
15528             this.width = w;
15529             this.height = h;
15530             return this;
15531         }
15532
15533         // prevent recalcs when not needed
15534         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
15535             return this;
15536         }
15537         this.lastSize = {width: w, height: h};
15538
15539         var adj = this.adjustSize(w, h);
15540         var aw = adj.width, ah = adj.height;
15541         if(aw !== undefined || ah !== undefined){ // this code is nasty but performs better with floaters
15542             var rz = this.getResizeEl();
15543             if(!this.deferHeight && aw !== undefined && ah !== undefined){
15544                 rz.setSize(aw, ah);
15545             }else if(!this.deferHeight && ah !== undefined){
15546                 rz.setHeight(ah);
15547             }else if(aw !== undefined){
15548                 rz.setWidth(aw);
15549             }
15550             this.onResize(aw, ah, w, h);
15551             this.fireEvent('resize', this, aw, ah, w, h);
15552         }
15553         return this;
15554     },
15555
15556     /**
15557      * Gets the current size of the component's underlying element.
15558      * @return {Object} An object containing the element's size {width: (element width), height: (element height)}
15559      */
15560     getSize : function(){
15561         return this.el.getSize();
15562     },
15563
15564     /**
15565      * Gets the current XY position of the component's underlying element.
15566      * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
15567      * @return {Array} The XY position of the element (e.g., [100, 200])
15568      */
15569     getPosition : function(local){
15570         if(local === true){
15571             return [this.el.getLeft(true), this.el.getTop(true)];
15572         }
15573         return this.xy || this.el.getXY();
15574     },
15575
15576     /**
15577      * Gets the current box measurements of the component's underlying element.
15578      * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
15579      * @returns {Object} box An object in the format {x, y, width, height}
15580      */
15581     getBox : function(local){
15582         var s = this.el.getSize();
15583         if(local){
15584             s.x = this.el.getLeft(true);
15585             s.y = this.el.getTop(true);
15586         }else{
15587             var xy = this.xy || this.el.getXY();
15588             s.x = xy[0];
15589             s.y = xy[1];
15590         }
15591         return s;
15592     },
15593
15594     /**
15595      * Sets the current box measurements of the component's underlying element.
15596      * @param {Object} box An object in the format {x, y, width, height}
15597      * @returns {Roo.BoxComponent} this
15598      */
15599     updateBox : function(box){
15600         this.setSize(box.width, box.height);
15601         this.setPagePosition(box.x, box.y);
15602         return this;
15603     },
15604
15605     // protected
15606     getResizeEl : function(){
15607         return this.resizeEl || this.el;
15608     },
15609
15610     // protected
15611     getPositionEl : function(){
15612         return this.positionEl || this.el;
15613     },
15614
15615     /**
15616      * Sets the left and top of the component.  To set the page XY position instead, use {@link #setPagePosition}.
15617      * This method fires the move event.
15618      * @param {Number} left The new left
15619      * @param {Number} top The new top
15620      * @returns {Roo.BoxComponent} this
15621      */
15622     setPosition : function(x, y){
15623         this.x = x;
15624         this.y = y;
15625         if(!this.boxReady){
15626             return this;
15627         }
15628         var adj = this.adjustPosition(x, y);
15629         var ax = adj.x, ay = adj.y;
15630
15631         var el = this.getPositionEl();
15632         if(ax !== undefined || ay !== undefined){
15633             if(ax !== undefined && ay !== undefined){
15634                 el.setLeftTop(ax, ay);
15635             }else if(ax !== undefined){
15636                 el.setLeft(ax);
15637             }else if(ay !== undefined){
15638                 el.setTop(ay);
15639             }
15640             this.onPosition(ax, ay);
15641             this.fireEvent('move', this, ax, ay);
15642         }
15643         return this;
15644     },
15645
15646     /**
15647      * Sets the page XY position of the component.  To set the left and top instead, use {@link #setPosition}.
15648      * This method fires the move event.
15649      * @param {Number} x The new x position
15650      * @param {Number} y The new y position
15651      * @returns {Roo.BoxComponent} this
15652      */
15653     setPagePosition : function(x, y){
15654         this.pageX = x;
15655         this.pageY = y;
15656         if(!this.boxReady){
15657             return;
15658         }
15659         if(x === undefined || y === undefined){ // cannot translate undefined points
15660             return;
15661         }
15662         var p = this.el.translatePoints(x, y);
15663         this.setPosition(p.left, p.top);
15664         return this;
15665     },
15666
15667     // private
15668     onRender : function(ct, position){
15669         Roo.BoxComponent.superclass.onRender.call(this, ct, position);
15670         if(this.resizeEl){
15671             this.resizeEl = Roo.get(this.resizeEl);
15672         }
15673         if(this.positionEl){
15674             this.positionEl = Roo.get(this.positionEl);
15675         }
15676     },
15677
15678     // private
15679     afterRender : function(){
15680         Roo.BoxComponent.superclass.afterRender.call(this);
15681         this.boxReady = true;
15682         this.setSize(this.width, this.height);
15683         if(this.x || this.y){
15684             this.setPosition(this.x, this.y);
15685         }
15686         if(this.pageX || this.pageY){
15687             this.setPagePosition(this.pageX, this.pageY);
15688         }
15689     },
15690
15691     /**
15692      * Force the component's size to recalculate based on the underlying element's current height and width.
15693      * @returns {Roo.BoxComponent} this
15694      */
15695     syncSize : function(){
15696         delete this.lastSize;
15697         this.setSize(this.el.getWidth(), this.el.getHeight());
15698         return this;
15699     },
15700
15701     /**
15702      * Called after the component is resized, this method is empty by default but can be implemented by any
15703      * subclass that needs to perform custom logic after a resize occurs.
15704      * @param {Number} adjWidth The box-adjusted width that was set
15705      * @param {Number} adjHeight The box-adjusted height that was set
15706      * @param {Number} rawWidth The width that was originally specified
15707      * @param {Number} rawHeight The height that was originally specified
15708      */
15709     onResize : function(adjWidth, adjHeight, rawWidth, rawHeight){
15710
15711     },
15712
15713     /**
15714      * Called after the component is moved, this method is empty by default but can be implemented by any
15715      * subclass that needs to perform custom logic after a move occurs.
15716      * @param {Number} x The new x position
15717      * @param {Number} y The new y position
15718      */
15719     onPosition : function(x, y){
15720
15721     },
15722
15723     // private
15724     adjustSize : function(w, h){
15725         if(this.autoWidth){
15726             w = 'auto';
15727         }
15728         if(this.autoHeight){
15729             h = 'auto';
15730         }
15731         return {width : w, height: h};
15732     },
15733
15734     // private
15735     adjustPosition : function(x, y){
15736         return {x : x, y: y};
15737     }
15738 });/*
15739  * Original code for Roojs - LGPL
15740  * <script type="text/javascript">
15741  */
15742  
15743 /**
15744  * @class Roo.XComponent
15745  * A delayed Element creator...
15746  * Or a way to group chunks of interface together.
15747  * technically this is a wrapper around a tree of Roo elements (which defines a 'module'),
15748  *  used in conjunction with XComponent.build() it will create an instance of each element,
15749  *  then call addxtype() to build the User interface.
15750  * 
15751  * Mypart.xyx = new Roo.XComponent({
15752
15753     parent : 'Mypart.xyz', // empty == document.element.!!
15754     order : '001',
15755     name : 'xxxx'
15756     region : 'xxxx'
15757     disabled : function() {} 
15758      
15759     tree : function() { // return an tree of xtype declared components
15760         var MODULE = this;
15761         return 
15762         {
15763             xtype : 'NestedLayoutPanel',
15764             // technicall
15765         }
15766      ]
15767  *})
15768  *
15769  *
15770  * It can be used to build a big heiracy, with parent etc.
15771  * or you can just use this to render a single compoent to a dom element
15772  * MYPART.render(Roo.Element | String(id) | dom_element )
15773  *
15774  *
15775  * Usage patterns.
15776  *
15777  * Classic Roo
15778  *
15779  * Roo is designed primarily as a single page application, so the UI build for a standard interface will
15780  * expect a single 'TOP' level module normally indicated by the 'parent' of the XComponent definition being defined as false.
15781  *
15782  * Each sub module is expected to have a parent pointing to the class name of it's parent module.
15783  *
15784  * When the top level is false, a 'Roo.BorderLayout' is created and the element is flagged as 'topModule'
15785  * - if mulitple topModules exist, the last one is defined as the top module.
15786  *
15787  * Embeded Roo
15788  * 
15789  * When the top level or multiple modules are to embedded into a existing HTML page,
15790  * the parent element can container '#id' of the element where the module will be drawn.
15791  *
15792  * Bootstrap Roo
15793  *
15794  * Unlike classic Roo, the bootstrap tends not to be used as a single page.
15795  * it relies more on a include mechanism, where sub modules are included into an outer page.
15796  * This is normally managed by the builder tools using Roo.apply( options, Included.Sub.Module )
15797  * 
15798  * Bootstrap Roo Included elements
15799  *
15800  * Our builder application needs the ability to preview these sub compoennts. They will normally have parent=false set,
15801  * hence confusing the component builder as it thinks there are multiple top level elements. 
15802  *
15803  * 
15804  * 
15805  * @extends Roo.util.Observable
15806  * @constructor
15807  * @param cfg {Object} configuration of component
15808  * 
15809  */
15810 Roo.XComponent = function(cfg) {
15811     Roo.apply(this, cfg);
15812     this.addEvents({ 
15813         /**
15814              * @event built
15815              * Fires when this the componnt is built
15816              * @param {Roo.XComponent} c the component
15817              */
15818         'built' : true
15819         
15820     });
15821     this.region = this.region || 'center'; // default..
15822     Roo.XComponent.register(this);
15823     this.modules = false;
15824     this.el = false; // where the layout goes..
15825     
15826     
15827 }
15828 Roo.extend(Roo.XComponent, Roo.util.Observable, {
15829     /**
15830      * @property el
15831      * The created element (with Roo.factory())
15832      * @type {Roo.Layout}
15833      */
15834     el  : false,
15835     
15836     /**
15837      * @property el
15838      * for BC  - use el in new code
15839      * @type {Roo.Layout}
15840      */
15841     panel : false,
15842     
15843     /**
15844      * @property layout
15845      * for BC  - use el in new code
15846      * @type {Roo.Layout}
15847      */
15848     layout : false,
15849     
15850      /**
15851      * @cfg {Function|boolean} disabled
15852      * If this module is disabled by some rule, return true from the funtion
15853      */
15854     disabled : false,
15855     
15856     /**
15857      * @cfg {String} parent 
15858      * Name of parent element which it get xtype added to..
15859      */
15860     parent: false,
15861     
15862     /**
15863      * @cfg {String} order
15864      * Used to set the order in which elements are created (usefull for multiple tabs)
15865      */
15866     
15867     order : false,
15868     /**
15869      * @cfg {String} name
15870      * String to display while loading.
15871      */
15872     name : false,
15873     /**
15874      * @cfg {String} region
15875      * Region to render component to (defaults to center)
15876      */
15877     region : 'center',
15878     
15879     /**
15880      * @cfg {Array} items
15881      * A single item array - the first element is the root of the tree..
15882      * It's done this way to stay compatible with the Xtype system...
15883      */
15884     items : false,
15885     
15886     /**
15887      * @property _tree
15888      * The method that retuns the tree of parts that make up this compoennt 
15889      * @type {function}
15890      */
15891     _tree  : false,
15892     
15893      /**
15894      * render
15895      * render element to dom or tree
15896      * @param {Roo.Element|String|DomElement} optional render to if parent is not set.
15897      */
15898     
15899     render : function(el)
15900     {
15901         
15902         el = el || false;
15903         var hp = this.parent ? 1 : 0;
15904         Roo.log(this);
15905         
15906         if (!el && typeof(this.parent) == 'string' && this.parent.substring(0,1) == '#') {
15907             // if parent is a '#.....' string, then let's use that..
15908             var ename = this.parent.substr(1);
15909             this.parent = false;
15910             Roo.log(ename);
15911             switch (ename) {
15912                 case 'bootstrap-body' :
15913                     if (typeof(Roo.bootstrap.Body) != 'undefined') {
15914                         this.parent = { el :  new  Roo.bootstrap.Body() };
15915                         Roo.log("setting el to doc body");
15916                          
15917                     } else {
15918                         throw "Container is bootstrap body, but Roo.bootstrap.Body is not defined";
15919                     }
15920                     break;
15921                 case 'bootstrap':
15922                     this.parent = { el : true};
15923                     // fall through
15924                 default:
15925                     el = Roo.get(ename);
15926                     break;
15927             }
15928                 
15929             
15930             if (!el && !this.parent) {
15931                 Roo.log("Warning - element can not be found :#" + ename );
15932                 return;
15933             }
15934         }
15935         Roo.log("EL:");Roo.log(el);
15936         Roo.log("this.parent.el:");Roo.log(this.parent.el);
15937         
15938         var tree = this._tree ? this._tree() : this.tree();
15939
15940         // altertive root elements ??? - we need a better way to indicate these.
15941         var is_alt = (typeof(Roo.bootstrap) != 'undefined' && tree.xns == Roo.bootstrap) ||
15942                         (typeof(Roo.mailer) != 'undefined' && tree.xns == Roo.mailer) ;
15943         
15944         if (!this.parent && is_alt) {
15945             //el = Roo.get(document.body);
15946             this.parent = { el : true };
15947         }
15948             
15949             
15950         
15951         if (!this.parent) {
15952             
15953             Roo.log("no parent - creating one");
15954             
15955             el = el ? Roo.get(el) : false;      
15956             
15957             // it's a top level one..
15958             this.parent =  {
15959                 el : new Roo.BorderLayout(el || document.body, {
15960                 
15961                      center: {
15962                          titlebar: false,
15963                          autoScroll:false,
15964                          closeOnTab: true,
15965                          tabPosition: 'top',
15966                           //resizeTabs: true,
15967                          alwaysShowTabs: el && hp? false :  true,
15968                          hideTabs: el || !hp ? true :  false,
15969                          minTabWidth: 140
15970                      }
15971                  })
15972             }
15973         }
15974         
15975                 if (!this.parent.el) {
15976                         // probably an old style ctor, which has been disabled.
15977                         return;
15978                         
15979                 }
15980                 // The 'tree' method is  '_tree now' 
15981             
15982         tree.region = tree.region || this.region;
15983         
15984         if (this.parent.el === true) {
15985             // bootstrap... - body..
15986             this.parent.el = Roo.factory(tree);
15987         }
15988         
15989         this.el = this.parent.el.addxtype(tree);
15990         this.fireEvent('built', this);
15991         
15992         this.panel = this.el;
15993         this.layout = this.panel.layout;
15994                 this.parentLayout = this.parent.layout  || false;  
15995          
15996     }
15997     
15998 });
15999
16000 Roo.apply(Roo.XComponent, {
16001     /**
16002      * @property  hideProgress
16003      * true to disable the building progress bar.. usefull on single page renders.
16004      * @type Boolean
16005      */
16006     hideProgress : false,
16007     /**
16008      * @property  buildCompleted
16009      * True when the builder has completed building the interface.
16010      * @type Boolean
16011      */
16012     buildCompleted : false,
16013      
16014     /**
16015      * @property  topModule
16016      * the upper most module - uses document.element as it's constructor.
16017      * @type Object
16018      */
16019      
16020     topModule  : false,
16021       
16022     /**
16023      * @property  modules
16024      * array of modules to be created by registration system.
16025      * @type {Array} of Roo.XComponent
16026      */
16027     
16028     modules : [],
16029     /**
16030      * @property  elmodules
16031      * array of modules to be created by which use #ID 
16032      * @type {Array} of Roo.XComponent
16033      */
16034      
16035     elmodules : [],
16036
16037      /**
16038      * @property  build_from_html
16039      * Build elements from html - used by bootstrap HTML stuff 
16040      *    - this is cleared after build is completed
16041      * @type {boolean} true  (default false)
16042      */
16043      
16044     build_from_html : false,
16045
16046     /**
16047      * Register components to be built later.
16048      *
16049      * This solves the following issues
16050      * - Building is not done on page load, but after an authentication process has occured.
16051      * - Interface elements are registered on page load
16052      * - Parent Interface elements may not be loaded before child, so this handles that..
16053      * 
16054      *
16055      * example:
16056      * 
16057      * MyApp.register({
16058           order : '000001',
16059           module : 'Pman.Tab.projectMgr',
16060           region : 'center',
16061           parent : 'Pman.layout',
16062           disabled : false,  // or use a function..
16063         })
16064      
16065      * * @param {Object} details about module
16066      */
16067     register : function(obj) {
16068                 
16069         Roo.XComponent.event.fireEvent('register', obj);
16070         switch(typeof(obj.disabled) ) {
16071                 
16072             case 'undefined':
16073                 break;
16074             
16075             case 'function':
16076                 if ( obj.disabled() ) {
16077                         return;
16078                 }
16079                 break;
16080             
16081             default:
16082                 if (obj.disabled) {
16083                         return;
16084                 }
16085                 break;
16086         }
16087                 
16088         this.modules.push(obj);
16089          
16090     },
16091     /**
16092      * convert a string to an object..
16093      * eg. 'AAA.BBB' -> finds AAA.BBB
16094
16095      */
16096     
16097     toObject : function(str)
16098     {
16099         if (!str || typeof(str) == 'object') {
16100             return str;
16101         }
16102         if (str.substring(0,1) == '#') {
16103             return str;
16104         }
16105
16106         var ar = str.split('.');
16107         var rt, o;
16108         rt = ar.shift();
16109             /** eval:var:o */
16110         try {
16111             eval('if (typeof ' + rt + ' == "undefined"){ o = false;} o = ' + rt + ';');
16112         } catch (e) {
16113             throw "Module not found : " + str;
16114         }
16115         
16116         if (o === false) {
16117             throw "Module not found : " + str;
16118         }
16119         Roo.each(ar, function(e) {
16120             if (typeof(o[e]) == 'undefined') {
16121                 throw "Module not found : " + str;
16122             }
16123             o = o[e];
16124         });
16125         
16126         return o;
16127         
16128     },
16129     
16130     
16131     /**
16132      * move modules into their correct place in the tree..
16133      * 
16134      */
16135     preBuild : function ()
16136     {
16137         var _t = this;
16138         Roo.each(this.modules , function (obj)
16139         {
16140             Roo.XComponent.event.fireEvent('beforebuild', obj);
16141             
16142             var opar = obj.parent;
16143             try { 
16144                 obj.parent = this.toObject(opar);
16145             } catch(e) {
16146                 Roo.log("parent:toObject failed: " + e.toString());
16147                 return;
16148             }
16149             
16150             if (!obj.parent) {
16151                 Roo.debug && Roo.log("GOT top level module");
16152                 Roo.debug && Roo.log(obj);
16153                 obj.modules = new Roo.util.MixedCollection(false, 
16154                     function(o) { return o.order + '' }
16155                 );
16156                 this.topModule = obj;
16157                 return;
16158             }
16159                         // parent is a string (usually a dom element name..)
16160             if (typeof(obj.parent) == 'string') {
16161                 this.elmodules.push(obj);
16162                 return;
16163             }
16164             if (obj.parent.constructor != Roo.XComponent) {
16165                 Roo.log("Warning : Object Parent is not instance of XComponent:" + obj.name)
16166             }
16167             if (!obj.parent.modules) {
16168                 obj.parent.modules = new Roo.util.MixedCollection(false, 
16169                     function(o) { return o.order + '' }
16170                 );
16171             }
16172             if (obj.parent.disabled) {
16173                 obj.disabled = true;
16174             }
16175             obj.parent.modules.add(obj);
16176         }, this);
16177     },
16178     
16179      /**
16180      * make a list of modules to build.
16181      * @return {Array} list of modules. 
16182      */ 
16183     
16184     buildOrder : function()
16185     {
16186         var _this = this;
16187         var cmp = function(a,b) {   
16188             return String(a).toUpperCase() > String(b).toUpperCase() ? 1 : -1;
16189         };
16190         if ((!this.topModule || !this.topModule.modules) && !this.elmodules.length) {
16191             throw "No top level modules to build";
16192         }
16193         
16194         // make a flat list in order of modules to build.
16195         var mods = this.topModule ? [ this.topModule ] : [];
16196                 
16197         
16198         // elmodules (is a list of DOM based modules )
16199         Roo.each(this.elmodules, function(e) {
16200             mods.push(e);
16201             if (!this.topModule &&
16202                 typeof(e.parent) == 'string' &&
16203                 e.parent.substring(0,1) == '#' &&
16204                 Roo.get(e.parent.substr(1))
16205                ) {
16206                 
16207                 _this.topModule = e;
16208             }
16209             
16210         });
16211
16212         
16213         // add modules to their parents..
16214         var addMod = function(m) {
16215             Roo.debug && Roo.log("build Order: add: " + m.name);
16216                 
16217             mods.push(m);
16218             if (m.modules && !m.disabled) {
16219                 Roo.debug && Roo.log("build Order: " + m.modules.length + " child modules");
16220                 m.modules.keySort('ASC',  cmp );
16221                 Roo.debug && Roo.log("build Order: " + m.modules.length + " child modules (after sort)");
16222     
16223                 m.modules.each(addMod);
16224             } else {
16225                 Roo.debug && Roo.log("build Order: no child modules");
16226             }
16227             // not sure if this is used any more..
16228             if (m.finalize) {
16229                 m.finalize.name = m.name + " (clean up) ";
16230                 mods.push(m.finalize);
16231             }
16232             
16233         }
16234         if (this.topModule && this.topModule.modules) { 
16235             this.topModule.modules.keySort('ASC',  cmp );
16236             this.topModule.modules.each(addMod);
16237         } 
16238         return mods;
16239     },
16240     
16241      /**
16242      * Build the registered modules.
16243      * @param {Object} parent element.
16244      * @param {Function} optional method to call after module has been added.
16245      * 
16246      */ 
16247    
16248     build : function(opts) 
16249     {
16250         
16251         if (typeof(opts) != 'undefined') {
16252             Roo.apply(this,opts);
16253         }
16254         
16255         this.preBuild();
16256         var mods = this.buildOrder();
16257       
16258         //this.allmods = mods;
16259         //Roo.debug && Roo.log(mods);
16260         //return;
16261         if (!mods.length) { // should not happen
16262             throw "NO modules!!!";
16263         }
16264         
16265         
16266         var msg = "Building Interface...";
16267         // flash it up as modal - so we store the mask!?
16268         if (!this.hideProgress && Roo.MessageBox) {
16269             Roo.MessageBox.show({ title: 'loading' });
16270             Roo.MessageBox.show({
16271                title: "Please wait...",
16272                msg: msg,
16273                width:450,
16274                progress:true,
16275                closable:false,
16276                modal: false
16277               
16278             });
16279         }
16280         var total = mods.length;
16281         
16282         var _this = this;
16283         var progressRun = function() {
16284             if (!mods.length) {
16285                 Roo.debug && Roo.log('hide?');
16286                 if (!this.hideProgress && Roo.MessageBox) {
16287                     Roo.MessageBox.hide();
16288                 }
16289                 Roo.XComponent.build_from_html = false; // reset, so dialogs will be build from javascript
16290                 
16291                 Roo.XComponent.event.fireEvent('buildcomplete', _this.topModule);
16292                 
16293                 // THE END...
16294                 return false;   
16295             }
16296             
16297             var m = mods.shift();
16298             
16299             
16300             Roo.debug && Roo.log(m);
16301             // not sure if this is supported any more.. - modules that are are just function
16302             if (typeof(m) == 'function') { 
16303                 m.call(this);
16304                 return progressRun.defer(10, _this);
16305             } 
16306             
16307             
16308             msg = "Building Interface " + (total  - mods.length) + 
16309                     " of " + total + 
16310                     (m.name ? (' - ' + m.name) : '');
16311                         Roo.debug && Roo.log(msg);
16312             if (!this.hideProgress &&  Roo.MessageBox) { 
16313                 Roo.MessageBox.updateProgress(  (total  - mods.length)/total, msg  );
16314             }
16315             
16316          
16317             // is the module disabled?
16318             var disabled = (typeof(m.disabled) == 'function') ?
16319                 m.disabled.call(m.module.disabled) : m.disabled;    
16320             
16321             
16322             if (disabled) {
16323                 return progressRun(); // we do not update the display!
16324             }
16325             
16326             // now build 
16327             
16328                         
16329                         
16330             m.render();
16331             // it's 10 on top level, and 1 on others??? why...
16332             return progressRun.defer(10, _this);
16333              
16334         }
16335         progressRun.defer(1, _this);
16336      
16337         
16338         
16339     },
16340         
16341         
16342         /**
16343          * Event Object.
16344          *
16345          *
16346          */
16347         event: false, 
16348     /**
16349          * wrapper for event.on - aliased later..  
16350          * Typically use to register a event handler for register:
16351          *
16352          * eg. Roo.XComponent.on('register', function(comp) { comp.disable = true } );
16353          *
16354          */
16355     on : false
16356    
16357     
16358     
16359 });
16360
16361 Roo.XComponent.event = new Roo.util.Observable({
16362                 events : { 
16363                         /**
16364                          * @event register
16365                          * Fires when an Component is registered,
16366                          * set the disable property on the Component to stop registration.
16367                          * @param {Roo.XComponent} c the component being registerd.
16368                          * 
16369                          */
16370                         'register' : true,
16371             /**
16372                          * @event beforebuild
16373                          * Fires before each Component is built
16374                          * can be used to apply permissions.
16375                          * @param {Roo.XComponent} c the component being registerd.
16376                          * 
16377                          */
16378                         'beforebuild' : true,
16379                         /**
16380                          * @event buildcomplete
16381                          * Fires on the top level element when all elements have been built
16382                          * @param {Roo.XComponent} the top level component.
16383                          */
16384                         'buildcomplete' : true
16385                         
16386                 }
16387 });
16388
16389 Roo.XComponent.on = Roo.XComponent.event.on.createDelegate(Roo.XComponent.event); 
16390  /*
16391  * Based on:
16392  * Ext JS Library 1.1.1
16393  * Copyright(c) 2006-2007, Ext JS, LLC.
16394  *
16395  * Originally Released Under LGPL - original licence link has changed is not relivant.
16396  *
16397  * Fork - LGPL
16398  * <script type="text/javascript">
16399  */
16400
16401
16402
16403 /*
16404  * These classes are derivatives of the similarly named classes in the YUI Library.
16405  * The original license:
16406  * Copyright (c) 2006, Yahoo! Inc. All rights reserved.
16407  * Code licensed under the BSD License:
16408  * http://developer.yahoo.net/yui/license.txt
16409  */
16410
16411 (function() {
16412
16413 var Event=Roo.EventManager;
16414 var Dom=Roo.lib.Dom;
16415
16416 /**
16417  * @class Roo.dd.DragDrop
16418  * @extends Roo.util.Observable
16419  * Defines the interface and base operation of items that that can be
16420  * dragged or can be drop targets.  It was designed to be extended, overriding
16421  * the event handlers for startDrag, onDrag, onDragOver and onDragOut.
16422  * Up to three html elements can be associated with a DragDrop instance:
16423  * <ul>
16424  * <li>linked element: the element that is passed into the constructor.
16425  * This is the element which defines the boundaries for interaction with
16426  * other DragDrop objects.</li>
16427  * <li>handle element(s): The drag operation only occurs if the element that
16428  * was clicked matches a handle element.  By default this is the linked
16429  * element, but there are times that you will want only a portion of the
16430  * linked element to initiate the drag operation, and the setHandleElId()
16431  * method provides a way to define this.</li>
16432  * <li>drag element: this represents the element that would be moved along
16433  * with the cursor during a drag operation.  By default, this is the linked
16434  * element itself as in {@link Roo.dd.DD}.  setDragElId() lets you define
16435  * a separate element that would be moved, as in {@link Roo.dd.DDProxy}.
16436  * </li>
16437  * </ul>
16438  * This class should not be instantiated until the onload event to ensure that
16439  * the associated elements are available.
16440  * The following would define a DragDrop obj that would interact with any
16441  * other DragDrop obj in the "group1" group:
16442  * <pre>
16443  *  dd = new Roo.dd.DragDrop("div1", "group1");
16444  * </pre>
16445  * Since none of the event handlers have been implemented, nothing would
16446  * actually happen if you were to run the code above.  Normally you would
16447  * override this class or one of the default implementations, but you can
16448  * also override the methods you want on an instance of the class...
16449  * <pre>
16450  *  dd.onDragDrop = function(e, id) {
16451  *  &nbsp;&nbsp;alert("dd was dropped on " + id);
16452  *  }
16453  * </pre>
16454  * @constructor
16455  * @param {String} id of the element that is linked to this instance
16456  * @param {String} sGroup the group of related DragDrop objects
16457  * @param {object} config an object containing configurable attributes
16458  *                Valid properties for DragDrop:
16459  *                    padding, isTarget, maintainOffset, primaryButtonOnly
16460  */
16461 Roo.dd.DragDrop = function(id, sGroup, config) {
16462     if (id) {
16463         this.init(id, sGroup, config);
16464     }
16465     
16466 };
16467
16468 Roo.extend(Roo.dd.DragDrop, Roo.util.Observable , {
16469
16470     /**
16471      * The id of the element associated with this object.  This is what we
16472      * refer to as the "linked element" because the size and position of
16473      * this element is used to determine when the drag and drop objects have
16474      * interacted.
16475      * @property id
16476      * @type String
16477      */
16478     id: null,
16479
16480     /**
16481      * Configuration attributes passed into the constructor
16482      * @property config
16483      * @type object
16484      */
16485     config: null,
16486
16487     /**
16488      * The id of the element that will be dragged.  By default this is same
16489      * as the linked element , but could be changed to another element. Ex:
16490      * Roo.dd.DDProxy
16491      * @property dragElId
16492      * @type String
16493      * @private
16494      */
16495     dragElId: null,
16496
16497     /**
16498      * the id of the element that initiates the drag operation.  By default
16499      * this is the linked element, but could be changed to be a child of this
16500      * element.  This lets us do things like only starting the drag when the
16501      * header element within the linked html element is clicked.
16502      * @property handleElId
16503      * @type String
16504      * @private
16505      */
16506     handleElId: null,
16507
16508     /**
16509      * An associative array of HTML tags that will be ignored if clicked.
16510      * @property invalidHandleTypes
16511      * @type {string: string}
16512      */
16513     invalidHandleTypes: null,
16514
16515     /**
16516      * An associative array of ids for elements that will be ignored if clicked
16517      * @property invalidHandleIds
16518      * @type {string: string}
16519      */
16520     invalidHandleIds: null,
16521
16522     /**
16523      * An indexted array of css class names for elements that will be ignored
16524      * if clicked.
16525      * @property invalidHandleClasses
16526      * @type string[]
16527      */
16528     invalidHandleClasses: null,
16529
16530     /**
16531      * The linked element's absolute X position at the time the drag was
16532      * started
16533      * @property startPageX
16534      * @type int
16535      * @private
16536      */
16537     startPageX: 0,
16538
16539     /**
16540      * The linked element's absolute X position at the time the drag was
16541      * started
16542      * @property startPageY
16543      * @type int
16544      * @private
16545      */
16546     startPageY: 0,
16547
16548     /**
16549      * The group defines a logical collection of DragDrop objects that are
16550      * related.  Instances only get events when interacting with other
16551      * DragDrop object in the same group.  This lets us define multiple
16552      * groups using a single DragDrop subclass if we want.
16553      * @property groups
16554      * @type {string: string}
16555      */
16556     groups: null,
16557
16558     /**
16559      * Individual drag/drop instances can be locked.  This will prevent
16560      * onmousedown start drag.
16561      * @property locked
16562      * @type boolean
16563      * @private
16564      */
16565     locked: false,
16566
16567     /**
16568      * Lock this instance
16569      * @method lock
16570      */
16571     lock: function() { this.locked = true; },
16572
16573     /**
16574      * Unlock this instace
16575      * @method unlock
16576      */
16577     unlock: function() { this.locked = false; },
16578
16579     /**
16580      * By default, all insances can be a drop target.  This can be disabled by
16581      * setting isTarget to false.
16582      * @method isTarget
16583      * @type boolean
16584      */
16585     isTarget: true,
16586
16587     /**
16588      * The padding configured for this drag and drop object for calculating
16589      * the drop zone intersection with this object.
16590      * @method padding
16591      * @type int[]
16592      */
16593     padding: null,
16594
16595     /**
16596      * Cached reference to the linked element
16597      * @property _domRef
16598      * @private
16599      */
16600     _domRef: null,
16601
16602     /**
16603      * Internal typeof flag
16604      * @property __ygDragDrop
16605      * @private
16606      */
16607     __ygDragDrop: true,
16608
16609     /**
16610      * Set to true when horizontal contraints are applied
16611      * @property constrainX
16612      * @type boolean
16613      * @private
16614      */
16615     constrainX: false,
16616
16617     /**
16618      * Set to true when vertical contraints are applied
16619      * @property constrainY
16620      * @type boolean
16621      * @private
16622      */
16623     constrainY: false,
16624
16625     /**
16626      * The left constraint
16627      * @property minX
16628      * @type int
16629      * @private
16630      */
16631     minX: 0,
16632
16633     /**
16634      * The right constraint
16635      * @property maxX
16636      * @type int
16637      * @private
16638      */
16639     maxX: 0,
16640
16641     /**
16642      * The up constraint
16643      * @property minY
16644      * @type int
16645      * @type int
16646      * @private
16647      */
16648     minY: 0,
16649
16650     /**
16651      * The down constraint
16652      * @property maxY
16653      * @type int
16654      * @private
16655      */
16656     maxY: 0,
16657
16658     /**
16659      * Maintain offsets when we resetconstraints.  Set to true when you want
16660      * the position of the element relative to its parent to stay the same
16661      * when the page changes
16662      *
16663      * @property maintainOffset
16664      * @type boolean
16665      */
16666     maintainOffset: false,
16667
16668     /**
16669      * Array of pixel locations the element will snap to if we specified a
16670      * horizontal graduation/interval.  This array is generated automatically
16671      * when you define a tick interval.
16672      * @property xTicks
16673      * @type int[]
16674      */
16675     xTicks: null,
16676
16677     /**
16678      * Array of pixel locations the element will snap to if we specified a
16679      * vertical graduation/interval.  This array is generated automatically
16680      * when you define a tick interval.
16681      * @property yTicks
16682      * @type int[]
16683      */
16684     yTicks: null,
16685
16686     /**
16687      * By default the drag and drop instance will only respond to the primary
16688      * button click (left button for a right-handed mouse).  Set to true to
16689      * allow drag and drop to start with any mouse click that is propogated
16690      * by the browser
16691      * @property primaryButtonOnly
16692      * @type boolean
16693      */
16694     primaryButtonOnly: true,
16695
16696     /**
16697      * The availabe property is false until the linked dom element is accessible.
16698      * @property available
16699      * @type boolean
16700      */
16701     available: false,
16702
16703     /**
16704      * By default, drags can only be initiated if the mousedown occurs in the
16705      * region the linked element is.  This is done in part to work around a
16706      * bug in some browsers that mis-report the mousedown if the previous
16707      * mouseup happened outside of the window.  This property is set to true
16708      * if outer handles are defined.
16709      *
16710      * @property hasOuterHandles
16711      * @type boolean
16712      * @default false
16713      */
16714     hasOuterHandles: false,
16715
16716     /**
16717      * Code that executes immediately before the startDrag event
16718      * @method b4StartDrag
16719      * @private
16720      */
16721     b4StartDrag: function(x, y) { },
16722
16723     /**
16724      * Abstract method called after a drag/drop object is clicked
16725      * and the drag or mousedown time thresholds have beeen met.
16726      * @method startDrag
16727      * @param {int} X click location
16728      * @param {int} Y click location
16729      */
16730     startDrag: function(x, y) { /* override this */ },
16731
16732     /**
16733      * Code that executes immediately before the onDrag event
16734      * @method b4Drag
16735      * @private
16736      */
16737     b4Drag: function(e) { },
16738
16739     /**
16740      * Abstract method called during the onMouseMove event while dragging an
16741      * object.
16742      * @method onDrag
16743      * @param {Event} e the mousemove event
16744      */
16745     onDrag: function(e) { /* override this */ },
16746
16747     /**
16748      * Abstract method called when this element fist begins hovering over
16749      * another DragDrop obj
16750      * @method onDragEnter
16751      * @param {Event} e the mousemove event
16752      * @param {String|DragDrop[]} id In POINT mode, the element
16753      * id this is hovering over.  In INTERSECT mode, an array of one or more
16754      * dragdrop items being hovered over.
16755      */
16756     onDragEnter: function(e, id) { /* override this */ },
16757
16758     /**
16759      * Code that executes immediately before the onDragOver event
16760      * @method b4DragOver
16761      * @private
16762      */
16763     b4DragOver: function(e) { },
16764
16765     /**
16766      * Abstract method called when this element is hovering over another
16767      * DragDrop obj
16768      * @method onDragOver
16769      * @param {Event} e the mousemove event
16770      * @param {String|DragDrop[]} id In POINT mode, the element
16771      * id this is hovering over.  In INTERSECT mode, an array of dd items
16772      * being hovered over.
16773      */
16774     onDragOver: function(e, id) { /* override this */ },
16775
16776     /**
16777      * Code that executes immediately before the onDragOut event
16778      * @method b4DragOut
16779      * @private
16780      */
16781     b4DragOut: function(e) { },
16782
16783     /**
16784      * Abstract method called when we are no longer hovering over an element
16785      * @method onDragOut
16786      * @param {Event} e the mousemove event
16787      * @param {String|DragDrop[]} id In POINT mode, the element
16788      * id this was hovering over.  In INTERSECT mode, an array of dd items
16789      * that the mouse is no longer over.
16790      */
16791     onDragOut: function(e, id) { /* override this */ },
16792
16793     /**
16794      * Code that executes immediately before the onDragDrop event
16795      * @method b4DragDrop
16796      * @private
16797      */
16798     b4DragDrop: function(e) { },
16799
16800     /**
16801      * Abstract method called when this item is dropped on another DragDrop
16802      * obj
16803      * @method onDragDrop
16804      * @param {Event} e the mouseup event
16805      * @param {String|DragDrop[]} id In POINT mode, the element
16806      * id this was dropped on.  In INTERSECT mode, an array of dd items this
16807      * was dropped on.
16808      */
16809     onDragDrop: function(e, id) { /* override this */ },
16810
16811     /**
16812      * Abstract method called when this item is dropped on an area with no
16813      * drop target
16814      * @method onInvalidDrop
16815      * @param {Event} e the mouseup event
16816      */
16817     onInvalidDrop: function(e) { /* override this */ },
16818
16819     /**
16820      * Code that executes immediately before the endDrag event
16821      * @method b4EndDrag
16822      * @private
16823      */
16824     b4EndDrag: function(e) { },
16825
16826     /**
16827      * Fired when we are done dragging the object
16828      * @method endDrag
16829      * @param {Event} e the mouseup event
16830      */
16831     endDrag: function(e) { /* override this */ },
16832
16833     /**
16834      * Code executed immediately before the onMouseDown event
16835      * @method b4MouseDown
16836      * @param {Event} e the mousedown event
16837      * @private
16838      */
16839     b4MouseDown: function(e) {  },
16840
16841     /**
16842      * Event handler that fires when a drag/drop obj gets a mousedown
16843      * @method onMouseDown
16844      * @param {Event} e the mousedown event
16845      */
16846     onMouseDown: function(e) { /* override this */ },
16847
16848     /**
16849      * Event handler that fires when a drag/drop obj gets a mouseup
16850      * @method onMouseUp
16851      * @param {Event} e the mouseup event
16852      */
16853     onMouseUp: function(e) { /* override this */ },
16854
16855     /**
16856      * Override the onAvailable method to do what is needed after the initial
16857      * position was determined.
16858      * @method onAvailable
16859      */
16860     onAvailable: function () {
16861     },
16862
16863     /*
16864      * Provides default constraint padding to "constrainTo" elements (defaults to {left: 0, right:0, top:0, bottom:0}).
16865      * @type Object
16866      */
16867     defaultPadding : {left:0, right:0, top:0, bottom:0},
16868
16869     /*
16870      * Initializes the drag drop object's constraints to restrict movement to a certain element.
16871  *
16872  * Usage:
16873  <pre><code>
16874  var dd = new Roo.dd.DDProxy("dragDiv1", "proxytest",
16875                 { dragElId: "existingProxyDiv" });
16876  dd.startDrag = function(){
16877      this.constrainTo("parent-id");
16878  };
16879  </code></pre>
16880  * Or you can initalize it using the {@link Roo.Element} object:
16881  <pre><code>
16882  Roo.get("dragDiv1").initDDProxy("proxytest", {dragElId: "existingProxyDiv"}, {
16883      startDrag : function(){
16884          this.constrainTo("parent-id");
16885      }
16886  });
16887  </code></pre>
16888      * @param {String/HTMLElement/Element} constrainTo The element to constrain to.
16889      * @param {Object/Number} pad (optional) Pad provides a way to specify "padding" of the constraints,
16890      * and can be either a number for symmetrical padding (4 would be equal to {left:4, right:4, top:4, bottom:4}) or
16891      * an object containing the sides to pad. For example: {right:10, bottom:10}
16892      * @param {Boolean} inContent (optional) Constrain the draggable in the content box of the element (inside padding and borders)
16893      */
16894     constrainTo : function(constrainTo, pad, inContent){
16895         if(typeof pad == "number"){
16896             pad = {left: pad, right:pad, top:pad, bottom:pad};
16897         }
16898         pad = pad || this.defaultPadding;
16899         var b = Roo.get(this.getEl()).getBox();
16900         var ce = Roo.get(constrainTo);
16901         var s = ce.getScroll();
16902         var c, cd = ce.dom;
16903         if(cd == document.body){
16904             c = { x: s.left, y: s.top, width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
16905         }else{
16906             xy = ce.getXY();
16907             c = {x : xy[0]+s.left, y: xy[1]+s.top, width: cd.clientWidth, height: cd.clientHeight};
16908         }
16909
16910
16911         var topSpace = b.y - c.y;
16912         var leftSpace = b.x - c.x;
16913
16914         this.resetConstraints();
16915         this.setXConstraint(leftSpace - (pad.left||0), // left
16916                 c.width - leftSpace - b.width - (pad.right||0) //right
16917         );
16918         this.setYConstraint(topSpace - (pad.top||0), //top
16919                 c.height - topSpace - b.height - (pad.bottom||0) //bottom
16920         );
16921     },
16922
16923     /**
16924      * Returns a reference to the linked element
16925      * @method getEl
16926      * @return {HTMLElement} the html element
16927      */
16928     getEl: function() {
16929         if (!this._domRef) {
16930             this._domRef = Roo.getDom(this.id);
16931         }
16932
16933         return this._domRef;
16934     },
16935
16936     /**
16937      * Returns a reference to the actual element to drag.  By default this is
16938      * the same as the html element, but it can be assigned to another
16939      * element. An example of this can be found in Roo.dd.DDProxy
16940      * @method getDragEl
16941      * @return {HTMLElement} the html element
16942      */
16943     getDragEl: function() {
16944         return Roo.getDom(this.dragElId);
16945     },
16946
16947     /**
16948      * Sets up the DragDrop object.  Must be called in the constructor of any
16949      * Roo.dd.DragDrop subclass
16950      * @method init
16951      * @param id the id of the linked element
16952      * @param {String} sGroup the group of related items
16953      * @param {object} config configuration attributes
16954      */
16955     init: function(id, sGroup, config) {
16956         this.initTarget(id, sGroup, config);
16957         if (!Roo.isTouch) {
16958             Event.on(this.id, "mousedown", this.handleMouseDown, this);
16959         }
16960         Event.on(this.id, "touchstart", this.handleMouseDown, this);
16961         // Event.on(this.id, "selectstart", Event.preventDefault);
16962     },
16963
16964     /**
16965      * Initializes Targeting functionality only... the object does not
16966      * get a mousedown handler.
16967      * @method initTarget
16968      * @param id the id of the linked element
16969      * @param {String} sGroup the group of related items
16970      * @param {object} config configuration attributes
16971      */
16972     initTarget: function(id, sGroup, config) {
16973
16974         // configuration attributes
16975         this.config = config || {};
16976
16977         // create a local reference to the drag and drop manager
16978         this.DDM = Roo.dd.DDM;
16979         // initialize the groups array
16980         this.groups = {};
16981
16982         // assume that we have an element reference instead of an id if the
16983         // parameter is not a string
16984         if (typeof id !== "string") {
16985             id = Roo.id(id);
16986         }
16987
16988         // set the id
16989         this.id = id;
16990
16991         // add to an interaction group
16992         this.addToGroup((sGroup) ? sGroup : "default");
16993
16994         // We don't want to register this as the handle with the manager
16995         // so we just set the id rather than calling the setter.
16996         this.handleElId = id;
16997
16998         // the linked element is the element that gets dragged by default
16999         this.setDragElId(id);
17000
17001         // by default, clicked anchors will not start drag operations.
17002         this.invalidHandleTypes = { A: "A" };
17003         this.invalidHandleIds = {};
17004         this.invalidHandleClasses = [];
17005
17006         this.applyConfig();
17007
17008         this.handleOnAvailable();
17009     },
17010
17011     /**
17012      * Applies the configuration parameters that were passed into the constructor.
17013      * This is supposed to happen at each level through the inheritance chain.  So
17014      * a DDProxy implentation will execute apply config on DDProxy, DD, and
17015      * DragDrop in order to get all of the parameters that are available in
17016      * each object.
17017      * @method applyConfig
17018      */
17019     applyConfig: function() {
17020
17021         // configurable properties:
17022         //    padding, isTarget, maintainOffset, primaryButtonOnly
17023         this.padding           = this.config.padding || [0, 0, 0, 0];
17024         this.isTarget          = (this.config.isTarget !== false);
17025         this.maintainOffset    = (this.config.maintainOffset);
17026         this.primaryButtonOnly = (this.config.primaryButtonOnly !== false);
17027
17028     },
17029
17030     /**
17031      * Executed when the linked element is available
17032      * @method handleOnAvailable
17033      * @private
17034      */
17035     handleOnAvailable: function() {
17036         this.available = true;
17037         this.resetConstraints();
17038         this.onAvailable();
17039     },
17040
17041      /**
17042      * Configures the padding for the target zone in px.  Effectively expands
17043      * (or reduces) the virtual object size for targeting calculations.
17044      * Supports css-style shorthand; if only one parameter is passed, all sides
17045      * will have that padding, and if only two are passed, the top and bottom
17046      * will have the first param, the left and right the second.
17047      * @method setPadding
17048      * @param {int} iTop    Top pad
17049      * @param {int} iRight  Right pad
17050      * @param {int} iBot    Bot pad
17051      * @param {int} iLeft   Left pad
17052      */
17053     setPadding: function(iTop, iRight, iBot, iLeft) {
17054         // this.padding = [iLeft, iRight, iTop, iBot];
17055         if (!iRight && 0 !== iRight) {
17056             this.padding = [iTop, iTop, iTop, iTop];
17057         } else if (!iBot && 0 !== iBot) {
17058             this.padding = [iTop, iRight, iTop, iRight];
17059         } else {
17060             this.padding = [iTop, iRight, iBot, iLeft];
17061         }
17062     },
17063
17064     /**
17065      * Stores the initial placement of the linked element.
17066      * @method setInitialPosition
17067      * @param {int} diffX   the X offset, default 0
17068      * @param {int} diffY   the Y offset, default 0
17069      */
17070     setInitPosition: function(diffX, diffY) {
17071         var el = this.getEl();
17072
17073         if (!this.DDM.verifyEl(el)) {
17074             return;
17075         }
17076
17077         var dx = diffX || 0;
17078         var dy = diffY || 0;
17079
17080         var p = Dom.getXY( el );
17081
17082         this.initPageX = p[0] - dx;
17083         this.initPageY = p[1] - dy;
17084
17085         this.lastPageX = p[0];
17086         this.lastPageY = p[1];
17087
17088
17089         this.setStartPosition(p);
17090     },
17091
17092     /**
17093      * Sets the start position of the element.  This is set when the obj
17094      * is initialized, the reset when a drag is started.
17095      * @method setStartPosition
17096      * @param pos current position (from previous lookup)
17097      * @private
17098      */
17099     setStartPosition: function(pos) {
17100         var p = pos || Dom.getXY( this.getEl() );
17101         this.deltaSetXY = null;
17102
17103         this.startPageX = p[0];
17104         this.startPageY = p[1];
17105     },
17106
17107     /**
17108      * Add this instance to a group of related drag/drop objects.  All
17109      * instances belong to at least one group, and can belong to as many
17110      * groups as needed.
17111      * @method addToGroup
17112      * @param sGroup {string} the name of the group
17113      */
17114     addToGroup: function(sGroup) {
17115         this.groups[sGroup] = true;
17116         this.DDM.regDragDrop(this, sGroup);
17117     },
17118
17119     /**
17120      * Remove's this instance from the supplied interaction group
17121      * @method removeFromGroup
17122      * @param {string}  sGroup  The group to drop
17123      */
17124     removeFromGroup: function(sGroup) {
17125         if (this.groups[sGroup]) {
17126             delete this.groups[sGroup];
17127         }
17128
17129         this.DDM.removeDDFromGroup(this, sGroup);
17130     },
17131
17132     /**
17133      * Allows you to specify that an element other than the linked element
17134      * will be moved with the cursor during a drag
17135      * @method setDragElId
17136      * @param id {string} the id of the element that will be used to initiate the drag
17137      */
17138     setDragElId: function(id) {
17139         this.dragElId = id;
17140     },
17141
17142     /**
17143      * Allows you to specify a child of the linked element that should be
17144      * used to initiate the drag operation.  An example of this would be if
17145      * you have a content div with text and links.  Clicking anywhere in the
17146      * content area would normally start the drag operation.  Use this method
17147      * to specify that an element inside of the content div is the element
17148      * that starts the drag operation.
17149      * @method setHandleElId
17150      * @param id {string} the id of the element that will be used to
17151      * initiate the drag.
17152      */
17153     setHandleElId: function(id) {
17154         if (typeof id !== "string") {
17155             id = Roo.id(id);
17156         }
17157         this.handleElId = id;
17158         this.DDM.regHandle(this.id, id);
17159     },
17160
17161     /**
17162      * Allows you to set an element outside of the linked element as a drag
17163      * handle
17164      * @method setOuterHandleElId
17165      * @param id the id of the element that will be used to initiate the drag
17166      */
17167     setOuterHandleElId: function(id) {
17168         if (typeof id !== "string") {
17169             id = Roo.id(id);
17170         }
17171         Event.on(id, "mousedown",
17172                 this.handleMouseDown, this);
17173         this.setHandleElId(id);
17174
17175         this.hasOuterHandles = true;
17176     },
17177
17178     /**
17179      * Remove all drag and drop hooks for this element
17180      * @method unreg
17181      */
17182     unreg: function() {
17183         Event.un(this.id, "mousedown",
17184                 this.handleMouseDown);
17185         Event.un(this.id, "touchstart",
17186                 this.handleMouseDown);
17187         this._domRef = null;
17188         this.DDM._remove(this);
17189     },
17190
17191     destroy : function(){
17192         this.unreg();
17193     },
17194
17195     /**
17196      * Returns true if this instance is locked, or the drag drop mgr is locked
17197      * (meaning that all drag/drop is disabled on the page.)
17198      * @method isLocked
17199      * @return {boolean} true if this obj or all drag/drop is locked, else
17200      * false
17201      */
17202     isLocked: function() {
17203         return (this.DDM.isLocked() || this.locked);
17204     },
17205
17206     /**
17207      * Fired when this object is clicked
17208      * @method handleMouseDown
17209      * @param {Event} e
17210      * @param {Roo.dd.DragDrop} oDD the clicked dd object (this dd obj)
17211      * @private
17212      */
17213     handleMouseDown: function(e, oDD){
17214      
17215         if (!Roo.isTouch && this.primaryButtonOnly && e.button != 0) {
17216             //Roo.log('not touch/ button !=0');
17217             return;
17218         }
17219         if (e.browserEvent.touches && e.browserEvent.touches.length != 1) {
17220             return; // double touch..
17221         }
17222         
17223
17224         if (this.isLocked()) {
17225             //Roo.log('locked');
17226             return;
17227         }
17228
17229         this.DDM.refreshCache(this.groups);
17230 //        Roo.log([Roo.lib.Event.getPageX(e), Roo.lib.Event.getPageY(e)]);
17231         var pt = new Roo.lib.Point(Roo.lib.Event.getPageX(e), Roo.lib.Event.getPageY(e));
17232         if (!this.hasOuterHandles && !this.DDM.isOverTarget(pt, this) )  {
17233             //Roo.log('no outer handes or not over target');
17234                 // do nothing.
17235         } else {
17236 //            Roo.log('check validator');
17237             if (this.clickValidator(e)) {
17238 //                Roo.log('validate success');
17239                 // set the initial element position
17240                 this.setStartPosition();
17241
17242
17243                 this.b4MouseDown(e);
17244                 this.onMouseDown(e);
17245
17246                 this.DDM.handleMouseDown(e, this);
17247
17248                 this.DDM.stopEvent(e);
17249             } else {
17250
17251
17252             }
17253         }
17254     },
17255
17256     clickValidator: function(e) {
17257         var target = e.getTarget();
17258         return ( this.isValidHandleChild(target) &&
17259                     (this.id == this.handleElId ||
17260                         this.DDM.handleWasClicked(target, this.id)) );
17261     },
17262
17263     /**
17264      * Allows you to specify a tag name that should not start a drag operation
17265      * when clicked.  This is designed to facilitate embedding links within a
17266      * drag handle that do something other than start the drag.
17267      * @method addInvalidHandleType
17268      * @param {string} tagName the type of element to exclude
17269      */
17270     addInvalidHandleType: function(tagName) {
17271         var type = tagName.toUpperCase();
17272         this.invalidHandleTypes[type] = type;
17273     },
17274
17275     /**
17276      * Lets you to specify an element id for a child of a drag handle
17277      * that should not initiate a drag
17278      * @method addInvalidHandleId
17279      * @param {string} id the element id of the element you wish to ignore
17280      */
17281     addInvalidHandleId: function(id) {
17282         if (typeof id !== "string") {
17283             id = Roo.id(id);
17284         }
17285         this.invalidHandleIds[id] = id;
17286     },
17287
17288     /**
17289      * Lets you specify a css class of elements that will not initiate a drag
17290      * @method addInvalidHandleClass
17291      * @param {string} cssClass the class of the elements you wish to ignore
17292      */
17293     addInvalidHandleClass: function(cssClass) {
17294         this.invalidHandleClasses.push(cssClass);
17295     },
17296
17297     /**
17298      * Unsets an excluded tag name set by addInvalidHandleType
17299      * @method removeInvalidHandleType
17300      * @param {string} tagName the type of element to unexclude
17301      */
17302     removeInvalidHandleType: function(tagName) {
17303         var type = tagName.toUpperCase();
17304         // this.invalidHandleTypes[type] = null;
17305         delete this.invalidHandleTypes[type];
17306     },
17307
17308     /**
17309      * Unsets an invalid handle id
17310      * @method removeInvalidHandleId
17311      * @param {string} id the id of the element to re-enable
17312      */
17313     removeInvalidHandleId: function(id) {
17314         if (typeof id !== "string") {
17315             id = Roo.id(id);
17316         }
17317         delete this.invalidHandleIds[id];
17318     },
17319
17320     /**
17321      * Unsets an invalid css class
17322      * @method removeInvalidHandleClass
17323      * @param {string} cssClass the class of the element(s) you wish to
17324      * re-enable
17325      */
17326     removeInvalidHandleClass: function(cssClass) {
17327         for (var i=0, len=this.invalidHandleClasses.length; i<len; ++i) {
17328             if (this.invalidHandleClasses[i] == cssClass) {
17329                 delete this.invalidHandleClasses[i];
17330             }
17331         }
17332     },
17333
17334     /**
17335      * Checks the tag exclusion list to see if this click should be ignored
17336      * @method isValidHandleChild
17337      * @param {HTMLElement} node the HTMLElement to evaluate
17338      * @return {boolean} true if this is a valid tag type, false if not
17339      */
17340     isValidHandleChild: function(node) {
17341
17342         var valid = true;
17343         // var n = (node.nodeName == "#text") ? node.parentNode : node;
17344         var nodeName;
17345         try {
17346             nodeName = node.nodeName.toUpperCase();
17347         } catch(e) {
17348             nodeName = node.nodeName;
17349         }
17350         valid = valid && !this.invalidHandleTypes[nodeName];
17351         valid = valid && !this.invalidHandleIds[node.id];
17352
17353         for (var i=0, len=this.invalidHandleClasses.length; valid && i<len; ++i) {
17354             valid = !Dom.hasClass(node, this.invalidHandleClasses[i]);
17355         }
17356
17357
17358         return valid;
17359
17360     },
17361
17362     /**
17363      * Create the array of horizontal tick marks if an interval was specified
17364      * in setXConstraint().
17365      * @method setXTicks
17366      * @private
17367      */
17368     setXTicks: function(iStartX, iTickSize) {
17369         this.xTicks = [];
17370         this.xTickSize = iTickSize;
17371
17372         var tickMap = {};
17373
17374         for (var i = this.initPageX; i >= this.minX; i = i - iTickSize) {
17375             if (!tickMap[i]) {
17376                 this.xTicks[this.xTicks.length] = i;
17377                 tickMap[i] = true;
17378             }
17379         }
17380
17381         for (i = this.initPageX; i <= this.maxX; i = i + iTickSize) {
17382             if (!tickMap[i]) {
17383                 this.xTicks[this.xTicks.length] = i;
17384                 tickMap[i] = true;
17385             }
17386         }
17387
17388         this.xTicks.sort(this.DDM.numericSort) ;
17389     },
17390
17391     /**
17392      * Create the array of vertical tick marks if an interval was specified in
17393      * setYConstraint().
17394      * @method setYTicks
17395      * @private
17396      */
17397     setYTicks: function(iStartY, iTickSize) {
17398         this.yTicks = [];
17399         this.yTickSize = iTickSize;
17400
17401         var tickMap = {};
17402
17403         for (var i = this.initPageY; i >= this.minY; i = i - iTickSize) {
17404             if (!tickMap[i]) {
17405                 this.yTicks[this.yTicks.length] = i;
17406                 tickMap[i] = true;
17407             }
17408         }
17409
17410         for (i = this.initPageY; i <= this.maxY; i = i + iTickSize) {
17411             if (!tickMap[i]) {
17412                 this.yTicks[this.yTicks.length] = i;
17413                 tickMap[i] = true;
17414             }
17415         }
17416
17417         this.yTicks.sort(this.DDM.numericSort) ;
17418     },
17419
17420     /**
17421      * By default, the element can be dragged any place on the screen.  Use
17422      * this method to limit the horizontal travel of the element.  Pass in
17423      * 0,0 for the parameters if you want to lock the drag to the y axis.
17424      * @method setXConstraint
17425      * @param {int} iLeft the number of pixels the element can move to the left
17426      * @param {int} iRight the number of pixels the element can move to the
17427      * right
17428      * @param {int} iTickSize optional parameter for specifying that the
17429      * element
17430      * should move iTickSize pixels at a time.
17431      */
17432     setXConstraint: function(iLeft, iRight, iTickSize) {
17433         this.leftConstraint = iLeft;
17434         this.rightConstraint = iRight;
17435
17436         this.minX = this.initPageX - iLeft;
17437         this.maxX = this.initPageX + iRight;
17438         if (iTickSize) { this.setXTicks(this.initPageX, iTickSize); }
17439
17440         this.constrainX = true;
17441     },
17442
17443     /**
17444      * Clears any constraints applied to this instance.  Also clears ticks
17445      * since they can't exist independent of a constraint at this time.
17446      * @method clearConstraints
17447      */
17448     clearConstraints: function() {
17449         this.constrainX = false;
17450         this.constrainY = false;
17451         this.clearTicks();
17452     },
17453
17454     /**
17455      * Clears any tick interval defined for this instance
17456      * @method clearTicks
17457      */
17458     clearTicks: function() {
17459         this.xTicks = null;
17460         this.yTicks = null;
17461         this.xTickSize = 0;
17462         this.yTickSize = 0;
17463     },
17464
17465     /**
17466      * By default, the element can be dragged any place on the screen.  Set
17467      * this to limit the vertical travel of the element.  Pass in 0,0 for the
17468      * parameters if you want to lock the drag to the x axis.
17469      * @method setYConstraint
17470      * @param {int} iUp the number of pixels the element can move up
17471      * @param {int} iDown the number of pixels the element can move down
17472      * @param {int} iTickSize optional parameter for specifying that the
17473      * element should move iTickSize pixels at a time.
17474      */
17475     setYConstraint: function(iUp, iDown, iTickSize) {
17476         this.topConstraint = iUp;
17477         this.bottomConstraint = iDown;
17478
17479         this.minY = this.initPageY - iUp;
17480         this.maxY = this.initPageY + iDown;
17481         if (iTickSize) { this.setYTicks(this.initPageY, iTickSize); }
17482
17483         this.constrainY = true;
17484
17485     },
17486
17487     /**
17488      * resetConstraints must be called if you manually reposition a dd element.
17489      * @method resetConstraints
17490      * @param {boolean} maintainOffset
17491      */
17492     resetConstraints: function() {
17493
17494
17495         // Maintain offsets if necessary
17496         if (this.initPageX || this.initPageX === 0) {
17497             // figure out how much this thing has moved
17498             var dx = (this.maintainOffset) ? this.lastPageX - this.initPageX : 0;
17499             var dy = (this.maintainOffset) ? this.lastPageY - this.initPageY : 0;
17500
17501             this.setInitPosition(dx, dy);
17502
17503         // This is the first time we have detected the element's position
17504         } else {
17505             this.setInitPosition();
17506         }
17507
17508         if (this.constrainX) {
17509             this.setXConstraint( this.leftConstraint,
17510                                  this.rightConstraint,
17511                                  this.xTickSize        );
17512         }
17513
17514         if (this.constrainY) {
17515             this.setYConstraint( this.topConstraint,
17516                                  this.bottomConstraint,
17517                                  this.yTickSize         );
17518         }
17519     },
17520
17521     /**
17522      * Normally the drag element is moved pixel by pixel, but we can specify
17523      * that it move a number of pixels at a time.  This method resolves the
17524      * location when we have it set up like this.
17525      * @method getTick
17526      * @param {int} val where we want to place the object
17527      * @param {int[]} tickArray sorted array of valid points
17528      * @return {int} the closest tick
17529      * @private
17530      */
17531     getTick: function(val, tickArray) {
17532
17533         if (!tickArray) {
17534             // If tick interval is not defined, it is effectively 1 pixel,
17535             // so we return the value passed to us.
17536             return val;
17537         } else if (tickArray[0] >= val) {
17538             // The value is lower than the first tick, so we return the first
17539             // tick.
17540             return tickArray[0];
17541         } else {
17542             for (var i=0, len=tickArray.length; i<len; ++i) {
17543                 var next = i + 1;
17544                 if (tickArray[next] && tickArray[next] >= val) {
17545                     var diff1 = val - tickArray[i];
17546                     var diff2 = tickArray[next] - val;
17547                     return (diff2 > diff1) ? tickArray[i] : tickArray[next];
17548                 }
17549             }
17550
17551             // The value is larger than the last tick, so we return the last
17552             // tick.
17553             return tickArray[tickArray.length - 1];
17554         }
17555     },
17556
17557     /**
17558      * toString method
17559      * @method toString
17560      * @return {string} string representation of the dd obj
17561      */
17562     toString: function() {
17563         return ("DragDrop " + this.id);
17564     }
17565
17566 });
17567
17568 })();
17569 /*
17570  * Based on:
17571  * Ext JS Library 1.1.1
17572  * Copyright(c) 2006-2007, Ext JS, LLC.
17573  *
17574  * Originally Released Under LGPL - original licence link has changed is not relivant.
17575  *
17576  * Fork - LGPL
17577  * <script type="text/javascript">
17578  */
17579
17580
17581 /**
17582  * The drag and drop utility provides a framework for building drag and drop
17583  * applications.  In addition to enabling drag and drop for specific elements,
17584  * the drag and drop elements are tracked by the manager class, and the
17585  * interactions between the various elements are tracked during the drag and
17586  * the implementing code is notified about these important moments.
17587  */
17588
17589 // Only load the library once.  Rewriting the manager class would orphan
17590 // existing drag and drop instances.
17591 if (!Roo.dd.DragDropMgr) {
17592
17593 /**
17594  * @class Roo.dd.DragDropMgr
17595  * DragDropMgr is a singleton that tracks the element interaction for
17596  * all DragDrop items in the window.  Generally, you will not call
17597  * this class directly, but it does have helper methods that could
17598  * be useful in your DragDrop implementations.
17599  * @singleton
17600  */
17601 Roo.dd.DragDropMgr = function() {
17602
17603     var Event = Roo.EventManager;
17604
17605     return {
17606
17607         /**
17608          * Two dimensional Array of registered DragDrop objects.  The first
17609          * dimension is the DragDrop item group, the second the DragDrop
17610          * object.
17611          * @property ids
17612          * @type {string: string}
17613          * @private
17614          * @static
17615          */
17616         ids: {},
17617
17618         /**
17619          * Array of element ids defined as drag handles.  Used to determine
17620          * if the element that generated the mousedown event is actually the
17621          * handle and not the html element itself.
17622          * @property handleIds
17623          * @type {string: string}
17624          * @private
17625          * @static
17626          */
17627         handleIds: {},
17628
17629         /**
17630          * the DragDrop object that is currently being dragged
17631          * @property dragCurrent
17632          * @type DragDrop
17633          * @private
17634          * @static
17635          **/
17636         dragCurrent: null,
17637
17638         /**
17639          * the DragDrop object(s) that are being hovered over
17640          * @property dragOvers
17641          * @type Array
17642          * @private
17643          * @static
17644          */
17645         dragOvers: {},
17646
17647         /**
17648          * the X distance between the cursor and the object being dragged
17649          * @property deltaX
17650          * @type int
17651          * @private
17652          * @static
17653          */
17654         deltaX: 0,
17655
17656         /**
17657          * the Y distance between the cursor and the object being dragged
17658          * @property deltaY
17659          * @type int
17660          * @private
17661          * @static
17662          */
17663         deltaY: 0,
17664
17665         /**
17666          * Flag to determine if we should prevent the default behavior of the
17667          * events we define. By default this is true, but this can be set to
17668          * false if you need the default behavior (not recommended)
17669          * @property preventDefault
17670          * @type boolean
17671          * @static
17672          */
17673         preventDefault: true,
17674
17675         /**
17676          * Flag to determine if we should stop the propagation of the events
17677          * we generate. This is true by default but you may want to set it to
17678          * false if the html element contains other features that require the
17679          * mouse click.
17680          * @property stopPropagation
17681          * @type boolean
17682          * @static
17683          */
17684         stopPropagation: true,
17685
17686         /**
17687          * Internal flag that is set to true when drag and drop has been
17688          * intialized
17689          * @property initialized
17690          * @private
17691          * @static
17692          */
17693         initalized: false,
17694
17695         /**
17696          * All drag and drop can be disabled.
17697          * @property locked
17698          * @private
17699          * @static
17700          */
17701         locked: false,
17702
17703         /**
17704          * Called the first time an element is registered.
17705          * @method init
17706          * @private
17707          * @static
17708          */
17709         init: function() {
17710             this.initialized = true;
17711         },
17712
17713         /**
17714          * In point mode, drag and drop interaction is defined by the
17715          * location of the cursor during the drag/drop
17716          * @property POINT
17717          * @type int
17718          * @static
17719          */
17720         POINT: 0,
17721
17722         /**
17723          * In intersect mode, drag and drop interactio nis defined by the
17724          * overlap of two or more drag and drop objects.
17725          * @property INTERSECT
17726          * @type int
17727          * @static
17728          */
17729         INTERSECT: 1,
17730
17731         /**
17732          * The current drag and drop mode.  Default: POINT
17733          * @property mode
17734          * @type int
17735          * @static
17736          */
17737         mode: 0,
17738
17739         /**
17740          * Runs method on all drag and drop objects
17741          * @method _execOnAll
17742          * @private
17743          * @static
17744          */
17745         _execOnAll: function(sMethod, args) {
17746             for (var i in this.ids) {
17747                 for (var j in this.ids[i]) {
17748                     var oDD = this.ids[i][j];
17749                     if (! this.isTypeOfDD(oDD)) {
17750                         continue;
17751                     }
17752                     oDD[sMethod].apply(oDD, args);
17753                 }
17754             }
17755         },
17756
17757         /**
17758          * Drag and drop initialization.  Sets up the global event handlers
17759          * @method _onLoad
17760          * @private
17761          * @static
17762          */
17763         _onLoad: function() {
17764
17765             this.init();
17766
17767             if (!Roo.isTouch) {
17768                 Event.on(document, "mouseup",   this.handleMouseUp, this, true);
17769                 Event.on(document, "mousemove", this.handleMouseMove, this, true);
17770             }
17771             Event.on(document, "touchend",   this.handleMouseUp, this, true);
17772             Event.on(document, "touchmove", this.handleMouseMove, this, true);
17773             
17774             Event.on(window,   "unload",    this._onUnload, this, true);
17775             Event.on(window,   "resize",    this._onResize, this, true);
17776             // Event.on(window,   "mouseout",    this._test);
17777
17778         },
17779
17780         /**
17781          * Reset constraints on all drag and drop objs
17782          * @method _onResize
17783          * @private
17784          * @static
17785          */
17786         _onResize: function(e) {
17787             this._execOnAll("resetConstraints", []);
17788         },
17789
17790         /**
17791          * Lock all drag and drop functionality
17792          * @method lock
17793          * @static
17794          */
17795         lock: function() { this.locked = true; },
17796
17797         /**
17798          * Unlock all drag and drop functionality
17799          * @method unlock
17800          * @static
17801          */
17802         unlock: function() { this.locked = false; },
17803
17804         /**
17805          * Is drag and drop locked?
17806          * @method isLocked
17807          * @return {boolean} True if drag and drop is locked, false otherwise.
17808          * @static
17809          */
17810         isLocked: function() { return this.locked; },
17811
17812         /**
17813          * Location cache that is set for all drag drop objects when a drag is
17814          * initiated, cleared when the drag is finished.
17815          * @property locationCache
17816          * @private
17817          * @static
17818          */
17819         locationCache: {},
17820
17821         /**
17822          * Set useCache to false if you want to force object the lookup of each
17823          * drag and drop linked element constantly during a drag.
17824          * @property useCache
17825          * @type boolean
17826          * @static
17827          */
17828         useCache: true,
17829
17830         /**
17831          * The number of pixels that the mouse needs to move after the
17832          * mousedown before the drag is initiated.  Default=3;
17833          * @property clickPixelThresh
17834          * @type int
17835          * @static
17836          */
17837         clickPixelThresh: 3,
17838
17839         /**
17840          * The number of milliseconds after the mousedown event to initiate the
17841          * drag if we don't get a mouseup event. Default=1000
17842          * @property clickTimeThresh
17843          * @type int
17844          * @static
17845          */
17846         clickTimeThresh: 350,
17847
17848         /**
17849          * Flag that indicates that either the drag pixel threshold or the
17850          * mousdown time threshold has been met
17851          * @property dragThreshMet
17852          * @type boolean
17853          * @private
17854          * @static
17855          */
17856         dragThreshMet: false,
17857
17858         /**
17859          * Timeout used for the click time threshold
17860          * @property clickTimeout
17861          * @type Object
17862          * @private
17863          * @static
17864          */
17865         clickTimeout: null,
17866
17867         /**
17868          * The X position of the mousedown event stored for later use when a
17869          * drag threshold is met.
17870          * @property startX
17871          * @type int
17872          * @private
17873          * @static
17874          */
17875         startX: 0,
17876
17877         /**
17878          * The Y position of the mousedown event stored for later use when a
17879          * drag threshold is met.
17880          * @property startY
17881          * @type int
17882          * @private
17883          * @static
17884          */
17885         startY: 0,
17886
17887         /**
17888          * Each DragDrop instance must be registered with the DragDropMgr.
17889          * This is executed in DragDrop.init()
17890          * @method regDragDrop
17891          * @param {DragDrop} oDD the DragDrop object to register
17892          * @param {String} sGroup the name of the group this element belongs to
17893          * @static
17894          */
17895         regDragDrop: function(oDD, sGroup) {
17896             if (!this.initialized) { this.init(); }
17897
17898             if (!this.ids[sGroup]) {
17899                 this.ids[sGroup] = {};
17900             }
17901             this.ids[sGroup][oDD.id] = oDD;
17902         },
17903
17904         /**
17905          * Removes the supplied dd instance from the supplied group. Executed
17906          * by DragDrop.removeFromGroup, so don't call this function directly.
17907          * @method removeDDFromGroup
17908          * @private
17909          * @static
17910          */
17911         removeDDFromGroup: function(oDD, sGroup) {
17912             if (!this.ids[sGroup]) {
17913                 this.ids[sGroup] = {};
17914             }
17915
17916             var obj = this.ids[sGroup];
17917             if (obj && obj[oDD.id]) {
17918                 delete obj[oDD.id];
17919             }
17920         },
17921
17922         /**
17923          * Unregisters a drag and drop item.  This is executed in
17924          * DragDrop.unreg, use that method instead of calling this directly.
17925          * @method _remove
17926          * @private
17927          * @static
17928          */
17929         _remove: function(oDD) {
17930             for (var g in oDD.groups) {
17931                 if (g && this.ids[g][oDD.id]) {
17932                     delete this.ids[g][oDD.id];
17933                 }
17934             }
17935             delete this.handleIds[oDD.id];
17936         },
17937
17938         /**
17939          * Each DragDrop handle element must be registered.  This is done
17940          * automatically when executing DragDrop.setHandleElId()
17941          * @method regHandle
17942          * @param {String} sDDId the DragDrop id this element is a handle for
17943          * @param {String} sHandleId the id of the element that is the drag
17944          * handle
17945          * @static
17946          */
17947         regHandle: function(sDDId, sHandleId) {
17948             if (!this.handleIds[sDDId]) {
17949                 this.handleIds[sDDId] = {};
17950             }
17951             this.handleIds[sDDId][sHandleId] = sHandleId;
17952         },
17953
17954         /**
17955          * Utility function to determine if a given element has been
17956          * registered as a drag drop item.
17957          * @method isDragDrop
17958          * @param {String} id the element id to check
17959          * @return {boolean} true if this element is a DragDrop item,
17960          * false otherwise
17961          * @static
17962          */
17963         isDragDrop: function(id) {
17964             return ( this.getDDById(id) ) ? true : false;
17965         },
17966
17967         /**
17968          * Returns the drag and drop instances that are in all groups the
17969          * passed in instance belongs to.
17970          * @method getRelated
17971          * @param {DragDrop} p_oDD the obj to get related data for
17972          * @param {boolean} bTargetsOnly if true, only return targetable objs
17973          * @return {DragDrop[]} the related instances
17974          * @static
17975          */
17976         getRelated: function(p_oDD, bTargetsOnly) {
17977             var oDDs = [];
17978             for (var i in p_oDD.groups) {
17979                 for (j in this.ids[i]) {
17980                     var dd = this.ids[i][j];
17981                     if (! this.isTypeOfDD(dd)) {
17982                         continue;
17983                     }
17984                     if (!bTargetsOnly || dd.isTarget) {
17985                         oDDs[oDDs.length] = dd;
17986                     }
17987                 }
17988             }
17989
17990             return oDDs;
17991         },
17992
17993         /**
17994          * Returns true if the specified dd target is a legal target for
17995          * the specifice drag obj
17996          * @method isLegalTarget
17997          * @param {DragDrop} the drag obj
17998          * @param {DragDrop} the target
17999          * @return {boolean} true if the target is a legal target for the
18000          * dd obj
18001          * @static
18002          */
18003         isLegalTarget: function (oDD, oTargetDD) {
18004             var targets = this.getRelated(oDD, true);
18005             for (var i=0, len=targets.length;i<len;++i) {
18006                 if (targets[i].id == oTargetDD.id) {
18007                     return true;
18008                 }
18009             }
18010
18011             return false;
18012         },
18013
18014         /**
18015          * My goal is to be able to transparently determine if an object is
18016          * typeof DragDrop, and the exact subclass of DragDrop.  typeof
18017          * returns "object", oDD.constructor.toString() always returns
18018          * "DragDrop" and not the name of the subclass.  So for now it just
18019          * evaluates a well-known variable in DragDrop.
18020          * @method isTypeOfDD
18021          * @param {Object} the object to evaluate
18022          * @return {boolean} true if typeof oDD = DragDrop
18023          * @static
18024          */
18025         isTypeOfDD: function (oDD) {
18026             return (oDD && oDD.__ygDragDrop);
18027         },
18028
18029         /**
18030          * Utility function to determine if a given element has been
18031          * registered as a drag drop handle for the given Drag Drop object.
18032          * @method isHandle
18033          * @param {String} id the element id to check
18034          * @return {boolean} true if this element is a DragDrop handle, false
18035          * otherwise
18036          * @static
18037          */
18038         isHandle: function(sDDId, sHandleId) {
18039             return ( this.handleIds[sDDId] &&
18040                             this.handleIds[sDDId][sHandleId] );
18041         },
18042
18043         /**
18044          * Returns the DragDrop instance for a given id
18045          * @method getDDById
18046          * @param {String} id the id of the DragDrop object
18047          * @return {DragDrop} the drag drop object, null if it is not found
18048          * @static
18049          */
18050         getDDById: function(id) {
18051             for (var i in this.ids) {
18052                 if (this.ids[i][id]) {
18053                     return this.ids[i][id];
18054                 }
18055             }
18056             return null;
18057         },
18058
18059         /**
18060          * Fired after a registered DragDrop object gets the mousedown event.
18061          * Sets up the events required to track the object being dragged
18062          * @method handleMouseDown
18063          * @param {Event} e the event
18064          * @param oDD the DragDrop object being dragged
18065          * @private
18066          * @static
18067          */
18068         handleMouseDown: function(e, oDD) {
18069             if(Roo.QuickTips){
18070                 Roo.QuickTips.disable();
18071             }
18072             this.currentTarget = e.getTarget();
18073
18074             this.dragCurrent = oDD;
18075
18076             var el = oDD.getEl();
18077
18078             // track start position
18079             this.startX = e.getPageX();
18080             this.startY = e.getPageY();
18081
18082             this.deltaX = this.startX - el.offsetLeft;
18083             this.deltaY = this.startY - el.offsetTop;
18084
18085             this.dragThreshMet = false;
18086
18087             this.clickTimeout = setTimeout(
18088                     function() {
18089                         var DDM = Roo.dd.DDM;
18090                         DDM.startDrag(DDM.startX, DDM.startY);
18091                     },
18092                     this.clickTimeThresh );
18093         },
18094
18095         /**
18096          * Fired when either the drag pixel threshol or the mousedown hold
18097          * time threshold has been met.
18098          * @method startDrag
18099          * @param x {int} the X position of the original mousedown
18100          * @param y {int} the Y position of the original mousedown
18101          * @static
18102          */
18103         startDrag: function(x, y) {
18104             clearTimeout(this.clickTimeout);
18105             if (this.dragCurrent) {
18106                 this.dragCurrent.b4StartDrag(x, y);
18107                 this.dragCurrent.startDrag(x, y);
18108             }
18109             this.dragThreshMet = true;
18110         },
18111
18112         /**
18113          * Internal function to handle the mouseup event.  Will be invoked
18114          * from the context of the document.
18115          * @method handleMouseUp
18116          * @param {Event} e the event
18117          * @private
18118          * @static
18119          */
18120         handleMouseUp: function(e) {
18121
18122             if(Roo.QuickTips){
18123                 Roo.QuickTips.enable();
18124             }
18125             if (! this.dragCurrent) {
18126                 return;
18127             }
18128
18129             clearTimeout(this.clickTimeout);
18130
18131             if (this.dragThreshMet) {
18132                 this.fireEvents(e, true);
18133             } else {
18134             }
18135
18136             this.stopDrag(e);
18137
18138             this.stopEvent(e);
18139         },
18140
18141         /**
18142          * Utility to stop event propagation and event default, if these
18143          * features are turned on.
18144          * @method stopEvent
18145          * @param {Event} e the event as returned by this.getEvent()
18146          * @static
18147          */
18148         stopEvent: function(e){
18149             if(this.stopPropagation) {
18150                 e.stopPropagation();
18151             }
18152
18153             if (this.preventDefault) {
18154                 e.preventDefault();
18155             }
18156         },
18157
18158         /**
18159          * Internal function to clean up event handlers after the drag
18160          * operation is complete
18161          * @method stopDrag
18162          * @param {Event} e the event
18163          * @private
18164          * @static
18165          */
18166         stopDrag: function(e) {
18167             // Fire the drag end event for the item that was dragged
18168             if (this.dragCurrent) {
18169                 if (this.dragThreshMet) {
18170                     this.dragCurrent.b4EndDrag(e);
18171                     this.dragCurrent.endDrag(e);
18172                 }
18173
18174                 this.dragCurrent.onMouseUp(e);
18175             }
18176
18177             this.dragCurrent = null;
18178             this.dragOvers = {};
18179         },
18180
18181         /**
18182          * Internal function to handle the mousemove event.  Will be invoked
18183          * from the context of the html element.
18184          *
18185          * @TODO figure out what we can do about mouse events lost when the
18186          * user drags objects beyond the window boundary.  Currently we can
18187          * detect this in internet explorer by verifying that the mouse is
18188          * down during the mousemove event.  Firefox doesn't give us the
18189          * button state on the mousemove event.
18190          * @method handleMouseMove
18191          * @param {Event} e the event
18192          * @private
18193          * @static
18194          */
18195         handleMouseMove: function(e) {
18196             if (! this.dragCurrent) {
18197                 return true;
18198             }
18199
18200             // var button = e.which || e.button;
18201
18202             // check for IE mouseup outside of page boundary
18203             if (Roo.isIE && (e.button !== 0 && e.button !== 1 && e.button !== 2)) {
18204                 this.stopEvent(e);
18205                 return this.handleMouseUp(e);
18206             }
18207
18208             if (!this.dragThreshMet) {
18209                 var diffX = Math.abs(this.startX - e.getPageX());
18210                 var diffY = Math.abs(this.startY - e.getPageY());
18211                 if (diffX > this.clickPixelThresh ||
18212                             diffY > this.clickPixelThresh) {
18213                     this.startDrag(this.startX, this.startY);
18214                 }
18215             }
18216
18217             if (this.dragThreshMet) {
18218                 this.dragCurrent.b4Drag(e);
18219                 this.dragCurrent.onDrag(e);
18220                 if(!this.dragCurrent.moveOnly){
18221                     this.fireEvents(e, false);
18222                 }
18223             }
18224
18225             this.stopEvent(e);
18226
18227             return true;
18228         },
18229
18230         /**
18231          * Iterates over all of the DragDrop elements to find ones we are
18232          * hovering over or dropping on
18233          * @method fireEvents
18234          * @param {Event} e the event
18235          * @param {boolean} isDrop is this a drop op or a mouseover op?
18236          * @private
18237          * @static
18238          */
18239         fireEvents: function(e, isDrop) {
18240             var dc = this.dragCurrent;
18241
18242             // If the user did the mouse up outside of the window, we could
18243             // get here even though we have ended the drag.
18244             if (!dc || dc.isLocked()) {
18245                 return;
18246             }
18247
18248             var pt = e.getPoint();
18249
18250             // cache the previous dragOver array
18251             var oldOvers = [];
18252
18253             var outEvts   = [];
18254             var overEvts  = [];
18255             var dropEvts  = [];
18256             var enterEvts = [];
18257
18258             // Check to see if the object(s) we were hovering over is no longer
18259             // being hovered over so we can fire the onDragOut event
18260             for (var i in this.dragOvers) {
18261
18262                 var ddo = this.dragOvers[i];
18263
18264                 if (! this.isTypeOfDD(ddo)) {
18265                     continue;
18266                 }
18267
18268                 if (! this.isOverTarget(pt, ddo, this.mode)) {
18269                     outEvts.push( ddo );
18270                 }
18271
18272                 oldOvers[i] = true;
18273                 delete this.dragOvers[i];
18274             }
18275
18276             for (var sGroup in dc.groups) {
18277
18278                 if ("string" != typeof sGroup) {
18279                     continue;
18280                 }
18281
18282                 for (i in this.ids[sGroup]) {
18283                     var oDD = this.ids[sGroup][i];
18284                     if (! this.isTypeOfDD(oDD)) {
18285                         continue;
18286                     }
18287
18288                     if (oDD.isTarget && !oDD.isLocked() && oDD != dc) {
18289                         if (this.isOverTarget(pt, oDD, this.mode)) {
18290                             // look for drop interactions
18291                             if (isDrop) {
18292                                 dropEvts.push( oDD );
18293                             // look for drag enter and drag over interactions
18294                             } else {
18295
18296                                 // initial drag over: dragEnter fires
18297                                 if (!oldOvers[oDD.id]) {
18298                                     enterEvts.push( oDD );
18299                                 // subsequent drag overs: dragOver fires
18300                                 } else {
18301                                     overEvts.push( oDD );
18302                                 }
18303
18304                                 this.dragOvers[oDD.id] = oDD;
18305                             }
18306                         }
18307                     }
18308                 }
18309             }
18310
18311             if (this.mode) {
18312                 if (outEvts.length) {
18313                     dc.b4DragOut(e, outEvts);
18314                     dc.onDragOut(e, outEvts);
18315                 }
18316
18317                 if (enterEvts.length) {
18318                     dc.onDragEnter(e, enterEvts);
18319                 }
18320
18321                 if (overEvts.length) {
18322                     dc.b4DragOver(e, overEvts);
18323                     dc.onDragOver(e, overEvts);
18324                 }
18325
18326                 if (dropEvts.length) {
18327                     dc.b4DragDrop(e, dropEvts);
18328                     dc.onDragDrop(e, dropEvts);
18329                 }
18330
18331             } else {
18332                 // fire dragout events
18333                 var len = 0;
18334                 for (i=0, len=outEvts.length; i<len; ++i) {
18335                     dc.b4DragOut(e, outEvts[i].id);
18336                     dc.onDragOut(e, outEvts[i].id);
18337                 }
18338
18339                 // fire enter events
18340                 for (i=0,len=enterEvts.length; i<len; ++i) {
18341                     // dc.b4DragEnter(e, oDD.id);
18342                     dc.onDragEnter(e, enterEvts[i].id);
18343                 }
18344
18345                 // fire over events
18346                 for (i=0,len=overEvts.length; i<len; ++i) {
18347                     dc.b4DragOver(e, overEvts[i].id);
18348                     dc.onDragOver(e, overEvts[i].id);
18349                 }
18350
18351                 // fire drop events
18352                 for (i=0, len=dropEvts.length; i<len; ++i) {
18353                     dc.b4DragDrop(e, dropEvts[i].id);
18354                     dc.onDragDrop(e, dropEvts[i].id);
18355                 }
18356
18357             }
18358
18359             // notify about a drop that did not find a target
18360             if (isDrop && !dropEvts.length) {
18361                 dc.onInvalidDrop(e);
18362             }
18363
18364         },
18365
18366         /**
18367          * Helper function for getting the best match from the list of drag
18368          * and drop objects returned by the drag and drop events when we are
18369          * in INTERSECT mode.  It returns either the first object that the
18370          * cursor is over, or the object that has the greatest overlap with
18371          * the dragged element.
18372          * @method getBestMatch
18373          * @param  {DragDrop[]} dds The array of drag and drop objects
18374          * targeted
18375          * @return {DragDrop}       The best single match
18376          * @static
18377          */
18378         getBestMatch: function(dds) {
18379             var winner = null;
18380             // Return null if the input is not what we expect
18381             //if (!dds || !dds.length || dds.length == 0) {
18382                // winner = null;
18383             // If there is only one item, it wins
18384             //} else if (dds.length == 1) {
18385
18386             var len = dds.length;
18387
18388             if (len == 1) {
18389                 winner = dds[0];
18390             } else {
18391                 // Loop through the targeted items
18392                 for (var i=0; i<len; ++i) {
18393                     var dd = dds[i];
18394                     // If the cursor is over the object, it wins.  If the
18395                     // cursor is over multiple matches, the first one we come
18396                     // to wins.
18397                     if (dd.cursorIsOver) {
18398                         winner = dd;
18399                         break;
18400                     // Otherwise the object with the most overlap wins
18401                     } else {
18402                         if (!winner ||
18403                             winner.overlap.getArea() < dd.overlap.getArea()) {
18404                             winner = dd;
18405                         }
18406                     }
18407                 }
18408             }
18409
18410             return winner;
18411         },
18412
18413         /**
18414          * Refreshes the cache of the top-left and bottom-right points of the
18415          * drag and drop objects in the specified group(s).  This is in the
18416          * format that is stored in the drag and drop instance, so typical
18417          * usage is:
18418          * <code>
18419          * Roo.dd.DragDropMgr.refreshCache(ddinstance.groups);
18420          * </code>
18421          * Alternatively:
18422          * <code>
18423          * Roo.dd.DragDropMgr.refreshCache({group1:true, group2:true});
18424          * </code>
18425          * @TODO this really should be an indexed array.  Alternatively this
18426          * method could accept both.
18427          * @method refreshCache
18428          * @param {Object} groups an associative array of groups to refresh
18429          * @static
18430          */
18431         refreshCache: function(groups) {
18432             for (var sGroup in groups) {
18433                 if ("string" != typeof sGroup) {
18434                     continue;
18435                 }
18436                 for (var i in this.ids[sGroup]) {
18437                     var oDD = this.ids[sGroup][i];
18438
18439                     if (this.isTypeOfDD(oDD)) {
18440                     // if (this.isTypeOfDD(oDD) && oDD.isTarget) {
18441                         var loc = this.getLocation(oDD);
18442                         if (loc) {
18443                             this.locationCache[oDD.id] = loc;
18444                         } else {
18445                             delete this.locationCache[oDD.id];
18446                             // this will unregister the drag and drop object if
18447                             // the element is not in a usable state
18448                             // oDD.unreg();
18449                         }
18450                     }
18451                 }
18452             }
18453         },
18454
18455         /**
18456          * This checks to make sure an element exists and is in the DOM.  The
18457          * main purpose is to handle cases where innerHTML is used to remove
18458          * drag and drop objects from the DOM.  IE provides an 'unspecified
18459          * error' when trying to access the offsetParent of such an element
18460          * @method verifyEl
18461          * @param {HTMLElement} el the element to check
18462          * @return {boolean} true if the element looks usable
18463          * @static
18464          */
18465         verifyEl: function(el) {
18466             if (el) {
18467                 var parent;
18468                 if(Roo.isIE){
18469                     try{
18470                         parent = el.offsetParent;
18471                     }catch(e){}
18472                 }else{
18473                     parent = el.offsetParent;
18474                 }
18475                 if (parent) {
18476                     return true;
18477                 }
18478             }
18479
18480             return false;
18481         },
18482
18483         /**
18484          * Returns a Region object containing the drag and drop element's position
18485          * and size, including the padding configured for it
18486          * @method getLocation
18487          * @param {DragDrop} oDD the drag and drop object to get the
18488          *                       location for
18489          * @return {Roo.lib.Region} a Region object representing the total area
18490          *                             the element occupies, including any padding
18491          *                             the instance is configured for.
18492          * @static
18493          */
18494         getLocation: function(oDD) {
18495             if (! this.isTypeOfDD(oDD)) {
18496                 return null;
18497             }
18498
18499             var el = oDD.getEl(), pos, x1, x2, y1, y2, t, r, b, l;
18500
18501             try {
18502                 pos= Roo.lib.Dom.getXY(el);
18503             } catch (e) { }
18504
18505             if (!pos) {
18506                 return null;
18507             }
18508
18509             x1 = pos[0];
18510             x2 = x1 + el.offsetWidth;
18511             y1 = pos[1];
18512             y2 = y1 + el.offsetHeight;
18513
18514             t = y1 - oDD.padding[0];
18515             r = x2 + oDD.padding[1];
18516             b = y2 + oDD.padding[2];
18517             l = x1 - oDD.padding[3];
18518
18519             return new Roo.lib.Region( t, r, b, l );
18520         },
18521
18522         /**
18523          * Checks the cursor location to see if it over the target
18524          * @method isOverTarget
18525          * @param {Roo.lib.Point} pt The point to evaluate
18526          * @param {DragDrop} oTarget the DragDrop object we are inspecting
18527          * @return {boolean} true if the mouse is over the target
18528          * @private
18529          * @static
18530          */
18531         isOverTarget: function(pt, oTarget, intersect) {
18532             // use cache if available
18533             var loc = this.locationCache[oTarget.id];
18534             if (!loc || !this.useCache) {
18535                 loc = this.getLocation(oTarget);
18536                 this.locationCache[oTarget.id] = loc;
18537
18538             }
18539
18540             if (!loc) {
18541                 return false;
18542             }
18543
18544             oTarget.cursorIsOver = loc.contains( pt );
18545
18546             // DragDrop is using this as a sanity check for the initial mousedown
18547             // in this case we are done.  In POINT mode, if the drag obj has no
18548             // contraints, we are also done. Otherwise we need to evaluate the
18549             // location of the target as related to the actual location of the
18550             // dragged element.
18551             var dc = this.dragCurrent;
18552             if (!dc || !dc.getTargetCoord ||
18553                     (!intersect && !dc.constrainX && !dc.constrainY)) {
18554                 return oTarget.cursorIsOver;
18555             }
18556
18557             oTarget.overlap = null;
18558
18559             // Get the current location of the drag element, this is the
18560             // location of the mouse event less the delta that represents
18561             // where the original mousedown happened on the element.  We
18562             // need to consider constraints and ticks as well.
18563             var pos = dc.getTargetCoord(pt.x, pt.y);
18564
18565             var el = dc.getDragEl();
18566             var curRegion = new Roo.lib.Region( pos.y,
18567                                                    pos.x + el.offsetWidth,
18568                                                    pos.y + el.offsetHeight,
18569                                                    pos.x );
18570
18571             var overlap = curRegion.intersect(loc);
18572
18573             if (overlap) {
18574                 oTarget.overlap = overlap;
18575                 return (intersect) ? true : oTarget.cursorIsOver;
18576             } else {
18577                 return false;
18578             }
18579         },
18580
18581         /**
18582          * unload event handler
18583          * @method _onUnload
18584          * @private
18585          * @static
18586          */
18587         _onUnload: function(e, me) {
18588             Roo.dd.DragDropMgr.unregAll();
18589         },
18590
18591         /**
18592          * Cleans up the drag and drop events and objects.
18593          * @method unregAll
18594          * @private
18595          * @static
18596          */
18597         unregAll: function() {
18598
18599             if (this.dragCurrent) {
18600                 this.stopDrag();
18601                 this.dragCurrent = null;
18602             }
18603
18604             this._execOnAll("unreg", []);
18605
18606             for (i in this.elementCache) {
18607                 delete this.elementCache[i];
18608             }
18609
18610             this.elementCache = {};
18611             this.ids = {};
18612         },
18613
18614         /**
18615          * A cache of DOM elements
18616          * @property elementCache
18617          * @private
18618          * @static
18619          */
18620         elementCache: {},
18621
18622         /**
18623          * Get the wrapper for the DOM element specified
18624          * @method getElWrapper
18625          * @param {String} id the id of the element to get
18626          * @return {Roo.dd.DDM.ElementWrapper} the wrapped element
18627          * @private
18628          * @deprecated This wrapper isn't that useful
18629          * @static
18630          */
18631         getElWrapper: function(id) {
18632             var oWrapper = this.elementCache[id];
18633             if (!oWrapper || !oWrapper.el) {
18634                 oWrapper = this.elementCache[id] =
18635                     new this.ElementWrapper(Roo.getDom(id));
18636             }
18637             return oWrapper;
18638         },
18639
18640         /**
18641          * Returns the actual DOM element
18642          * @method getElement
18643          * @param {String} id the id of the elment to get
18644          * @return {Object} The element
18645          * @deprecated use Roo.getDom instead
18646          * @static
18647          */
18648         getElement: function(id) {
18649             return Roo.getDom(id);
18650         },
18651
18652         /**
18653          * Returns the style property for the DOM element (i.e.,
18654          * document.getElById(id).style)
18655          * @method getCss
18656          * @param {String} id the id of the elment to get
18657          * @return {Object} The style property of the element
18658          * @deprecated use Roo.getDom instead
18659          * @static
18660          */
18661         getCss: function(id) {
18662             var el = Roo.getDom(id);
18663             return (el) ? el.style : null;
18664         },
18665
18666         /**
18667          * Inner class for cached elements
18668          * @class DragDropMgr.ElementWrapper
18669          * @for DragDropMgr
18670          * @private
18671          * @deprecated
18672          */
18673         ElementWrapper: function(el) {
18674                 /**
18675                  * The element
18676                  * @property el
18677                  */
18678                 this.el = el || null;
18679                 /**
18680                  * The element id
18681                  * @property id
18682                  */
18683                 this.id = this.el && el.id;
18684                 /**
18685                  * A reference to the style property
18686                  * @property css
18687                  */
18688                 this.css = this.el && el.style;
18689             },
18690
18691         /**
18692          * Returns the X position of an html element
18693          * @method getPosX
18694          * @param el the element for which to get the position
18695          * @return {int} the X coordinate
18696          * @for DragDropMgr
18697          * @deprecated use Roo.lib.Dom.getX instead
18698          * @static
18699          */
18700         getPosX: function(el) {
18701             return Roo.lib.Dom.getX(el);
18702         },
18703
18704         /**
18705          * Returns the Y position of an html element
18706          * @method getPosY
18707          * @param el the element for which to get the position
18708          * @return {int} the Y coordinate
18709          * @deprecated use Roo.lib.Dom.getY instead
18710          * @static
18711          */
18712         getPosY: function(el) {
18713             return Roo.lib.Dom.getY(el);
18714         },
18715
18716         /**
18717          * Swap two nodes.  In IE, we use the native method, for others we
18718          * emulate the IE behavior
18719          * @method swapNode
18720          * @param n1 the first node to swap
18721          * @param n2 the other node to swap
18722          * @static
18723          */
18724         swapNode: function(n1, n2) {
18725             if (n1.swapNode) {
18726                 n1.swapNode(n2);
18727             } else {
18728                 var p = n2.parentNode;
18729                 var s = n2.nextSibling;
18730
18731                 if (s == n1) {
18732                     p.insertBefore(n1, n2);
18733                 } else if (n2 == n1.nextSibling) {
18734                     p.insertBefore(n2, n1);
18735                 } else {
18736                     n1.parentNode.replaceChild(n2, n1);
18737                     p.insertBefore(n1, s);
18738                 }
18739             }
18740         },
18741
18742         /**
18743          * Returns the current scroll position
18744          * @method getScroll
18745          * @private
18746          * @static
18747          */
18748         getScroll: function () {
18749             var t, l, dde=document.documentElement, db=document.body;
18750             if (dde && (dde.scrollTop || dde.scrollLeft)) {
18751                 t = dde.scrollTop;
18752                 l = dde.scrollLeft;
18753             } else if (db) {
18754                 t = db.scrollTop;
18755                 l = db.scrollLeft;
18756             } else {
18757
18758             }
18759             return { top: t, left: l };
18760         },
18761
18762         /**
18763          * Returns the specified element style property
18764          * @method getStyle
18765          * @param {HTMLElement} el          the element
18766          * @param {string}      styleProp   the style property
18767          * @return {string} The value of the style property
18768          * @deprecated use Roo.lib.Dom.getStyle
18769          * @static
18770          */
18771         getStyle: function(el, styleProp) {
18772             return Roo.fly(el).getStyle(styleProp);
18773         },
18774
18775         /**
18776          * Gets the scrollTop
18777          * @method getScrollTop
18778          * @return {int} the document's scrollTop
18779          * @static
18780          */
18781         getScrollTop: function () { return this.getScroll().top; },
18782
18783         /**
18784          * Gets the scrollLeft
18785          * @method getScrollLeft
18786          * @return {int} the document's scrollTop
18787          * @static
18788          */
18789         getScrollLeft: function () { return this.getScroll().left; },
18790
18791         /**
18792          * Sets the x/y position of an element to the location of the
18793          * target element.
18794          * @method moveToEl
18795          * @param {HTMLElement} moveEl      The element to move
18796          * @param {HTMLElement} targetEl    The position reference element
18797          * @static
18798          */
18799         moveToEl: function (moveEl, targetEl) {
18800             var aCoord = Roo.lib.Dom.getXY(targetEl);
18801             Roo.lib.Dom.setXY(moveEl, aCoord);
18802         },
18803
18804         /**
18805          * Numeric array sort function
18806          * @method numericSort
18807          * @static
18808          */
18809         numericSort: function(a, b) { return (a - b); },
18810
18811         /**
18812          * Internal counter
18813          * @property _timeoutCount
18814          * @private
18815          * @static
18816          */
18817         _timeoutCount: 0,
18818
18819         /**
18820          * Trying to make the load order less important.  Without this we get
18821          * an error if this file is loaded before the Event Utility.
18822          * @method _addListeners
18823          * @private
18824          * @static
18825          */
18826         _addListeners: function() {
18827             var DDM = Roo.dd.DDM;
18828             if ( Roo.lib.Event && document ) {
18829                 DDM._onLoad();
18830             } else {
18831                 if (DDM._timeoutCount > 2000) {
18832                 } else {
18833                     setTimeout(DDM._addListeners, 10);
18834                     if (document && document.body) {
18835                         DDM._timeoutCount += 1;
18836                     }
18837                 }
18838             }
18839         },
18840
18841         /**
18842          * Recursively searches the immediate parent and all child nodes for
18843          * the handle element in order to determine wheter or not it was
18844          * clicked.
18845          * @method handleWasClicked
18846          * @param node the html element to inspect
18847          * @static
18848          */
18849         handleWasClicked: function(node, id) {
18850             if (this.isHandle(id, node.id)) {
18851                 return true;
18852             } else {
18853                 // check to see if this is a text node child of the one we want
18854                 var p = node.parentNode;
18855
18856                 while (p) {
18857                     if (this.isHandle(id, p.id)) {
18858                         return true;
18859                     } else {
18860                         p = p.parentNode;
18861                     }
18862                 }
18863             }
18864
18865             return false;
18866         }
18867
18868     };
18869
18870 }();
18871
18872 // shorter alias, save a few bytes
18873 Roo.dd.DDM = Roo.dd.DragDropMgr;
18874 Roo.dd.DDM._addListeners();
18875
18876 }/*
18877  * Based on:
18878  * Ext JS Library 1.1.1
18879  * Copyright(c) 2006-2007, Ext JS, LLC.
18880  *
18881  * Originally Released Under LGPL - original licence link has changed is not relivant.
18882  *
18883  * Fork - LGPL
18884  * <script type="text/javascript">
18885  */
18886
18887 /**
18888  * @class Roo.dd.DD
18889  * A DragDrop implementation where the linked element follows the
18890  * mouse cursor during a drag.
18891  * @extends Roo.dd.DragDrop
18892  * @constructor
18893  * @param {String} id the id of the linked element
18894  * @param {String} sGroup the group of related DragDrop items
18895  * @param {object} config an object containing configurable attributes
18896  *                Valid properties for DD:
18897  *                    scroll
18898  */
18899 Roo.dd.DD = function(id, sGroup, config) {
18900     if (id) {
18901         this.init(id, sGroup, config);
18902     }
18903 };
18904
18905 Roo.extend(Roo.dd.DD, Roo.dd.DragDrop, {
18906
18907     /**
18908      * When set to true, the utility automatically tries to scroll the browser
18909      * window wehn a drag and drop element is dragged near the viewport boundary.
18910      * Defaults to true.
18911      * @property scroll
18912      * @type boolean
18913      */
18914     scroll: true,
18915
18916     /**
18917      * Sets the pointer offset to the distance between the linked element's top
18918      * left corner and the location the element was clicked
18919      * @method autoOffset
18920      * @param {int} iPageX the X coordinate of the click
18921      * @param {int} iPageY the Y coordinate of the click
18922      */
18923     autoOffset: function(iPageX, iPageY) {
18924         var x = iPageX - this.startPageX;
18925         var y = iPageY - this.startPageY;
18926         this.setDelta(x, y);
18927     },
18928
18929     /**
18930      * Sets the pointer offset.  You can call this directly to force the
18931      * offset to be in a particular location (e.g., pass in 0,0 to set it
18932      * to the center of the object)
18933      * @method setDelta
18934      * @param {int} iDeltaX the distance from the left
18935      * @param {int} iDeltaY the distance from the top
18936      */
18937     setDelta: function(iDeltaX, iDeltaY) {
18938         this.deltaX = iDeltaX;
18939         this.deltaY = iDeltaY;
18940     },
18941
18942     /**
18943      * Sets the drag element to the location of the mousedown or click event,
18944      * maintaining the cursor location relative to the location on the element
18945      * that was clicked.  Override this if you want to place the element in a
18946      * location other than where the cursor is.
18947      * @method setDragElPos
18948      * @param {int} iPageX the X coordinate of the mousedown or drag event
18949      * @param {int} iPageY the Y coordinate of the mousedown or drag event
18950      */
18951     setDragElPos: function(iPageX, iPageY) {
18952         // the first time we do this, we are going to check to make sure
18953         // the element has css positioning
18954
18955         var el = this.getDragEl();
18956         this.alignElWithMouse(el, iPageX, iPageY);
18957     },
18958
18959     /**
18960      * Sets the element to the location of the mousedown or click event,
18961      * maintaining the cursor location relative to the location on the element
18962      * that was clicked.  Override this if you want to place the element in a
18963      * location other than where the cursor is.
18964      * @method alignElWithMouse
18965      * @param {HTMLElement} el the element to move
18966      * @param {int} iPageX the X coordinate of the mousedown or drag event
18967      * @param {int} iPageY the Y coordinate of the mousedown or drag event
18968      */
18969     alignElWithMouse: function(el, iPageX, iPageY) {
18970         var oCoord = this.getTargetCoord(iPageX, iPageY);
18971         var fly = el.dom ? el : Roo.fly(el);
18972         if (!this.deltaSetXY) {
18973             var aCoord = [oCoord.x, oCoord.y];
18974             fly.setXY(aCoord);
18975             var newLeft = fly.getLeft(true);
18976             var newTop  = fly.getTop(true);
18977             this.deltaSetXY = [ newLeft - oCoord.x, newTop - oCoord.y ];
18978         } else {
18979             fly.setLeftTop(oCoord.x + this.deltaSetXY[0], oCoord.y + this.deltaSetXY[1]);
18980         }
18981
18982         this.cachePosition(oCoord.x, oCoord.y);
18983         this.autoScroll(oCoord.x, oCoord.y, el.offsetHeight, el.offsetWidth);
18984         return oCoord;
18985     },
18986
18987     /**
18988      * Saves the most recent position so that we can reset the constraints and
18989      * tick marks on-demand.  We need to know this so that we can calculate the
18990      * number of pixels the element is offset from its original position.
18991      * @method cachePosition
18992      * @param iPageX the current x position (optional, this just makes it so we
18993      * don't have to look it up again)
18994      * @param iPageY the current y position (optional, this just makes it so we
18995      * don't have to look it up again)
18996      */
18997     cachePosition: function(iPageX, iPageY) {
18998         if (iPageX) {
18999             this.lastPageX = iPageX;
19000             this.lastPageY = iPageY;
19001         } else {
19002             var aCoord = Roo.lib.Dom.getXY(this.getEl());
19003             this.lastPageX = aCoord[0];
19004             this.lastPageY = aCoord[1];
19005         }
19006     },
19007
19008     /**
19009      * Auto-scroll the window if the dragged object has been moved beyond the
19010      * visible window boundary.
19011      * @method autoScroll
19012      * @param {int} x the drag element's x position
19013      * @param {int} y the drag element's y position
19014      * @param {int} h the height of the drag element
19015      * @param {int} w the width of the drag element
19016      * @private
19017      */
19018     autoScroll: function(x, y, h, w) {
19019
19020         if (this.scroll) {
19021             // The client height
19022             var clientH = Roo.lib.Dom.getViewWidth();
19023
19024             // The client width
19025             var clientW = Roo.lib.Dom.getViewHeight();
19026
19027             // The amt scrolled down
19028             var st = this.DDM.getScrollTop();
19029
19030             // The amt scrolled right
19031             var sl = this.DDM.getScrollLeft();
19032
19033             // Location of the bottom of the element
19034             var bot = h + y;
19035
19036             // Location of the right of the element
19037             var right = w + x;
19038
19039             // The distance from the cursor to the bottom of the visible area,
19040             // adjusted so that we don't scroll if the cursor is beyond the
19041             // element drag constraints
19042             var toBot = (clientH + st - y - this.deltaY);
19043
19044             // The distance from the cursor to the right of the visible area
19045             var toRight = (clientW + sl - x - this.deltaX);
19046
19047
19048             // How close to the edge the cursor must be before we scroll
19049             // var thresh = (document.all) ? 100 : 40;
19050             var thresh = 40;
19051
19052             // How many pixels to scroll per autoscroll op.  This helps to reduce
19053             // clunky scrolling. IE is more sensitive about this ... it needs this
19054             // value to be higher.
19055             var scrAmt = (document.all) ? 80 : 30;
19056
19057             // Scroll down if we are near the bottom of the visible page and the
19058             // obj extends below the crease
19059             if ( bot > clientH && toBot < thresh ) {
19060                 window.scrollTo(sl, st + scrAmt);
19061             }
19062
19063             // Scroll up if the window is scrolled down and the top of the object
19064             // goes above the top border
19065             if ( y < st && st > 0 && y - st < thresh ) {
19066                 window.scrollTo(sl, st - scrAmt);
19067             }
19068
19069             // Scroll right if the obj is beyond the right border and the cursor is
19070             // near the border.
19071             if ( right > clientW && toRight < thresh ) {
19072                 window.scrollTo(sl + scrAmt, st);
19073             }
19074
19075             // Scroll left if the window has been scrolled to the right and the obj
19076             // extends past the left border
19077             if ( x < sl && sl > 0 && x - sl < thresh ) {
19078                 window.scrollTo(sl - scrAmt, st);
19079             }
19080         }
19081     },
19082
19083     /**
19084      * Finds the location the element should be placed if we want to move
19085      * it to where the mouse location less the click offset would place us.
19086      * @method getTargetCoord
19087      * @param {int} iPageX the X coordinate of the click
19088      * @param {int} iPageY the Y coordinate of the click
19089      * @return an object that contains the coordinates (Object.x and Object.y)
19090      * @private
19091      */
19092     getTargetCoord: function(iPageX, iPageY) {
19093
19094
19095         var x = iPageX - this.deltaX;
19096         var y = iPageY - this.deltaY;
19097
19098         if (this.constrainX) {
19099             if (x < this.minX) { x = this.minX; }
19100             if (x > this.maxX) { x = this.maxX; }
19101         }
19102
19103         if (this.constrainY) {
19104             if (y < this.minY) { y = this.minY; }
19105             if (y > this.maxY) { y = this.maxY; }
19106         }
19107
19108         x = this.getTick(x, this.xTicks);
19109         y = this.getTick(y, this.yTicks);
19110
19111
19112         return {x:x, y:y};
19113     },
19114
19115     /*
19116      * Sets up config options specific to this class. Overrides
19117      * Roo.dd.DragDrop, but all versions of this method through the
19118      * inheritance chain are called
19119      */
19120     applyConfig: function() {
19121         Roo.dd.DD.superclass.applyConfig.call(this);
19122         this.scroll = (this.config.scroll !== false);
19123     },
19124
19125     /*
19126      * Event that fires prior to the onMouseDown event.  Overrides
19127      * Roo.dd.DragDrop.
19128      */
19129     b4MouseDown: function(e) {
19130         // this.resetConstraints();
19131         this.autoOffset(e.getPageX(),
19132                             e.getPageY());
19133     },
19134
19135     /*
19136      * Event that fires prior to the onDrag event.  Overrides
19137      * Roo.dd.DragDrop.
19138      */
19139     b4Drag: function(e) {
19140         this.setDragElPos(e.getPageX(),
19141                             e.getPageY());
19142     },
19143
19144     toString: function() {
19145         return ("DD " + this.id);
19146     }
19147
19148     //////////////////////////////////////////////////////////////////////////
19149     // Debugging ygDragDrop events that can be overridden
19150     //////////////////////////////////////////////////////////////////////////
19151     /*
19152     startDrag: function(x, y) {
19153     },
19154
19155     onDrag: function(e) {
19156     },
19157
19158     onDragEnter: function(e, id) {
19159     },
19160
19161     onDragOver: function(e, id) {
19162     },
19163
19164     onDragOut: function(e, id) {
19165     },
19166
19167     onDragDrop: function(e, id) {
19168     },
19169
19170     endDrag: function(e) {
19171     }
19172
19173     */
19174
19175 });/*
19176  * Based on:
19177  * Ext JS Library 1.1.1
19178  * Copyright(c) 2006-2007, Ext JS, LLC.
19179  *
19180  * Originally Released Under LGPL - original licence link has changed is not relivant.
19181  *
19182  * Fork - LGPL
19183  * <script type="text/javascript">
19184  */
19185
19186 /**
19187  * @class Roo.dd.DDProxy
19188  * A DragDrop implementation that inserts an empty, bordered div into
19189  * the document that follows the cursor during drag operations.  At the time of
19190  * the click, the frame div is resized to the dimensions of the linked html
19191  * element, and moved to the exact location of the linked element.
19192  *
19193  * References to the "frame" element refer to the single proxy element that
19194  * was created to be dragged in place of all DDProxy elements on the
19195  * page.
19196  *
19197  * @extends Roo.dd.DD
19198  * @constructor
19199  * @param {String} id the id of the linked html element
19200  * @param {String} sGroup the group of related DragDrop objects
19201  * @param {object} config an object containing configurable attributes
19202  *                Valid properties for DDProxy in addition to those in DragDrop:
19203  *                   resizeFrame, centerFrame, dragElId
19204  */
19205 Roo.dd.DDProxy = function(id, sGroup, config) {
19206     if (id) {
19207         this.init(id, sGroup, config);
19208         this.initFrame();
19209     }
19210 };
19211
19212 /**
19213  * The default drag frame div id
19214  * @property Roo.dd.DDProxy.dragElId
19215  * @type String
19216  * @static
19217  */
19218 Roo.dd.DDProxy.dragElId = "ygddfdiv";
19219
19220 Roo.extend(Roo.dd.DDProxy, Roo.dd.DD, {
19221
19222     /**
19223      * By default we resize the drag frame to be the same size as the element
19224      * we want to drag (this is to get the frame effect).  We can turn it off
19225      * if we want a different behavior.
19226      * @property resizeFrame
19227      * @type boolean
19228      */
19229     resizeFrame: true,
19230
19231     /**
19232      * By default the frame is positioned exactly where the drag element is, so
19233      * we use the cursor offset provided by Roo.dd.DD.  Another option that works only if
19234      * you do not have constraints on the obj is to have the drag frame centered
19235      * around the cursor.  Set centerFrame to true for this effect.
19236      * @property centerFrame
19237      * @type boolean
19238      */
19239     centerFrame: false,
19240
19241     /**
19242      * Creates the proxy element if it does not yet exist
19243      * @method createFrame
19244      */
19245     createFrame: function() {
19246         var self = this;
19247         var body = document.body;
19248
19249         if (!body || !body.firstChild) {
19250             setTimeout( function() { self.createFrame(); }, 50 );
19251             return;
19252         }
19253
19254         var div = this.getDragEl();
19255
19256         if (!div) {
19257             div    = document.createElement("div");
19258             div.id = this.dragElId;
19259             var s  = div.style;
19260
19261             s.position   = "absolute";
19262             s.visibility = "hidden";
19263             s.cursor     = "move";
19264             s.border     = "2px solid #aaa";
19265             s.zIndex     = 999;
19266
19267             // appendChild can blow up IE if invoked prior to the window load event
19268             // while rendering a table.  It is possible there are other scenarios
19269             // that would cause this to happen as well.
19270             body.insertBefore(div, body.firstChild);
19271         }
19272     },
19273
19274     /**
19275      * Initialization for the drag frame element.  Must be called in the
19276      * constructor of all subclasses
19277      * @method initFrame
19278      */
19279     initFrame: function() {
19280         this.createFrame();
19281     },
19282
19283     applyConfig: function() {
19284         Roo.dd.DDProxy.superclass.applyConfig.call(this);
19285
19286         this.resizeFrame = (this.config.resizeFrame !== false);
19287         this.centerFrame = (this.config.centerFrame);
19288         this.setDragElId(this.config.dragElId || Roo.dd.DDProxy.dragElId);
19289     },
19290
19291     /**
19292      * Resizes the drag frame to the dimensions of the clicked object, positions
19293      * it over the object, and finally displays it
19294      * @method showFrame
19295      * @param {int} iPageX X click position
19296      * @param {int} iPageY Y click position
19297      * @private
19298      */
19299     showFrame: function(iPageX, iPageY) {
19300         var el = this.getEl();
19301         var dragEl = this.getDragEl();
19302         var s = dragEl.style;
19303
19304         this._resizeProxy();
19305
19306         if (this.centerFrame) {
19307             this.setDelta( Math.round(parseInt(s.width,  10)/2),
19308                            Math.round(parseInt(s.height, 10)/2) );
19309         }
19310
19311         this.setDragElPos(iPageX, iPageY);
19312
19313         Roo.fly(dragEl).show();
19314     },
19315
19316     /**
19317      * The proxy is automatically resized to the dimensions of the linked
19318      * element when a drag is initiated, unless resizeFrame is set to false
19319      * @method _resizeProxy
19320      * @private
19321      */
19322     _resizeProxy: function() {
19323         if (this.resizeFrame) {
19324             var el = this.getEl();
19325             Roo.fly(this.getDragEl()).setSize(el.offsetWidth, el.offsetHeight);
19326         }
19327     },
19328
19329     // overrides Roo.dd.DragDrop
19330     b4MouseDown: function(e) {
19331         var x = e.getPageX();
19332         var y = e.getPageY();
19333         this.autoOffset(x, y);
19334         this.setDragElPos(x, y);
19335     },
19336
19337     // overrides Roo.dd.DragDrop
19338     b4StartDrag: function(x, y) {
19339         // show the drag frame
19340         this.showFrame(x, y);
19341     },
19342
19343     // overrides Roo.dd.DragDrop
19344     b4EndDrag: function(e) {
19345         Roo.fly(this.getDragEl()).hide();
19346     },
19347
19348     // overrides Roo.dd.DragDrop
19349     // By default we try to move the element to the last location of the frame.
19350     // This is so that the default behavior mirrors that of Roo.dd.DD.
19351     endDrag: function(e) {
19352
19353         var lel = this.getEl();
19354         var del = this.getDragEl();
19355
19356         // Show the drag frame briefly so we can get its position
19357         del.style.visibility = "";
19358
19359         this.beforeMove();
19360         // Hide the linked element before the move to get around a Safari
19361         // rendering bug.
19362         lel.style.visibility = "hidden";
19363         Roo.dd.DDM.moveToEl(lel, del);
19364         del.style.visibility = "hidden";
19365         lel.style.visibility = "";
19366
19367         this.afterDrag();
19368     },
19369
19370     beforeMove : function(){
19371
19372     },
19373
19374     afterDrag : function(){
19375
19376     },
19377
19378     toString: function() {
19379         return ("DDProxy " + this.id);
19380     }
19381
19382 });
19383 /*
19384  * Based on:
19385  * Ext JS Library 1.1.1
19386  * Copyright(c) 2006-2007, Ext JS, LLC.
19387  *
19388  * Originally Released Under LGPL - original licence link has changed is not relivant.
19389  *
19390  * Fork - LGPL
19391  * <script type="text/javascript">
19392  */
19393
19394  /**
19395  * @class Roo.dd.DDTarget
19396  * A DragDrop implementation that does not move, but can be a drop
19397  * target.  You would get the same result by simply omitting implementation
19398  * for the event callbacks, but this way we reduce the processing cost of the
19399  * event listener and the callbacks.
19400  * @extends Roo.dd.DragDrop
19401  * @constructor
19402  * @param {String} id the id of the element that is a drop target
19403  * @param {String} sGroup the group of related DragDrop objects
19404  * @param {object} config an object containing configurable attributes
19405  *                 Valid properties for DDTarget in addition to those in
19406  *                 DragDrop:
19407  *                    none
19408  */
19409 Roo.dd.DDTarget = function(id, sGroup, config) {
19410     if (id) {
19411         this.initTarget(id, sGroup, config);
19412     }
19413     if (config.listeners || config.events) { 
19414        Roo.dd.DragDrop.superclass.constructor.call(this,  { 
19415             listeners : config.listeners || {}, 
19416             events : config.events || {} 
19417         });    
19418     }
19419 };
19420
19421 // Roo.dd.DDTarget.prototype = new Roo.dd.DragDrop();
19422 Roo.extend(Roo.dd.DDTarget, Roo.dd.DragDrop, {
19423     toString: function() {
19424         return ("DDTarget " + this.id);
19425     }
19426 });
19427 /*
19428  * Based on:
19429  * Ext JS Library 1.1.1
19430  * Copyright(c) 2006-2007, Ext JS, LLC.
19431  *
19432  * Originally Released Under LGPL - original licence link has changed is not relivant.
19433  *
19434  * Fork - LGPL
19435  * <script type="text/javascript">
19436  */
19437  
19438
19439 /**
19440  * @class Roo.dd.ScrollManager
19441  * Provides automatic scrolling of overflow regions in the page during drag operations.<br><br>
19442  * <b>Note: This class uses "Point Mode" and is untested in "Intersect Mode".</b>
19443  * @singleton
19444  */
19445 Roo.dd.ScrollManager = function(){
19446     var ddm = Roo.dd.DragDropMgr;
19447     var els = {};
19448     var dragEl = null;
19449     var proc = {};
19450     
19451     
19452     
19453     var onStop = function(e){
19454         dragEl = null;
19455         clearProc();
19456     };
19457     
19458     var triggerRefresh = function(){
19459         if(ddm.dragCurrent){
19460              ddm.refreshCache(ddm.dragCurrent.groups);
19461         }
19462     };
19463     
19464     var doScroll = function(){
19465         if(ddm.dragCurrent){
19466             var dds = Roo.dd.ScrollManager;
19467             if(!dds.animate){
19468                 if(proc.el.scroll(proc.dir, dds.increment)){
19469                     triggerRefresh();
19470                 }
19471             }else{
19472                 proc.el.scroll(proc.dir, dds.increment, true, dds.animDuration, triggerRefresh);
19473             }
19474         }
19475     };
19476     
19477     var clearProc = function(){
19478         if(proc.id){
19479             clearInterval(proc.id);
19480         }
19481         proc.id = 0;
19482         proc.el = null;
19483         proc.dir = "";
19484     };
19485     
19486     var startProc = function(el, dir){
19487          Roo.log('scroll startproc');
19488         clearProc();
19489         proc.el = el;
19490         proc.dir = dir;
19491         proc.id = setInterval(doScroll, Roo.dd.ScrollManager.frequency);
19492     };
19493     
19494     var onFire = function(e, isDrop){
19495        
19496         if(isDrop || !ddm.dragCurrent){ return; }
19497         var dds = Roo.dd.ScrollManager;
19498         if(!dragEl || dragEl != ddm.dragCurrent){
19499             dragEl = ddm.dragCurrent;
19500             // refresh regions on drag start
19501             dds.refreshCache();
19502         }
19503         
19504         var xy = Roo.lib.Event.getXY(e);
19505         var pt = new Roo.lib.Point(xy[0], xy[1]);
19506         for(var id in els){
19507             var el = els[id], r = el._region;
19508             if(r && r.contains(pt) && el.isScrollable()){
19509                 if(r.bottom - pt.y <= dds.thresh){
19510                     if(proc.el != el){
19511                         startProc(el, "down");
19512                     }
19513                     return;
19514                 }else if(r.right - pt.x <= dds.thresh){
19515                     if(proc.el != el){
19516                         startProc(el, "left");
19517                     }
19518                     return;
19519                 }else if(pt.y - r.top <= dds.thresh){
19520                     if(proc.el != el){
19521                         startProc(el, "up");
19522                     }
19523                     return;
19524                 }else if(pt.x - r.left <= dds.thresh){
19525                     if(proc.el != el){
19526                         startProc(el, "right");
19527                     }
19528                     return;
19529                 }
19530             }
19531         }
19532         clearProc();
19533     };
19534     
19535     ddm.fireEvents = ddm.fireEvents.createSequence(onFire, ddm);
19536     ddm.stopDrag = ddm.stopDrag.createSequence(onStop, ddm);
19537     
19538     return {
19539         /**
19540          * Registers new overflow element(s) to auto scroll
19541          * @param {String/HTMLElement/Element/Array} el The id of or the element to be scrolled or an array of either
19542          */
19543         register : function(el){
19544             if(el instanceof Array){
19545                 for(var i = 0, len = el.length; i < len; i++) {
19546                         this.register(el[i]);
19547                 }
19548             }else{
19549                 el = Roo.get(el);
19550                 els[el.id] = el;
19551             }
19552             Roo.dd.ScrollManager.els = els;
19553         },
19554         
19555         /**
19556          * Unregisters overflow element(s) so they are no longer scrolled
19557          * @param {String/HTMLElement/Element/Array} el The id of or the element to be removed or an array of either
19558          */
19559         unregister : function(el){
19560             if(el instanceof Array){
19561                 for(var i = 0, len = el.length; i < len; i++) {
19562                         this.unregister(el[i]);
19563                 }
19564             }else{
19565                 el = Roo.get(el);
19566                 delete els[el.id];
19567             }
19568         },
19569         
19570         /**
19571          * The number of pixels from the edge of a container the pointer needs to be to 
19572          * trigger scrolling (defaults to 25)
19573          * @type Number
19574          */
19575         thresh : 25,
19576         
19577         /**
19578          * The number of pixels to scroll in each scroll increment (defaults to 50)
19579          * @type Number
19580          */
19581         increment : 100,
19582         
19583         /**
19584          * The frequency of scrolls in milliseconds (defaults to 500)
19585          * @type Number
19586          */
19587         frequency : 500,
19588         
19589         /**
19590          * True to animate the scroll (defaults to true)
19591          * @type Boolean
19592          */
19593         animate: true,
19594         
19595         /**
19596          * The animation duration in seconds - 
19597          * MUST BE less than Roo.dd.ScrollManager.frequency! (defaults to .4)
19598          * @type Number
19599          */
19600         animDuration: .4,
19601         
19602         /**
19603          * Manually trigger a cache refresh.
19604          */
19605         refreshCache : function(){
19606             for(var id in els){
19607                 if(typeof els[id] == 'object'){ // for people extending the object prototype
19608                     els[id]._region = els[id].getRegion();
19609                 }
19610             }
19611         }
19612     };
19613 }();/*
19614  * Based on:
19615  * Ext JS Library 1.1.1
19616  * Copyright(c) 2006-2007, Ext JS, LLC.
19617  *
19618  * Originally Released Under LGPL - original licence link has changed is not relivant.
19619  *
19620  * Fork - LGPL
19621  * <script type="text/javascript">
19622  */
19623  
19624
19625 /**
19626  * @class Roo.dd.Registry
19627  * Provides easy access to all drag drop components that are registered on a page.  Items can be retrieved either
19628  * directly by DOM node id, or by passing in the drag drop event that occurred and looking up the event target.
19629  * @singleton
19630  */
19631 Roo.dd.Registry = function(){
19632     var elements = {}; 
19633     var handles = {}; 
19634     var autoIdSeed = 0;
19635
19636     var getId = function(el, autogen){
19637         if(typeof el == "string"){
19638             return el;
19639         }
19640         var id = el.id;
19641         if(!id && autogen !== false){
19642             id = "roodd-" + (++autoIdSeed);
19643             el.id = id;
19644         }
19645         return id;
19646     };
19647     
19648     return {
19649     /**
19650      * Register a drag drop element
19651      * @param {String|HTMLElement} element The id or DOM node to register
19652      * @param {Object} data (optional) A custom data object that will be passed between the elements that are involved
19653      * in drag drop operations.  You can populate this object with any arbitrary properties that your own code
19654      * knows how to interpret, plus there are some specific properties known to the Registry that should be
19655      * populated in the data object (if applicable):
19656      * <pre>
19657 Value      Description<br />
19658 ---------  ------------------------------------------<br />
19659 handles    Array of DOM nodes that trigger dragging<br />
19660            for the element being registered<br />
19661 isHandle   True if the element passed in triggers<br />
19662            dragging itself, else false
19663 </pre>
19664      */
19665         register : function(el, data){
19666             data = data || {};
19667             if(typeof el == "string"){
19668                 el = document.getElementById(el);
19669             }
19670             data.ddel = el;
19671             elements[getId(el)] = data;
19672             if(data.isHandle !== false){
19673                 handles[data.ddel.id] = data;
19674             }
19675             if(data.handles){
19676                 var hs = data.handles;
19677                 for(var i = 0, len = hs.length; i < len; i++){
19678                         handles[getId(hs[i])] = data;
19679                 }
19680             }
19681         },
19682
19683     /**
19684      * Unregister a drag drop element
19685      * @param {String|HTMLElement}  element The id or DOM node to unregister
19686      */
19687         unregister : function(el){
19688             var id = getId(el, false);
19689             var data = elements[id];
19690             if(data){
19691                 delete elements[id];
19692                 if(data.handles){
19693                     var hs = data.handles;
19694                     for(var i = 0, len = hs.length; i < len; i++){
19695                         delete handles[getId(hs[i], false)];
19696                     }
19697                 }
19698             }
19699         },
19700
19701     /**
19702      * Returns the handle registered for a DOM Node by id
19703      * @param {String|HTMLElement} id The DOM node or id to look up
19704      * @return {Object} handle The custom handle data
19705      */
19706         getHandle : function(id){
19707             if(typeof id != "string"){ // must be element?
19708                 id = id.id;
19709             }
19710             return handles[id];
19711         },
19712
19713     /**
19714      * Returns the handle that is registered for the DOM node that is the target of the event
19715      * @param {Event} e The event
19716      * @return {Object} handle The custom handle data
19717      */
19718         getHandleFromEvent : function(e){
19719             var t = Roo.lib.Event.getTarget(e);
19720             return t ? handles[t.id] : null;
19721         },
19722
19723     /**
19724      * Returns a custom data object that is registered for a DOM node by id
19725      * @param {String|HTMLElement} id The DOM node or id to look up
19726      * @return {Object} data The custom data
19727      */
19728         getTarget : function(id){
19729             if(typeof id != "string"){ // must be element?
19730                 id = id.id;
19731             }
19732             return elements[id];
19733         },
19734
19735     /**
19736      * Returns a custom data object that is registered for the DOM node that is the target of the event
19737      * @param {Event} e The event
19738      * @return {Object} data The custom data
19739      */
19740         getTargetFromEvent : function(e){
19741             var t = Roo.lib.Event.getTarget(e);
19742             return t ? elements[t.id] || handles[t.id] : null;
19743         }
19744     };
19745 }();/*
19746  * Based on:
19747  * Ext JS Library 1.1.1
19748  * Copyright(c) 2006-2007, Ext JS, LLC.
19749  *
19750  * Originally Released Under LGPL - original licence link has changed is not relivant.
19751  *
19752  * Fork - LGPL
19753  * <script type="text/javascript">
19754  */
19755  
19756
19757 /**
19758  * @class Roo.dd.StatusProxy
19759  * A specialized drag proxy that supports a drop status icon, {@link Roo.Layer} styles and auto-repair.  This is the
19760  * default drag proxy used by all Roo.dd components.
19761  * @constructor
19762  * @param {Object} config
19763  */
19764 Roo.dd.StatusProxy = function(config){
19765     Roo.apply(this, config);
19766     this.id = this.id || Roo.id();
19767     this.el = new Roo.Layer({
19768         dh: {
19769             id: this.id, tag: "div", cls: "x-dd-drag-proxy "+this.dropNotAllowed, children: [
19770                 {tag: "div", cls: "x-dd-drop-icon"},
19771                 {tag: "div", cls: "x-dd-drag-ghost"}
19772             ]
19773         }, 
19774         shadow: !config || config.shadow !== false
19775     });
19776     this.ghost = Roo.get(this.el.dom.childNodes[1]);
19777     this.dropStatus = this.dropNotAllowed;
19778 };
19779
19780 Roo.dd.StatusProxy.prototype = {
19781     /**
19782      * @cfg {String} dropAllowed
19783      * The CSS class to apply to the status element when drop is allowed (defaults to "x-dd-drop-ok").
19784      */
19785     dropAllowed : "x-dd-drop-ok",
19786     /**
19787      * @cfg {String} dropNotAllowed
19788      * The CSS class to apply to the status element when drop is not allowed (defaults to "x-dd-drop-nodrop").
19789      */
19790     dropNotAllowed : "x-dd-drop-nodrop",
19791
19792     /**
19793      * Updates the proxy's visual element to indicate the status of whether or not drop is allowed
19794      * over the current target element.
19795      * @param {String} cssClass The css class for the new drop status indicator image
19796      */
19797     setStatus : function(cssClass){
19798         cssClass = cssClass || this.dropNotAllowed;
19799         if(this.dropStatus != cssClass){
19800             this.el.replaceClass(this.dropStatus, cssClass);
19801             this.dropStatus = cssClass;
19802         }
19803     },
19804
19805     /**
19806      * Resets the status indicator to the default dropNotAllowed value
19807      * @param {Boolean} clearGhost True to also remove all content from the ghost, false to preserve it
19808      */
19809     reset : function(clearGhost){
19810         this.el.dom.className = "x-dd-drag-proxy " + this.dropNotAllowed;
19811         this.dropStatus = this.dropNotAllowed;
19812         if(clearGhost){
19813             this.ghost.update("");
19814         }
19815     },
19816
19817     /**
19818      * Updates the contents of the ghost element
19819      * @param {String} html The html that will replace the current innerHTML of the ghost element
19820      */
19821     update : function(html){
19822         if(typeof html == "string"){
19823             this.ghost.update(html);
19824         }else{
19825             this.ghost.update("");
19826             html.style.margin = "0";
19827             this.ghost.dom.appendChild(html);
19828         }
19829         // ensure float = none set?? cant remember why though.
19830         var el = this.ghost.dom.firstChild;
19831                 if(el){
19832                         Roo.fly(el).setStyle('float', 'none');
19833                 }
19834     },
19835     
19836     /**
19837      * Returns the underlying proxy {@link Roo.Layer}
19838      * @return {Roo.Layer} el
19839     */
19840     getEl : function(){
19841         return this.el;
19842     },
19843
19844     /**
19845      * Returns the ghost element
19846      * @return {Roo.Element} el
19847      */
19848     getGhost : function(){
19849         return this.ghost;
19850     },
19851
19852     /**
19853      * Hides the proxy
19854      * @param {Boolean} clear True to reset the status and clear the ghost contents, false to preserve them
19855      */
19856     hide : function(clear){
19857         this.el.hide();
19858         if(clear){
19859             this.reset(true);
19860         }
19861     },
19862
19863     /**
19864      * Stops the repair animation if it's currently running
19865      */
19866     stop : function(){
19867         if(this.anim && this.anim.isAnimated && this.anim.isAnimated()){
19868             this.anim.stop();
19869         }
19870     },
19871
19872     /**
19873      * Displays this proxy
19874      */
19875     show : function(){
19876         this.el.show();
19877     },
19878
19879     /**
19880      * Force the Layer to sync its shadow and shim positions to the element
19881      */
19882     sync : function(){
19883         this.el.sync();
19884     },
19885
19886     /**
19887      * Causes the proxy to return to its position of origin via an animation.  Should be called after an
19888      * invalid drop operation by the item being dragged.
19889      * @param {Array} xy The XY position of the element ([x, y])
19890      * @param {Function} callback The function to call after the repair is complete
19891      * @param {Object} scope The scope in which to execute the callback
19892      */
19893     repair : function(xy, callback, scope){
19894         this.callback = callback;
19895         this.scope = scope;
19896         if(xy && this.animRepair !== false){
19897             this.el.addClass("x-dd-drag-repair");
19898             this.el.hideUnders(true);
19899             this.anim = this.el.shift({
19900                 duration: this.repairDuration || .5,
19901                 easing: 'easeOut',
19902                 xy: xy,
19903                 stopFx: true,
19904                 callback: this.afterRepair,
19905                 scope: this
19906             });
19907         }else{
19908             this.afterRepair();
19909         }
19910     },
19911
19912     // private
19913     afterRepair : function(){
19914         this.hide(true);
19915         if(typeof this.callback == "function"){
19916             this.callback.call(this.scope || this);
19917         }
19918         this.callback = null;
19919         this.scope = null;
19920     }
19921 };/*
19922  * Based on:
19923  * Ext JS Library 1.1.1
19924  * Copyright(c) 2006-2007, Ext JS, LLC.
19925  *
19926  * Originally Released Under LGPL - original licence link has changed is not relivant.
19927  *
19928  * Fork - LGPL
19929  * <script type="text/javascript">
19930  */
19931
19932 /**
19933  * @class Roo.dd.DragSource
19934  * @extends Roo.dd.DDProxy
19935  * A simple class that provides the basic implementation needed to make any element draggable.
19936  * @constructor
19937  * @param {String/HTMLElement/Element} el The container element
19938  * @param {Object} config
19939  */
19940 Roo.dd.DragSource = function(el, config){
19941     this.el = Roo.get(el);
19942     this.dragData = {};
19943     
19944     Roo.apply(this, config);
19945     
19946     if(!this.proxy){
19947         this.proxy = new Roo.dd.StatusProxy();
19948     }
19949
19950     Roo.dd.DragSource.superclass.constructor.call(this, this.el.dom, this.ddGroup || this.group,
19951           {dragElId : this.proxy.id, resizeFrame: false, isTarget: false, scroll: this.scroll === true});
19952     
19953     this.dragging = false;
19954 };
19955
19956 Roo.extend(Roo.dd.DragSource, Roo.dd.DDProxy, {
19957     /**
19958      * @cfg {String} dropAllowed
19959      * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
19960      */
19961     dropAllowed : "x-dd-drop-ok",
19962     /**
19963      * @cfg {String} dropNotAllowed
19964      * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
19965      */
19966     dropNotAllowed : "x-dd-drop-nodrop",
19967
19968     /**
19969      * Returns the data object associated with this drag source
19970      * @return {Object} data An object containing arbitrary data
19971      */
19972     getDragData : function(e){
19973         return this.dragData;
19974     },
19975
19976     // private
19977     onDragEnter : function(e, id){
19978         var target = Roo.dd.DragDropMgr.getDDById(id);
19979         this.cachedTarget = target;
19980         if(this.beforeDragEnter(target, e, id) !== false){
19981             if(target.isNotifyTarget){
19982                 var status = target.notifyEnter(this, e, this.dragData);
19983                 this.proxy.setStatus(status);
19984             }else{
19985                 this.proxy.setStatus(this.dropAllowed);
19986             }
19987             
19988             if(this.afterDragEnter){
19989                 /**
19990                  * An empty function by default, but provided so that you can perform a custom action
19991                  * when the dragged item enters the drop target by providing an implementation.
19992                  * @param {Roo.dd.DragDrop} target The drop target
19993                  * @param {Event} e The event object
19994                  * @param {String} id The id of the dragged element
19995                  * @method afterDragEnter
19996                  */
19997                 this.afterDragEnter(target, e, id);
19998             }
19999         }
20000     },
20001
20002     /**
20003      * An empty function by default, but provided so that you can perform a custom action
20004      * before the dragged item enters the drop target and optionally cancel the onDragEnter.
20005      * @param {Roo.dd.DragDrop} target The drop target
20006      * @param {Event} e The event object
20007      * @param {String} id The id of the dragged element
20008      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
20009      */
20010     beforeDragEnter : function(target, e, id){
20011         return true;
20012     },
20013
20014     // private
20015     alignElWithMouse: function() {
20016         Roo.dd.DragSource.superclass.alignElWithMouse.apply(this, arguments);
20017         this.proxy.sync();
20018     },
20019
20020     // private
20021     onDragOver : function(e, id){
20022         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
20023         if(this.beforeDragOver(target, e, id) !== false){
20024             if(target.isNotifyTarget){
20025                 var status = target.notifyOver(this, e, this.dragData);
20026                 this.proxy.setStatus(status);
20027             }
20028
20029             if(this.afterDragOver){
20030                 /**
20031                  * An empty function by default, but provided so that you can perform a custom action
20032                  * while the dragged item is over the drop target by providing an implementation.
20033                  * @param {Roo.dd.DragDrop} target The drop target
20034                  * @param {Event} e The event object
20035                  * @param {String} id The id of the dragged element
20036                  * @method afterDragOver
20037                  */
20038                 this.afterDragOver(target, e, id);
20039             }
20040         }
20041     },
20042
20043     /**
20044      * An empty function by default, but provided so that you can perform a custom action
20045      * while the dragged item is over the drop target and optionally cancel the onDragOver.
20046      * @param {Roo.dd.DragDrop} target The drop target
20047      * @param {Event} e The event object
20048      * @param {String} id The id of the dragged element
20049      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
20050      */
20051     beforeDragOver : function(target, e, id){
20052         return true;
20053     },
20054
20055     // private
20056     onDragOut : function(e, id){
20057         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
20058         if(this.beforeDragOut(target, e, id) !== false){
20059             if(target.isNotifyTarget){
20060                 target.notifyOut(this, e, this.dragData);
20061             }
20062             this.proxy.reset();
20063             if(this.afterDragOut){
20064                 /**
20065                  * An empty function by default, but provided so that you can perform a custom action
20066                  * after the dragged item is dragged out of the target without dropping.
20067                  * @param {Roo.dd.DragDrop} target The drop target
20068                  * @param {Event} e The event object
20069                  * @param {String} id The id of the dragged element
20070                  * @method afterDragOut
20071                  */
20072                 this.afterDragOut(target, e, id);
20073             }
20074         }
20075         this.cachedTarget = null;
20076     },
20077
20078     /**
20079      * An empty function by default, but provided so that you can perform a custom action before the dragged
20080      * item is dragged out of the target without dropping, and optionally cancel the onDragOut.
20081      * @param {Roo.dd.DragDrop} target The drop target
20082      * @param {Event} e The event object
20083      * @param {String} id The id of the dragged element
20084      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
20085      */
20086     beforeDragOut : function(target, e, id){
20087         return true;
20088     },
20089     
20090     // private
20091     onDragDrop : function(e, id){
20092         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
20093         if(this.beforeDragDrop(target, e, id) !== false){
20094             if(target.isNotifyTarget){
20095                 if(target.notifyDrop(this, e, this.dragData)){ // valid drop?
20096                     this.onValidDrop(target, e, id);
20097                 }else{
20098                     this.onInvalidDrop(target, e, id);
20099                 }
20100             }else{
20101                 this.onValidDrop(target, e, id);
20102             }
20103             
20104             if(this.afterDragDrop){
20105                 /**
20106                  * An empty function by default, but provided so that you can perform a custom action
20107                  * after a valid drag drop has occurred by providing an implementation.
20108                  * @param {Roo.dd.DragDrop} target The drop target
20109                  * @param {Event} e The event object
20110                  * @param {String} id The id of the dropped element
20111                  * @method afterDragDrop
20112                  */
20113                 this.afterDragDrop(target, e, id);
20114             }
20115         }
20116         delete this.cachedTarget;
20117     },
20118
20119     /**
20120      * An empty function by default, but provided so that you can perform a custom action before the dragged
20121      * item is dropped onto the target and optionally cancel the onDragDrop.
20122      * @param {Roo.dd.DragDrop} target The drop target
20123      * @param {Event} e The event object
20124      * @param {String} id The id of the dragged element
20125      * @return {Boolean} isValid True if the drag drop event is valid, else false to cancel
20126      */
20127     beforeDragDrop : function(target, e, id){
20128         return true;
20129     },
20130
20131     // private
20132     onValidDrop : function(target, e, id){
20133         this.hideProxy();
20134         if(this.afterValidDrop){
20135             /**
20136              * An empty function by default, but provided so that you can perform a custom action
20137              * after a valid drop has occurred by providing an implementation.
20138              * @param {Object} target The target DD 
20139              * @param {Event} e The event object
20140              * @param {String} id The id of the dropped element
20141              * @method afterInvalidDrop
20142              */
20143             this.afterValidDrop(target, e, id);
20144         }
20145     },
20146
20147     // private
20148     getRepairXY : function(e, data){
20149         return this.el.getXY();  
20150     },
20151
20152     // private
20153     onInvalidDrop : function(target, e, id){
20154         this.beforeInvalidDrop(target, e, id);
20155         if(this.cachedTarget){
20156             if(this.cachedTarget.isNotifyTarget){
20157                 this.cachedTarget.notifyOut(this, e, this.dragData);
20158             }
20159             this.cacheTarget = null;
20160         }
20161         this.proxy.repair(this.getRepairXY(e, this.dragData), this.afterRepair, this);
20162
20163         if(this.afterInvalidDrop){
20164             /**
20165              * An empty function by default, but provided so that you can perform a custom action
20166              * after an invalid drop has occurred by providing an implementation.
20167              * @param {Event} e The event object
20168              * @param {String} id The id of the dropped element
20169              * @method afterInvalidDrop
20170              */
20171             this.afterInvalidDrop(e, id);
20172         }
20173     },
20174
20175     // private
20176     afterRepair : function(){
20177         if(Roo.enableFx){
20178             this.el.highlight(this.hlColor || "c3daf9");
20179         }
20180         this.dragging = false;
20181     },
20182
20183     /**
20184      * An empty function by default, but provided so that you can perform a custom action after an invalid
20185      * drop has occurred.
20186      * @param {Roo.dd.DragDrop} target The drop target
20187      * @param {Event} e The event object
20188      * @param {String} id The id of the dragged element
20189      * @return {Boolean} isValid True if the invalid drop should proceed, else false to cancel
20190      */
20191     beforeInvalidDrop : function(target, e, id){
20192         return true;
20193     },
20194
20195     // private
20196     handleMouseDown : function(e){
20197         if(this.dragging) {
20198             return;
20199         }
20200         var data = this.getDragData(e);
20201         if(data && this.onBeforeDrag(data, e) !== false){
20202             this.dragData = data;
20203             this.proxy.stop();
20204             Roo.dd.DragSource.superclass.handleMouseDown.apply(this, arguments);
20205         } 
20206     },
20207
20208     /**
20209      * An empty function by default, but provided so that you can perform a custom action before the initial
20210      * drag event begins and optionally cancel it.
20211      * @param {Object} data An object containing arbitrary data to be shared with drop targets
20212      * @param {Event} e The event object
20213      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
20214      */
20215     onBeforeDrag : function(data, e){
20216         return true;
20217     },
20218
20219     /**
20220      * An empty function by default, but provided so that you can perform a custom action once the initial
20221      * drag event has begun.  The drag cannot be canceled from this function.
20222      * @param {Number} x The x position of the click on the dragged object
20223      * @param {Number} y The y position of the click on the dragged object
20224      */
20225     onStartDrag : Roo.emptyFn,
20226
20227     // private - YUI override
20228     startDrag : function(x, y){
20229         this.proxy.reset();
20230         this.dragging = true;
20231         this.proxy.update("");
20232         this.onInitDrag(x, y);
20233         this.proxy.show();
20234     },
20235
20236     // private
20237     onInitDrag : function(x, y){
20238         var clone = this.el.dom.cloneNode(true);
20239         clone.id = Roo.id(); // prevent duplicate ids
20240         this.proxy.update(clone);
20241         this.onStartDrag(x, y);
20242         return true;
20243     },
20244
20245     /**
20246      * Returns the drag source's underlying {@link Roo.dd.StatusProxy}
20247      * @return {Roo.dd.StatusProxy} proxy The StatusProxy
20248      */
20249     getProxy : function(){
20250         return this.proxy;  
20251     },
20252
20253     /**
20254      * Hides the drag source's {@link Roo.dd.StatusProxy}
20255      */
20256     hideProxy : function(){
20257         this.proxy.hide();  
20258         this.proxy.reset(true);
20259         this.dragging = false;
20260     },
20261
20262     // private
20263     triggerCacheRefresh : function(){
20264         Roo.dd.DDM.refreshCache(this.groups);
20265     },
20266
20267     // private - override to prevent hiding
20268     b4EndDrag: function(e) {
20269     },
20270
20271     // private - override to prevent moving
20272     endDrag : function(e){
20273         this.onEndDrag(this.dragData, e);
20274     },
20275
20276     // private
20277     onEndDrag : function(data, e){
20278     },
20279     
20280     // private - pin to cursor
20281     autoOffset : function(x, y) {
20282         this.setDelta(-12, -20);
20283     }    
20284 });/*
20285  * Based on:
20286  * Ext JS Library 1.1.1
20287  * Copyright(c) 2006-2007, Ext JS, LLC.
20288  *
20289  * Originally Released Under LGPL - original licence link has changed is not relivant.
20290  *
20291  * Fork - LGPL
20292  * <script type="text/javascript">
20293  */
20294
20295
20296 /**
20297  * @class Roo.dd.DropTarget
20298  * @extends Roo.dd.DDTarget
20299  * A simple class that provides the basic implementation needed to make any element a drop target that can have
20300  * draggable items dropped onto it.  The drop has no effect until an implementation of notifyDrop is provided.
20301  * @constructor
20302  * @param {String/HTMLElement/Element} el The container element
20303  * @param {Object} config
20304  */
20305 Roo.dd.DropTarget = function(el, config){
20306     this.el = Roo.get(el);
20307     
20308     var listeners = false; ;
20309     if (config && config.listeners) {
20310         listeners= config.listeners;
20311         delete config.listeners;
20312     }
20313     Roo.apply(this, config);
20314     
20315     if(this.containerScroll){
20316         Roo.dd.ScrollManager.register(this.el);
20317     }
20318     this.addEvents( {
20319          /**
20320          * @scope Roo.dd.DropTarget
20321          */
20322          
20323          /**
20324          * @event enter
20325          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source is now over the
20326          * target.  This default implementation adds the CSS class specified by overClass (if any) to the drop element
20327          * and returns the dropAllowed config value.  This method should be overridden if drop validation is required.
20328          * 
20329          * IMPORTANT : it should set this.overClass and this.dropAllowed
20330          * 
20331          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
20332          * @param {Event} e The event
20333          * @param {Object} data An object containing arbitrary data supplied by the drag source
20334          */
20335         "enter" : true,
20336         
20337          /**
20338          * @event over
20339          * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the target.
20340          * This method will be called on every mouse movement while the drag source is over the drop target.
20341          * This default implementation simply returns the dropAllowed config value.
20342          * 
20343          * IMPORTANT : it should set this.dropAllowed
20344          * 
20345          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
20346          * @param {Event} e The event
20347          * @param {Object} data An object containing arbitrary data supplied by the drag source
20348          
20349          */
20350         "over" : true,
20351         /**
20352          * @event out
20353          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source has been dragged
20354          * out of the target without dropping.  This default implementation simply removes the CSS class specified by
20355          * overClass (if any) from the drop element.
20356          * 
20357          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
20358          * @param {Event} e The event
20359          * @param {Object} data An object containing arbitrary data supplied by the drag source
20360          */
20361          "out" : true,
20362          
20363         /**
20364          * @event drop
20365          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the dragged item has
20366          * been dropped on it.  This method has no default implementation and returns false, so you must provide an
20367          * implementation that does something to process the drop event and returns true so that the drag source's
20368          * repair action does not run.
20369          * 
20370          * IMPORTANT : it should set this.success
20371          * 
20372          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
20373          * @param {Event} e The event
20374          * @param {Object} data An object containing arbitrary data supplied by the drag source
20375         */
20376          "drop" : true
20377     });
20378             
20379      
20380     Roo.dd.DropTarget.superclass.constructor.call(  this, 
20381         this.el.dom, 
20382         this.ddGroup || this.group,
20383         {
20384             isTarget: true,
20385             listeners : listeners || {} 
20386            
20387         
20388         }
20389     );
20390
20391 };
20392
20393 Roo.extend(Roo.dd.DropTarget, Roo.dd.DDTarget, {
20394     /**
20395      * @cfg {String} overClass
20396      * The CSS class applied to the drop target element while the drag source is over it (defaults to "").
20397      */
20398      /**
20399      * @cfg {String} ddGroup
20400      * The drag drop group to handle drop events for
20401      */
20402      
20403     /**
20404      * @cfg {String} dropAllowed
20405      * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
20406      */
20407     dropAllowed : "x-dd-drop-ok",
20408     /**
20409      * @cfg {String} dropNotAllowed
20410      * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
20411      */
20412     dropNotAllowed : "x-dd-drop-nodrop",
20413     /**
20414      * @cfg {boolean} success
20415      * set this after drop listener.. 
20416      */
20417     success : false,
20418     /**
20419      * @cfg {boolean|String} valid true/false or string (ok-add/ok-sub/ok/nodrop)
20420      * if the drop point is valid for over/enter..
20421      */
20422     valid : false,
20423     // private
20424     isTarget : true,
20425
20426     // private
20427     isNotifyTarget : true,
20428     
20429     /**
20430      * @hide
20431      */
20432     notifyEnter : function(dd, e, data)
20433     {
20434         this.valid = true;
20435         this.fireEvent('enter', dd, e, data);
20436         if(this.overClass){
20437             this.el.addClass(this.overClass);
20438         }
20439         return typeof(this.valid) == 'string' ? 'x-dd-drop-' + this.valid : (
20440             this.valid ? this.dropAllowed : this.dropNotAllowed
20441         );
20442     },
20443
20444     /**
20445      * @hide
20446      */
20447     notifyOver : function(dd, e, data)
20448     {
20449         this.valid = true;
20450         this.fireEvent('over', dd, e, data);
20451         return typeof(this.valid) == 'string' ? 'x-dd-drop-' + this.valid : (
20452             this.valid ? this.dropAllowed : this.dropNotAllowed
20453         );
20454     },
20455
20456     /**
20457      * @hide
20458      */
20459     notifyOut : function(dd, e, data)
20460     {
20461         this.fireEvent('out', dd, e, data);
20462         if(this.overClass){
20463             this.el.removeClass(this.overClass);
20464         }
20465     },
20466
20467     /**
20468      * @hide
20469      */
20470     notifyDrop : function(dd, e, data)
20471     {
20472         this.success = false;
20473         this.fireEvent('drop', dd, e, data);
20474         return this.success;
20475     }
20476 });/*
20477  * Based on:
20478  * Ext JS Library 1.1.1
20479  * Copyright(c) 2006-2007, Ext JS, LLC.
20480  *
20481  * Originally Released Under LGPL - original licence link has changed is not relivant.
20482  *
20483  * Fork - LGPL
20484  * <script type="text/javascript">
20485  */
20486
20487
20488 /**
20489  * @class Roo.dd.DragZone
20490  * @extends Roo.dd.DragSource
20491  * This class provides a container DD instance that proxies for multiple child node sources.<br />
20492  * By default, this class requires that draggable child nodes are registered with {@link Roo.dd.Registry}.
20493  * @constructor
20494  * @param {String/HTMLElement/Element} el The container element
20495  * @param {Object} config
20496  */
20497 Roo.dd.DragZone = function(el, config){
20498     Roo.dd.DragZone.superclass.constructor.call(this, el, config);
20499     if(this.containerScroll){
20500         Roo.dd.ScrollManager.register(this.el);
20501     }
20502 };
20503
20504 Roo.extend(Roo.dd.DragZone, Roo.dd.DragSource, {
20505     /**
20506      * @cfg {Boolean} containerScroll True to register this container with the Scrollmanager
20507      * for auto scrolling during drag operations.
20508      */
20509     /**
20510      * @cfg {String} hlColor The color to use when visually highlighting the drag source in the afterRepair
20511      * method after a failed drop (defaults to "c3daf9" - light blue)
20512      */
20513
20514     /**
20515      * Called when a mousedown occurs in this container. Looks in {@link Roo.dd.Registry}
20516      * for a valid target to drag based on the mouse down. Override this method
20517      * to provide your own lookup logic (e.g. finding a child by class name). Make sure your returned
20518      * object has a "ddel" attribute (with an HTML Element) for other functions to work.
20519      * @param {EventObject} e The mouse down event
20520      * @return {Object} The dragData
20521      */
20522     getDragData : function(e){
20523         return Roo.dd.Registry.getHandleFromEvent(e);
20524     },
20525     
20526     /**
20527      * Called once drag threshold has been reached to initialize the proxy element. By default, it clones the
20528      * this.dragData.ddel
20529      * @param {Number} x The x position of the click on the dragged object
20530      * @param {Number} y The y position of the click on the dragged object
20531      * @return {Boolean} true to continue the drag, false to cancel
20532      */
20533     onInitDrag : function(x, y){
20534         this.proxy.update(this.dragData.ddel.cloneNode(true));
20535         this.onStartDrag(x, y);
20536         return true;
20537     },
20538     
20539     /**
20540      * Called after a repair of an invalid drop. By default, highlights this.dragData.ddel 
20541      */
20542     afterRepair : function(){
20543         if(Roo.enableFx){
20544             Roo.Element.fly(this.dragData.ddel).highlight(this.hlColor || "c3daf9");
20545         }
20546         this.dragging = false;
20547     },
20548
20549     /**
20550      * Called before a repair of an invalid drop to get the XY to animate to. By default returns
20551      * the XY of this.dragData.ddel
20552      * @param {EventObject} e The mouse up event
20553      * @return {Array} The xy location (e.g. [100, 200])
20554      */
20555     getRepairXY : function(e){
20556         return Roo.Element.fly(this.dragData.ddel).getXY();  
20557     }
20558 });/*
20559  * Based on:
20560  * Ext JS Library 1.1.1
20561  * Copyright(c) 2006-2007, Ext JS, LLC.
20562  *
20563  * Originally Released Under LGPL - original licence link has changed is not relivant.
20564  *
20565  * Fork - LGPL
20566  * <script type="text/javascript">
20567  */
20568 /**
20569  * @class Roo.dd.DropZone
20570  * @extends Roo.dd.DropTarget
20571  * This class provides a container DD instance that proxies for multiple child node targets.<br />
20572  * By default, this class requires that child nodes accepting drop are registered with {@link Roo.dd.Registry}.
20573  * @constructor
20574  * @param {String/HTMLElement/Element} el The container element
20575  * @param {Object} config
20576  */
20577 Roo.dd.DropZone = function(el, config){
20578     Roo.dd.DropZone.superclass.constructor.call(this, el, config);
20579 };
20580
20581 Roo.extend(Roo.dd.DropZone, Roo.dd.DropTarget, {
20582     /**
20583      * Returns a custom data object associated with the DOM node that is the target of the event.  By default
20584      * this looks up the event target in the {@link Roo.dd.Registry}, although you can override this method to
20585      * provide your own custom lookup.
20586      * @param {Event} e The event
20587      * @return {Object} data The custom data
20588      */
20589     getTargetFromEvent : function(e){
20590         return Roo.dd.Registry.getTargetFromEvent(e);
20591     },
20592
20593     /**
20594      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has entered a drop node
20595      * that it has registered.  This method has no default implementation and should be overridden to provide
20596      * node-specific processing if necessary.
20597      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from 
20598      * {@link #getTargetFromEvent} for this node)
20599      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
20600      * @param {Event} e The event
20601      * @param {Object} data An object containing arbitrary data supplied by the drag source
20602      */
20603     onNodeEnter : function(n, dd, e, data){
20604         
20605     },
20606
20607     /**
20608      * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is over a drop node
20609      * that it has registered.  The default implementation returns this.dropNotAllowed, so it should be
20610      * overridden to provide the proper feedback.
20611      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
20612      * {@link #getTargetFromEvent} for this node)
20613      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
20614      * @param {Event} e The event
20615      * @param {Object} data An object containing arbitrary data supplied by the drag source
20616      * @return {String} status The CSS class that communicates the drop status back to the source so that the
20617      * underlying {@link Roo.dd.StatusProxy} can be updated
20618      */
20619     onNodeOver : function(n, dd, e, data){
20620         return this.dropAllowed;
20621     },
20622
20623     /**
20624      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dragged out of
20625      * the drop node without dropping.  This method has no default implementation and should be overridden to provide
20626      * node-specific processing if necessary.
20627      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
20628      * {@link #getTargetFromEvent} for this node)
20629      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
20630      * @param {Event} e The event
20631      * @param {Object} data An object containing arbitrary data supplied by the drag source
20632      */
20633     onNodeOut : function(n, dd, e, data){
20634         
20635     },
20636
20637     /**
20638      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped onto
20639      * the drop node.  The default implementation returns false, so it should be overridden to provide the
20640      * appropriate processing of the drop event and return true so that the drag source's repair action does not run.
20641      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
20642      * {@link #getTargetFromEvent} for this node)
20643      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
20644      * @param {Event} e The event
20645      * @param {Object} data An object containing arbitrary data supplied by the drag source
20646      * @return {Boolean} True if the drop was valid, else false
20647      */
20648     onNodeDrop : function(n, dd, e, data){
20649         return false;
20650     },
20651
20652     /**
20653      * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is being dragged over it,
20654      * but not over any of its registered drop nodes.  The default implementation returns this.dropNotAllowed, so
20655      * it should be overridden to provide the proper feedback if necessary.
20656      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
20657      * @param {Event} e The event
20658      * @param {Object} data An object containing arbitrary data supplied by the drag source
20659      * @return {String} status The CSS class that communicates the drop status back to the source so that the
20660      * underlying {@link Roo.dd.StatusProxy} can be updated
20661      */
20662     onContainerOver : function(dd, e, data){
20663         return this.dropNotAllowed;
20664     },
20665
20666     /**
20667      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped on it,
20668      * but not on any of its registered drop nodes.  The default implementation returns false, so it should be
20669      * overridden to provide the appropriate processing of the drop event if you need the drop zone itself to
20670      * be able to accept drops.  It should return true when valid so that the drag source's repair action does not run.
20671      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
20672      * @param {Event} e The event
20673      * @param {Object} data An object containing arbitrary data supplied by the drag source
20674      * @return {Boolean} True if the drop was valid, else false
20675      */
20676     onContainerDrop : function(dd, e, data){
20677         return false;
20678     },
20679
20680     /**
20681      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source is now over
20682      * the zone.  The default implementation returns this.dropNotAllowed and expects that only registered drop
20683      * nodes can process drag drop operations, so if you need the drop zone itself to be able to process drops
20684      * you should override this method and provide a custom implementation.
20685      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
20686      * @param {Event} e The event
20687      * @param {Object} data An object containing arbitrary data supplied by the drag source
20688      * @return {String} status The CSS class that communicates the drop status back to the source so that the
20689      * underlying {@link Roo.dd.StatusProxy} can be updated
20690      */
20691     notifyEnter : function(dd, e, data){
20692         return this.dropNotAllowed;
20693     },
20694
20695     /**
20696      * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the drop zone.
20697      * This method will be called on every mouse movement while the drag source is over the drop zone.
20698      * It will call {@link #onNodeOver} while the drag source is over a registered node, and will also automatically
20699      * delegate to the appropriate node-specific methods as necessary when the drag source enters and exits
20700      * registered nodes ({@link #onNodeEnter}, {@link #onNodeOut}). If the drag source is not currently over a
20701      * registered node, it will call {@link #onContainerOver}.
20702      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
20703      * @param {Event} e The event
20704      * @param {Object} data An object containing arbitrary data supplied by the drag source
20705      * @return {String} status The CSS class that communicates the drop status back to the source so that the
20706      * underlying {@link Roo.dd.StatusProxy} can be updated
20707      */
20708     notifyOver : function(dd, e, data){
20709         var n = this.getTargetFromEvent(e);
20710         if(!n){ // not over valid drop target
20711             if(this.lastOverNode){
20712                 this.onNodeOut(this.lastOverNode, dd, e, data);
20713                 this.lastOverNode = null;
20714             }
20715             return this.onContainerOver(dd, e, data);
20716         }
20717         if(this.lastOverNode != n){
20718             if(this.lastOverNode){
20719                 this.onNodeOut(this.lastOverNode, dd, e, data);
20720             }
20721             this.onNodeEnter(n, dd, e, data);
20722             this.lastOverNode = n;
20723         }
20724         return this.onNodeOver(n, dd, e, data);
20725     },
20726
20727     /**
20728      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source has been dragged
20729      * out of the zone without dropping.  If the drag source is currently over a registered node, the notification
20730      * will be delegated to {@link #onNodeOut} for node-specific handling, otherwise it will be ignored.
20731      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
20732      * @param {Event} e The event
20733      * @param {Object} data An object containing arbitrary data supplied by the drag zone
20734      */
20735     notifyOut : function(dd, e, data){
20736         if(this.lastOverNode){
20737             this.onNodeOut(this.lastOverNode, dd, e, data);
20738             this.lastOverNode = null;
20739         }
20740     },
20741
20742     /**
20743      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the dragged item has
20744      * been dropped on it.  The drag zone will look up the target node based on the event passed in, and if there
20745      * is a node registered for that event, it will delegate to {@link #onNodeDrop} for node-specific handling,
20746      * otherwise it will call {@link #onContainerDrop}.
20747      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
20748      * @param {Event} e The event
20749      * @param {Object} data An object containing arbitrary data supplied by the drag source
20750      * @return {Boolean} True if the drop was valid, else false
20751      */
20752     notifyDrop : function(dd, e, data){
20753         if(this.lastOverNode){
20754             this.onNodeOut(this.lastOverNode, dd, e, data);
20755             this.lastOverNode = null;
20756         }
20757         var n = this.getTargetFromEvent(e);
20758         return n ?
20759             this.onNodeDrop(n, dd, e, data) :
20760             this.onContainerDrop(dd, e, data);
20761     },
20762
20763     // private
20764     triggerCacheRefresh : function(){
20765         Roo.dd.DDM.refreshCache(this.groups);
20766     }  
20767 });/*
20768  * Based on:
20769  * Ext JS Library 1.1.1
20770  * Copyright(c) 2006-2007, Ext JS, LLC.
20771  *
20772  * Originally Released Under LGPL - original licence link has changed is not relivant.
20773  *
20774  * Fork - LGPL
20775  * <script type="text/javascript">
20776  */
20777
20778
20779 /**
20780  * @class Roo.data.SortTypes
20781  * @singleton
20782  * Defines the default sorting (casting?) comparison functions used when sorting data.
20783  */
20784 Roo.data.SortTypes = {
20785     /**
20786      * Default sort that does nothing
20787      * @param {Mixed} s The value being converted
20788      * @return {Mixed} The comparison value
20789      */
20790     none : function(s){
20791         return s;
20792     },
20793     
20794     /**
20795      * The regular expression used to strip tags
20796      * @type {RegExp}
20797      * @property
20798      */
20799     stripTagsRE : /<\/?[^>]+>/gi,
20800     
20801     /**
20802      * Strips all HTML tags to sort on text only
20803      * @param {Mixed} s The value being converted
20804      * @return {String} The comparison value
20805      */
20806     asText : function(s){
20807         return String(s).replace(this.stripTagsRE, "");
20808     },
20809     
20810     /**
20811      * Strips all HTML tags to sort on text only - Case insensitive
20812      * @param {Mixed} s The value being converted
20813      * @return {String} The comparison value
20814      */
20815     asUCText : function(s){
20816         return String(s).toUpperCase().replace(this.stripTagsRE, "");
20817     },
20818     
20819     /**
20820      * Case insensitive string
20821      * @param {Mixed} s The value being converted
20822      * @return {String} The comparison value
20823      */
20824     asUCString : function(s) {
20825         return String(s).toUpperCase();
20826     },
20827     
20828     /**
20829      * Date sorting
20830      * @param {Mixed} s The value being converted
20831      * @return {Number} The comparison value
20832      */
20833     asDate : function(s) {
20834         if(!s){
20835             return 0;
20836         }
20837         if(s instanceof Date){
20838             return s.getTime();
20839         }
20840         return Date.parse(String(s));
20841     },
20842     
20843     /**
20844      * Float sorting
20845      * @param {Mixed} s The value being converted
20846      * @return {Float} The comparison value
20847      */
20848     asFloat : function(s) {
20849         var val = parseFloat(String(s).replace(/,/g, ""));
20850         if(isNaN(val)) val = 0;
20851         return val;
20852     },
20853     
20854     /**
20855      * Integer sorting
20856      * @param {Mixed} s The value being converted
20857      * @return {Number} The comparison value
20858      */
20859     asInt : function(s) {
20860         var val = parseInt(String(s).replace(/,/g, ""));
20861         if(isNaN(val)) val = 0;
20862         return val;
20863     }
20864 };/*
20865  * Based on:
20866  * Ext JS Library 1.1.1
20867  * Copyright(c) 2006-2007, Ext JS, LLC.
20868  *
20869  * Originally Released Under LGPL - original licence link has changed is not relivant.
20870  *
20871  * Fork - LGPL
20872  * <script type="text/javascript">
20873  */
20874
20875 /**
20876 * @class Roo.data.Record
20877  * Instances of this class encapsulate both record <em>definition</em> information, and record
20878  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
20879  * to access Records cached in an {@link Roo.data.Store} object.<br>
20880  * <p>
20881  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
20882  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
20883  * objects.<br>
20884  * <p>
20885  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
20886  * @constructor
20887  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
20888  * {@link #create}. The parameters are the same.
20889  * @param {Array} data An associative Array of data values keyed by the field name.
20890  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
20891  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
20892  * not specified an integer id is generated.
20893  */
20894 Roo.data.Record = function(data, id){
20895     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
20896     this.data = data;
20897 };
20898
20899 /**
20900  * Generate a constructor for a specific record layout.
20901  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
20902  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
20903  * Each field definition object may contain the following properties: <ul>
20904  * <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,
20905  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
20906  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
20907  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
20908  * is being used, then this is a string containing the javascript expression to reference the data relative to 
20909  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
20910  * to the data item relative to the record element. If the mapping expression is the same as the field name,
20911  * this may be omitted.</p></li>
20912  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
20913  * <ul><li>auto (Default, implies no conversion)</li>
20914  * <li>string</li>
20915  * <li>int</li>
20916  * <li>float</li>
20917  * <li>boolean</li>
20918  * <li>date</li></ul></p></li>
20919  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
20920  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
20921  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
20922  * by the Reader into an object that will be stored in the Record. It is passed the
20923  * following parameters:<ul>
20924  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
20925  * </ul></p></li>
20926  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
20927  * </ul>
20928  * <br>usage:<br><pre><code>
20929 var TopicRecord = Roo.data.Record.create(
20930     {name: 'title', mapping: 'topic_title'},
20931     {name: 'author', mapping: 'username'},
20932     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
20933     {name: 'lastPost', mapping: 'post_time', type: 'date'},
20934     {name: 'lastPoster', mapping: 'user2'},
20935     {name: 'excerpt', mapping: 'post_text'}
20936 );
20937
20938 var myNewRecord = new TopicRecord({
20939     title: 'Do my job please',
20940     author: 'noobie',
20941     totalPosts: 1,
20942     lastPost: new Date(),
20943     lastPoster: 'Animal',
20944     excerpt: 'No way dude!'
20945 });
20946 myStore.add(myNewRecord);
20947 </code></pre>
20948  * @method create
20949  * @static
20950  */
20951 Roo.data.Record.create = function(o){
20952     var f = function(){
20953         f.superclass.constructor.apply(this, arguments);
20954     };
20955     Roo.extend(f, Roo.data.Record);
20956     var p = f.prototype;
20957     p.fields = new Roo.util.MixedCollection(false, function(field){
20958         return field.name;
20959     });
20960     for(var i = 0, len = o.length; i < len; i++){
20961         p.fields.add(new Roo.data.Field(o[i]));
20962     }
20963     f.getField = function(name){
20964         return p.fields.get(name);  
20965     };
20966     return f;
20967 };
20968
20969 Roo.data.Record.AUTO_ID = 1000;
20970 Roo.data.Record.EDIT = 'edit';
20971 Roo.data.Record.REJECT = 'reject';
20972 Roo.data.Record.COMMIT = 'commit';
20973
20974 Roo.data.Record.prototype = {
20975     /**
20976      * Readonly flag - true if this record has been modified.
20977      * @type Boolean
20978      */
20979     dirty : false,
20980     editing : false,
20981     error: null,
20982     modified: null,
20983
20984     // private
20985     join : function(store){
20986         this.store = store;
20987     },
20988
20989     /**
20990      * Set the named field to the specified value.
20991      * @param {String} name The name of the field to set.
20992      * @param {Object} value The value to set the field to.
20993      */
20994     set : function(name, value){
20995         if(this.data[name] == value){
20996             return;
20997         }
20998         this.dirty = true;
20999         if(!this.modified){
21000             this.modified = {};
21001         }
21002         if(typeof this.modified[name] == 'undefined'){
21003             this.modified[name] = this.data[name];
21004         }
21005         this.data[name] = value;
21006         if(!this.editing && this.store){
21007             this.store.afterEdit(this);
21008         }       
21009     },
21010
21011     /**
21012      * Get the value of the named field.
21013      * @param {String} name The name of the field to get the value of.
21014      * @return {Object} The value of the field.
21015      */
21016     get : function(name){
21017         return this.data[name]; 
21018     },
21019
21020     // private
21021     beginEdit : function(){
21022         this.editing = true;
21023         this.modified = {}; 
21024     },
21025
21026     // private
21027     cancelEdit : function(){
21028         this.editing = false;
21029         delete this.modified;
21030     },
21031
21032     // private
21033     endEdit : function(){
21034         this.editing = false;
21035         if(this.dirty && this.store){
21036             this.store.afterEdit(this);
21037         }
21038     },
21039
21040     /**
21041      * Usually called by the {@link Roo.data.Store} which owns the Record.
21042      * Rejects all changes made to the Record since either creation, or the last commit operation.
21043      * Modified fields are reverted to their original values.
21044      * <p>
21045      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
21046      * of reject operations.
21047      */
21048     reject : function(){
21049         var m = this.modified;
21050         for(var n in m){
21051             if(typeof m[n] != "function"){
21052                 this.data[n] = m[n];
21053             }
21054         }
21055         this.dirty = false;
21056         delete this.modified;
21057         this.editing = false;
21058         if(this.store){
21059             this.store.afterReject(this);
21060         }
21061     },
21062
21063     /**
21064      * Usually called by the {@link Roo.data.Store} which owns the Record.
21065      * Commits all changes made to the Record since either creation, or the last commit operation.
21066      * <p>
21067      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
21068      * of commit operations.
21069      */
21070     commit : function(){
21071         this.dirty = false;
21072         delete this.modified;
21073         this.editing = false;
21074         if(this.store){
21075             this.store.afterCommit(this);
21076         }
21077     },
21078
21079     // private
21080     hasError : function(){
21081         return this.error != null;
21082     },
21083
21084     // private
21085     clearError : function(){
21086         this.error = null;
21087     },
21088
21089     /**
21090      * Creates a copy of this record.
21091      * @param {String} id (optional) A new record id if you don't want to use this record's id
21092      * @return {Record}
21093      */
21094     copy : function(newId) {
21095         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
21096     }
21097 };/*
21098  * Based on:
21099  * Ext JS Library 1.1.1
21100  * Copyright(c) 2006-2007, Ext JS, LLC.
21101  *
21102  * Originally Released Under LGPL - original licence link has changed is not relivant.
21103  *
21104  * Fork - LGPL
21105  * <script type="text/javascript">
21106  */
21107
21108
21109
21110 /**
21111  * @class Roo.data.Store
21112  * @extends Roo.util.Observable
21113  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
21114  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
21115  * <p>
21116  * 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
21117  * has no knowledge of the format of the data returned by the Proxy.<br>
21118  * <p>
21119  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
21120  * instances from the data object. These records are cached and made available through accessor functions.
21121  * @constructor
21122  * Creates a new Store.
21123  * @param {Object} config A config object containing the objects needed for the Store to access data,
21124  * and read the data into Records.
21125  */
21126 Roo.data.Store = function(config){
21127     this.data = new Roo.util.MixedCollection(false);
21128     this.data.getKey = function(o){
21129         return o.id;
21130     };
21131     this.baseParams = {};
21132     // private
21133     this.paramNames = {
21134         "start" : "start",
21135         "limit" : "limit",
21136         "sort" : "sort",
21137         "dir" : "dir",
21138         "multisort" : "_multisort"
21139     };
21140
21141     if(config && config.data){
21142         this.inlineData = config.data;
21143         delete config.data;
21144     }
21145
21146     Roo.apply(this, config);
21147     
21148     if(this.reader){ // reader passed
21149         this.reader = Roo.factory(this.reader, Roo.data);
21150         this.reader.xmodule = this.xmodule || false;
21151         if(!this.recordType){
21152             this.recordType = this.reader.recordType;
21153         }
21154         if(this.reader.onMetaChange){
21155             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
21156         }
21157     }
21158
21159     if(this.recordType){
21160         this.fields = this.recordType.prototype.fields;
21161     }
21162     this.modified = [];
21163
21164     this.addEvents({
21165         /**
21166          * @event datachanged
21167          * Fires when the data cache has changed, and a widget which is using this Store
21168          * as a Record cache should refresh its view.
21169          * @param {Store} this
21170          */
21171         datachanged : true,
21172         /**
21173          * @event metachange
21174          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
21175          * @param {Store} this
21176          * @param {Object} meta The JSON metadata
21177          */
21178         metachange : true,
21179         /**
21180          * @event add
21181          * Fires when Records have been added to the Store
21182          * @param {Store} this
21183          * @param {Roo.data.Record[]} records The array of Records added
21184          * @param {Number} index The index at which the record(s) were added
21185          */
21186         add : true,
21187         /**
21188          * @event remove
21189          * Fires when a Record has been removed from the Store
21190          * @param {Store} this
21191          * @param {Roo.data.Record} record The Record that was removed
21192          * @param {Number} index The index at which the record was removed
21193          */
21194         remove : true,
21195         /**
21196          * @event update
21197          * Fires when a Record has been updated
21198          * @param {Store} this
21199          * @param {Roo.data.Record} record The Record that was updated
21200          * @param {String} operation The update operation being performed.  Value may be one of:
21201          * <pre><code>
21202  Roo.data.Record.EDIT
21203  Roo.data.Record.REJECT
21204  Roo.data.Record.COMMIT
21205          * </code></pre>
21206          */
21207         update : true,
21208         /**
21209          * @event clear
21210          * Fires when the data cache has been cleared.
21211          * @param {Store} this
21212          */
21213         clear : true,
21214         /**
21215          * @event beforeload
21216          * Fires before a request is made for a new data object.  If the beforeload handler returns false
21217          * the load action will be canceled.
21218          * @param {Store} this
21219          * @param {Object} options The loading options that were specified (see {@link #load} for details)
21220          */
21221         beforeload : true,
21222         /**
21223          * @event beforeloadadd
21224          * Fires after a new set of Records has been loaded.
21225          * @param {Store} this
21226          * @param {Roo.data.Record[]} records The Records that were loaded
21227          * @param {Object} options The loading options that were specified (see {@link #load} for details)
21228          */
21229         beforeloadadd : true,
21230         /**
21231          * @event load
21232          * Fires after a new set of Records has been loaded, before they are added to the store.
21233          * @param {Store} this
21234          * @param {Roo.data.Record[]} records The Records that were loaded
21235          * @param {Object} options The loading options that were specified (see {@link #load} for details)
21236          * @params {Object} return from reader
21237          */
21238         load : true,
21239         /**
21240          * @event loadexception
21241          * Fires if an exception occurs in the Proxy during loading.
21242          * Called with the signature of the Proxy's "loadexception" event.
21243          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
21244          * 
21245          * @param {Proxy} 
21246          * @param {Object} return from JsonData.reader() - success, totalRecords, records
21247          * @param {Object} load options 
21248          * @param {Object} jsonData from your request (normally this contains the Exception)
21249          */
21250         loadexception : true
21251     });
21252     
21253     if(this.proxy){
21254         this.proxy = Roo.factory(this.proxy, Roo.data);
21255         this.proxy.xmodule = this.xmodule || false;
21256         this.relayEvents(this.proxy,  ["loadexception"]);
21257     }
21258     this.sortToggle = {};
21259     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
21260
21261     Roo.data.Store.superclass.constructor.call(this);
21262
21263     if(this.inlineData){
21264         this.loadData(this.inlineData);
21265         delete this.inlineData;
21266     }
21267 };
21268
21269 Roo.extend(Roo.data.Store, Roo.util.Observable, {
21270      /**
21271     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
21272     * without a remote query - used by combo/forms at present.
21273     */
21274     
21275     /**
21276     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
21277     */
21278     /**
21279     * @cfg {Array} data Inline data to be loaded when the store is initialized.
21280     */
21281     /**
21282     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
21283     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
21284     */
21285     /**
21286     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
21287     * on any HTTP request
21288     */
21289     /**
21290     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
21291     */
21292     /**
21293     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
21294     */
21295     multiSort: false,
21296     /**
21297     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
21298     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
21299     */
21300     remoteSort : false,
21301
21302     /**
21303     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
21304      * loaded or when a record is removed. (defaults to false).
21305     */
21306     pruneModifiedRecords : false,
21307
21308     // private
21309     lastOptions : null,
21310
21311     /**
21312      * Add Records to the Store and fires the add event.
21313      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
21314      */
21315     add : function(records){
21316         records = [].concat(records);
21317         for(var i = 0, len = records.length; i < len; i++){
21318             records[i].join(this);
21319         }
21320         var index = this.data.length;
21321         this.data.addAll(records);
21322         this.fireEvent("add", this, records, index);
21323     },
21324
21325     /**
21326      * Remove a Record from the Store and fires the remove event.
21327      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
21328      */
21329     remove : function(record){
21330         var index = this.data.indexOf(record);
21331         this.data.removeAt(index);
21332         if(this.pruneModifiedRecords){
21333             this.modified.remove(record);
21334         }
21335         this.fireEvent("remove", this, record, index);
21336     },
21337
21338     /**
21339      * Remove all Records from the Store and fires the clear event.
21340      */
21341     removeAll : function(){
21342         this.data.clear();
21343         if(this.pruneModifiedRecords){
21344             this.modified = [];
21345         }
21346         this.fireEvent("clear", this);
21347     },
21348
21349     /**
21350      * Inserts Records to the Store at the given index and fires the add event.
21351      * @param {Number} index The start index at which to insert the passed Records.
21352      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
21353      */
21354     insert : function(index, records){
21355         records = [].concat(records);
21356         for(var i = 0, len = records.length; i < len; i++){
21357             this.data.insert(index, records[i]);
21358             records[i].join(this);
21359         }
21360         this.fireEvent("add", this, records, index);
21361     },
21362
21363     /**
21364      * Get the index within the cache of the passed Record.
21365      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
21366      * @return {Number} The index of the passed Record. Returns -1 if not found.
21367      */
21368     indexOf : function(record){
21369         return this.data.indexOf(record);
21370     },
21371
21372     /**
21373      * Get the index within the cache of the Record with the passed id.
21374      * @param {String} id The id of the Record to find.
21375      * @return {Number} The index of the Record. Returns -1 if not found.
21376      */
21377     indexOfId : function(id){
21378         return this.data.indexOfKey(id);
21379     },
21380
21381     /**
21382      * Get the Record with the specified id.
21383      * @param {String} id The id of the Record to find.
21384      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
21385      */
21386     getById : function(id){
21387         return this.data.key(id);
21388     },
21389
21390     /**
21391      * Get the Record at the specified index.
21392      * @param {Number} index The index of the Record to find.
21393      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
21394      */
21395     getAt : function(index){
21396         return this.data.itemAt(index);
21397     },
21398
21399     /**
21400      * Returns a range of Records between specified indices.
21401      * @param {Number} startIndex (optional) The starting index (defaults to 0)
21402      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
21403      * @return {Roo.data.Record[]} An array of Records
21404      */
21405     getRange : function(start, end){
21406         return this.data.getRange(start, end);
21407     },
21408
21409     // private
21410     storeOptions : function(o){
21411         o = Roo.apply({}, o);
21412         delete o.callback;
21413         delete o.scope;
21414         this.lastOptions = o;
21415     },
21416
21417     /**
21418      * Loads the Record cache from the configured Proxy using the configured Reader.
21419      * <p>
21420      * If using remote paging, then the first load call must specify the <em>start</em>
21421      * and <em>limit</em> properties in the options.params property to establish the initial
21422      * position within the dataset, and the number of Records to cache on each read from the Proxy.
21423      * <p>
21424      * <strong>It is important to note that for remote data sources, loading is asynchronous,
21425      * and this call will return before the new data has been loaded. Perform any post-processing
21426      * in a callback function, or in a "load" event handler.</strong>
21427      * <p>
21428      * @param {Object} options An object containing properties which control loading options:<ul>
21429      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
21430      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
21431      * passed the following arguments:<ul>
21432      * <li>r : Roo.data.Record[]</li>
21433      * <li>options: Options object from the load call</li>
21434      * <li>success: Boolean success indicator</li></ul></li>
21435      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
21436      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
21437      * </ul>
21438      */
21439     load : function(options){
21440         options = options || {};
21441         if(this.fireEvent("beforeload", this, options) !== false){
21442             this.storeOptions(options);
21443             var p = Roo.apply(options.params || {}, this.baseParams);
21444             // if meta was not loaded from remote source.. try requesting it.
21445             if (!this.reader.metaFromRemote) {
21446                 p._requestMeta = 1;
21447             }
21448             if(this.sortInfo && this.remoteSort){
21449                 var pn = this.paramNames;
21450                 p[pn["sort"]] = this.sortInfo.field;
21451                 p[pn["dir"]] = this.sortInfo.direction;
21452             }
21453             if (this.multiSort) {
21454                 var pn = this.paramNames;
21455                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
21456             }
21457             
21458             this.proxy.load(p, this.reader, this.loadRecords, this, options);
21459         }
21460     },
21461
21462     /**
21463      * Reloads the Record cache from the configured Proxy using the configured Reader and
21464      * the options from the last load operation performed.
21465      * @param {Object} options (optional) An object containing properties which may override the options
21466      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
21467      * the most recently used options are reused).
21468      */
21469     reload : function(options){
21470         this.load(Roo.applyIf(options||{}, this.lastOptions));
21471     },
21472
21473     // private
21474     // Called as a callback by the Reader during a load operation.
21475     loadRecords : function(o, options, success){
21476         if(!o || success === false){
21477             if(success !== false){
21478                 this.fireEvent("load", this, [], options, o);
21479             }
21480             if(options.callback){
21481                 options.callback.call(options.scope || this, [], options, false);
21482             }
21483             return;
21484         }
21485         // if data returned failure - throw an exception.
21486         if (o.success === false) {
21487             // show a message if no listener is registered.
21488             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
21489                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
21490             }
21491             // loadmask wil be hooked into this..
21492             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
21493             return;
21494         }
21495         var r = o.records, t = o.totalRecords || r.length;
21496         
21497         this.fireEvent("beforeloadadd", this, r, options, o);
21498         
21499         if(!options || options.add !== true){
21500             if(this.pruneModifiedRecords){
21501                 this.modified = [];
21502             }
21503             for(var i = 0, len = r.length; i < len; i++){
21504                 r[i].join(this);
21505             }
21506             if(this.snapshot){
21507                 this.data = this.snapshot;
21508                 delete this.snapshot;
21509             }
21510             this.data.clear();
21511             this.data.addAll(r);
21512             this.totalLength = t;
21513             this.applySort();
21514             this.fireEvent("datachanged", this);
21515         }else{
21516             this.totalLength = Math.max(t, this.data.length+r.length);
21517             this.add(r);
21518         }
21519         this.fireEvent("load", this, r, options, o);
21520         if(options.callback){
21521             options.callback.call(options.scope || this, r, options, true);
21522         }
21523     },
21524
21525
21526     /**
21527      * Loads data from a passed data block. A Reader which understands the format of the data
21528      * must have been configured in the constructor.
21529      * @param {Object} data The data block from which to read the Records.  The format of the data expected
21530      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
21531      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
21532      */
21533     loadData : function(o, append){
21534         var r = this.reader.readRecords(o);
21535         this.loadRecords(r, {add: append}, true);
21536     },
21537
21538     /**
21539      * Gets the number of cached records.
21540      * <p>
21541      * <em>If using paging, this may not be the total size of the dataset. If the data object
21542      * used by the Reader contains the dataset size, then the getTotalCount() function returns
21543      * the data set size</em>
21544      */
21545     getCount : function(){
21546         return this.data.length || 0;
21547     },
21548
21549     /**
21550      * Gets the total number of records in the dataset as returned by the server.
21551      * <p>
21552      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
21553      * the dataset size</em>
21554      */
21555     getTotalCount : function(){
21556         return this.totalLength || 0;
21557     },
21558
21559     /**
21560      * Returns the sort state of the Store as an object with two properties:
21561      * <pre><code>
21562  field {String} The name of the field by which the Records are sorted
21563  direction {String} The sort order, "ASC" or "DESC"
21564      * </code></pre>
21565      */
21566     getSortState : function(){
21567         return this.sortInfo;
21568     },
21569
21570     // private
21571     applySort : function(){
21572         if(this.sortInfo && !this.remoteSort){
21573             var s = this.sortInfo, f = s.field;
21574             var st = this.fields.get(f).sortType;
21575             var fn = function(r1, r2){
21576                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
21577                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
21578             };
21579             this.data.sort(s.direction, fn);
21580             if(this.snapshot && this.snapshot != this.data){
21581                 this.snapshot.sort(s.direction, fn);
21582             }
21583         }
21584     },
21585
21586     /**
21587      * Sets the default sort column and order to be used by the next load operation.
21588      * @param {String} fieldName The name of the field to sort by.
21589      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
21590      */
21591     setDefaultSort : function(field, dir){
21592         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
21593     },
21594
21595     /**
21596      * Sort the Records.
21597      * If remote sorting is used, the sort is performed on the server, and the cache is
21598      * reloaded. If local sorting is used, the cache is sorted internally.
21599      * @param {String} fieldName The name of the field to sort by.
21600      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
21601      */
21602     sort : function(fieldName, dir){
21603         var f = this.fields.get(fieldName);
21604         if(!dir){
21605             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
21606             
21607             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
21608                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
21609             }else{
21610                 dir = f.sortDir;
21611             }
21612         }
21613         this.sortToggle[f.name] = dir;
21614         this.sortInfo = {field: f.name, direction: dir};
21615         if(!this.remoteSort){
21616             this.applySort();
21617             this.fireEvent("datachanged", this);
21618         }else{
21619             this.load(this.lastOptions);
21620         }
21621     },
21622
21623     /**
21624      * Calls the specified function for each of the Records in the cache.
21625      * @param {Function} fn The function to call. The Record is passed as the first parameter.
21626      * Returning <em>false</em> aborts and exits the iteration.
21627      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
21628      */
21629     each : function(fn, scope){
21630         this.data.each(fn, scope);
21631     },
21632
21633     /**
21634      * Gets all records modified since the last commit.  Modified records are persisted across load operations
21635      * (e.g., during paging).
21636      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
21637      */
21638     getModifiedRecords : function(){
21639         return this.modified;
21640     },
21641
21642     // private
21643     createFilterFn : function(property, value, anyMatch){
21644         if(!value.exec){ // not a regex
21645             value = String(value);
21646             if(value.length == 0){
21647                 return false;
21648             }
21649             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
21650         }
21651         return function(r){
21652             return value.test(r.data[property]);
21653         };
21654     },
21655
21656     /**
21657      * Sums the value of <i>property</i> for each record between start and end and returns the result.
21658      * @param {String} property A field on your records
21659      * @param {Number} start The record index to start at (defaults to 0)
21660      * @param {Number} end The last record index to include (defaults to length - 1)
21661      * @return {Number} The sum
21662      */
21663     sum : function(property, start, end){
21664         var rs = this.data.items, v = 0;
21665         start = start || 0;
21666         end = (end || end === 0) ? end : rs.length-1;
21667
21668         for(var i = start; i <= end; i++){
21669             v += (rs[i].data[property] || 0);
21670         }
21671         return v;
21672     },
21673
21674     /**
21675      * Filter the records by a specified property.
21676      * @param {String} field A field on your records
21677      * @param {String/RegExp} value Either a string that the field
21678      * should start with or a RegExp to test against the field
21679      * @param {Boolean} anyMatch True to match any part not just the beginning
21680      */
21681     filter : function(property, value, anyMatch){
21682         var fn = this.createFilterFn(property, value, anyMatch);
21683         return fn ? this.filterBy(fn) : this.clearFilter();
21684     },
21685
21686     /**
21687      * Filter by a function. The specified function will be called with each
21688      * record in this data source. If the function returns true the record is included,
21689      * otherwise it is filtered.
21690      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
21691      * @param {Object} scope (optional) The scope of the function (defaults to this)
21692      */
21693     filterBy : function(fn, scope){
21694         this.snapshot = this.snapshot || this.data;
21695         this.data = this.queryBy(fn, scope||this);
21696         this.fireEvent("datachanged", this);
21697     },
21698
21699     /**
21700      * Query the records by a specified property.
21701      * @param {String} field A field on your records
21702      * @param {String/RegExp} value Either a string that the field
21703      * should start with or a RegExp to test against the field
21704      * @param {Boolean} anyMatch True to match any part not just the beginning
21705      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
21706      */
21707     query : function(property, value, anyMatch){
21708         var fn = this.createFilterFn(property, value, anyMatch);
21709         return fn ? this.queryBy(fn) : this.data.clone();
21710     },
21711
21712     /**
21713      * Query by a function. The specified function will be called with each
21714      * record in this data source. If the function returns true the record is included
21715      * in the results.
21716      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
21717      * @param {Object} scope (optional) The scope of the function (defaults to this)
21718       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
21719      **/
21720     queryBy : function(fn, scope){
21721         var data = this.snapshot || this.data;
21722         return data.filterBy(fn, scope||this);
21723     },
21724
21725     /**
21726      * Collects unique values for a particular dataIndex from this store.
21727      * @param {String} dataIndex The property to collect
21728      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
21729      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
21730      * @return {Array} An array of the unique values
21731      **/
21732     collect : function(dataIndex, allowNull, bypassFilter){
21733         var d = (bypassFilter === true && this.snapshot) ?
21734                 this.snapshot.items : this.data.items;
21735         var v, sv, r = [], l = {};
21736         for(var i = 0, len = d.length; i < len; i++){
21737             v = d[i].data[dataIndex];
21738             sv = String(v);
21739             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
21740                 l[sv] = true;
21741                 r[r.length] = v;
21742             }
21743         }
21744         return r;
21745     },
21746
21747     /**
21748      * Revert to a view of the Record cache with no filtering applied.
21749      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
21750      */
21751     clearFilter : function(suppressEvent){
21752         if(this.snapshot && this.snapshot != this.data){
21753             this.data = this.snapshot;
21754             delete this.snapshot;
21755             if(suppressEvent !== true){
21756                 this.fireEvent("datachanged", this);
21757             }
21758         }
21759     },
21760
21761     // private
21762     afterEdit : function(record){
21763         if(this.modified.indexOf(record) == -1){
21764             this.modified.push(record);
21765         }
21766         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
21767     },
21768     
21769     // private
21770     afterReject : function(record){
21771         this.modified.remove(record);
21772         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
21773     },
21774
21775     // private
21776     afterCommit : function(record){
21777         this.modified.remove(record);
21778         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
21779     },
21780
21781     /**
21782      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
21783      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
21784      */
21785     commitChanges : function(){
21786         var m = this.modified.slice(0);
21787         this.modified = [];
21788         for(var i = 0, len = m.length; i < len; i++){
21789             m[i].commit();
21790         }
21791     },
21792
21793     /**
21794      * Cancel outstanding changes on all changed records.
21795      */
21796     rejectChanges : function(){
21797         var m = this.modified.slice(0);
21798         this.modified = [];
21799         for(var i = 0, len = m.length; i < len; i++){
21800             m[i].reject();
21801         }
21802     },
21803
21804     onMetaChange : function(meta, rtype, o){
21805         this.recordType = rtype;
21806         this.fields = rtype.prototype.fields;
21807         delete this.snapshot;
21808         this.sortInfo = meta.sortInfo || this.sortInfo;
21809         this.modified = [];
21810         this.fireEvent('metachange', this, this.reader.meta);
21811     },
21812     
21813     moveIndex : function(data, type)
21814     {
21815         var index = this.indexOf(data);
21816         
21817         var newIndex = index + type;
21818         
21819         this.remove(data);
21820         
21821         this.insert(newIndex, data);
21822         
21823     }
21824 });/*
21825  * Based on:
21826  * Ext JS Library 1.1.1
21827  * Copyright(c) 2006-2007, Ext JS, LLC.
21828  *
21829  * Originally Released Under LGPL - original licence link has changed is not relivant.
21830  *
21831  * Fork - LGPL
21832  * <script type="text/javascript">
21833  */
21834
21835 /**
21836  * @class Roo.data.SimpleStore
21837  * @extends Roo.data.Store
21838  * Small helper class to make creating Stores from Array data easier.
21839  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
21840  * @cfg {Array} fields An array of field definition objects, or field name strings.
21841  * @cfg {Array} data The multi-dimensional array of data
21842  * @constructor
21843  * @param {Object} config
21844  */
21845 Roo.data.SimpleStore = function(config){
21846     Roo.data.SimpleStore.superclass.constructor.call(this, {
21847         isLocal : true,
21848         reader: new Roo.data.ArrayReader({
21849                 id: config.id
21850             },
21851             Roo.data.Record.create(config.fields)
21852         ),
21853         proxy : new Roo.data.MemoryProxy(config.data)
21854     });
21855     this.load();
21856 };
21857 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
21858  * Based on:
21859  * Ext JS Library 1.1.1
21860  * Copyright(c) 2006-2007, Ext JS, LLC.
21861  *
21862  * Originally Released Under LGPL - original licence link has changed is not relivant.
21863  *
21864  * Fork - LGPL
21865  * <script type="text/javascript">
21866  */
21867
21868 /**
21869 /**
21870  * @extends Roo.data.Store
21871  * @class Roo.data.JsonStore
21872  * Small helper class to make creating Stores for JSON data easier. <br/>
21873 <pre><code>
21874 var store = new Roo.data.JsonStore({
21875     url: 'get-images.php',
21876     root: 'images',
21877     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
21878 });
21879 </code></pre>
21880  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
21881  * JsonReader and HttpProxy (unless inline data is provided).</b>
21882  * @cfg {Array} fields An array of field definition objects, or field name strings.
21883  * @constructor
21884  * @param {Object} config
21885  */
21886 Roo.data.JsonStore = function(c){
21887     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
21888         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
21889         reader: new Roo.data.JsonReader(c, c.fields)
21890     }));
21891 };
21892 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
21893  * Based on:
21894  * Ext JS Library 1.1.1
21895  * Copyright(c) 2006-2007, Ext JS, LLC.
21896  *
21897  * Originally Released Under LGPL - original licence link has changed is not relivant.
21898  *
21899  * Fork - LGPL
21900  * <script type="text/javascript">
21901  */
21902
21903  
21904 Roo.data.Field = function(config){
21905     if(typeof config == "string"){
21906         config = {name: config};
21907     }
21908     Roo.apply(this, config);
21909     
21910     if(!this.type){
21911         this.type = "auto";
21912     }
21913     
21914     var st = Roo.data.SortTypes;
21915     // named sortTypes are supported, here we look them up
21916     if(typeof this.sortType == "string"){
21917         this.sortType = st[this.sortType];
21918     }
21919     
21920     // set default sortType for strings and dates
21921     if(!this.sortType){
21922         switch(this.type){
21923             case "string":
21924                 this.sortType = st.asUCString;
21925                 break;
21926             case "date":
21927                 this.sortType = st.asDate;
21928                 break;
21929             default:
21930                 this.sortType = st.none;
21931         }
21932     }
21933
21934     // define once
21935     var stripRe = /[\$,%]/g;
21936
21937     // prebuilt conversion function for this field, instead of
21938     // switching every time we're reading a value
21939     if(!this.convert){
21940         var cv, dateFormat = this.dateFormat;
21941         switch(this.type){
21942             case "":
21943             case "auto":
21944             case undefined:
21945                 cv = function(v){ return v; };
21946                 break;
21947             case "string":
21948                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
21949                 break;
21950             case "int":
21951                 cv = function(v){
21952                     return v !== undefined && v !== null && v !== '' ?
21953                            parseInt(String(v).replace(stripRe, ""), 10) : '';
21954                     };
21955                 break;
21956             case "float":
21957                 cv = function(v){
21958                     return v !== undefined && v !== null && v !== '' ?
21959                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
21960                     };
21961                 break;
21962             case "bool":
21963             case "boolean":
21964                 cv = function(v){ return v === true || v === "true" || v == 1; };
21965                 break;
21966             case "date":
21967                 cv = function(v){
21968                     if(!v){
21969                         return '';
21970                     }
21971                     if(v instanceof Date){
21972                         return v;
21973                     }
21974                     if(dateFormat){
21975                         if(dateFormat == "timestamp"){
21976                             return new Date(v*1000);
21977                         }
21978                         return Date.parseDate(v, dateFormat);
21979                     }
21980                     var parsed = Date.parse(v);
21981                     return parsed ? new Date(parsed) : null;
21982                 };
21983              break;
21984             
21985         }
21986         this.convert = cv;
21987     }
21988 };
21989
21990 Roo.data.Field.prototype = {
21991     dateFormat: null,
21992     defaultValue: "",
21993     mapping: null,
21994     sortType : null,
21995     sortDir : "ASC"
21996 };/*
21997  * Based on:
21998  * Ext JS Library 1.1.1
21999  * Copyright(c) 2006-2007, Ext JS, LLC.
22000  *
22001  * Originally Released Under LGPL - original licence link has changed is not relivant.
22002  *
22003  * Fork - LGPL
22004  * <script type="text/javascript">
22005  */
22006  
22007 // Base class for reading structured data from a data source.  This class is intended to be
22008 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
22009
22010 /**
22011  * @class Roo.data.DataReader
22012  * Base class for reading structured data from a data source.  This class is intended to be
22013  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
22014  */
22015
22016 Roo.data.DataReader = function(meta, recordType){
22017     
22018     this.meta = meta;
22019     
22020     this.recordType = recordType instanceof Array ? 
22021         Roo.data.Record.create(recordType) : recordType;
22022 };
22023
22024 Roo.data.DataReader.prototype = {
22025      /**
22026      * Create an empty record
22027      * @param {Object} data (optional) - overlay some values
22028      * @return {Roo.data.Record} record created.
22029      */
22030     newRow :  function(d) {
22031         var da =  {};
22032         this.recordType.prototype.fields.each(function(c) {
22033             switch( c.type) {
22034                 case 'int' : da[c.name] = 0; break;
22035                 case 'date' : da[c.name] = new Date(); break;
22036                 case 'float' : da[c.name] = 0.0; break;
22037                 case 'boolean' : da[c.name] = false; break;
22038                 default : da[c.name] = ""; break;
22039             }
22040             
22041         });
22042         return new this.recordType(Roo.apply(da, d));
22043     }
22044     
22045 };/*
22046  * Based on:
22047  * Ext JS Library 1.1.1
22048  * Copyright(c) 2006-2007, Ext JS, LLC.
22049  *
22050  * Originally Released Under LGPL - original licence link has changed is not relivant.
22051  *
22052  * Fork - LGPL
22053  * <script type="text/javascript">
22054  */
22055
22056 /**
22057  * @class Roo.data.DataProxy
22058  * @extends Roo.data.Observable
22059  * This class is an abstract base class for implementations which provide retrieval of
22060  * unformatted data objects.<br>
22061  * <p>
22062  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
22063  * (of the appropriate type which knows how to parse the data object) to provide a block of
22064  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
22065  * <p>
22066  * Custom implementations must implement the load method as described in
22067  * {@link Roo.data.HttpProxy#load}.
22068  */
22069 Roo.data.DataProxy = function(){
22070     this.addEvents({
22071         /**
22072          * @event beforeload
22073          * Fires before a network request is made to retrieve a data object.
22074          * @param {Object} This DataProxy object.
22075          * @param {Object} params The params parameter to the load function.
22076          */
22077         beforeload : true,
22078         /**
22079          * @event load
22080          * Fires before the load method's callback is called.
22081          * @param {Object} This DataProxy object.
22082          * @param {Object} o The data object.
22083          * @param {Object} arg The callback argument object passed to the load function.
22084          */
22085         load : true,
22086         /**
22087          * @event loadexception
22088          * Fires if an Exception occurs during data retrieval.
22089          * @param {Object} This DataProxy object.
22090          * @param {Object} o The data object.
22091          * @param {Object} arg The callback argument object passed to the load function.
22092          * @param {Object} e The Exception.
22093          */
22094         loadexception : true
22095     });
22096     Roo.data.DataProxy.superclass.constructor.call(this);
22097 };
22098
22099 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
22100
22101     /**
22102      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
22103      */
22104 /*
22105  * Based on:
22106  * Ext JS Library 1.1.1
22107  * Copyright(c) 2006-2007, Ext JS, LLC.
22108  *
22109  * Originally Released Under LGPL - original licence link has changed is not relivant.
22110  *
22111  * Fork - LGPL
22112  * <script type="text/javascript">
22113  */
22114 /**
22115  * @class Roo.data.MemoryProxy
22116  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
22117  * to the Reader when its load method is called.
22118  * @constructor
22119  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
22120  */
22121 Roo.data.MemoryProxy = function(data){
22122     if (data.data) {
22123         data = data.data;
22124     }
22125     Roo.data.MemoryProxy.superclass.constructor.call(this);
22126     this.data = data;
22127 };
22128
22129 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
22130     /**
22131      * Load data from the requested source (in this case an in-memory
22132      * data object passed to the constructor), read the data object into
22133      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
22134      * process that block using the passed callback.
22135      * @param {Object} params This parameter is not used by the MemoryProxy class.
22136      * @param {Roo.data.DataReader} reader The Reader object which converts the data
22137      * object into a block of Roo.data.Records.
22138      * @param {Function} callback The function into which to pass the block of Roo.data.records.
22139      * The function must be passed <ul>
22140      * <li>The Record block object</li>
22141      * <li>The "arg" argument from the load function</li>
22142      * <li>A boolean success indicator</li>
22143      * </ul>
22144      * @param {Object} scope The scope in which to call the callback
22145      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
22146      */
22147     load : function(params, reader, callback, scope, arg){
22148         params = params || {};
22149         var result;
22150         try {
22151             result = reader.readRecords(this.data);
22152         }catch(e){
22153             this.fireEvent("loadexception", this, arg, null, e);
22154             callback.call(scope, null, arg, false);
22155             return;
22156         }
22157         callback.call(scope, result, arg, true);
22158     },
22159     
22160     // private
22161     update : function(params, records){
22162         
22163     }
22164 });/*
22165  * Based on:
22166  * Ext JS Library 1.1.1
22167  * Copyright(c) 2006-2007, Ext JS, LLC.
22168  *
22169  * Originally Released Under LGPL - original licence link has changed is not relivant.
22170  *
22171  * Fork - LGPL
22172  * <script type="text/javascript">
22173  */
22174 /**
22175  * @class Roo.data.HttpProxy
22176  * @extends Roo.data.DataProxy
22177  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
22178  * configured to reference a certain URL.<br><br>
22179  * <p>
22180  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
22181  * from which the running page was served.<br><br>
22182  * <p>
22183  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
22184  * <p>
22185  * Be aware that to enable the browser to parse an XML document, the server must set
22186  * the Content-Type header in the HTTP response to "text/xml".
22187  * @constructor
22188  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
22189  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
22190  * will be used to make the request.
22191  */
22192 Roo.data.HttpProxy = function(conn){
22193     Roo.data.HttpProxy.superclass.constructor.call(this);
22194     // is conn a conn config or a real conn?
22195     this.conn = conn;
22196     this.useAjax = !conn || !conn.events;
22197   
22198 };
22199
22200 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
22201     // thse are take from connection...
22202     
22203     /**
22204      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
22205      */
22206     /**
22207      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
22208      * extra parameters to each request made by this object. (defaults to undefined)
22209      */
22210     /**
22211      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
22212      *  to each request made by this object. (defaults to undefined)
22213      */
22214     /**
22215      * @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)
22216      */
22217     /**
22218      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
22219      */
22220      /**
22221      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
22222      * @type Boolean
22223      */
22224   
22225
22226     /**
22227      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
22228      * @type Boolean
22229      */
22230     /**
22231      * Return the {@link Roo.data.Connection} object being used by this Proxy.
22232      * @return {Connection} The Connection object. This object may be used to subscribe to events on
22233      * a finer-grained basis than the DataProxy events.
22234      */
22235     getConnection : function(){
22236         return this.useAjax ? Roo.Ajax : this.conn;
22237     },
22238
22239     /**
22240      * Load data from the configured {@link Roo.data.Connection}, read the data object into
22241      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
22242      * process that block using the passed callback.
22243      * @param {Object} params An object containing properties which are to be used as HTTP parameters
22244      * for the request to the remote server.
22245      * @param {Roo.data.DataReader} reader The Reader object which converts the data
22246      * object into a block of Roo.data.Records.
22247      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
22248      * The function must be passed <ul>
22249      * <li>The Record block object</li>
22250      * <li>The "arg" argument from the load function</li>
22251      * <li>A boolean success indicator</li>
22252      * </ul>
22253      * @param {Object} scope The scope in which to call the callback
22254      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
22255      */
22256     load : function(params, reader, callback, scope, arg){
22257         if(this.fireEvent("beforeload", this, params) !== false){
22258             var  o = {
22259                 params : params || {},
22260                 request: {
22261                     callback : callback,
22262                     scope : scope,
22263                     arg : arg
22264                 },
22265                 reader: reader,
22266                 callback : this.loadResponse,
22267                 scope: this
22268             };
22269             if(this.useAjax){
22270                 Roo.applyIf(o, this.conn);
22271                 if(this.activeRequest){
22272                     Roo.Ajax.abort(this.activeRequest);
22273                 }
22274                 this.activeRequest = Roo.Ajax.request(o);
22275             }else{
22276                 this.conn.request(o);
22277             }
22278         }else{
22279             callback.call(scope||this, null, arg, false);
22280         }
22281     },
22282
22283     // private
22284     loadResponse : function(o, success, response){
22285         delete this.activeRequest;
22286         if(!success){
22287             this.fireEvent("loadexception", this, o, response);
22288             o.request.callback.call(o.request.scope, null, o.request.arg, false);
22289             return;
22290         }
22291         var result;
22292         try {
22293             result = o.reader.read(response);
22294         }catch(e){
22295             this.fireEvent("loadexception", this, o, response, e);
22296             o.request.callback.call(o.request.scope, null, o.request.arg, false);
22297             return;
22298         }
22299         
22300         this.fireEvent("load", this, o, o.request.arg);
22301         o.request.callback.call(o.request.scope, result, o.request.arg, true);
22302     },
22303
22304     // private
22305     update : function(dataSet){
22306
22307     },
22308
22309     // private
22310     updateResponse : function(dataSet){
22311
22312     }
22313 });/*
22314  * Based on:
22315  * Ext JS Library 1.1.1
22316  * Copyright(c) 2006-2007, Ext JS, LLC.
22317  *
22318  * Originally Released Under LGPL - original licence link has changed is not relivant.
22319  *
22320  * Fork - LGPL
22321  * <script type="text/javascript">
22322  */
22323
22324 /**
22325  * @class Roo.data.ScriptTagProxy
22326  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
22327  * other than the originating domain of the running page.<br><br>
22328  * <p>
22329  * <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
22330  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
22331  * <p>
22332  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
22333  * source code that is used as the source inside a &lt;script> tag.<br><br>
22334  * <p>
22335  * In order for the browser to process the returned data, the server must wrap the data object
22336  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
22337  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
22338  * depending on whether the callback name was passed:
22339  * <p>
22340  * <pre><code>
22341 boolean scriptTag = false;
22342 String cb = request.getParameter("callback");
22343 if (cb != null) {
22344     scriptTag = true;
22345     response.setContentType("text/javascript");
22346 } else {
22347     response.setContentType("application/x-json");
22348 }
22349 Writer out = response.getWriter();
22350 if (scriptTag) {
22351     out.write(cb + "(");
22352 }
22353 out.print(dataBlock.toJsonString());
22354 if (scriptTag) {
22355     out.write(");");
22356 }
22357 </pre></code>
22358  *
22359  * @constructor
22360  * @param {Object} config A configuration object.
22361  */
22362 Roo.data.ScriptTagProxy = function(config){
22363     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
22364     Roo.apply(this, config);
22365     this.head = document.getElementsByTagName("head")[0];
22366 };
22367
22368 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
22369
22370 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
22371     /**
22372      * @cfg {String} url The URL from which to request the data object.
22373      */
22374     /**
22375      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
22376      */
22377     timeout : 30000,
22378     /**
22379      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
22380      * the server the name of the callback function set up by the load call to process the returned data object.
22381      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
22382      * javascript output which calls this named function passing the data object as its only parameter.
22383      */
22384     callbackParam : "callback",
22385     /**
22386      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
22387      * name to the request.
22388      */
22389     nocache : true,
22390
22391     /**
22392      * Load data from the configured URL, read the data object into
22393      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
22394      * process that block using the passed callback.
22395      * @param {Object} params An object containing properties which are to be used as HTTP parameters
22396      * for the request to the remote server.
22397      * @param {Roo.data.DataReader} reader The Reader object which converts the data
22398      * object into a block of Roo.data.Records.
22399      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
22400      * The function must be passed <ul>
22401      * <li>The Record block object</li>
22402      * <li>The "arg" argument from the load function</li>
22403      * <li>A boolean success indicator</li>
22404      * </ul>
22405      * @param {Object} scope The scope in which to call the callback
22406      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
22407      */
22408     load : function(params, reader, callback, scope, arg){
22409         if(this.fireEvent("beforeload", this, params) !== false){
22410
22411             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
22412
22413             var url = this.url;
22414             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
22415             if(this.nocache){
22416                 url += "&_dc=" + (new Date().getTime());
22417             }
22418             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
22419             var trans = {
22420                 id : transId,
22421                 cb : "stcCallback"+transId,
22422                 scriptId : "stcScript"+transId,
22423                 params : params,
22424                 arg : arg,
22425                 url : url,
22426                 callback : callback,
22427                 scope : scope,
22428                 reader : reader
22429             };
22430             var conn = this;
22431
22432             window[trans.cb] = function(o){
22433                 conn.handleResponse(o, trans);
22434             };
22435
22436             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
22437
22438             if(this.autoAbort !== false){
22439                 this.abort();
22440             }
22441
22442             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
22443
22444             var script = document.createElement("script");
22445             script.setAttribute("src", url);
22446             script.setAttribute("type", "text/javascript");
22447             script.setAttribute("id", trans.scriptId);
22448             this.head.appendChild(script);
22449
22450             this.trans = trans;
22451         }else{
22452             callback.call(scope||this, null, arg, false);
22453         }
22454     },
22455
22456     // private
22457     isLoading : function(){
22458         return this.trans ? true : false;
22459     },
22460
22461     /**
22462      * Abort the current server request.
22463      */
22464     abort : function(){
22465         if(this.isLoading()){
22466             this.destroyTrans(this.trans);
22467         }
22468     },
22469
22470     // private
22471     destroyTrans : function(trans, isLoaded){
22472         this.head.removeChild(document.getElementById(trans.scriptId));
22473         clearTimeout(trans.timeoutId);
22474         if(isLoaded){
22475             window[trans.cb] = undefined;
22476             try{
22477                 delete window[trans.cb];
22478             }catch(e){}
22479         }else{
22480             // if hasn't been loaded, wait for load to remove it to prevent script error
22481             window[trans.cb] = function(){
22482                 window[trans.cb] = undefined;
22483                 try{
22484                     delete window[trans.cb];
22485                 }catch(e){}
22486             };
22487         }
22488     },
22489
22490     // private
22491     handleResponse : function(o, trans){
22492         this.trans = false;
22493         this.destroyTrans(trans, true);
22494         var result;
22495         try {
22496             result = trans.reader.readRecords(o);
22497         }catch(e){
22498             this.fireEvent("loadexception", this, o, trans.arg, e);
22499             trans.callback.call(trans.scope||window, null, trans.arg, false);
22500             return;
22501         }
22502         this.fireEvent("load", this, o, trans.arg);
22503         trans.callback.call(trans.scope||window, result, trans.arg, true);
22504     },
22505
22506     // private
22507     handleFailure : function(trans){
22508         this.trans = false;
22509         this.destroyTrans(trans, false);
22510         this.fireEvent("loadexception", this, null, trans.arg);
22511         trans.callback.call(trans.scope||window, null, trans.arg, false);
22512     }
22513 });/*
22514  * Based on:
22515  * Ext JS Library 1.1.1
22516  * Copyright(c) 2006-2007, Ext JS, LLC.
22517  *
22518  * Originally Released Under LGPL - original licence link has changed is not relivant.
22519  *
22520  * Fork - LGPL
22521  * <script type="text/javascript">
22522  */
22523
22524 /**
22525  * @class Roo.data.JsonReader
22526  * @extends Roo.data.DataReader
22527  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
22528  * based on mappings in a provided Roo.data.Record constructor.
22529  * 
22530  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
22531  * in the reply previously. 
22532  * 
22533  * <p>
22534  * Example code:
22535  * <pre><code>
22536 var RecordDef = Roo.data.Record.create([
22537     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
22538     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
22539 ]);
22540 var myReader = new Roo.data.JsonReader({
22541     totalProperty: "results",    // The property which contains the total dataset size (optional)
22542     root: "rows",                // The property which contains an Array of row objects
22543     id: "id"                     // The property within each row object that provides an ID for the record (optional)
22544 }, RecordDef);
22545 </code></pre>
22546  * <p>
22547  * This would consume a JSON file like this:
22548  * <pre><code>
22549 { 'results': 2, 'rows': [
22550     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
22551     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
22552 }
22553 </code></pre>
22554  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
22555  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
22556  * paged from the remote server.
22557  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
22558  * @cfg {String} root name of the property which contains the Array of row objects.
22559  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
22560  * @constructor
22561  * Create a new JsonReader
22562  * @param {Object} meta Metadata configuration options
22563  * @param {Object} recordType Either an Array of field definition objects,
22564  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
22565  */
22566 Roo.data.JsonReader = function(meta, recordType){
22567     
22568     meta = meta || {};
22569     // set some defaults:
22570     Roo.applyIf(meta, {
22571         totalProperty: 'total',
22572         successProperty : 'success',
22573         root : 'data',
22574         id : 'id'
22575     });
22576     
22577     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
22578 };
22579 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
22580     
22581     /**
22582      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
22583      * Used by Store query builder to append _requestMeta to params.
22584      * 
22585      */
22586     metaFromRemote : false,
22587     /**
22588      * This method is only used by a DataProxy which has retrieved data from a remote server.
22589      * @param {Object} response The XHR object which contains the JSON data in its responseText.
22590      * @return {Object} data A data block which is used by an Roo.data.Store object as
22591      * a cache of Roo.data.Records.
22592      */
22593     read : function(response){
22594         var json = response.responseText;
22595        
22596         var o = /* eval:var:o */ eval("("+json+")");
22597         if(!o) {
22598             throw {message: "JsonReader.read: Json object not found"};
22599         }
22600         
22601         if(o.metaData){
22602             
22603             delete this.ef;
22604             this.metaFromRemote = true;
22605             this.meta = o.metaData;
22606             this.recordType = Roo.data.Record.create(o.metaData.fields);
22607             this.onMetaChange(this.meta, this.recordType, o);
22608         }
22609         return this.readRecords(o);
22610     },
22611
22612     // private function a store will implement
22613     onMetaChange : function(meta, recordType, o){
22614
22615     },
22616
22617     /**
22618          * @ignore
22619          */
22620     simpleAccess: function(obj, subsc) {
22621         return obj[subsc];
22622     },
22623
22624         /**
22625          * @ignore
22626          */
22627     getJsonAccessor: function(){
22628         var re = /[\[\.]/;
22629         return function(expr) {
22630             try {
22631                 return(re.test(expr))
22632                     ? new Function("obj", "return obj." + expr)
22633                     : function(obj){
22634                         return obj[expr];
22635                     };
22636             } catch(e){}
22637             return Roo.emptyFn;
22638         };
22639     }(),
22640
22641     /**
22642      * Create a data block containing Roo.data.Records from an XML document.
22643      * @param {Object} o An object which contains an Array of row objects in the property specified
22644      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
22645      * which contains the total size of the dataset.
22646      * @return {Object} data A data block which is used by an Roo.data.Store object as
22647      * a cache of Roo.data.Records.
22648      */
22649     readRecords : function(o){
22650         /**
22651          * After any data loads, the raw JSON data is available for further custom processing.
22652          * @type Object
22653          */
22654         this.o = o;
22655         var s = this.meta, Record = this.recordType,
22656             f = Record.prototype.fields, fi = f.items, fl = f.length;
22657
22658 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
22659         if (!this.ef) {
22660             if(s.totalProperty) {
22661                     this.getTotal = this.getJsonAccessor(s.totalProperty);
22662                 }
22663                 if(s.successProperty) {
22664                     this.getSuccess = this.getJsonAccessor(s.successProperty);
22665                 }
22666                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
22667                 if (s.id) {
22668                         var g = this.getJsonAccessor(s.id);
22669                         this.getId = function(rec) {
22670                                 var r = g(rec);
22671                                 return (r === undefined || r === "") ? null : r;
22672                         };
22673                 } else {
22674                         this.getId = function(){return null;};
22675                 }
22676             this.ef = [];
22677             for(var jj = 0; jj < fl; jj++){
22678                 f = fi[jj];
22679                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
22680                 this.ef[jj] = this.getJsonAccessor(map);
22681             }
22682         }
22683
22684         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
22685         if(s.totalProperty){
22686             var vt = parseInt(this.getTotal(o), 10);
22687             if(!isNaN(vt)){
22688                 totalRecords = vt;
22689             }
22690         }
22691         if(s.successProperty){
22692             var vs = this.getSuccess(o);
22693             if(vs === false || vs === 'false'){
22694                 success = false;
22695             }
22696         }
22697         var records = [];
22698             for(var i = 0; i < c; i++){
22699                     var n = root[i];
22700                 var values = {};
22701                 var id = this.getId(n);
22702                 for(var j = 0; j < fl; j++){
22703                     f = fi[j];
22704                 var v = this.ef[j](n);
22705                 if (!f.convert) {
22706                     Roo.log('missing convert for ' + f.name);
22707                     Roo.log(f);
22708                     continue;
22709                 }
22710                 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
22711                 }
22712                 var record = new Record(values, id);
22713                 record.json = n;
22714                 records[i] = record;
22715             }
22716             return {
22717             raw : o,
22718                 success : success,
22719                 records : records,
22720                 totalRecords : totalRecords
22721             };
22722     }
22723 });/*
22724  * Based on:
22725  * Ext JS Library 1.1.1
22726  * Copyright(c) 2006-2007, Ext JS, LLC.
22727  *
22728  * Originally Released Under LGPL - original licence link has changed is not relivant.
22729  *
22730  * Fork - LGPL
22731  * <script type="text/javascript">
22732  */
22733
22734 /**
22735  * @class Roo.data.XmlReader
22736  * @extends Roo.data.DataReader
22737  * Data reader class to create an Array of {@link Roo.data.Record} objects from an XML document
22738  * based on mappings in a provided Roo.data.Record constructor.<br><br>
22739  * <p>
22740  * <em>Note that in order for the browser to parse a returned XML document, the Content-Type
22741  * header in the HTTP response must be set to "text/xml".</em>
22742  * <p>
22743  * Example code:
22744  * <pre><code>
22745 var RecordDef = Roo.data.Record.create([
22746    {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
22747    {name: 'occupation'}                 // This field will use "occupation" as the mapping.
22748 ]);
22749 var myReader = new Roo.data.XmlReader({
22750    totalRecords: "results", // The element which contains the total dataset size (optional)
22751    record: "row",           // The repeated element which contains row information
22752    id: "id"                 // The element within the row that provides an ID for the record (optional)
22753 }, RecordDef);
22754 </code></pre>
22755  * <p>
22756  * This would consume an XML file like this:
22757  * <pre><code>
22758 &lt;?xml?>
22759 &lt;dataset>
22760  &lt;results>2&lt;/results>
22761  &lt;row>
22762    &lt;id>1&lt;/id>
22763    &lt;name>Bill&lt;/name>
22764    &lt;occupation>Gardener&lt;/occupation>
22765  &lt;/row>
22766  &lt;row>
22767    &lt;id>2&lt;/id>
22768    &lt;name>Ben&lt;/name>
22769    &lt;occupation>Horticulturalist&lt;/occupation>
22770  &lt;/row>
22771 &lt;/dataset>
22772 </code></pre>
22773  * @cfg {String} totalRecords The DomQuery path from which to retrieve the total number of records
22774  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
22775  * paged from the remote server.
22776  * @cfg {String} record The DomQuery path to the repeated element which contains record information.
22777  * @cfg {String} success The DomQuery path to the success attribute used by forms.
22778  * @cfg {String} id The DomQuery path relative from the record element to the element that contains
22779  * a record identifier value.
22780  * @constructor
22781  * Create a new XmlReader
22782  * @param {Object} meta Metadata configuration options
22783  * @param {Mixed} recordType The definition of the data record type to produce.  This can be either a valid
22784  * Record subclass created with {@link Roo.data.Record#create}, or an array of objects with which to call
22785  * Roo.data.Record.create.  See the {@link Roo.data.Record} class for more details.
22786  */
22787 Roo.data.XmlReader = function(meta, recordType){
22788     meta = meta || {};
22789     Roo.data.XmlReader.superclass.constructor.call(this, meta, recordType||meta.fields);
22790 };
22791 Roo.extend(Roo.data.XmlReader, Roo.data.DataReader, {
22792     /**
22793      * This method is only used by a DataProxy which has retrieved data from a remote server.
22794          * @param {Object} response The XHR object which contains the parsed XML document.  The response is expected
22795          * to contain a method called 'responseXML' that returns an XML document object.
22796      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
22797      * a cache of Roo.data.Records.
22798      */
22799     read : function(response){
22800         var doc = response.responseXML;
22801         if(!doc) {
22802             throw {message: "XmlReader.read: XML Document not available"};
22803         }
22804         return this.readRecords(doc);
22805     },
22806
22807     /**
22808      * Create a data block containing Roo.data.Records from an XML document.
22809          * @param {Object} doc A parsed XML document.
22810      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
22811      * a cache of Roo.data.Records.
22812      */
22813     readRecords : function(doc){
22814         /**
22815          * After any data loads/reads, the raw XML Document is available for further custom processing.
22816          * @type XMLDocument
22817          */
22818         this.xmlData = doc;
22819         var root = doc.documentElement || doc;
22820         var q = Roo.DomQuery;
22821         var recordType = this.recordType, fields = recordType.prototype.fields;
22822         var sid = this.meta.id;
22823         var totalRecords = 0, success = true;
22824         if(this.meta.totalRecords){
22825             totalRecords = q.selectNumber(this.meta.totalRecords, root, 0);
22826         }
22827         
22828         if(this.meta.success){
22829             var sv = q.selectValue(this.meta.success, root, true);
22830             success = sv !== false && sv !== 'false';
22831         }
22832         var records = [];
22833         var ns = q.select(this.meta.record, root);
22834         for(var i = 0, len = ns.length; i < len; i++) {
22835                 var n = ns[i];
22836                 var values = {};
22837                 var id = sid ? q.selectValue(sid, n) : undefined;
22838                 for(var j = 0, jlen = fields.length; j < jlen; j++){
22839                     var f = fields.items[j];
22840                 var v = q.selectValue(f.mapping || f.name, n, f.defaultValue);
22841                     v = f.convert(v);
22842                     values[f.name] = v;
22843                 }
22844                 var record = new recordType(values, id);
22845                 record.node = n;
22846                 records[records.length] = record;
22847             }
22848
22849             return {
22850                 success : success,
22851                 records : records,
22852                 totalRecords : totalRecords || records.length
22853             };
22854     }
22855 });/*
22856  * Based on:
22857  * Ext JS Library 1.1.1
22858  * Copyright(c) 2006-2007, Ext JS, LLC.
22859  *
22860  * Originally Released Under LGPL - original licence link has changed is not relivant.
22861  *
22862  * Fork - LGPL
22863  * <script type="text/javascript">
22864  */
22865
22866 /**
22867  * @class Roo.data.ArrayReader
22868  * @extends Roo.data.DataReader
22869  * Data reader class to create an Array of Roo.data.Record objects from an Array.
22870  * Each element of that Array represents a row of data fields. The
22871  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
22872  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
22873  * <p>
22874  * Example code:.
22875  * <pre><code>
22876 var RecordDef = Roo.data.Record.create([
22877     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
22878     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
22879 ]);
22880 var myReader = new Roo.data.ArrayReader({
22881     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
22882 }, RecordDef);
22883 </code></pre>
22884  * <p>
22885  * This would consume an Array like this:
22886  * <pre><code>
22887 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
22888   </code></pre>
22889  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
22890  * @constructor
22891  * Create a new JsonReader
22892  * @param {Object} meta Metadata configuration options.
22893  * @param {Object} recordType Either an Array of field definition objects
22894  * as specified to {@link Roo.data.Record#create},
22895  * or an {@link Roo.data.Record} object
22896  * created using {@link Roo.data.Record#create}.
22897  */
22898 Roo.data.ArrayReader = function(meta, recordType){
22899     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
22900 };
22901
22902 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
22903     /**
22904      * Create a data block containing Roo.data.Records from an XML document.
22905      * @param {Object} o An Array of row objects which represents the dataset.
22906      * @return {Object} data A data block which is used by an Roo.data.Store object as
22907      * a cache of Roo.data.Records.
22908      */
22909     readRecords : function(o){
22910         var sid = this.meta ? this.meta.id : null;
22911         var recordType = this.recordType, fields = recordType.prototype.fields;
22912         var records = [];
22913         var root = o;
22914             for(var i = 0; i < root.length; i++){
22915                     var n = root[i];
22916                 var values = {};
22917                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
22918                 for(var j = 0, jlen = fields.length; j < jlen; j++){
22919                 var f = fields.items[j];
22920                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
22921                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
22922                 v = f.convert(v);
22923                 values[f.name] = v;
22924             }
22925                 var record = new recordType(values, id);
22926                 record.json = n;
22927                 records[records.length] = record;
22928             }
22929             return {
22930                 records : records,
22931                 totalRecords : records.length
22932             };
22933     }
22934 });/*
22935  * Based on:
22936  * Ext JS Library 1.1.1
22937  * Copyright(c) 2006-2007, Ext JS, LLC.
22938  *
22939  * Originally Released Under LGPL - original licence link has changed is not relivant.
22940  *
22941  * Fork - LGPL
22942  * <script type="text/javascript">
22943  */
22944
22945
22946 /**
22947  * @class Roo.data.Tree
22948  * @extends Roo.util.Observable
22949  * Represents a tree data structure and bubbles all the events for its nodes. The nodes
22950  * in the tree have most standard DOM functionality.
22951  * @constructor
22952  * @param {Node} root (optional) The root node
22953  */
22954 Roo.data.Tree = function(root){
22955    this.nodeHash = {};
22956    /**
22957     * The root node for this tree
22958     * @type Node
22959     */
22960    this.root = null;
22961    if(root){
22962        this.setRootNode(root);
22963    }
22964    this.addEvents({
22965        /**
22966         * @event append
22967         * Fires when a new child node is appended to a node in this tree.
22968         * @param {Tree} tree The owner tree
22969         * @param {Node} parent The parent node
22970         * @param {Node} node The newly appended node
22971         * @param {Number} index The index of the newly appended node
22972         */
22973        "append" : true,
22974        /**
22975         * @event remove
22976         * Fires when a child node is removed from a node in this tree.
22977         * @param {Tree} tree The owner tree
22978         * @param {Node} parent The parent node
22979         * @param {Node} node The child node removed
22980         */
22981        "remove" : true,
22982        /**
22983         * @event move
22984         * Fires when a node is moved to a new location in the tree
22985         * @param {Tree} tree The owner tree
22986         * @param {Node} node The node moved
22987         * @param {Node} oldParent The old parent of this node
22988         * @param {Node} newParent The new parent of this node
22989         * @param {Number} index The index it was moved to
22990         */
22991        "move" : true,
22992        /**
22993         * @event insert
22994         * Fires when a new child node is inserted in a node in this tree.
22995         * @param {Tree} tree The owner tree
22996         * @param {Node} parent The parent node
22997         * @param {Node} node The child node inserted
22998         * @param {Node} refNode The child node the node was inserted before
22999         */
23000        "insert" : true,
23001        /**
23002         * @event beforeappend
23003         * Fires before a new child is appended to a node in this tree, return false to cancel the append.
23004         * @param {Tree} tree The owner tree
23005         * @param {Node} parent The parent node
23006         * @param {Node} node The child node to be appended
23007         */
23008        "beforeappend" : true,
23009        /**
23010         * @event beforeremove
23011         * Fires before a child is removed from a node in this tree, return false to cancel the remove.
23012         * @param {Tree} tree The owner tree
23013         * @param {Node} parent The parent node
23014         * @param {Node} node The child node to be removed
23015         */
23016        "beforeremove" : true,
23017        /**
23018         * @event beforemove
23019         * Fires before a node is moved to a new location in the tree. Return false to cancel the move.
23020         * @param {Tree} tree The owner tree
23021         * @param {Node} node The node being moved
23022         * @param {Node} oldParent The parent of the node
23023         * @param {Node} newParent The new parent the node is moving to
23024         * @param {Number} index The index it is being moved to
23025         */
23026        "beforemove" : true,
23027        /**
23028         * @event beforeinsert
23029         * Fires before a new child is inserted in a node in this tree, return false to cancel the insert.
23030         * @param {Tree} tree The owner tree
23031         * @param {Node} parent The parent node
23032         * @param {Node} node The child node to be inserted
23033         * @param {Node} refNode The child node the node is being inserted before
23034         */
23035        "beforeinsert" : true
23036    });
23037
23038     Roo.data.Tree.superclass.constructor.call(this);
23039 };
23040
23041 Roo.extend(Roo.data.Tree, Roo.util.Observable, {
23042     pathSeparator: "/",
23043
23044     proxyNodeEvent : function(){
23045         return this.fireEvent.apply(this, arguments);
23046     },
23047
23048     /**
23049      * Returns the root node for this tree.
23050      * @return {Node}
23051      */
23052     getRootNode : function(){
23053         return this.root;
23054     },
23055
23056     /**
23057      * Sets the root node for this tree.
23058      * @param {Node} node
23059      * @return {Node}
23060      */
23061     setRootNode : function(node){
23062         this.root = node;
23063         node.ownerTree = this;
23064         node.isRoot = true;
23065         this.registerNode(node);
23066         return node;
23067     },
23068
23069     /**
23070      * Gets a node in this tree by its id.
23071      * @param {String} id
23072      * @return {Node}
23073      */
23074     getNodeById : function(id){
23075         return this.nodeHash[id];
23076     },
23077
23078     registerNode : function(node){
23079         this.nodeHash[node.id] = node;
23080     },
23081
23082     unregisterNode : function(node){
23083         delete this.nodeHash[node.id];
23084     },
23085
23086     toString : function(){
23087         return "[Tree"+(this.id?" "+this.id:"")+"]";
23088     }
23089 });
23090
23091 /**
23092  * @class Roo.data.Node
23093  * @extends Roo.util.Observable
23094  * @cfg {Boolean} leaf true if this node is a leaf and does not have children
23095  * @cfg {String} id The id for this node. If one is not specified, one is generated.
23096  * @constructor
23097  * @param {Object} attributes The attributes/config for the node
23098  */
23099 Roo.data.Node = function(attributes){
23100     /**
23101      * The attributes supplied for the node. You can use this property to access any custom attributes you supplied.
23102      * @type {Object}
23103      */
23104     this.attributes = attributes || {};
23105     this.leaf = this.attributes.leaf;
23106     /**
23107      * The node id. @type String
23108      */
23109     this.id = this.attributes.id;
23110     if(!this.id){
23111         this.id = Roo.id(null, "ynode-");
23112         this.attributes.id = this.id;
23113     }
23114      
23115     
23116     /**
23117      * All child nodes of this node. @type Array
23118      */
23119     this.childNodes = [];
23120     if(!this.childNodes.indexOf){ // indexOf is a must
23121         this.childNodes.indexOf = function(o){
23122             for(var i = 0, len = this.length; i < len; i++){
23123                 if(this[i] == o) {
23124                     return i;
23125                 }
23126             }
23127             return -1;
23128         };
23129     }
23130     /**
23131      * The parent node for this node. @type Node
23132      */
23133     this.parentNode = null;
23134     /**
23135      * The first direct child node of this node, or null if this node has no child nodes. @type Node
23136      */
23137     this.firstChild = null;
23138     /**
23139      * The last direct child node of this node, or null if this node has no child nodes. @type Node
23140      */
23141     this.lastChild = null;
23142     /**
23143      * The node immediately preceding this node in the tree, or null if there is no sibling node. @type Node
23144      */
23145     this.previousSibling = null;
23146     /**
23147      * The node immediately following this node in the tree, or null if there is no sibling node. @type Node
23148      */
23149     this.nextSibling = null;
23150
23151     this.addEvents({
23152        /**
23153         * @event append
23154         * Fires when a new child node is appended
23155         * @param {Tree} tree The owner tree
23156         * @param {Node} this This node
23157         * @param {Node} node The newly appended node
23158         * @param {Number} index The index of the newly appended node
23159         */
23160        "append" : true,
23161        /**
23162         * @event remove
23163         * Fires when a child node is removed
23164         * @param {Tree} tree The owner tree
23165         * @param {Node} this This node
23166         * @param {Node} node The removed node
23167         */
23168        "remove" : true,
23169        /**
23170         * @event move
23171         * Fires when this node is moved to a new location in the tree
23172         * @param {Tree} tree The owner tree
23173         * @param {Node} this This node
23174         * @param {Node} oldParent The old parent of this node
23175         * @param {Node} newParent The new parent of this node
23176         * @param {Number} index The index it was moved to
23177         */
23178        "move" : true,
23179        /**
23180         * @event insert
23181         * Fires when a new child node is inserted.
23182         * @param {Tree} tree The owner tree
23183         * @param {Node} this This node
23184         * @param {Node} node The child node inserted
23185         * @param {Node} refNode The child node the node was inserted before
23186         */
23187        "insert" : true,
23188        /**
23189         * @event beforeappend
23190         * Fires before a new child is appended, return false to cancel the append.
23191         * @param {Tree} tree The owner tree
23192         * @param {Node} this This node
23193         * @param {Node} node The child node to be appended
23194         */
23195        "beforeappend" : true,
23196        /**
23197         * @event beforeremove
23198         * Fires before a child is removed, return false to cancel the remove.
23199         * @param {Tree} tree The owner tree
23200         * @param {Node} this This node
23201         * @param {Node} node The child node to be removed
23202         */
23203        "beforeremove" : true,
23204        /**
23205         * @event beforemove
23206         * Fires before this node is moved to a new location in the tree. Return false to cancel the move.
23207         * @param {Tree} tree The owner tree
23208         * @param {Node} this This node
23209         * @param {Node} oldParent The parent of this node
23210         * @param {Node} newParent The new parent this node is moving to
23211         * @param {Number} index The index it is being moved to
23212         */
23213        "beforemove" : true,
23214        /**
23215         * @event beforeinsert
23216         * Fires before a new child is inserted, return false to cancel the insert.
23217         * @param {Tree} tree The owner tree
23218         * @param {Node} this This node
23219         * @param {Node} node The child node to be inserted
23220         * @param {Node} refNode The child node the node is being inserted before
23221         */
23222        "beforeinsert" : true
23223    });
23224     this.listeners = this.attributes.listeners;
23225     Roo.data.Node.superclass.constructor.call(this);
23226 };
23227
23228 Roo.extend(Roo.data.Node, Roo.util.Observable, {
23229     fireEvent : function(evtName){
23230         // first do standard event for this node
23231         if(Roo.data.Node.superclass.fireEvent.apply(this, arguments) === false){
23232             return false;
23233         }
23234         // then bubble it up to the tree if the event wasn't cancelled
23235         var ot = this.getOwnerTree();
23236         if(ot){
23237             if(ot.proxyNodeEvent.apply(ot, arguments) === false){
23238                 return false;
23239             }
23240         }
23241         return true;
23242     },
23243
23244     /**
23245      * Returns true if this node is a leaf
23246      * @return {Boolean}
23247      */
23248     isLeaf : function(){
23249         return this.leaf === true;
23250     },
23251
23252     // private
23253     setFirstChild : function(node){
23254         this.firstChild = node;
23255     },
23256
23257     //private
23258     setLastChild : function(node){
23259         this.lastChild = node;
23260     },
23261
23262
23263     /**
23264      * Returns true if this node is the last child of its parent
23265      * @return {Boolean}
23266      */
23267     isLast : function(){
23268        return (!this.parentNode ? true : this.parentNode.lastChild == this);
23269     },
23270
23271     /**
23272      * Returns true if this node is the first child of its parent
23273      * @return {Boolean}
23274      */
23275     isFirst : function(){
23276        return (!this.parentNode ? true : this.parentNode.firstChild == this);
23277     },
23278
23279     hasChildNodes : function(){
23280         return !this.isLeaf() && this.childNodes.length > 0;
23281     },
23282
23283     /**
23284      * Insert node(s) as the last child node of this node.
23285      * @param {Node/Array} node The node or Array of nodes to append
23286      * @return {Node} The appended node if single append, or null if an array was passed
23287      */
23288     appendChild : function(node){
23289         var multi = false;
23290         if(node instanceof Array){
23291             multi = node;
23292         }else if(arguments.length > 1){
23293             multi = arguments;
23294         }
23295         // if passed an array or multiple args do them one by one
23296         if(multi){
23297             for(var i = 0, len = multi.length; i < len; i++) {
23298                 this.appendChild(multi[i]);
23299             }
23300         }else{
23301             if(this.fireEvent("beforeappend", this.ownerTree, this, node) === false){
23302                 return false;
23303             }
23304             var index = this.childNodes.length;
23305             var oldParent = node.parentNode;
23306             // it's a move, make sure we move it cleanly
23307             if(oldParent){
23308                 if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index) === false){
23309                     return false;
23310                 }
23311                 oldParent.removeChild(node);
23312             }
23313             index = this.childNodes.length;
23314             if(index == 0){
23315                 this.setFirstChild(node);
23316             }
23317             this.childNodes.push(node);
23318             node.parentNode = this;
23319             var ps = this.childNodes[index-1];
23320             if(ps){
23321                 node.previousSibling = ps;
23322                 ps.nextSibling = node;
23323             }else{
23324                 node.previousSibling = null;
23325             }
23326             node.nextSibling = null;
23327             this.setLastChild(node);
23328             node.setOwnerTree(this.getOwnerTree());
23329             this.fireEvent("append", this.ownerTree, this, node, index);
23330             if(oldParent){
23331                 node.fireEvent("move", this.ownerTree, node, oldParent, this, index);
23332             }
23333             return node;
23334         }
23335     },
23336
23337     /**
23338      * Removes a child node from this node.
23339      * @param {Node} node The node to remove
23340      * @return {Node} The removed node
23341      */
23342     removeChild : function(node){
23343         var index = this.childNodes.indexOf(node);
23344         if(index == -1){
23345             return false;
23346         }
23347         if(this.fireEvent("beforeremove", this.ownerTree, this, node) === false){
23348             return false;
23349         }
23350
23351         // remove it from childNodes collection
23352         this.childNodes.splice(index, 1);
23353
23354         // update siblings
23355         if(node.previousSibling){
23356             node.previousSibling.nextSibling = node.nextSibling;
23357         }
23358         if(node.nextSibling){
23359             node.nextSibling.previousSibling = node.previousSibling;
23360         }
23361
23362         // update child refs
23363         if(this.firstChild == node){
23364             this.setFirstChild(node.nextSibling);
23365         }
23366         if(this.lastChild == node){
23367             this.setLastChild(node.previousSibling);
23368         }
23369
23370         node.setOwnerTree(null);
23371         // clear any references from the node
23372         node.parentNode = null;
23373         node.previousSibling = null;
23374         node.nextSibling = null;
23375         this.fireEvent("remove", this.ownerTree, this, node);
23376         return node;
23377     },
23378
23379     /**
23380      * Inserts the first node before the second node in this nodes childNodes collection.
23381      * @param {Node} node The node to insert
23382      * @param {Node} refNode The node to insert before (if null the node is appended)
23383      * @return {Node} The inserted node
23384      */
23385     insertBefore : function(node, refNode){
23386         if(!refNode){ // like standard Dom, refNode can be null for append
23387             return this.appendChild(node);
23388         }
23389         // nothing to do
23390         if(node == refNode){
23391             return false;
23392         }
23393
23394         if(this.fireEvent("beforeinsert", this.ownerTree, this, node, refNode) === false){
23395             return false;
23396         }
23397         var index = this.childNodes.indexOf(refNode);
23398         var oldParent = node.parentNode;
23399         var refIndex = index;
23400
23401         // when moving internally, indexes will change after remove
23402         if(oldParent == this && this.childNodes.indexOf(node) < index){
23403             refIndex--;
23404         }
23405
23406         // it's a move, make sure we move it cleanly
23407         if(oldParent){
23408             if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index, refNode) === false){
23409                 return false;
23410             }
23411             oldParent.removeChild(node);
23412         }
23413         if(refIndex == 0){
23414             this.setFirstChild(node);
23415         }
23416         this.childNodes.splice(refIndex, 0, node);
23417         node.parentNode = this;
23418         var ps = this.childNodes[refIndex-1];
23419         if(ps){
23420             node.previousSibling = ps;
23421             ps.nextSibling = node;
23422         }else{
23423             node.previousSibling = null;
23424         }
23425         node.nextSibling = refNode;
23426         refNode.previousSibling = node;
23427         node.setOwnerTree(this.getOwnerTree());
23428         this.fireEvent("insert", this.ownerTree, this, node, refNode);
23429         if(oldParent){
23430             node.fireEvent("move", this.ownerTree, node, oldParent, this, refIndex, refNode);
23431         }
23432         return node;
23433     },
23434
23435     /**
23436      * Returns the child node at the specified index.
23437      * @param {Number} index
23438      * @return {Node}
23439      */
23440     item : function(index){
23441         return this.childNodes[index];
23442     },
23443
23444     /**
23445      * Replaces one child node in this node with another.
23446      * @param {Node} newChild The replacement node
23447      * @param {Node} oldChild The node to replace
23448      * @return {Node} The replaced node
23449      */
23450     replaceChild : function(newChild, oldChild){
23451         this.insertBefore(newChild, oldChild);
23452         this.removeChild(oldChild);
23453         return oldChild;
23454     },
23455
23456     /**
23457      * Returns the index of a child node
23458      * @param {Node} node
23459      * @return {Number} The index of the node or -1 if it was not found
23460      */
23461     indexOf : function(child){
23462         return this.childNodes.indexOf(child);
23463     },
23464
23465     /**
23466      * Returns the tree this node is in.
23467      * @return {Tree}
23468      */
23469     getOwnerTree : function(){
23470         // if it doesn't have one, look for one
23471         if(!this.ownerTree){
23472             var p = this;
23473             while(p){
23474                 if(p.ownerTree){
23475                     this.ownerTree = p.ownerTree;
23476                     break;
23477                 }
23478                 p = p.parentNode;
23479             }
23480         }
23481         return this.ownerTree;
23482     },
23483
23484     /**
23485      * Returns depth of this node (the root node has a depth of 0)
23486      * @return {Number}
23487      */
23488     getDepth : function(){
23489         var depth = 0;
23490         var p = this;
23491         while(p.parentNode){
23492             ++depth;
23493             p = p.parentNode;
23494         }
23495         return depth;
23496     },
23497
23498     // private
23499     setOwnerTree : function(tree){
23500         // if it's move, we need to update everyone
23501         if(tree != this.ownerTree){
23502             if(this.ownerTree){
23503                 this.ownerTree.unregisterNode(this);
23504             }
23505             this.ownerTree = tree;
23506             var cs = this.childNodes;
23507             for(var i = 0, len = cs.length; i < len; i++) {
23508                 cs[i].setOwnerTree(tree);
23509             }
23510             if(tree){
23511                 tree.registerNode(this);
23512             }
23513         }
23514     },
23515
23516     /**
23517      * Returns the path for this node. The path can be used to expand or select this node programmatically.
23518      * @param {String} attr (optional) The attr to use for the path (defaults to the node's id)
23519      * @return {String} The path
23520      */
23521     getPath : function(attr){
23522         attr = attr || "id";
23523         var p = this.parentNode;
23524         var b = [this.attributes[attr]];
23525         while(p){
23526             b.unshift(p.attributes[attr]);
23527             p = p.parentNode;
23528         }
23529         var sep = this.getOwnerTree().pathSeparator;
23530         return sep + b.join(sep);
23531     },
23532
23533     /**
23534      * Bubbles up the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
23535      * function call will be the scope provided or the current node. The arguments to the function
23536      * will be the args provided or the current node. If the function returns false at any point,
23537      * the bubble is stopped.
23538      * @param {Function} fn The function to call
23539      * @param {Object} scope (optional) The scope of the function (defaults to current node)
23540      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
23541      */
23542     bubble : function(fn, scope, args){
23543         var p = this;
23544         while(p){
23545             if(fn.call(scope || p, args || p) === false){
23546                 break;
23547             }
23548             p = p.parentNode;
23549         }
23550     },
23551
23552     /**
23553      * Cascades down the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
23554      * function call will be the scope provided or the current node. The arguments to the function
23555      * will be the args provided or the current node. If the function returns false at any point,
23556      * the cascade is stopped on that branch.
23557      * @param {Function} fn The function to call
23558      * @param {Object} scope (optional) The scope of the function (defaults to current node)
23559      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
23560      */
23561     cascade : function(fn, scope, args){
23562         if(fn.call(scope || this, args || this) !== false){
23563             var cs = this.childNodes;
23564             for(var i = 0, len = cs.length; i < len; i++) {
23565                 cs[i].cascade(fn, scope, args);
23566             }
23567         }
23568     },
23569
23570     /**
23571      * Interates the child nodes of this node, calling the specified function with each node. The scope (<i>this</i>) of
23572      * function call will be the scope provided or the current node. The arguments to the function
23573      * will be the args provided or the current node. If the function returns false at any point,
23574      * the iteration stops.
23575      * @param {Function} fn The function to call
23576      * @param {Object} scope (optional) The scope of the function (defaults to current node)
23577      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
23578      */
23579     eachChild : function(fn, scope, args){
23580         var cs = this.childNodes;
23581         for(var i = 0, len = cs.length; i < len; i++) {
23582                 if(fn.call(scope || this, args || cs[i]) === false){
23583                     break;
23584                 }
23585         }
23586     },
23587
23588     /**
23589      * Finds the first child that has the attribute with the specified value.
23590      * @param {String} attribute The attribute name
23591      * @param {Mixed} value The value to search for
23592      * @return {Node} The found child or null if none was found
23593      */
23594     findChild : function(attribute, value){
23595         var cs = this.childNodes;
23596         for(var i = 0, len = cs.length; i < len; i++) {
23597                 if(cs[i].attributes[attribute] == value){
23598                     return cs[i];
23599                 }
23600         }
23601         return null;
23602     },
23603
23604     /**
23605      * Finds the first child by a custom function. The child matches if the function passed
23606      * returns true.
23607      * @param {Function} fn
23608      * @param {Object} scope (optional)
23609      * @return {Node} The found child or null if none was found
23610      */
23611     findChildBy : function(fn, scope){
23612         var cs = this.childNodes;
23613         for(var i = 0, len = cs.length; i < len; i++) {
23614                 if(fn.call(scope||cs[i], cs[i]) === true){
23615                     return cs[i];
23616                 }
23617         }
23618         return null;
23619     },
23620
23621     /**
23622      * Sorts this nodes children using the supplied sort function
23623      * @param {Function} fn
23624      * @param {Object} scope (optional)
23625      */
23626     sort : function(fn, scope){
23627         var cs = this.childNodes;
23628         var len = cs.length;
23629         if(len > 0){
23630             var sortFn = scope ? function(){fn.apply(scope, arguments);} : fn;
23631             cs.sort(sortFn);
23632             for(var i = 0; i < len; i++){
23633                 var n = cs[i];
23634                 n.previousSibling = cs[i-1];
23635                 n.nextSibling = cs[i+1];
23636                 if(i == 0){
23637                     this.setFirstChild(n);
23638                 }
23639                 if(i == len-1){
23640                     this.setLastChild(n);
23641                 }
23642             }
23643         }
23644     },
23645
23646     /**
23647      * Returns true if this node is an ancestor (at any point) of the passed node.
23648      * @param {Node} node
23649      * @return {Boolean}
23650      */
23651     contains : function(node){
23652         return node.isAncestor(this);
23653     },
23654
23655     /**
23656      * Returns true if the passed node is an ancestor (at any point) of this node.
23657      * @param {Node} node
23658      * @return {Boolean}
23659      */
23660     isAncestor : function(node){
23661         var p = this.parentNode;
23662         while(p){
23663             if(p == node){
23664                 return true;
23665             }
23666             p = p.parentNode;
23667         }
23668         return false;
23669     },
23670
23671     toString : function(){
23672         return "[Node"+(this.id?" "+this.id:"")+"]";
23673     }
23674 });/*
23675  * Based on:
23676  * Ext JS Library 1.1.1
23677  * Copyright(c) 2006-2007, Ext JS, LLC.
23678  *
23679  * Originally Released Under LGPL - original licence link has changed is not relivant.
23680  *
23681  * Fork - LGPL
23682  * <script type="text/javascript">
23683  */
23684  (function(){ 
23685 /**
23686  * @class Roo.Layer
23687  * @extends Roo.Element
23688  * An extended {@link Roo.Element} object that supports a shadow and shim, constrain to viewport and
23689  * automatic maintaining of shadow/shim positions.
23690  * @cfg {Boolean} shim False to disable the iframe shim in browsers which need one (defaults to true)
23691  * @cfg {String/Boolean} shadow True to create a shadow element with default class "x-layer-shadow", or
23692  * you can pass a string with a CSS class name. False turns off the shadow.
23693  * @cfg {Object} dh DomHelper object config to create element with (defaults to {tag: "div", cls: "x-layer"}).
23694  * @cfg {Boolean} constrain False to disable constrain to viewport (defaults to true)
23695  * @cfg {String} cls CSS class to add to the element
23696  * @cfg {Number} zindex Starting z-index (defaults to 11000)
23697  * @cfg {Number} shadowOffset Number of pixels to offset the shadow (defaults to 3)
23698  * @constructor
23699  * @param {Object} config An object with config options.
23700  * @param {String/HTMLElement} existingEl (optional) Uses an existing DOM element. If the element is not found it creates it.
23701  */
23702
23703 Roo.Layer = function(config, existingEl){
23704     config = config || {};
23705     var dh = Roo.DomHelper;
23706     var cp = config.parentEl, pel = cp ? Roo.getDom(cp) : document.body;
23707     if(existingEl){
23708         this.dom = Roo.getDom(existingEl);
23709     }
23710     if(!this.dom){
23711         var o = config.dh || {tag: "div", cls: "x-layer"};
23712         this.dom = dh.append(pel, o);
23713     }
23714     if(config.cls){
23715         this.addClass(config.cls);
23716     }
23717     this.constrain = config.constrain !== false;
23718     this.visibilityMode = Roo.Element.VISIBILITY;
23719     if(config.id){
23720         this.id = this.dom.id = config.id;
23721     }else{
23722         this.id = Roo.id(this.dom);
23723     }
23724     this.zindex = config.zindex || this.getZIndex();
23725     this.position("absolute", this.zindex);
23726     if(config.shadow){
23727         this.shadowOffset = config.shadowOffset || 4;
23728         this.shadow = new Roo.Shadow({
23729             offset : this.shadowOffset,
23730             mode : config.shadow
23731         });
23732     }else{
23733         this.shadowOffset = 0;
23734     }
23735     this.useShim = config.shim !== false && Roo.useShims;
23736     this.useDisplay = config.useDisplay;
23737     this.hide();
23738 };
23739
23740 var supr = Roo.Element.prototype;
23741
23742 // shims are shared among layer to keep from having 100 iframes
23743 var shims = [];
23744
23745 Roo.extend(Roo.Layer, Roo.Element, {
23746
23747     getZIndex : function(){
23748         return this.zindex || parseInt(this.getStyle("z-index"), 10) || 11000;
23749     },
23750
23751     getShim : function(){
23752         if(!this.useShim){
23753             return null;
23754         }
23755         if(this.shim){
23756             return this.shim;
23757         }
23758         var shim = shims.shift();
23759         if(!shim){
23760             shim = this.createShim();
23761             shim.enableDisplayMode('block');
23762             shim.dom.style.display = 'none';
23763             shim.dom.style.visibility = 'visible';
23764         }
23765         var pn = this.dom.parentNode;
23766         if(shim.dom.parentNode != pn){
23767             pn.insertBefore(shim.dom, this.dom);
23768         }
23769         shim.setStyle('z-index', this.getZIndex()-2);
23770         this.shim = shim;
23771         return shim;
23772     },
23773
23774     hideShim : function(){
23775         if(this.shim){
23776             this.shim.setDisplayed(false);
23777             shims.push(this.shim);
23778             delete this.shim;
23779         }
23780     },
23781
23782     disableShadow : function(){
23783         if(this.shadow){
23784             this.shadowDisabled = true;
23785             this.shadow.hide();
23786             this.lastShadowOffset = this.shadowOffset;
23787             this.shadowOffset = 0;
23788         }
23789     },
23790
23791     enableShadow : function(show){
23792         if(this.shadow){
23793             this.shadowDisabled = false;
23794             this.shadowOffset = this.lastShadowOffset;
23795             delete this.lastShadowOffset;
23796             if(show){
23797                 this.sync(true);
23798             }
23799         }
23800     },
23801
23802     // private
23803     // this code can execute repeatedly in milliseconds (i.e. during a drag) so
23804     // code size was sacrificed for effeciency (e.g. no getBox/setBox, no XY calls)
23805     sync : function(doShow){
23806         var sw = this.shadow;
23807         if(!this.updating && this.isVisible() && (sw || this.useShim)){
23808             var sh = this.getShim();
23809
23810             var w = this.getWidth(),
23811                 h = this.getHeight();
23812
23813             var l = this.getLeft(true),
23814                 t = this.getTop(true);
23815
23816             if(sw && !this.shadowDisabled){
23817                 if(doShow && !sw.isVisible()){
23818                     sw.show(this);
23819                 }else{
23820                     sw.realign(l, t, w, h);
23821                 }
23822                 if(sh){
23823                     if(doShow){
23824                        sh.show();
23825                     }
23826                     // fit the shim behind the shadow, so it is shimmed too
23827                     var a = sw.adjusts, s = sh.dom.style;
23828                     s.left = (Math.min(l, l+a.l))+"px";
23829                     s.top = (Math.min(t, t+a.t))+"px";
23830                     s.width = (w+a.w)+"px";
23831                     s.height = (h+a.h)+"px";
23832                 }
23833             }else if(sh){
23834                 if(doShow){
23835                    sh.show();
23836                 }
23837                 sh.setSize(w, h);
23838                 sh.setLeftTop(l, t);
23839             }
23840             
23841         }
23842     },
23843
23844     // private
23845     destroy : function(){
23846         this.hideShim();
23847         if(this.shadow){
23848             this.shadow.hide();
23849         }
23850         this.removeAllListeners();
23851         var pn = this.dom.parentNode;
23852         if(pn){
23853             pn.removeChild(this.dom);
23854         }
23855         Roo.Element.uncache(this.id);
23856     },
23857
23858     remove : function(){
23859         this.destroy();
23860     },
23861
23862     // private
23863     beginUpdate : function(){
23864         this.updating = true;
23865     },
23866
23867     // private
23868     endUpdate : function(){
23869         this.updating = false;
23870         this.sync(true);
23871     },
23872
23873     // private
23874     hideUnders : function(negOffset){
23875         if(this.shadow){
23876             this.shadow.hide();
23877         }
23878         this.hideShim();
23879     },
23880
23881     // private
23882     constrainXY : function(){
23883         if(this.constrain){
23884             var vw = Roo.lib.Dom.getViewWidth(),
23885                 vh = Roo.lib.Dom.getViewHeight();
23886             var s = Roo.get(document).getScroll();
23887
23888             var xy = this.getXY();
23889             var x = xy[0], y = xy[1];   
23890             var w = this.dom.offsetWidth+this.shadowOffset, h = this.dom.offsetHeight+this.shadowOffset;
23891             // only move it if it needs it
23892             var moved = false;
23893             // first validate right/bottom
23894             if((x + w) > vw+s.left){
23895                 x = vw - w - this.shadowOffset;
23896                 moved = true;
23897             }
23898             if((y + h) > vh+s.top){
23899                 y = vh - h - this.shadowOffset;
23900                 moved = true;
23901             }
23902             // then make sure top/left isn't negative
23903             if(x < s.left){
23904                 x = s.left;
23905                 moved = true;
23906             }
23907             if(y < s.top){
23908                 y = s.top;
23909                 moved = true;
23910             }
23911             if(moved){
23912                 if(this.avoidY){
23913                     var ay = this.avoidY;
23914                     if(y <= ay && (y+h) >= ay){
23915                         y = ay-h-5;   
23916                     }
23917                 }
23918                 xy = [x, y];
23919                 this.storeXY(xy);
23920                 supr.setXY.call(this, xy);
23921                 this.sync();
23922             }
23923         }
23924     },
23925
23926     isVisible : function(){
23927         return this.visible;    
23928     },
23929
23930     // private
23931     showAction : function(){
23932         this.visible = true; // track visibility to prevent getStyle calls
23933         if(this.useDisplay === true){
23934             this.setDisplayed("");
23935         }else if(this.lastXY){
23936             supr.setXY.call(this, this.lastXY);
23937         }else if(this.lastLT){
23938             supr.setLeftTop.call(this, this.lastLT[0], this.lastLT[1]);
23939         }
23940     },
23941
23942     // private
23943     hideAction : function(){
23944         this.visible = false;
23945         if(this.useDisplay === true){
23946             this.setDisplayed(false);
23947         }else{
23948             this.setLeftTop(-10000,-10000);
23949         }
23950     },
23951
23952     // overridden Element method
23953     setVisible : function(v, a, d, c, e){
23954         if(v){
23955             this.showAction();
23956         }
23957         if(a && v){
23958             var cb = function(){
23959                 this.sync(true);
23960                 if(c){
23961                     c();
23962                 }
23963             }.createDelegate(this);
23964             supr.setVisible.call(this, true, true, d, cb, e);
23965         }else{
23966             if(!v){
23967                 this.hideUnders(true);
23968             }
23969             var cb = c;
23970             if(a){
23971                 cb = function(){
23972                     this.hideAction();
23973                     if(c){
23974                         c();
23975                     }
23976                 }.createDelegate(this);
23977             }
23978             supr.setVisible.call(this, v, a, d, cb, e);
23979             if(v){
23980                 this.sync(true);
23981             }else if(!a){
23982                 this.hideAction();
23983             }
23984         }
23985     },
23986
23987     storeXY : function(xy){
23988         delete this.lastLT;
23989         this.lastXY = xy;
23990     },
23991
23992     storeLeftTop : function(left, top){
23993         delete this.lastXY;
23994         this.lastLT = [left, top];
23995     },
23996
23997     // private
23998     beforeFx : function(){
23999         this.beforeAction();
24000         return Roo.Layer.superclass.beforeFx.apply(this, arguments);
24001     },
24002
24003     // private
24004     afterFx : function(){
24005         Roo.Layer.superclass.afterFx.apply(this, arguments);
24006         this.sync(this.isVisible());
24007     },
24008
24009     // private
24010     beforeAction : function(){
24011         if(!this.updating && this.shadow){
24012             this.shadow.hide();
24013         }
24014     },
24015
24016     // overridden Element method
24017     setLeft : function(left){
24018         this.storeLeftTop(left, this.getTop(true));
24019         supr.setLeft.apply(this, arguments);
24020         this.sync();
24021     },
24022
24023     setTop : function(top){
24024         this.storeLeftTop(this.getLeft(true), top);
24025         supr.setTop.apply(this, arguments);
24026         this.sync();
24027     },
24028
24029     setLeftTop : function(left, top){
24030         this.storeLeftTop(left, top);
24031         supr.setLeftTop.apply(this, arguments);
24032         this.sync();
24033     },
24034
24035     setXY : function(xy, a, d, c, e){
24036         this.fixDisplay();
24037         this.beforeAction();
24038         this.storeXY(xy);
24039         var cb = this.createCB(c);
24040         supr.setXY.call(this, xy, a, d, cb, e);
24041         if(!a){
24042             cb();
24043         }
24044     },
24045
24046     // private
24047     createCB : function(c){
24048         var el = this;
24049         return function(){
24050             el.constrainXY();
24051             el.sync(true);
24052             if(c){
24053                 c();
24054             }
24055         };
24056     },
24057
24058     // overridden Element method
24059     setX : function(x, a, d, c, e){
24060         this.setXY([x, this.getY()], a, d, c, e);
24061     },
24062
24063     // overridden Element method
24064     setY : function(y, a, d, c, e){
24065         this.setXY([this.getX(), y], a, d, c, e);
24066     },
24067
24068     // overridden Element method
24069     setSize : function(w, h, a, d, c, e){
24070         this.beforeAction();
24071         var cb = this.createCB(c);
24072         supr.setSize.call(this, w, h, a, d, cb, e);
24073         if(!a){
24074             cb();
24075         }
24076     },
24077
24078     // overridden Element method
24079     setWidth : function(w, a, d, c, e){
24080         this.beforeAction();
24081         var cb = this.createCB(c);
24082         supr.setWidth.call(this, w, a, d, cb, e);
24083         if(!a){
24084             cb();
24085         }
24086     },
24087
24088     // overridden Element method
24089     setHeight : function(h, a, d, c, e){
24090         this.beforeAction();
24091         var cb = this.createCB(c);
24092         supr.setHeight.call(this, h, a, d, cb, e);
24093         if(!a){
24094             cb();
24095         }
24096     },
24097
24098     // overridden Element method
24099     setBounds : function(x, y, w, h, a, d, c, e){
24100         this.beforeAction();
24101         var cb = this.createCB(c);
24102         if(!a){
24103             this.storeXY([x, y]);
24104             supr.setXY.call(this, [x, y]);
24105             supr.setSize.call(this, w, h, a, d, cb, e);
24106             cb();
24107         }else{
24108             supr.setBounds.call(this, x, y, w, h, a, d, cb, e);
24109         }
24110         return this;
24111     },
24112     
24113     /**
24114      * Sets the z-index of this layer and adjusts any shadow and shim z-indexes. The layer z-index is automatically
24115      * incremented by two more than the value passed in so that it always shows above any shadow or shim (the shadow
24116      * element, if any, will be assigned z-index + 1, and the shim element, if any, will be assigned the unmodified z-index).
24117      * @param {Number} zindex The new z-index to set
24118      * @return {this} The Layer
24119      */
24120     setZIndex : function(zindex){
24121         this.zindex = zindex;
24122         this.setStyle("z-index", zindex + 2);
24123         if(this.shadow){
24124             this.shadow.setZIndex(zindex + 1);
24125         }
24126         if(this.shim){
24127             this.shim.setStyle("z-index", zindex);
24128         }
24129     }
24130 });
24131 })();/*
24132  * Based on:
24133  * Ext JS Library 1.1.1
24134  * Copyright(c) 2006-2007, Ext JS, LLC.
24135  *
24136  * Originally Released Under LGPL - original licence link has changed is not relivant.
24137  *
24138  * Fork - LGPL
24139  * <script type="text/javascript">
24140  */
24141
24142
24143 /**
24144  * @class Roo.Shadow
24145  * Simple class that can provide a shadow effect for any element.  Note that the element MUST be absolutely positioned,
24146  * and the shadow does not provide any shimming.  This should be used only in simple cases -- for more advanced
24147  * functionality that can also provide the same shadow effect, see the {@link Roo.Layer} class.
24148  * @constructor
24149  * Create a new Shadow
24150  * @param {Object} config The config object
24151  */
24152 Roo.Shadow = function(config){
24153     Roo.apply(this, config);
24154     if(typeof this.mode != "string"){
24155         this.mode = this.defaultMode;
24156     }
24157     var o = this.offset, a = {h: 0};
24158     var rad = Math.floor(this.offset/2);
24159     switch(this.mode.toLowerCase()){ // all this hideous nonsense calculates the various offsets for shadows
24160         case "drop":
24161             a.w = 0;
24162             a.l = a.t = o;
24163             a.t -= 1;
24164             if(Roo.isIE){
24165                 a.l -= this.offset + rad;
24166                 a.t -= this.offset + rad;
24167                 a.w -= rad;
24168                 a.h -= rad;
24169                 a.t += 1;
24170             }
24171         break;
24172         case "sides":
24173             a.w = (o*2);
24174             a.l = -o;
24175             a.t = o-1;
24176             if(Roo.isIE){
24177                 a.l -= (this.offset - rad);
24178                 a.t -= this.offset + rad;
24179                 a.l += 1;
24180                 a.w -= (this.offset - rad)*2;
24181                 a.w -= rad + 1;
24182                 a.h -= 1;
24183             }
24184         break;
24185         case "frame":
24186             a.w = a.h = (o*2);
24187             a.l = a.t = -o;
24188             a.t += 1;
24189             a.h -= 2;
24190             if(Roo.isIE){
24191                 a.l -= (this.offset - rad);
24192                 a.t -= (this.offset - rad);
24193                 a.l += 1;
24194                 a.w -= (this.offset + rad + 1);
24195                 a.h -= (this.offset + rad);
24196                 a.h += 1;
24197             }
24198         break;
24199     };
24200
24201     this.adjusts = a;
24202 };
24203
24204 Roo.Shadow.prototype = {
24205     /**
24206      * @cfg {String} mode
24207      * The shadow display mode.  Supports the following options:<br />
24208      * sides: Shadow displays on both sides and bottom only<br />
24209      * frame: Shadow displays equally on all four sides<br />
24210      * drop: Traditional bottom-right drop shadow (default)
24211      */
24212     /**
24213      * @cfg {String} offset
24214      * The number of pixels to offset the shadow from the element (defaults to 4)
24215      */
24216     offset: 4,
24217
24218     // private
24219     defaultMode: "drop",
24220
24221     /**
24222      * Displays the shadow under the target element
24223      * @param {String/HTMLElement/Element} targetEl The id or element under which the shadow should display
24224      */
24225     show : function(target){
24226         target = Roo.get(target);
24227         if(!this.el){
24228             this.el = Roo.Shadow.Pool.pull();
24229             if(this.el.dom.nextSibling != target.dom){
24230                 this.el.insertBefore(target);
24231             }
24232         }
24233         this.el.setStyle("z-index", this.zIndex || parseInt(target.getStyle("z-index"), 10)-1);
24234         if(Roo.isIE){
24235             this.el.dom.style.filter="progid:DXImageTransform.Microsoft.alpha(opacity=50) progid:DXImageTransform.Microsoft.Blur(pixelradius="+(this.offset)+")";
24236         }
24237         this.realign(
24238             target.getLeft(true),
24239             target.getTop(true),
24240             target.getWidth(),
24241             target.getHeight()
24242         );
24243         this.el.dom.style.display = "block";
24244     },
24245
24246     /**
24247      * Returns true if the shadow is visible, else false
24248      */
24249     isVisible : function(){
24250         return this.el ? true : false;  
24251     },
24252
24253     /**
24254      * Direct alignment when values are already available. Show must be called at least once before
24255      * calling this method to ensure it is initialized.
24256      * @param {Number} left The target element left position
24257      * @param {Number} top The target element top position
24258      * @param {Number} width The target element width
24259      * @param {Number} height The target element height
24260      */
24261     realign : function(l, t, w, h){
24262         if(!this.el){
24263             return;
24264         }
24265         var a = this.adjusts, d = this.el.dom, s = d.style;
24266         var iea = 0;
24267         s.left = (l+a.l)+"px";
24268         s.top = (t+a.t)+"px";
24269         var sw = (w+a.w), sh = (h+a.h), sws = sw +"px", shs = sh + "px";
24270  
24271         if(s.width != sws || s.height != shs){
24272             s.width = sws;
24273             s.height = shs;
24274             if(!Roo.isIE){
24275                 var cn = d.childNodes;
24276                 var sww = Math.max(0, (sw-12))+"px";
24277                 cn[0].childNodes[1].style.width = sww;
24278                 cn[1].childNodes[1].style.width = sww;
24279                 cn[2].childNodes[1].style.width = sww;
24280                 cn[1].style.height = Math.max(0, (sh-12))+"px";
24281             }
24282         }
24283     },
24284
24285     /**
24286      * Hides this shadow
24287      */
24288     hide : function(){
24289         if(this.el){
24290             this.el.dom.style.display = "none";
24291             Roo.Shadow.Pool.push(this.el);
24292             delete this.el;
24293         }
24294     },
24295
24296     /**
24297      * Adjust the z-index of this shadow
24298      * @param {Number} zindex The new z-index
24299      */
24300     setZIndex : function(z){
24301         this.zIndex = z;
24302         if(this.el){
24303             this.el.setStyle("z-index", z);
24304         }
24305     }
24306 };
24307
24308 // Private utility class that manages the internal Shadow cache
24309 Roo.Shadow.Pool = function(){
24310     var p = [];
24311     var markup = Roo.isIE ?
24312                  '<div class="x-ie-shadow"></div>' :
24313                  '<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>';
24314     return {
24315         pull : function(){
24316             var sh = p.shift();
24317             if(!sh){
24318                 sh = Roo.get(Roo.DomHelper.insertHtml("beforeBegin", document.body.firstChild, markup));
24319                 sh.autoBoxAdjust = false;
24320             }
24321             return sh;
24322         },
24323
24324         push : function(sh){
24325             p.push(sh);
24326         }
24327     };
24328 }();/*
24329  * Based on:
24330  * Ext JS Library 1.1.1
24331  * Copyright(c) 2006-2007, Ext JS, LLC.
24332  *
24333  * Originally Released Under LGPL - original licence link has changed is not relivant.
24334  *
24335  * Fork - LGPL
24336  * <script type="text/javascript">
24337  */
24338
24339
24340 /**
24341  * @class Roo.SplitBar
24342  * @extends Roo.util.Observable
24343  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
24344  * <br><br>
24345  * Usage:
24346  * <pre><code>
24347 var split = new Roo.SplitBar("elementToDrag", "elementToSize",
24348                    Roo.SplitBar.HORIZONTAL, Roo.SplitBar.LEFT);
24349 split.setAdapter(new Roo.SplitBar.AbsoluteLayoutAdapter("container"));
24350 split.minSize = 100;
24351 split.maxSize = 600;
24352 split.animate = true;
24353 split.on('moved', splitterMoved);
24354 </code></pre>
24355  * @constructor
24356  * Create a new SplitBar
24357  * @param {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
24358  * @param {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
24359  * @param {Number} orientation (optional) Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
24360  * @param {Number} placement (optional) Either Roo.SplitBar.LEFT or Roo.SplitBar.RIGHT for horizontal or  
24361                         Roo.SplitBar.TOP or Roo.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
24362                         position of the SplitBar).
24363  */
24364 Roo.SplitBar = function(dragElement, resizingElement, orientation, placement, existingProxy){
24365     
24366     /** @private */
24367     this.el = Roo.get(dragElement, true);
24368     this.el.dom.unselectable = "on";
24369     /** @private */
24370     this.resizingEl = Roo.get(resizingElement, true);
24371
24372     /**
24373      * @private
24374      * The orientation of the split. Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
24375      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
24376      * @type Number
24377      */
24378     this.orientation = orientation || Roo.SplitBar.HORIZONTAL;
24379     
24380     /**
24381      * The minimum size of the resizing element. (Defaults to 0)
24382      * @type Number
24383      */
24384     this.minSize = 0;
24385     
24386     /**
24387      * The maximum size of the resizing element. (Defaults to 2000)
24388      * @type Number
24389      */
24390     this.maxSize = 2000;
24391     
24392     /**
24393      * Whether to animate the transition to the new size
24394      * @type Boolean
24395      */
24396     this.animate = false;
24397     
24398     /**
24399      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
24400      * @type Boolean
24401      */
24402     this.useShim = false;
24403     
24404     /** @private */
24405     this.shim = null;
24406     
24407     if(!existingProxy){
24408         /** @private */
24409         this.proxy = Roo.SplitBar.createProxy(this.orientation);
24410     }else{
24411         this.proxy = Roo.get(existingProxy).dom;
24412     }
24413     /** @private */
24414     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
24415     
24416     /** @private */
24417     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
24418     
24419     /** @private */
24420     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
24421     
24422     /** @private */
24423     this.dragSpecs = {};
24424     
24425     /**
24426      * @private The adapter to use to positon and resize elements
24427      */
24428     this.adapter = new Roo.SplitBar.BasicLayoutAdapter();
24429     this.adapter.init(this);
24430     
24431     if(this.orientation == Roo.SplitBar.HORIZONTAL){
24432         /** @private */
24433         this.placement = placement || (this.el.getX() > this.resizingEl.getX() ? Roo.SplitBar.LEFT : Roo.SplitBar.RIGHT);
24434         this.el.addClass("x-splitbar-h");
24435     }else{
24436         /** @private */
24437         this.placement = placement || (this.el.getY() > this.resizingEl.getY() ? Roo.SplitBar.TOP : Roo.SplitBar.BOTTOM);
24438         this.el.addClass("x-splitbar-v");
24439     }
24440     
24441     this.addEvents({
24442         /**
24443          * @event resize
24444          * Fires when the splitter is moved (alias for {@link #event-moved})
24445          * @param {Roo.SplitBar} this
24446          * @param {Number} newSize the new width or height
24447          */
24448         "resize" : true,
24449         /**
24450          * @event moved
24451          * Fires when the splitter is moved
24452          * @param {Roo.SplitBar} this
24453          * @param {Number} newSize the new width or height
24454          */
24455         "moved" : true,
24456         /**
24457          * @event beforeresize
24458          * Fires before the splitter is dragged
24459          * @param {Roo.SplitBar} this
24460          */
24461         "beforeresize" : true,
24462
24463         "beforeapply" : true
24464     });
24465
24466     Roo.util.Observable.call(this);
24467 };
24468
24469 Roo.extend(Roo.SplitBar, Roo.util.Observable, {
24470     onStartProxyDrag : function(x, y){
24471         this.fireEvent("beforeresize", this);
24472         if(!this.overlay){
24473             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "x-drag-overlay", html: "&#160;"}, true);
24474             o.unselectable();
24475             o.enableDisplayMode("block");
24476             // all splitbars share the same overlay
24477             Roo.SplitBar.prototype.overlay = o;
24478         }
24479         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
24480         this.overlay.show();
24481         Roo.get(this.proxy).setDisplayed("block");
24482         var size = this.adapter.getElementSize(this);
24483         this.activeMinSize = this.getMinimumSize();;
24484         this.activeMaxSize = this.getMaximumSize();;
24485         var c1 = size - this.activeMinSize;
24486         var c2 = Math.max(this.activeMaxSize - size, 0);
24487         if(this.orientation == Roo.SplitBar.HORIZONTAL){
24488             this.dd.resetConstraints();
24489             this.dd.setXConstraint(
24490                 this.placement == Roo.SplitBar.LEFT ? c1 : c2, 
24491                 this.placement == Roo.SplitBar.LEFT ? c2 : c1
24492             );
24493             this.dd.setYConstraint(0, 0);
24494         }else{
24495             this.dd.resetConstraints();
24496             this.dd.setXConstraint(0, 0);
24497             this.dd.setYConstraint(
24498                 this.placement == Roo.SplitBar.TOP ? c1 : c2, 
24499                 this.placement == Roo.SplitBar.TOP ? c2 : c1
24500             );
24501          }
24502         this.dragSpecs.startSize = size;
24503         this.dragSpecs.startPoint = [x, y];
24504         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
24505     },
24506     
24507     /** 
24508      * @private Called after the drag operation by the DDProxy
24509      */
24510     onEndProxyDrag : function(e){
24511         Roo.get(this.proxy).setDisplayed(false);
24512         var endPoint = Roo.lib.Event.getXY(e);
24513         if(this.overlay){
24514             this.overlay.hide();
24515         }
24516         var newSize;
24517         if(this.orientation == Roo.SplitBar.HORIZONTAL){
24518             newSize = this.dragSpecs.startSize + 
24519                 (this.placement == Roo.SplitBar.LEFT ?
24520                     endPoint[0] - this.dragSpecs.startPoint[0] :
24521                     this.dragSpecs.startPoint[0] - endPoint[0]
24522                 );
24523         }else{
24524             newSize = this.dragSpecs.startSize + 
24525                 (this.placement == Roo.SplitBar.TOP ?
24526                     endPoint[1] - this.dragSpecs.startPoint[1] :
24527                     this.dragSpecs.startPoint[1] - endPoint[1]
24528                 );
24529         }
24530         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
24531         if(newSize != this.dragSpecs.startSize){
24532             if(this.fireEvent('beforeapply', this, newSize) !== false){
24533                 this.adapter.setElementSize(this, newSize);
24534                 this.fireEvent("moved", this, newSize);
24535                 this.fireEvent("resize", this, newSize);
24536             }
24537         }
24538     },
24539     
24540     /**
24541      * Get the adapter this SplitBar uses
24542      * @return The adapter object
24543      */
24544     getAdapter : function(){
24545         return this.adapter;
24546     },
24547     
24548     /**
24549      * Set the adapter this SplitBar uses
24550      * @param {Object} adapter A SplitBar adapter object
24551      */
24552     setAdapter : function(adapter){
24553         this.adapter = adapter;
24554         this.adapter.init(this);
24555     },
24556     
24557     /**
24558      * Gets the minimum size for the resizing element
24559      * @return {Number} The minimum size
24560      */
24561     getMinimumSize : function(){
24562         return this.minSize;
24563     },
24564     
24565     /**
24566      * Sets the minimum size for the resizing element
24567      * @param {Number} minSize The minimum size
24568      */
24569     setMinimumSize : function(minSize){
24570         this.minSize = minSize;
24571     },
24572     
24573     /**
24574      * Gets the maximum size for the resizing element
24575      * @return {Number} The maximum size
24576      */
24577     getMaximumSize : function(){
24578         return this.maxSize;
24579     },
24580     
24581     /**
24582      * Sets the maximum size for the resizing element
24583      * @param {Number} maxSize The maximum size
24584      */
24585     setMaximumSize : function(maxSize){
24586         this.maxSize = maxSize;
24587     },
24588     
24589     /**
24590      * Sets the initialize size for the resizing element
24591      * @param {Number} size The initial size
24592      */
24593     setCurrentSize : function(size){
24594         var oldAnimate = this.animate;
24595         this.animate = false;
24596         this.adapter.setElementSize(this, size);
24597         this.animate = oldAnimate;
24598     },
24599     
24600     /**
24601      * Destroy this splitbar. 
24602      * @param {Boolean} removeEl True to remove the element
24603      */
24604     destroy : function(removeEl){
24605         if(this.shim){
24606             this.shim.remove();
24607         }
24608         this.dd.unreg();
24609         this.proxy.parentNode.removeChild(this.proxy);
24610         if(removeEl){
24611             this.el.remove();
24612         }
24613     }
24614 });
24615
24616 /**
24617  * @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.
24618  */
24619 Roo.SplitBar.createProxy = function(dir){
24620     var proxy = new Roo.Element(document.createElement("div"));
24621     proxy.unselectable();
24622     var cls = 'x-splitbar-proxy';
24623     proxy.addClass(cls + ' ' + (dir == Roo.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
24624     document.body.appendChild(proxy.dom);
24625     return proxy.dom;
24626 };
24627
24628 /** 
24629  * @class Roo.SplitBar.BasicLayoutAdapter
24630  * Default Adapter. It assumes the splitter and resizing element are not positioned
24631  * elements and only gets/sets the width of the element. Generally used for table based layouts.
24632  */
24633 Roo.SplitBar.BasicLayoutAdapter = function(){
24634 };
24635
24636 Roo.SplitBar.BasicLayoutAdapter.prototype = {
24637     // do nothing for now
24638     init : function(s){
24639     
24640     },
24641     /**
24642      * Called before drag operations to get the current size of the resizing element. 
24643      * @param {Roo.SplitBar} s The SplitBar using this adapter
24644      */
24645      getElementSize : function(s){
24646         if(s.orientation == Roo.SplitBar.HORIZONTAL){
24647             return s.resizingEl.getWidth();
24648         }else{
24649             return s.resizingEl.getHeight();
24650         }
24651     },
24652     
24653     /**
24654      * Called after drag operations to set the size of the resizing element.
24655      * @param {Roo.SplitBar} s The SplitBar using this adapter
24656      * @param {Number} newSize The new size to set
24657      * @param {Function} onComplete A function to be invoked when resizing is complete
24658      */
24659     setElementSize : function(s, newSize, onComplete){
24660         if(s.orientation == Roo.SplitBar.HORIZONTAL){
24661             if(!s.animate){
24662                 s.resizingEl.setWidth(newSize);
24663                 if(onComplete){
24664                     onComplete(s, newSize);
24665                 }
24666             }else{
24667                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
24668             }
24669         }else{
24670             
24671             if(!s.animate){
24672                 s.resizingEl.setHeight(newSize);
24673                 if(onComplete){
24674                     onComplete(s, newSize);
24675                 }
24676             }else{
24677                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
24678             }
24679         }
24680     }
24681 };
24682
24683 /** 
24684  *@class Roo.SplitBar.AbsoluteLayoutAdapter
24685  * @extends Roo.SplitBar.BasicLayoutAdapter
24686  * Adapter that  moves the splitter element to align with the resized sizing element. 
24687  * Used with an absolute positioned SplitBar.
24688  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
24689  * document.body, make sure you assign an id to the body element.
24690  */
24691 Roo.SplitBar.AbsoluteLayoutAdapter = function(container){
24692     this.basic = new Roo.SplitBar.BasicLayoutAdapter();
24693     this.container = Roo.get(container);
24694 };
24695
24696 Roo.SplitBar.AbsoluteLayoutAdapter.prototype = {
24697     init : function(s){
24698         this.basic.init(s);
24699     },
24700     
24701     getElementSize : function(s){
24702         return this.basic.getElementSize(s);
24703     },
24704     
24705     setElementSize : function(s, newSize, onComplete){
24706         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
24707     },
24708     
24709     moveSplitter : function(s){
24710         var yes = Roo.SplitBar;
24711         switch(s.placement){
24712             case yes.LEFT:
24713                 s.el.setX(s.resizingEl.getRight());
24714                 break;
24715             case yes.RIGHT:
24716                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
24717                 break;
24718             case yes.TOP:
24719                 s.el.setY(s.resizingEl.getBottom());
24720                 break;
24721             case yes.BOTTOM:
24722                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
24723                 break;
24724         }
24725     }
24726 };
24727
24728 /**
24729  * Orientation constant - Create a vertical SplitBar
24730  * @static
24731  * @type Number
24732  */
24733 Roo.SplitBar.VERTICAL = 1;
24734
24735 /**
24736  * Orientation constant - Create a horizontal SplitBar
24737  * @static
24738  * @type Number
24739  */
24740 Roo.SplitBar.HORIZONTAL = 2;
24741
24742 /**
24743  * Placement constant - The resizing element is to the left of the splitter element
24744  * @static
24745  * @type Number
24746  */
24747 Roo.SplitBar.LEFT = 1;
24748
24749 /**
24750  * Placement constant - The resizing element is to the right of the splitter element
24751  * @static
24752  * @type Number
24753  */
24754 Roo.SplitBar.RIGHT = 2;
24755
24756 /**
24757  * Placement constant - The resizing element is positioned above the splitter element
24758  * @static
24759  * @type Number
24760  */
24761 Roo.SplitBar.TOP = 3;
24762
24763 /**
24764  * Placement constant - The resizing element is positioned under splitter element
24765  * @static
24766  * @type Number
24767  */
24768 Roo.SplitBar.BOTTOM = 4;
24769 /*
24770  * Based on:
24771  * Ext JS Library 1.1.1
24772  * Copyright(c) 2006-2007, Ext JS, LLC.
24773  *
24774  * Originally Released Under LGPL - original licence link has changed is not relivant.
24775  *
24776  * Fork - LGPL
24777  * <script type="text/javascript">
24778  */
24779
24780 /**
24781  * @class Roo.View
24782  * @extends Roo.util.Observable
24783  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
24784  * This class also supports single and multi selection modes. <br>
24785  * Create a data model bound view:
24786  <pre><code>
24787  var store = new Roo.data.Store(...);
24788
24789  var view = new Roo.View({
24790     el : "my-element",
24791     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
24792  
24793     singleSelect: true,
24794     selectedClass: "ydataview-selected",
24795     store: store
24796  });
24797
24798  // listen for node click?
24799  view.on("click", function(vw, index, node, e){
24800  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
24801  });
24802
24803  // load XML data
24804  dataModel.load("foobar.xml");
24805  </code></pre>
24806  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
24807  * <br><br>
24808  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
24809  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
24810  * 
24811  * Note: old style constructor is still suported (container, template, config)
24812  * 
24813  * @constructor
24814  * Create a new View
24815  * @param {Object} config The config object
24816  * 
24817  */
24818 Roo.View = function(config, depreciated_tpl, depreciated_config){
24819     
24820     if (typeof(depreciated_tpl) == 'undefined') {
24821         // new way.. - universal constructor.
24822         Roo.apply(this, config);
24823         this.el  = Roo.get(this.el);
24824     } else {
24825         // old format..
24826         this.el  = Roo.get(config);
24827         this.tpl = depreciated_tpl;
24828         Roo.apply(this, depreciated_config);
24829     }
24830     this.wrapEl  = this.el.wrap().wrap();
24831     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
24832     
24833     
24834     if(typeof(this.tpl) == "string"){
24835         this.tpl = new Roo.Template(this.tpl);
24836     } else {
24837         // support xtype ctors..
24838         this.tpl = new Roo.factory(this.tpl, Roo);
24839     }
24840     
24841     
24842     this.tpl.compile();
24843    
24844   
24845     
24846      
24847     /** @private */
24848     this.addEvents({
24849         /**
24850          * @event beforeclick
24851          * Fires before a click is processed. Returns false to cancel the default action.
24852          * @param {Roo.View} this
24853          * @param {Number} index The index of the target node
24854          * @param {HTMLElement} node The target node
24855          * @param {Roo.EventObject} e The raw event object
24856          */
24857             "beforeclick" : true,
24858         /**
24859          * @event click
24860          * Fires when a template node is clicked.
24861          * @param {Roo.View} this
24862          * @param {Number} index The index of the target node
24863          * @param {HTMLElement} node The target node
24864          * @param {Roo.EventObject} e The raw event object
24865          */
24866             "click" : true,
24867         /**
24868          * @event dblclick
24869          * Fires when a template node is double clicked.
24870          * @param {Roo.View} this
24871          * @param {Number} index The index of the target node
24872          * @param {HTMLElement} node The target node
24873          * @param {Roo.EventObject} e The raw event object
24874          */
24875             "dblclick" : true,
24876         /**
24877          * @event contextmenu
24878          * Fires when a template node is right clicked.
24879          * @param {Roo.View} this
24880          * @param {Number} index The index of the target node
24881          * @param {HTMLElement} node The target node
24882          * @param {Roo.EventObject} e The raw event object
24883          */
24884             "contextmenu" : true,
24885         /**
24886          * @event selectionchange
24887          * Fires when the selected nodes change.
24888          * @param {Roo.View} this
24889          * @param {Array} selections Array of the selected nodes
24890          */
24891             "selectionchange" : true,
24892     
24893         /**
24894          * @event beforeselect
24895          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
24896          * @param {Roo.View} this
24897          * @param {HTMLElement} node The node to be selected
24898          * @param {Array} selections Array of currently selected nodes
24899          */
24900             "beforeselect" : true,
24901         /**
24902          * @event preparedata
24903          * Fires on every row to render, to allow you to change the data.
24904          * @param {Roo.View} this
24905          * @param {Object} data to be rendered (change this)
24906          */
24907           "preparedata" : true
24908           
24909           
24910         });
24911
24912
24913
24914     this.el.on({
24915         "click": this.onClick,
24916         "dblclick": this.onDblClick,
24917         "contextmenu": this.onContextMenu,
24918         scope:this
24919     });
24920
24921     this.selections = [];
24922     this.nodes = [];
24923     this.cmp = new Roo.CompositeElementLite([]);
24924     if(this.store){
24925         this.store = Roo.factory(this.store, Roo.data);
24926         this.setStore(this.store, true);
24927     }
24928     
24929     if ( this.footer && this.footer.xtype) {
24930            
24931          var fctr = this.wrapEl.appendChild(document.createElement("div"));
24932         
24933         this.footer.dataSource = this.store
24934         this.footer.container = fctr;
24935         this.footer = Roo.factory(this.footer, Roo);
24936         fctr.insertFirst(this.el);
24937         
24938         // this is a bit insane - as the paging toolbar seems to detach the el..
24939 //        dom.parentNode.parentNode.parentNode
24940          // they get detached?
24941     }
24942     
24943     
24944     Roo.View.superclass.constructor.call(this);
24945     
24946     
24947 };
24948
24949 Roo.extend(Roo.View, Roo.util.Observable, {
24950     
24951      /**
24952      * @cfg {Roo.data.Store} store Data store to load data from.
24953      */
24954     store : false,
24955     
24956     /**
24957      * @cfg {String|Roo.Element} el The container element.
24958      */
24959     el : '',
24960     
24961     /**
24962      * @cfg {String|Roo.Template} tpl The template used by this View 
24963      */
24964     tpl : false,
24965     /**
24966      * @cfg {String} dataName the named area of the template to use as the data area
24967      *                          Works with domtemplates roo-name="name"
24968      */
24969     dataName: false,
24970     /**
24971      * @cfg {String} selectedClass The css class to add to selected nodes
24972      */
24973     selectedClass : "x-view-selected",
24974      /**
24975      * @cfg {String} emptyText The empty text to show when nothing is loaded.
24976      */
24977     emptyText : "",
24978     
24979     /**
24980      * @cfg {String} text to display on mask (default Loading)
24981      */
24982     mask : false,
24983     /**
24984      * @cfg {Boolean} multiSelect Allow multiple selection
24985      */
24986     multiSelect : false,
24987     /**
24988      * @cfg {Boolean} singleSelect Allow single selection
24989      */
24990     singleSelect:  false,
24991     
24992     /**
24993      * @cfg {Boolean} toggleSelect - selecting 
24994      */
24995     toggleSelect : false,
24996     
24997     /**
24998      * Returns the element this view is bound to.
24999      * @return {Roo.Element}
25000      */
25001     getEl : function(){
25002         return this.wrapEl;
25003     },
25004     
25005     
25006
25007     /**
25008      * Refreshes the view. - called by datachanged on the store. - do not call directly.
25009      */
25010     refresh : function(){
25011         Roo.log('refresh');
25012         var t = this.tpl;
25013         
25014         // if we are using something like 'domtemplate', then
25015         // the what gets used is:
25016         // t.applySubtemplate(NAME, data, wrapping data..)
25017         // the outer template then get' applied with
25018         //     the store 'extra data'
25019         // and the body get's added to the
25020         //      roo-name="data" node?
25021         //      <span class='roo-tpl-{name}'></span> ?????
25022         
25023         
25024         
25025         this.clearSelections();
25026         this.el.update("");
25027         var html = [];
25028         var records = this.store.getRange();
25029         if(records.length < 1) {
25030             
25031             // is this valid??  = should it render a template??
25032             
25033             this.el.update(this.emptyText);
25034             return;
25035         }
25036         var el = this.el;
25037         if (this.dataName) {
25038             this.el.update(t.apply(this.store.meta)); //????
25039             el = this.el.child('.roo-tpl-' + this.dataName);
25040         }
25041         
25042         for(var i = 0, len = records.length; i < len; i++){
25043             var data = this.prepareData(records[i].data, i, records[i]);
25044             this.fireEvent("preparedata", this, data, i, records[i]);
25045             html[html.length] = Roo.util.Format.trim(
25046                 this.dataName ?
25047                     t.applySubtemplate(this.dataName, data, this.store.meta) :
25048                     t.apply(data)
25049             );
25050         }
25051         
25052         
25053         
25054         el.update(html.join(""));
25055         this.nodes = el.dom.childNodes;
25056         this.updateIndexes(0);
25057     },
25058     
25059
25060     /**
25061      * Function to override to reformat the data that is sent to
25062      * the template for each node.
25063      * DEPRICATED - use the preparedata event handler.
25064      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
25065      * a JSON object for an UpdateManager bound view).
25066      */
25067     prepareData : function(data, index, record)
25068     {
25069         this.fireEvent("preparedata", this, data, index, record);
25070         return data;
25071     },
25072
25073     onUpdate : function(ds, record){
25074          Roo.log('on update');   
25075         this.clearSelections();
25076         var index = this.store.indexOf(record);
25077         var n = this.nodes[index];
25078         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
25079         n.parentNode.removeChild(n);
25080         this.updateIndexes(index, index);
25081     },
25082
25083     
25084     
25085 // --------- FIXME     
25086     onAdd : function(ds, records, index)
25087     {
25088         Roo.log(['on Add', ds, records, index] );        
25089         this.clearSelections();
25090         if(this.nodes.length == 0){
25091             this.refresh();
25092             return;
25093         }
25094         var n = this.nodes[index];
25095         for(var i = 0, len = records.length; i < len; i++){
25096             var d = this.prepareData(records[i].data, i, records[i]);
25097             if(n){
25098                 this.tpl.insertBefore(n, d);
25099             }else{
25100                 
25101                 this.tpl.append(this.el, d);
25102             }
25103         }
25104         this.updateIndexes(index);
25105     },
25106
25107     onRemove : function(ds, record, index){
25108         Roo.log('onRemove');
25109         this.clearSelections();
25110         var el = this.dataName  ?
25111             this.el.child('.roo-tpl-' + this.dataName) :
25112             this.el; 
25113         
25114         el.dom.removeChild(this.nodes[index]);
25115         this.updateIndexes(index);
25116     },
25117
25118     /**
25119      * Refresh an individual node.
25120      * @param {Number} index
25121      */
25122     refreshNode : function(index){
25123         this.onUpdate(this.store, this.store.getAt(index));
25124     },
25125
25126     updateIndexes : function(startIndex, endIndex){
25127         var ns = this.nodes;
25128         startIndex = startIndex || 0;
25129         endIndex = endIndex || ns.length - 1;
25130         for(var i = startIndex; i <= endIndex; i++){
25131             ns[i].nodeIndex = i;
25132         }
25133     },
25134
25135     /**
25136      * Changes the data store this view uses and refresh the view.
25137      * @param {Store} store
25138      */
25139     setStore : function(store, initial){
25140         if(!initial && this.store){
25141             this.store.un("datachanged", this.refresh);
25142             this.store.un("add", this.onAdd);
25143             this.store.un("remove", this.onRemove);
25144             this.store.un("update", this.onUpdate);
25145             this.store.un("clear", this.refresh);
25146             this.store.un("beforeload", this.onBeforeLoad);
25147             this.store.un("load", this.onLoad);
25148             this.store.un("loadexception", this.onLoad);
25149         }
25150         if(store){
25151           
25152             store.on("datachanged", this.refresh, this);
25153             store.on("add", this.onAdd, this);
25154             store.on("remove", this.onRemove, this);
25155             store.on("update", this.onUpdate, this);
25156             store.on("clear", this.refresh, this);
25157             store.on("beforeload", this.onBeforeLoad, this);
25158             store.on("load", this.onLoad, this);
25159             store.on("loadexception", this.onLoad, this);
25160         }
25161         
25162         if(store){
25163             this.refresh();
25164         }
25165     },
25166     /**
25167      * onbeforeLoad - masks the loading area.
25168      *
25169      */
25170     onBeforeLoad : function(store,opts)
25171     {
25172          Roo.log('onBeforeLoad');   
25173         if (!opts.add) {
25174             this.el.update("");
25175         }
25176         this.el.mask(this.mask ? this.mask : "Loading" ); 
25177     },
25178     onLoad : function ()
25179     {
25180         this.el.unmask();
25181     },
25182     
25183
25184     /**
25185      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
25186      * @param {HTMLElement} node
25187      * @return {HTMLElement} The template node
25188      */
25189     findItemFromChild : function(node){
25190         var el = this.dataName  ?
25191             this.el.child('.roo-tpl-' + this.dataName,true) :
25192             this.el.dom; 
25193         
25194         if(!node || node.parentNode == el){
25195                     return node;
25196             }
25197             var p = node.parentNode;
25198             while(p && p != el){
25199             if(p.parentNode == el){
25200                 return p;
25201             }
25202             p = p.parentNode;
25203         }
25204             return null;
25205     },
25206
25207     /** @ignore */
25208     onClick : function(e){
25209         var item = this.findItemFromChild(e.getTarget());
25210         if(item){
25211             var index = this.indexOf(item);
25212             if(this.onItemClick(item, index, e) !== false){
25213                 this.fireEvent("click", this, index, item, e);
25214             }
25215         }else{
25216             this.clearSelections();
25217         }
25218     },
25219
25220     /** @ignore */
25221     onContextMenu : function(e){
25222         var item = this.findItemFromChild(e.getTarget());
25223         if(item){
25224             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
25225         }
25226     },
25227
25228     /** @ignore */
25229     onDblClick : function(e){
25230         var item = this.findItemFromChild(e.getTarget());
25231         if(item){
25232             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
25233         }
25234     },
25235
25236     onItemClick : function(item, index, e)
25237     {
25238         if(this.fireEvent("beforeclick", this, index, item, e) === false){
25239             return false;
25240         }
25241         if (this.toggleSelect) {
25242             var m = this.isSelected(item) ? 'unselect' : 'select';
25243             Roo.log(m);
25244             var _t = this;
25245             _t[m](item, true, false);
25246             return true;
25247         }
25248         if(this.multiSelect || this.singleSelect){
25249             if(this.multiSelect && e.shiftKey && this.lastSelection){
25250                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
25251             }else{
25252                 this.select(item, this.multiSelect && e.ctrlKey);
25253                 this.lastSelection = item;
25254             }
25255             e.preventDefault();
25256         }
25257         return true;
25258     },
25259
25260     /**
25261      * Get the number of selected nodes.
25262      * @return {Number}
25263      */
25264     getSelectionCount : function(){
25265         return this.selections.length;
25266     },
25267
25268     /**
25269      * Get the currently selected nodes.
25270      * @return {Array} An array of HTMLElements
25271      */
25272     getSelectedNodes : function(){
25273         return this.selections;
25274     },
25275
25276     /**
25277      * Get the indexes of the selected nodes.
25278      * @return {Array}
25279      */
25280     getSelectedIndexes : function(){
25281         var indexes = [], s = this.selections;
25282         for(var i = 0, len = s.length; i < len; i++){
25283             indexes.push(s[i].nodeIndex);
25284         }
25285         return indexes;
25286     },
25287
25288     /**
25289      * Clear all selections
25290      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
25291      */
25292     clearSelections : function(suppressEvent){
25293         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
25294             this.cmp.elements = this.selections;
25295             this.cmp.removeClass(this.selectedClass);
25296             this.selections = [];
25297             if(!suppressEvent){
25298                 this.fireEvent("selectionchange", this, this.selections);
25299             }
25300         }
25301     },
25302
25303     /**
25304      * Returns true if the passed node is selected
25305      * @param {HTMLElement/Number} node The node or node index
25306      * @return {Boolean}
25307      */
25308     isSelected : function(node){
25309         var s = this.selections;
25310         if(s.length < 1){
25311             return false;
25312         }
25313         node = this.getNode(node);
25314         return s.indexOf(node) !== -1;
25315     },
25316
25317     /**
25318      * Selects nodes.
25319      * @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
25320      * @param {Boolean} keepExisting (optional) true to keep existing selections
25321      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
25322      */
25323     select : function(nodeInfo, keepExisting, suppressEvent){
25324         if(nodeInfo instanceof Array){
25325             if(!keepExisting){
25326                 this.clearSelections(true);
25327             }
25328             for(var i = 0, len = nodeInfo.length; i < len; i++){
25329                 this.select(nodeInfo[i], true, true);
25330             }
25331             return;
25332         } 
25333         var node = this.getNode(nodeInfo);
25334         if(!node || this.isSelected(node)){
25335             return; // already selected.
25336         }
25337         if(!keepExisting){
25338             this.clearSelections(true);
25339         }
25340         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
25341             Roo.fly(node).addClass(this.selectedClass);
25342             this.selections.push(node);
25343             if(!suppressEvent){
25344                 this.fireEvent("selectionchange", this, this.selections);
25345             }
25346         }
25347         
25348         
25349     },
25350       /**
25351      * Unselects nodes.
25352      * @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
25353      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
25354      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
25355      */
25356     unselect : function(nodeInfo, keepExisting, suppressEvent)
25357     {
25358         if(nodeInfo instanceof Array){
25359             Roo.each(this.selections, function(s) {
25360                 this.unselect(s, nodeInfo);
25361             }, this);
25362             return;
25363         }
25364         var node = this.getNode(nodeInfo);
25365         if(!node || !this.isSelected(node)){
25366             Roo.log("not selected");
25367             return; // not selected.
25368         }
25369         // fireevent???
25370         var ns = [];
25371         Roo.each(this.selections, function(s) {
25372             if (s == node ) {
25373                 Roo.fly(node).removeClass(this.selectedClass);
25374
25375                 return;
25376             }
25377             ns.push(s);
25378         },this);
25379         
25380         this.selections= ns;
25381         this.fireEvent("selectionchange", this, this.selections);
25382     },
25383
25384     /**
25385      * Gets a template node.
25386      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
25387      * @return {HTMLElement} The node or null if it wasn't found
25388      */
25389     getNode : function(nodeInfo){
25390         if(typeof nodeInfo == "string"){
25391             return document.getElementById(nodeInfo);
25392         }else if(typeof nodeInfo == "number"){
25393             return this.nodes[nodeInfo];
25394         }
25395         return nodeInfo;
25396     },
25397
25398     /**
25399      * Gets a range template nodes.
25400      * @param {Number} startIndex
25401      * @param {Number} endIndex
25402      * @return {Array} An array of nodes
25403      */
25404     getNodes : function(start, end){
25405         var ns = this.nodes;
25406         start = start || 0;
25407         end = typeof end == "undefined" ? ns.length - 1 : end;
25408         var nodes = [];
25409         if(start <= end){
25410             for(var i = start; i <= end; i++){
25411                 nodes.push(ns[i]);
25412             }
25413         } else{
25414             for(var i = start; i >= end; i--){
25415                 nodes.push(ns[i]);
25416             }
25417         }
25418         return nodes;
25419     },
25420
25421     /**
25422      * Finds the index of the passed node
25423      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
25424      * @return {Number} The index of the node or -1
25425      */
25426     indexOf : function(node){
25427         node = this.getNode(node);
25428         if(typeof node.nodeIndex == "number"){
25429             return node.nodeIndex;
25430         }
25431         var ns = this.nodes;
25432         for(var i = 0, len = ns.length; i < len; i++){
25433             if(ns[i] == node){
25434                 return i;
25435             }
25436         }
25437         return -1;
25438     }
25439 });
25440 /*
25441  * Based on:
25442  * Ext JS Library 1.1.1
25443  * Copyright(c) 2006-2007, Ext JS, LLC.
25444  *
25445  * Originally Released Under LGPL - original licence link has changed is not relivant.
25446  *
25447  * Fork - LGPL
25448  * <script type="text/javascript">
25449  */
25450
25451 /**
25452  * @class Roo.JsonView
25453  * @extends Roo.View
25454  * Shortcut class to create a JSON + {@link Roo.UpdateManager} template view. Usage:
25455 <pre><code>
25456 var view = new Roo.JsonView({
25457     container: "my-element",
25458     tpl: '&lt;div id="{id}"&gt;{foo} - {bar}&lt;/div&gt;', // auto create template
25459     multiSelect: true, 
25460     jsonRoot: "data" 
25461 });
25462
25463 // listen for node click?
25464 view.on("click", function(vw, index, node, e){
25465     alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
25466 });
25467
25468 // direct load of JSON data
25469 view.load("foobar.php");
25470
25471 // Example from my blog list
25472 var tpl = new Roo.Template(
25473     '&lt;div class="entry"&gt;' +
25474     '&lt;a class="entry-title" href="{link}"&gt;{title}&lt;/a&gt;' +
25475     "&lt;h4&gt;{date} by {author} | {comments} Comments&lt;/h4&gt;{description}" +
25476     "&lt;/div&gt;&lt;hr /&gt;"
25477 );
25478
25479 var moreView = new Roo.JsonView({
25480     container :  "entry-list", 
25481     template : tpl,
25482     jsonRoot: "posts"
25483 });
25484 moreView.on("beforerender", this.sortEntries, this);
25485 moreView.load({
25486     url: "/blog/get-posts.php",
25487     params: "allposts=true",
25488     text: "Loading Blog Entries..."
25489 });
25490 </code></pre>
25491
25492 * Note: old code is supported with arguments : (container, template, config)
25493
25494
25495  * @constructor
25496  * Create a new JsonView
25497  * 
25498  * @param {Object} config The config object
25499  * 
25500  */
25501 Roo.JsonView = function(config, depreciated_tpl, depreciated_config){
25502     
25503     
25504     Roo.JsonView.superclass.constructor.call(this, config, depreciated_tpl, depreciated_config);
25505
25506     var um = this.el.getUpdateManager();
25507     um.setRenderer(this);
25508     um.on("update", this.onLoad, this);
25509     um.on("failure", this.onLoadException, this);
25510
25511     /**
25512      * @event beforerender
25513      * Fires before rendering of the downloaded JSON data.
25514      * @param {Roo.JsonView} this
25515      * @param {Object} data The JSON data loaded
25516      */
25517     /**
25518      * @event load
25519      * Fires when data is loaded.
25520      * @param {Roo.JsonView} this
25521      * @param {Object} data The JSON data loaded
25522      * @param {Object} response The raw Connect response object
25523      */
25524     /**
25525      * @event loadexception
25526      * Fires when loading fails.
25527      * @param {Roo.JsonView} this
25528      * @param {Object} response The raw Connect response object
25529      */
25530     this.addEvents({
25531         'beforerender' : true,
25532         'load' : true,
25533         'loadexception' : true
25534     });
25535 };
25536 Roo.extend(Roo.JsonView, Roo.View, {
25537     /**
25538      * @type {String} The root property in the loaded JSON object that contains the data
25539      */
25540     jsonRoot : "",
25541
25542     /**
25543      * Refreshes the view.
25544      */
25545     refresh : function(){
25546         this.clearSelections();
25547         this.el.update("");
25548         var html = [];
25549         var o = this.jsonData;
25550         if(o && o.length > 0){
25551             for(var i = 0, len = o.length; i < len; i++){
25552                 var data = this.prepareData(o[i], i, o);
25553                 html[html.length] = this.tpl.apply(data);
25554             }
25555         }else{
25556             html.push(this.emptyText);
25557         }
25558         this.el.update(html.join(""));
25559         this.nodes = this.el.dom.childNodes;
25560         this.updateIndexes(0);
25561     },
25562
25563     /**
25564      * 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.
25565      * @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:
25566      <pre><code>
25567      view.load({
25568          url: "your-url.php",
25569          params: {param1: "foo", param2: "bar"}, // or a URL encoded string
25570          callback: yourFunction,
25571          scope: yourObject, //(optional scope)
25572          discardUrl: false,
25573          nocache: false,
25574          text: "Loading...",
25575          timeout: 30,
25576          scripts: false
25577      });
25578      </code></pre>
25579      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
25580      * 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.
25581      * @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}
25582      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
25583      * @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.
25584      */
25585     load : function(){
25586         var um = this.el.getUpdateManager();
25587         um.update.apply(um, arguments);
25588     },
25589
25590     render : function(el, response){
25591         this.clearSelections();
25592         this.el.update("");
25593         var o;
25594         try{
25595             o = Roo.util.JSON.decode(response.responseText);
25596             if(this.jsonRoot){
25597                 
25598                 o = o[this.jsonRoot];
25599             }
25600         } catch(e){
25601         }
25602         /**
25603          * The current JSON data or null
25604          */
25605         this.jsonData = o;
25606         this.beforeRender();
25607         this.refresh();
25608     },
25609
25610 /**
25611  * Get the number of records in the current JSON dataset
25612  * @return {Number}
25613  */
25614     getCount : function(){
25615         return this.jsonData ? this.jsonData.length : 0;
25616     },
25617
25618 /**
25619  * Returns the JSON object for the specified node(s)
25620  * @param {HTMLElement/Array} node The node or an array of nodes
25621  * @return {Object/Array} If you pass in an array, you get an array back, otherwise
25622  * you get the JSON object for the node
25623  */
25624     getNodeData : function(node){
25625         if(node instanceof Array){
25626             var data = [];
25627             for(var i = 0, len = node.length; i < len; i++){
25628                 data.push(this.getNodeData(node[i]));
25629             }
25630             return data;
25631         }
25632         return this.jsonData[this.indexOf(node)] || null;
25633     },
25634
25635     beforeRender : function(){
25636         this.snapshot = this.jsonData;
25637         if(this.sortInfo){
25638             this.sort.apply(this, this.sortInfo);
25639         }
25640         this.fireEvent("beforerender", this, this.jsonData);
25641     },
25642
25643     onLoad : function(el, o){
25644         this.fireEvent("load", this, this.jsonData, o);
25645     },
25646
25647     onLoadException : function(el, o){
25648         this.fireEvent("loadexception", this, o);
25649     },
25650
25651 /**
25652  * Filter the data by a specific property.
25653  * @param {String} property A property on your JSON objects
25654  * @param {String/RegExp} value Either string that the property values
25655  * should start with, or a RegExp to test against the property
25656  */
25657     filter : function(property, value){
25658         if(this.jsonData){
25659             var data = [];
25660             var ss = this.snapshot;
25661             if(typeof value == "string"){
25662                 var vlen = value.length;
25663                 if(vlen == 0){
25664                     this.clearFilter();
25665                     return;
25666                 }
25667                 value = value.toLowerCase();
25668                 for(var i = 0, len = ss.length; i < len; i++){
25669                     var o = ss[i];
25670                     if(o[property].substr(0, vlen).toLowerCase() == value){
25671                         data.push(o);
25672                     }
25673                 }
25674             } else if(value.exec){ // regex?
25675                 for(var i = 0, len = ss.length; i < len; i++){
25676                     var o = ss[i];
25677                     if(value.test(o[property])){
25678                         data.push(o);
25679                     }
25680                 }
25681             } else{
25682                 return;
25683             }
25684             this.jsonData = data;
25685             this.refresh();
25686         }
25687     },
25688
25689 /**
25690  * Filter by a function. The passed function will be called with each
25691  * object in the current dataset. If the function returns true the value is kept,
25692  * otherwise it is filtered.
25693  * @param {Function} fn
25694  * @param {Object} scope (optional) The scope of the function (defaults to this JsonView)
25695  */
25696     filterBy : function(fn, scope){
25697         if(this.jsonData){
25698             var data = [];
25699             var ss = this.snapshot;
25700             for(var i = 0, len = ss.length; i < len; i++){
25701                 var o = ss[i];
25702                 if(fn.call(scope || this, o)){
25703                     data.push(o);
25704                 }
25705             }
25706             this.jsonData = data;
25707             this.refresh();
25708         }
25709     },
25710
25711 /**
25712  * Clears the current filter.
25713  */
25714     clearFilter : function(){
25715         if(this.snapshot && this.jsonData != this.snapshot){
25716             this.jsonData = this.snapshot;
25717             this.refresh();
25718         }
25719     },
25720
25721
25722 /**
25723  * Sorts the data for this view and refreshes it.
25724  * @param {String} property A property on your JSON objects to sort on
25725  * @param {String} direction (optional) "desc" or "asc" (defaults to "asc")
25726  * @param {Function} sortType (optional) A function to call to convert the data to a sortable value.
25727  */
25728     sort : function(property, dir, sortType){
25729         this.sortInfo = Array.prototype.slice.call(arguments, 0);
25730         if(this.jsonData){
25731             var p = property;
25732             var dsc = dir && dir.toLowerCase() == "desc";
25733             var f = function(o1, o2){
25734                 var v1 = sortType ? sortType(o1[p]) : o1[p];
25735                 var v2 = sortType ? sortType(o2[p]) : o2[p];
25736                 ;
25737                 if(v1 < v2){
25738                     return dsc ? +1 : -1;
25739                 } else if(v1 > v2){
25740                     return dsc ? -1 : +1;
25741                 } else{
25742                     return 0;
25743                 }
25744             };
25745             this.jsonData.sort(f);
25746             this.refresh();
25747             if(this.jsonData != this.snapshot){
25748                 this.snapshot.sort(f);
25749             }
25750         }
25751     }
25752 });/*
25753  * Based on:
25754  * Ext JS Library 1.1.1
25755  * Copyright(c) 2006-2007, Ext JS, LLC.
25756  *
25757  * Originally Released Under LGPL - original licence link has changed is not relivant.
25758  *
25759  * Fork - LGPL
25760  * <script type="text/javascript">
25761  */
25762  
25763
25764 /**
25765  * @class Roo.ColorPalette
25766  * @extends Roo.Component
25767  * Simple color palette class for choosing colors.  The palette can be rendered to any container.<br />
25768  * Here's an example of typical usage:
25769  * <pre><code>
25770 var cp = new Roo.ColorPalette({value:'993300'});  // initial selected color
25771 cp.render('my-div');
25772
25773 cp.on('select', function(palette, selColor){
25774     // do something with selColor
25775 });
25776 </code></pre>
25777  * @constructor
25778  * Create a new ColorPalette
25779  * @param {Object} config The config object
25780  */
25781 Roo.ColorPalette = function(config){
25782     Roo.ColorPalette.superclass.constructor.call(this, config);
25783     this.addEvents({
25784         /**
25785              * @event select
25786              * Fires when a color is selected
25787              * @param {ColorPalette} this
25788              * @param {String} color The 6-digit color hex code (without the # symbol)
25789              */
25790         select: true
25791     });
25792
25793     if(this.handler){
25794         this.on("select", this.handler, this.scope, true);
25795     }
25796 };
25797 Roo.extend(Roo.ColorPalette, Roo.Component, {
25798     /**
25799      * @cfg {String} itemCls
25800      * The CSS class to apply to the containing element (defaults to "x-color-palette")
25801      */
25802     itemCls : "x-color-palette",
25803     /**
25804      * @cfg {String} value
25805      * The initial color to highlight (should be a valid 6-digit color hex code without the # symbol).  Note that
25806      * the hex codes are case-sensitive.
25807      */
25808     value : null,
25809     clickEvent:'click',
25810     // private
25811     ctype: "Roo.ColorPalette",
25812
25813     /**
25814      * @cfg {Boolean} allowReselect If set to true then reselecting a color that is already selected fires the selection event
25815      */
25816     allowReselect : false,
25817
25818     /**
25819      * <p>An array of 6-digit color hex code strings (without the # symbol).  This array can contain any number
25820      * of colors, and each hex code should be unique.  The width of the palette is controlled via CSS by adjusting
25821      * the width property of the 'x-color-palette' class (or assigning a custom class), so you can balance the number
25822      * of colors with the width setting until the box is symmetrical.</p>
25823      * <p>You can override individual colors if needed:</p>
25824      * <pre><code>
25825 var cp = new Roo.ColorPalette();
25826 cp.colors[0] = "FF0000";  // change the first box to red
25827 </code></pre>
25828
25829 Or you can provide a custom array of your own for complete control:
25830 <pre><code>
25831 var cp = new Roo.ColorPalette();
25832 cp.colors = ["000000", "993300", "333300"];
25833 </code></pre>
25834      * @type Array
25835      */
25836     colors : [
25837         "000000", "993300", "333300", "003300", "003366", "000080", "333399", "333333",
25838         "800000", "FF6600", "808000", "008000", "008080", "0000FF", "666699", "808080",
25839         "FF0000", "FF9900", "99CC00", "339966", "33CCCC", "3366FF", "800080", "969696",
25840         "FF00FF", "FFCC00", "FFFF00", "00FF00", "00FFFF", "00CCFF", "993366", "C0C0C0",
25841         "FF99CC", "FFCC99", "FFFF99", "CCFFCC", "CCFFFF", "99CCFF", "CC99FF", "FFFFFF"
25842     ],
25843
25844     // private
25845     onRender : function(container, position){
25846         var t = new Roo.MasterTemplate(
25847             '<tpl><a href="#" class="color-{0}" hidefocus="on"><em><span style="background:#{0}" unselectable="on">&#160;</span></em></a></tpl>'
25848         );
25849         var c = this.colors;
25850         for(var i = 0, len = c.length; i < len; i++){
25851             t.add([c[i]]);
25852         }
25853         var el = document.createElement("div");
25854         el.className = this.itemCls;
25855         t.overwrite(el);
25856         container.dom.insertBefore(el, position);
25857         this.el = Roo.get(el);
25858         this.el.on(this.clickEvent, this.handleClick,  this, {delegate: "a"});
25859         if(this.clickEvent != 'click'){
25860             this.el.on('click', Roo.emptyFn,  this, {delegate: "a", preventDefault:true});
25861         }
25862     },
25863
25864     // private
25865     afterRender : function(){
25866         Roo.ColorPalette.superclass.afterRender.call(this);
25867         if(this.value){
25868             var s = this.value;
25869             this.value = null;
25870             this.select(s);
25871         }
25872     },
25873
25874     // private
25875     handleClick : function(e, t){
25876         e.preventDefault();
25877         if(!this.disabled){
25878             var c = t.className.match(/(?:^|\s)color-(.{6})(?:\s|$)/)[1];
25879             this.select(c.toUpperCase());
25880         }
25881     },
25882
25883     /**
25884      * Selects the specified color in the palette (fires the select event)
25885      * @param {String} color A valid 6-digit color hex code (# will be stripped if included)
25886      */
25887     select : function(color){
25888         color = color.replace("#", "");
25889         if(color != this.value || this.allowReselect){
25890             var el = this.el;
25891             if(this.value){
25892                 el.child("a.color-"+this.value).removeClass("x-color-palette-sel");
25893             }
25894             el.child("a.color-"+color).addClass("x-color-palette-sel");
25895             this.value = color;
25896             this.fireEvent("select", this, color);
25897         }
25898     }
25899 });/*
25900  * Based on:
25901  * Ext JS Library 1.1.1
25902  * Copyright(c) 2006-2007, Ext JS, LLC.
25903  *
25904  * Originally Released Under LGPL - original licence link has changed is not relivant.
25905  *
25906  * Fork - LGPL
25907  * <script type="text/javascript">
25908  */
25909  
25910 /**
25911  * @class Roo.DatePicker
25912  * @extends Roo.Component
25913  * Simple date picker class.
25914  * @constructor
25915  * Create a new DatePicker
25916  * @param {Object} config The config object
25917  */
25918 Roo.DatePicker = function(config){
25919     Roo.DatePicker.superclass.constructor.call(this, config);
25920
25921     this.value = config && config.value ?
25922                  config.value.clearTime() : new Date().clearTime();
25923
25924     this.addEvents({
25925         /**
25926              * @event select
25927              * Fires when a date is selected
25928              * @param {DatePicker} this
25929              * @param {Date} date The selected date
25930              */
25931         'select': true,
25932         /**
25933              * @event monthchange
25934              * Fires when the displayed month changes 
25935              * @param {DatePicker} this
25936              * @param {Date} date The selected month
25937              */
25938         'monthchange': true
25939     });
25940
25941     if(this.handler){
25942         this.on("select", this.handler,  this.scope || this);
25943     }
25944     // build the disabledDatesRE
25945     if(!this.disabledDatesRE && this.disabledDates){
25946         var dd = this.disabledDates;
25947         var re = "(?:";
25948         for(var i = 0; i < dd.length; i++){
25949             re += dd[i];
25950             if(i != dd.length-1) re += "|";
25951         }
25952         this.disabledDatesRE = new RegExp(re + ")");
25953     }
25954 };
25955
25956 Roo.extend(Roo.DatePicker, Roo.Component, {
25957     /**
25958      * @cfg {String} todayText
25959      * The text to display on the button that selects the current date (defaults to "Today")
25960      */
25961     todayText : "Today",
25962     /**
25963      * @cfg {String} okText
25964      * The text to display on the ok button
25965      */
25966     okText : "&#160;OK&#160;", // &#160; to give the user extra clicking room
25967     /**
25968      * @cfg {String} cancelText
25969      * The text to display on the cancel button
25970      */
25971     cancelText : "Cancel",
25972     /**
25973      * @cfg {String} todayTip
25974      * The tooltip to display for the button that selects the current date (defaults to "{current date} (Spacebar)")
25975      */
25976     todayTip : "{0} (Spacebar)",
25977     /**
25978      * @cfg {Date} minDate
25979      * Minimum allowable date (JavaScript date object, defaults to null)
25980      */
25981     minDate : null,
25982     /**
25983      * @cfg {Date} maxDate
25984      * Maximum allowable date (JavaScript date object, defaults to null)
25985      */
25986     maxDate : null,
25987     /**
25988      * @cfg {String} minText
25989      * The error text to display if the minDate validation fails (defaults to "This date is before the minimum date")
25990      */
25991     minText : "This date is before the minimum date",
25992     /**
25993      * @cfg {String} maxText
25994      * The error text to display if the maxDate validation fails (defaults to "This date is after the maximum date")
25995      */
25996     maxText : "This date is after the maximum date",
25997     /**
25998      * @cfg {String} format
25999      * The default date format string which can be overriden for localization support.  The format must be
26000      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
26001      */
26002     format : "m/d/y",
26003     /**
26004      * @cfg {Array} disabledDays
26005      * An array of days to disable, 0-based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
26006      */
26007     disabledDays : null,
26008     /**
26009      * @cfg {String} disabledDaysText
26010      * The tooltip to display when the date falls on a disabled day (defaults to "")
26011      */
26012     disabledDaysText : "",
26013     /**
26014      * @cfg {RegExp} disabledDatesRE
26015      * JavaScript regular expression used to disable a pattern of dates (defaults to null)
26016      */
26017     disabledDatesRE : null,
26018     /**
26019      * @cfg {String} disabledDatesText
26020      * The tooltip text to display when the date falls on a disabled date (defaults to "")
26021      */
26022     disabledDatesText : "",
26023     /**
26024      * @cfg {Boolean} constrainToViewport
26025      * True to constrain the date picker to the viewport (defaults to true)
26026      */
26027     constrainToViewport : true,
26028     /**
26029      * @cfg {Array} monthNames
26030      * An array of textual month names which can be overriden for localization support (defaults to Date.monthNames)
26031      */
26032     monthNames : Date.monthNames,
26033     /**
26034      * @cfg {Array} dayNames
26035      * An array of textual day names which can be overriden for localization support (defaults to Date.dayNames)
26036      */
26037     dayNames : Date.dayNames,
26038     /**
26039      * @cfg {String} nextText
26040      * The next month navigation button tooltip (defaults to 'Next Month (Control+Right)')
26041      */
26042     nextText: 'Next Month (Control+Right)',
26043     /**
26044      * @cfg {String} prevText
26045      * The previous month navigation button tooltip (defaults to 'Previous Month (Control+Left)')
26046      */
26047     prevText: 'Previous Month (Control+Left)',
26048     /**
26049      * @cfg {String} monthYearText
26050      * The header month selector tooltip (defaults to 'Choose a month (Control+Up/Down to move years)')
26051      */
26052     monthYearText: 'Choose a month (Control+Up/Down to move years)',
26053     /**
26054      * @cfg {Number} startDay
26055      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
26056      */
26057     startDay : 0,
26058     /**
26059      * @cfg {Bool} showClear
26060      * Show a clear button (usefull for date form elements that can be blank.)
26061      */
26062     
26063     showClear: false,
26064     
26065     /**
26066      * Sets the value of the date field
26067      * @param {Date} value The date to set
26068      */
26069     setValue : function(value){
26070         var old = this.value;
26071         
26072         if (typeof(value) == 'string') {
26073          
26074             value = Date.parseDate(value, this.format);
26075         }
26076         if (!value) {
26077             value = new Date();
26078         }
26079         
26080         this.value = value.clearTime(true);
26081         if(this.el){
26082             this.update(this.value);
26083         }
26084     },
26085
26086     /**
26087      * Gets the current selected value of the date field
26088      * @return {Date} The selected date
26089      */
26090     getValue : function(){
26091         return this.value;
26092     },
26093
26094     // private
26095     focus : function(){
26096         if(this.el){
26097             this.update(this.activeDate);
26098         }
26099     },
26100
26101     // privateval
26102     onRender : function(container, position){
26103         
26104         var m = [
26105              '<table cellspacing="0">',
26106                 '<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>',
26107                 '<tr><td colspan="3"><table class="x-date-inner" cellspacing="0"><thead><tr>'];
26108         var dn = this.dayNames;
26109         for(var i = 0; i < 7; i++){
26110             var d = this.startDay+i;
26111             if(d > 6){
26112                 d = d-7;
26113             }
26114             m.push("<th><span>", dn[d].substr(0,1), "</span></th>");
26115         }
26116         m[m.length] = "</tr></thead><tbody><tr>";
26117         for(var i = 0; i < 42; i++) {
26118             if(i % 7 == 0 && i != 0){
26119                 m[m.length] = "</tr><tr>";
26120             }
26121             m[m.length] = '<td><a href="#" hidefocus="on" class="x-date-date" tabIndex="1"><em><span></span></em></a></td>';
26122         }
26123         m[m.length] = '</tr></tbody></table></td></tr><tr>'+
26124             '<td colspan="3" class="x-date-bottom" align="center"></td></tr></table><div class="x-date-mp"></div>';
26125
26126         var el = document.createElement("div");
26127         el.className = "x-date-picker";
26128         el.innerHTML = m.join("");
26129
26130         container.dom.insertBefore(el, position);
26131
26132         this.el = Roo.get(el);
26133         this.eventEl = Roo.get(el.firstChild);
26134
26135         new Roo.util.ClickRepeater(this.el.child("td.x-date-left a"), {
26136             handler: this.showPrevMonth,
26137             scope: this,
26138             preventDefault:true,
26139             stopDefault:true
26140         });
26141
26142         new Roo.util.ClickRepeater(this.el.child("td.x-date-right a"), {
26143             handler: this.showNextMonth,
26144             scope: this,
26145             preventDefault:true,
26146             stopDefault:true
26147         });
26148
26149         this.eventEl.on("mousewheel", this.handleMouseWheel,  this);
26150
26151         this.monthPicker = this.el.down('div.x-date-mp');
26152         this.monthPicker.enableDisplayMode('block');
26153         
26154         var kn = new Roo.KeyNav(this.eventEl, {
26155             "left" : function(e){
26156                 e.ctrlKey ?
26157                     this.showPrevMonth() :
26158                     this.update(this.activeDate.add("d", -1));
26159             },
26160
26161             "right" : function(e){
26162                 e.ctrlKey ?
26163                     this.showNextMonth() :
26164                     this.update(this.activeDate.add("d", 1));
26165             },
26166
26167             "up" : function(e){
26168                 e.ctrlKey ?
26169                     this.showNextYear() :
26170                     this.update(this.activeDate.add("d", -7));
26171             },
26172
26173             "down" : function(e){
26174                 e.ctrlKey ?
26175                     this.showPrevYear() :
26176                     this.update(this.activeDate.add("d", 7));
26177             },
26178
26179             "pageUp" : function(e){
26180                 this.showNextMonth();
26181             },
26182
26183             "pageDown" : function(e){
26184                 this.showPrevMonth();
26185             },
26186
26187             "enter" : function(e){
26188                 e.stopPropagation();
26189                 return true;
26190             },
26191
26192             scope : this
26193         });
26194
26195         this.eventEl.on("click", this.handleDateClick,  this, {delegate: "a.x-date-date"});
26196
26197         this.eventEl.addKeyListener(Roo.EventObject.SPACE, this.selectToday,  this);
26198
26199         this.el.unselectable();
26200         
26201         this.cells = this.el.select("table.x-date-inner tbody td");
26202         this.textNodes = this.el.query("table.x-date-inner tbody span");
26203
26204         this.mbtn = new Roo.Button(this.el.child("td.x-date-middle", true), {
26205             text: "&#160;",
26206             tooltip: this.monthYearText
26207         });
26208
26209         this.mbtn.on('click', this.showMonthPicker, this);
26210         this.mbtn.el.child(this.mbtn.menuClassTarget).addClass("x-btn-with-menu");
26211
26212
26213         var today = (new Date()).dateFormat(this.format);
26214         
26215         var baseTb = new Roo.Toolbar(this.el.child("td.x-date-bottom", true));
26216         if (this.showClear) {
26217             baseTb.add( new Roo.Toolbar.Fill());
26218         }
26219         baseTb.add({
26220             text: String.format(this.todayText, today),
26221             tooltip: String.format(this.todayTip, today),
26222             handler: this.selectToday,
26223             scope: this
26224         });
26225         
26226         //var todayBtn = new Roo.Button(this.el.child("td.x-date-bottom", true), {
26227             
26228         //});
26229         if (this.showClear) {
26230             
26231             baseTb.add( new Roo.Toolbar.Fill());
26232             baseTb.add({
26233                 text: '&#160;',
26234                 cls: 'x-btn-icon x-btn-clear',
26235                 handler: function() {
26236                     //this.value = '';
26237                     this.fireEvent("select", this, '');
26238                 },
26239                 scope: this
26240             });
26241         }
26242         
26243         
26244         if(Roo.isIE){
26245             this.el.repaint();
26246         }
26247         this.update(this.value);
26248     },
26249
26250     createMonthPicker : function(){
26251         if(!this.monthPicker.dom.firstChild){
26252             var buf = ['<table border="0" cellspacing="0">'];
26253             for(var i = 0; i < 6; i++){
26254                 buf.push(
26255                     '<tr><td class="x-date-mp-month"><a href="#">', this.monthNames[i].substr(0, 3), '</a></td>',
26256                     '<td class="x-date-mp-month x-date-mp-sep"><a href="#">', this.monthNames[i+6].substr(0, 3), '</a></td>',
26257                     i == 0 ?
26258                     '<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>' :
26259                     '<td class="x-date-mp-year"><a href="#"></a></td><td class="x-date-mp-year"><a href="#"></a></td></tr>'
26260                 );
26261             }
26262             buf.push(
26263                 '<tr class="x-date-mp-btns"><td colspan="4"><button type="button" class="x-date-mp-ok">',
26264                     this.okText,
26265                     '</button><button type="button" class="x-date-mp-cancel">',
26266                     this.cancelText,
26267                     '</button></td></tr>',
26268                 '</table>'
26269             );
26270             this.monthPicker.update(buf.join(''));
26271             this.monthPicker.on('click', this.onMonthClick, this);
26272             this.monthPicker.on('dblclick', this.onMonthDblClick, this);
26273
26274             this.mpMonths = this.monthPicker.select('td.x-date-mp-month');
26275             this.mpYears = this.monthPicker.select('td.x-date-mp-year');
26276
26277             this.mpMonths.each(function(m, a, i){
26278                 i += 1;
26279                 if((i%2) == 0){
26280                     m.dom.xmonth = 5 + Math.round(i * .5);
26281                 }else{
26282                     m.dom.xmonth = Math.round((i-1) * .5);
26283                 }
26284             });
26285         }
26286     },
26287
26288     showMonthPicker : function(){
26289         this.createMonthPicker();
26290         var size = this.el.getSize();
26291         this.monthPicker.setSize(size);
26292         this.monthPicker.child('table').setSize(size);
26293
26294         this.mpSelMonth = (this.activeDate || this.value).getMonth();
26295         this.updateMPMonth(this.mpSelMonth);
26296         this.mpSelYear = (this.activeDate || this.value).getFullYear();
26297         this.updateMPYear(this.mpSelYear);
26298
26299         this.monthPicker.slideIn('t', {duration:.2});
26300     },
26301
26302     updateMPYear : function(y){
26303         this.mpyear = y;
26304         var ys = this.mpYears.elements;
26305         for(var i = 1; i <= 10; i++){
26306             var td = ys[i-1], y2;
26307             if((i%2) == 0){
26308                 y2 = y + Math.round(i * .5);
26309                 td.firstChild.innerHTML = y2;
26310                 td.xyear = y2;
26311             }else{
26312                 y2 = y - (5-Math.round(i * .5));
26313                 td.firstChild.innerHTML = y2;
26314                 td.xyear = y2;
26315             }
26316             this.mpYears.item(i-1)[y2 == this.mpSelYear ? 'addClass' : 'removeClass']('x-date-mp-sel');
26317         }
26318     },
26319
26320     updateMPMonth : function(sm){
26321         this.mpMonths.each(function(m, a, i){
26322             m[m.dom.xmonth == sm ? 'addClass' : 'removeClass']('x-date-mp-sel');
26323         });
26324     },
26325
26326     selectMPMonth: function(m){
26327         
26328     },
26329
26330     onMonthClick : function(e, t){
26331         e.stopEvent();
26332         var el = new Roo.Element(t), pn;
26333         if(el.is('button.x-date-mp-cancel')){
26334             this.hideMonthPicker();
26335         }
26336         else if(el.is('button.x-date-mp-ok')){
26337             this.update(new Date(this.mpSelYear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
26338             this.hideMonthPicker();
26339         }
26340         else if(pn = el.up('td.x-date-mp-month', 2)){
26341             this.mpMonths.removeClass('x-date-mp-sel');
26342             pn.addClass('x-date-mp-sel');
26343             this.mpSelMonth = pn.dom.xmonth;
26344         }
26345         else if(pn = el.up('td.x-date-mp-year', 2)){
26346             this.mpYears.removeClass('x-date-mp-sel');
26347             pn.addClass('x-date-mp-sel');
26348             this.mpSelYear = pn.dom.xyear;
26349         }
26350         else if(el.is('a.x-date-mp-prev')){
26351             this.updateMPYear(this.mpyear-10);
26352         }
26353         else if(el.is('a.x-date-mp-next')){
26354             this.updateMPYear(this.mpyear+10);
26355         }
26356     },
26357
26358     onMonthDblClick : function(e, t){
26359         e.stopEvent();
26360         var el = new Roo.Element(t), pn;
26361         if(pn = el.up('td.x-date-mp-month', 2)){
26362             this.update(new Date(this.mpSelYear, pn.dom.xmonth, (this.activeDate || this.value).getDate()));
26363             this.hideMonthPicker();
26364         }
26365         else if(pn = el.up('td.x-date-mp-year', 2)){
26366             this.update(new Date(pn.dom.xyear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
26367             this.hideMonthPicker();
26368         }
26369     },
26370
26371     hideMonthPicker : function(disableAnim){
26372         if(this.monthPicker){
26373             if(disableAnim === true){
26374                 this.monthPicker.hide();
26375             }else{
26376                 this.monthPicker.slideOut('t', {duration:.2});
26377             }
26378         }
26379     },
26380
26381     // private
26382     showPrevMonth : function(e){
26383         this.update(this.activeDate.add("mo", -1));
26384     },
26385
26386     // private
26387     showNextMonth : function(e){
26388         this.update(this.activeDate.add("mo", 1));
26389     },
26390
26391     // private
26392     showPrevYear : function(){
26393         this.update(this.activeDate.add("y", -1));
26394     },
26395
26396     // private
26397     showNextYear : function(){
26398         this.update(this.activeDate.add("y", 1));
26399     },
26400
26401     // private
26402     handleMouseWheel : function(e){
26403         var delta = e.getWheelDelta();
26404         if(delta > 0){
26405             this.showPrevMonth();
26406             e.stopEvent();
26407         } else if(delta < 0){
26408             this.showNextMonth();
26409             e.stopEvent();
26410         }
26411     },
26412
26413     // private
26414     handleDateClick : function(e, t){
26415         e.stopEvent();
26416         if(t.dateValue && !Roo.fly(t.parentNode).hasClass("x-date-disabled")){
26417             this.setValue(new Date(t.dateValue));
26418             this.fireEvent("select", this, this.value);
26419         }
26420     },
26421
26422     // private
26423     selectToday : function(){
26424         this.setValue(new Date().clearTime());
26425         this.fireEvent("select", this, this.value);
26426     },
26427
26428     // private
26429     update : function(date)
26430     {
26431         var vd = this.activeDate;
26432         this.activeDate = date;
26433         if(vd && this.el){
26434             var t = date.getTime();
26435             if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
26436                 this.cells.removeClass("x-date-selected");
26437                 this.cells.each(function(c){
26438                    if(c.dom.firstChild.dateValue == t){
26439                        c.addClass("x-date-selected");
26440                        setTimeout(function(){
26441                             try{c.dom.firstChild.focus();}catch(e){}
26442                        }, 50);
26443                        return false;
26444                    }
26445                 });
26446                 return;
26447             }
26448         }
26449         
26450         var days = date.getDaysInMonth();
26451         var firstOfMonth = date.getFirstDateOfMonth();
26452         var startingPos = firstOfMonth.getDay()-this.startDay;
26453
26454         if(startingPos <= this.startDay){
26455             startingPos += 7;
26456         }
26457
26458         var pm = date.add("mo", -1);
26459         var prevStart = pm.getDaysInMonth()-startingPos;
26460
26461         var cells = this.cells.elements;
26462         var textEls = this.textNodes;
26463         days += startingPos;
26464
26465         // convert everything to numbers so it's fast
26466         var day = 86400000;
26467         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
26468         var today = new Date().clearTime().getTime();
26469         var sel = date.clearTime().getTime();
26470         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
26471         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
26472         var ddMatch = this.disabledDatesRE;
26473         var ddText = this.disabledDatesText;
26474         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
26475         var ddaysText = this.disabledDaysText;
26476         var format = this.format;
26477
26478         var setCellClass = function(cal, cell){
26479             cell.title = "";
26480             var t = d.getTime();
26481             cell.firstChild.dateValue = t;
26482             if(t == today){
26483                 cell.className += " x-date-today";
26484                 cell.title = cal.todayText;
26485             }
26486             if(t == sel){
26487                 cell.className += " x-date-selected";
26488                 setTimeout(function(){
26489                     try{cell.firstChild.focus();}catch(e){}
26490                 }, 50);
26491             }
26492             // disabling
26493             if(t < min) {
26494                 cell.className = " x-date-disabled";
26495                 cell.title = cal.minText;
26496                 return;
26497             }
26498             if(t > max) {
26499                 cell.className = " x-date-disabled";
26500                 cell.title = cal.maxText;
26501                 return;
26502             }
26503             if(ddays){
26504                 if(ddays.indexOf(d.getDay()) != -1){
26505                     cell.title = ddaysText;
26506                     cell.className = " x-date-disabled";
26507                 }
26508             }
26509             if(ddMatch && format){
26510                 var fvalue = d.dateFormat(format);
26511                 if(ddMatch.test(fvalue)){
26512                     cell.title = ddText.replace("%0", fvalue);
26513                     cell.className = " x-date-disabled";
26514                 }
26515             }
26516         };
26517
26518         var i = 0;
26519         for(; i < startingPos; i++) {
26520             textEls[i].innerHTML = (++prevStart);
26521             d.setDate(d.getDate()+1);
26522             cells[i].className = "x-date-prevday";
26523             setCellClass(this, cells[i]);
26524         }
26525         for(; i < days; i++){
26526             intDay = i - startingPos + 1;
26527             textEls[i].innerHTML = (intDay);
26528             d.setDate(d.getDate()+1);
26529             cells[i].className = "x-date-active";
26530             setCellClass(this, cells[i]);
26531         }
26532         var extraDays = 0;
26533         for(; i < 42; i++) {
26534              textEls[i].innerHTML = (++extraDays);
26535              d.setDate(d.getDate()+1);
26536              cells[i].className = "x-date-nextday";
26537              setCellClass(this, cells[i]);
26538         }
26539
26540         this.mbtn.setText(this.monthNames[date.getMonth()] + " " + date.getFullYear());
26541         this.fireEvent('monthchange', this, date);
26542         
26543         if(!this.internalRender){
26544             var main = this.el.dom.firstChild;
26545             var w = main.offsetWidth;
26546             this.el.setWidth(w + this.el.getBorderWidth("lr"));
26547             Roo.fly(main).setWidth(w);
26548             this.internalRender = true;
26549             // opera does not respect the auto grow header center column
26550             // then, after it gets a width opera refuses to recalculate
26551             // without a second pass
26552             if(Roo.isOpera && !this.secondPass){
26553                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
26554                 this.secondPass = true;
26555                 this.update.defer(10, this, [date]);
26556             }
26557         }
26558         
26559         
26560     }
26561 });        /*
26562  * Based on:
26563  * Ext JS Library 1.1.1
26564  * Copyright(c) 2006-2007, Ext JS, LLC.
26565  *
26566  * Originally Released Under LGPL - original licence link has changed is not relivant.
26567  *
26568  * Fork - LGPL
26569  * <script type="text/javascript">
26570  */
26571 /**
26572  * @class Roo.TabPanel
26573  * @extends Roo.util.Observable
26574  * A lightweight tab container.
26575  * <br><br>
26576  * Usage:
26577  * <pre><code>
26578 // basic tabs 1, built from existing content
26579 var tabs = new Roo.TabPanel("tabs1");
26580 tabs.addTab("script", "View Script");
26581 tabs.addTab("markup", "View Markup");
26582 tabs.activate("script");
26583
26584 // more advanced tabs, built from javascript
26585 var jtabs = new Roo.TabPanel("jtabs");
26586 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
26587
26588 // set up the UpdateManager
26589 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
26590 var updater = tab2.getUpdateManager();
26591 updater.setDefaultUrl("ajax1.htm");
26592 tab2.on('activate', updater.refresh, updater, true);
26593
26594 // Use setUrl for Ajax loading
26595 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
26596 tab3.setUrl("ajax2.htm", null, true);
26597
26598 // Disabled tab
26599 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
26600 tab4.disable();
26601
26602 jtabs.activate("jtabs-1");
26603  * </code></pre>
26604  * @constructor
26605  * Create a new TabPanel.
26606  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
26607  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
26608  */
26609 Roo.TabPanel = function(container, config){
26610     /**
26611     * The container element for this TabPanel.
26612     * @type Roo.Element
26613     */
26614     this.el = Roo.get(container, true);
26615     if(config){
26616         if(typeof config == "boolean"){
26617             this.tabPosition = config ? "bottom" : "top";
26618         }else{
26619             Roo.apply(this, config);
26620         }
26621     }
26622     if(this.tabPosition == "bottom"){
26623         this.bodyEl = Roo.get(this.createBody(this.el.dom));
26624         this.el.addClass("x-tabs-bottom");
26625     }
26626     this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
26627     this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
26628     this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
26629     if(Roo.isIE){
26630         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
26631     }
26632     if(this.tabPosition != "bottom"){
26633         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
26634          * @type Roo.Element
26635          */
26636         this.bodyEl = Roo.get(this.createBody(this.el.dom));
26637         this.el.addClass("x-tabs-top");
26638     }
26639     this.items = [];
26640
26641     this.bodyEl.setStyle("position", "relative");
26642
26643     this.active = null;
26644     this.activateDelegate = this.activate.createDelegate(this);
26645
26646     this.addEvents({
26647         /**
26648          * @event tabchange
26649          * Fires when the active tab changes
26650          * @param {Roo.TabPanel} this
26651          * @param {Roo.TabPanelItem} activePanel The new active tab
26652          */
26653         "tabchange": true,
26654         /**
26655          * @event beforetabchange
26656          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
26657          * @param {Roo.TabPanel} this
26658          * @param {Object} e Set cancel to true on this object to cancel the tab change
26659          * @param {Roo.TabPanelItem} tab The tab being changed to
26660          */
26661         "beforetabchange" : true
26662     });
26663
26664     Roo.EventManager.onWindowResize(this.onResize, this);
26665     this.cpad = this.el.getPadding("lr");
26666     this.hiddenCount = 0;
26667
26668
26669     // toolbar on the tabbar support...
26670     if (this.toolbar) {
26671         var tcfg = this.toolbar;
26672         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
26673         this.toolbar = new Roo.Toolbar(tcfg);
26674         if (Roo.isSafari) {
26675             var tbl = tcfg.container.child('table', true);
26676             tbl.setAttribute('width', '100%');
26677         }
26678         
26679     }
26680    
26681
26682
26683     Roo.TabPanel.superclass.constructor.call(this);
26684 };
26685
26686 Roo.extend(Roo.TabPanel, Roo.util.Observable, {
26687     /*
26688      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
26689      */
26690     tabPosition : "top",
26691     /*
26692      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
26693      */
26694     currentTabWidth : 0,
26695     /*
26696      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
26697      */
26698     minTabWidth : 40,
26699     /*
26700      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
26701      */
26702     maxTabWidth : 250,
26703     /*
26704      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
26705      */
26706     preferredTabWidth : 175,
26707     /*
26708      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
26709      */
26710     resizeTabs : false,
26711     /*
26712      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
26713      */
26714     monitorResize : true,
26715     /*
26716      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
26717      */
26718     toolbar : false,
26719
26720     /**
26721      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
26722      * @param {String} id The id of the div to use <b>or create</b>
26723      * @param {String} text The text for the tab
26724      * @param {String} content (optional) Content to put in the TabPanelItem body
26725      * @param {Boolean} closable (optional) True to create a close icon on the tab
26726      * @return {Roo.TabPanelItem} The created TabPanelItem
26727      */
26728     addTab : function(id, text, content, closable){
26729         var item = new Roo.TabPanelItem(this, id, text, closable);
26730         this.addTabItem(item);
26731         if(content){
26732             item.setContent(content);
26733         }
26734         return item;
26735     },
26736
26737     /**
26738      * Returns the {@link Roo.TabPanelItem} with the specified id/index
26739      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
26740      * @return {Roo.TabPanelItem}
26741      */
26742     getTab : function(id){
26743         return this.items[id];
26744     },
26745
26746     /**
26747      * Hides the {@link Roo.TabPanelItem} with the specified id/index
26748      * @param {String/Number} id The id or index of the TabPanelItem to hide.
26749      */
26750     hideTab : function(id){
26751         var t = this.items[id];
26752         if(!t.isHidden()){
26753            t.setHidden(true);
26754            this.hiddenCount++;
26755            this.autoSizeTabs();
26756         }
26757     },
26758
26759     /**
26760      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
26761      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
26762      */
26763     unhideTab : function(id){
26764         var t = this.items[id];
26765         if(t.isHidden()){
26766            t.setHidden(false);
26767            this.hiddenCount--;
26768            this.autoSizeTabs();
26769         }
26770     },
26771
26772     /**
26773      * Adds an existing {@link Roo.TabPanelItem}.
26774      * @param {Roo.TabPanelItem} item The TabPanelItem to add
26775      */
26776     addTabItem : function(item){
26777         this.items[item.id] = item;
26778         this.items.push(item);
26779         if(this.resizeTabs){
26780            item.setWidth(this.currentTabWidth || this.preferredTabWidth);
26781            this.autoSizeTabs();
26782         }else{
26783             item.autoSize();
26784         }
26785     },
26786
26787     /**
26788      * Removes a {@link Roo.TabPanelItem}.
26789      * @param {String/Number} id The id or index of the TabPanelItem to remove.
26790      */
26791     removeTab : function(id){
26792         var items = this.items;
26793         var tab = items[id];
26794         if(!tab) { return; }
26795         var index = items.indexOf(tab);
26796         if(this.active == tab && items.length > 1){
26797             var newTab = this.getNextAvailable(index);
26798             if(newTab) {
26799                 newTab.activate();
26800             }
26801         }
26802         this.stripEl.dom.removeChild(tab.pnode.dom);
26803         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
26804             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
26805         }
26806         items.splice(index, 1);
26807         delete this.items[tab.id];
26808         tab.fireEvent("close", tab);
26809         tab.purgeListeners();
26810         this.autoSizeTabs();
26811     },
26812
26813     getNextAvailable : function(start){
26814         var items = this.items;
26815         var index = start;
26816         // look for a next tab that will slide over to
26817         // replace the one being removed
26818         while(index < items.length){
26819             var item = items[++index];
26820             if(item && !item.isHidden()){
26821                 return item;
26822             }
26823         }
26824         // if one isn't found select the previous tab (on the left)
26825         index = start;
26826         while(index >= 0){
26827             var item = items[--index];
26828             if(item && !item.isHidden()){
26829                 return item;
26830             }
26831         }
26832         return null;
26833     },
26834
26835     /**
26836      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
26837      * @param {String/Number} id The id or index of the TabPanelItem to disable.
26838      */
26839     disableTab : function(id){
26840         var tab = this.items[id];
26841         if(tab && this.active != tab){
26842             tab.disable();
26843         }
26844     },
26845
26846     /**
26847      * Enables a {@link Roo.TabPanelItem} that is disabled.
26848      * @param {String/Number} id The id or index of the TabPanelItem to enable.
26849      */
26850     enableTab : function(id){
26851         var tab = this.items[id];
26852         tab.enable();
26853     },
26854
26855     /**
26856      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
26857      * @param {String/Number} id The id or index of the TabPanelItem to activate.
26858      * @return {Roo.TabPanelItem} The TabPanelItem.
26859      */
26860     activate : function(id){
26861         var tab = this.items[id];
26862         if(!tab){
26863             return null;
26864         }
26865         if(tab == this.active || tab.disabled){
26866             return tab;
26867         }
26868         var e = {};
26869         this.fireEvent("beforetabchange", this, e, tab);
26870         if(e.cancel !== true && !tab.disabled){
26871             if(this.active){
26872                 this.active.hide();
26873             }
26874             this.active = this.items[id];
26875             this.active.show();
26876             this.fireEvent("tabchange", this, this.active);
26877         }
26878         return tab;
26879     },
26880
26881     /**
26882      * Gets the active {@link Roo.TabPanelItem}.
26883      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
26884      */
26885     getActiveTab : function(){
26886         return this.active;
26887     },
26888
26889     /**
26890      * Updates the tab body element to fit the height of the container element
26891      * for overflow scrolling
26892      * @param {Number} targetHeight (optional) Override the starting height from the elements height
26893      */
26894     syncHeight : function(targetHeight){
26895         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
26896         var bm = this.bodyEl.getMargins();
26897         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
26898         this.bodyEl.setHeight(newHeight);
26899         return newHeight;
26900     },
26901
26902     onResize : function(){
26903         if(this.monitorResize){
26904             this.autoSizeTabs();
26905         }
26906     },
26907
26908     /**
26909      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
26910      */
26911     beginUpdate : function(){
26912         this.updating = true;
26913     },
26914
26915     /**
26916      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
26917      */
26918     endUpdate : function(){
26919         this.updating = false;
26920         this.autoSizeTabs();
26921     },
26922
26923     /**
26924      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
26925      */
26926     autoSizeTabs : function(){
26927         var count = this.items.length;
26928         var vcount = count - this.hiddenCount;
26929         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) return;
26930         var w = Math.max(this.el.getWidth() - this.cpad, 10);
26931         var availWidth = Math.floor(w / vcount);
26932         var b = this.stripBody;
26933         if(b.getWidth() > w){
26934             var tabs = this.items;
26935             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
26936             if(availWidth < this.minTabWidth){
26937                 /*if(!this.sleft){    // incomplete scrolling code
26938                     this.createScrollButtons();
26939                 }
26940                 this.showScroll();
26941                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
26942             }
26943         }else{
26944             if(this.currentTabWidth < this.preferredTabWidth){
26945                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
26946             }
26947         }
26948     },
26949
26950     /**
26951      * Returns the number of tabs in this TabPanel.
26952      * @return {Number}
26953      */
26954      getCount : function(){
26955          return this.items.length;
26956      },
26957
26958     /**
26959      * Resizes all the tabs to the passed width
26960      * @param {Number} The new width
26961      */
26962     setTabWidth : function(width){
26963         this.currentTabWidth = width;
26964         for(var i = 0, len = this.items.length; i < len; i++) {
26965                 if(!this.items[i].isHidden())this.items[i].setWidth(width);
26966         }
26967     },
26968
26969     /**
26970      * Destroys this TabPanel
26971      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
26972      */
26973     destroy : function(removeEl){
26974         Roo.EventManager.removeResizeListener(this.onResize, this);
26975         for(var i = 0, len = this.items.length; i < len; i++){
26976             this.items[i].purgeListeners();
26977         }
26978         if(removeEl === true){
26979             this.el.update("");
26980             this.el.remove();
26981         }
26982     }
26983 });
26984
26985 /**
26986  * @class Roo.TabPanelItem
26987  * @extends Roo.util.Observable
26988  * Represents an individual item (tab plus body) in a TabPanel.
26989  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
26990  * @param {String} id The id of this TabPanelItem
26991  * @param {String} text The text for the tab of this TabPanelItem
26992  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
26993  */
26994 Roo.TabPanelItem = function(tabPanel, id, text, closable){
26995     /**
26996      * The {@link Roo.TabPanel} this TabPanelItem belongs to
26997      * @type Roo.TabPanel
26998      */
26999     this.tabPanel = tabPanel;
27000     /**
27001      * The id for this TabPanelItem
27002      * @type String
27003      */
27004     this.id = id;
27005     /** @private */
27006     this.disabled = false;
27007     /** @private */
27008     this.text = text;
27009     /** @private */
27010     this.loaded = false;
27011     this.closable = closable;
27012
27013     /**
27014      * The body element for this TabPanelItem.
27015      * @type Roo.Element
27016      */
27017     this.bodyEl = Roo.get(tabPanel.createItemBody(tabPanel.bodyEl.dom, id));
27018     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
27019     this.bodyEl.setStyle("display", "block");
27020     this.bodyEl.setStyle("zoom", "1");
27021     this.hideAction();
27022
27023     var els = tabPanel.createStripElements(tabPanel.stripEl.dom, text, closable);
27024     /** @private */
27025     this.el = Roo.get(els.el, true);
27026     this.inner = Roo.get(els.inner, true);
27027     this.textEl = Roo.get(this.el.dom.firstChild.firstChild.firstChild, true);
27028     this.pnode = Roo.get(els.el.parentNode, true);
27029     this.el.on("mousedown", this.onTabMouseDown, this);
27030     this.el.on("click", this.onTabClick, this);
27031     /** @private */
27032     if(closable){
27033         var c = Roo.get(els.close, true);
27034         c.dom.title = this.closeText;
27035         c.addClassOnOver("close-over");
27036         c.on("click", this.closeClick, this);
27037      }
27038
27039     this.addEvents({
27040          /**
27041          * @event activate
27042          * Fires when this tab becomes the active tab.
27043          * @param {Roo.TabPanel} tabPanel The parent TabPanel
27044          * @param {Roo.TabPanelItem} this
27045          */
27046         "activate": true,
27047         /**
27048          * @event beforeclose
27049          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
27050          * @param {Roo.TabPanelItem} this
27051          * @param {Object} e Set cancel to true on this object to cancel the close.
27052          */
27053         "beforeclose": true,
27054         /**
27055          * @event close
27056          * Fires when this tab is closed.
27057          * @param {Roo.TabPanelItem} this
27058          */
27059          "close": true,
27060         /**
27061          * @event deactivate
27062          * Fires when this tab is no longer the active tab.
27063          * @param {Roo.TabPanel} tabPanel The parent TabPanel
27064          * @param {Roo.TabPanelItem} this
27065          */
27066          "deactivate" : true
27067     });
27068     this.hidden = false;
27069
27070     Roo.TabPanelItem.superclass.constructor.call(this);
27071 };
27072
27073 Roo.extend(Roo.TabPanelItem, Roo.util.Observable, {
27074     purgeListeners : function(){
27075        Roo.util.Observable.prototype.purgeListeners.call(this);
27076        this.el.removeAllListeners();
27077     },
27078     /**
27079      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
27080      */
27081     show : function(){
27082         this.pnode.addClass("on");
27083         this.showAction();
27084         if(Roo.isOpera){
27085             this.tabPanel.stripWrap.repaint();
27086         }
27087         this.fireEvent("activate", this.tabPanel, this);
27088     },
27089
27090     /**
27091      * Returns true if this tab is the active tab.
27092      * @return {Boolean}
27093      */
27094     isActive : function(){
27095         return this.tabPanel.getActiveTab() == this;
27096     },
27097
27098     /**
27099      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
27100      */
27101     hide : function(){
27102         this.pnode.removeClass("on");
27103         this.hideAction();
27104         this.fireEvent("deactivate", this.tabPanel, this);
27105     },
27106
27107     hideAction : function(){
27108         this.bodyEl.hide();
27109         this.bodyEl.setStyle("position", "absolute");
27110         this.bodyEl.setLeft("-20000px");
27111         this.bodyEl.setTop("-20000px");
27112     },
27113
27114     showAction : function(){
27115         this.bodyEl.setStyle("position", "relative");
27116         this.bodyEl.setTop("");
27117         this.bodyEl.setLeft("");
27118         this.bodyEl.show();
27119     },
27120
27121     /**
27122      * Set the tooltip for the tab.
27123      * @param {String} tooltip The tab's tooltip
27124      */
27125     setTooltip : function(text){
27126         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
27127             this.textEl.dom.qtip = text;
27128             this.textEl.dom.removeAttribute('title');
27129         }else{
27130             this.textEl.dom.title = text;
27131         }
27132     },
27133
27134     onTabClick : function(e){
27135         e.preventDefault();
27136         this.tabPanel.activate(this.id);
27137     },
27138
27139     onTabMouseDown : function(e){
27140         e.preventDefault();
27141         this.tabPanel.activate(this.id);
27142     },
27143
27144     getWidth : function(){
27145         return this.inner.getWidth();
27146     },
27147
27148     setWidth : function(width){
27149         var iwidth = width - this.pnode.getPadding("lr");
27150         this.inner.setWidth(iwidth);
27151         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
27152         this.pnode.setWidth(width);
27153     },
27154
27155     /**
27156      * Show or hide the tab
27157      * @param {Boolean} hidden True to hide or false to show.
27158      */
27159     setHidden : function(hidden){
27160         this.hidden = hidden;
27161         this.pnode.setStyle("display", hidden ? "none" : "");
27162     },
27163
27164     /**
27165      * Returns true if this tab is "hidden"
27166      * @return {Boolean}
27167      */
27168     isHidden : function(){
27169         return this.hidden;
27170     },
27171
27172     /**
27173      * Returns the text for this tab
27174      * @return {String}
27175      */
27176     getText : function(){
27177         return this.text;
27178     },
27179
27180     autoSize : function(){
27181         //this.el.beginMeasure();
27182         this.textEl.setWidth(1);
27183         /*
27184          *  #2804 [new] Tabs in Roojs
27185          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
27186          */
27187         this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr") + 2);
27188         //this.el.endMeasure();
27189     },
27190
27191     /**
27192      * Sets the text for the tab (Note: this also sets the tooltip text)
27193      * @param {String} text The tab's text and tooltip
27194      */
27195     setText : function(text){
27196         this.text = text;
27197         this.textEl.update(text);
27198         this.setTooltip(text);
27199         if(!this.tabPanel.resizeTabs){
27200             this.autoSize();
27201         }
27202     },
27203     /**
27204      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
27205      */
27206     activate : function(){
27207         this.tabPanel.activate(this.id);
27208     },
27209
27210     /**
27211      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
27212      */
27213     disable : function(){
27214         if(this.tabPanel.active != this){
27215             this.disabled = true;
27216             this.pnode.addClass("disabled");
27217         }
27218     },
27219
27220     /**
27221      * Enables this TabPanelItem if it was previously disabled.
27222      */
27223     enable : function(){
27224         this.disabled = false;
27225         this.pnode.removeClass("disabled");
27226     },
27227
27228     /**
27229      * Sets the content for this TabPanelItem.
27230      * @param {String} content The content
27231      * @param {Boolean} loadScripts true to look for and load scripts
27232      */
27233     setContent : function(content, loadScripts){
27234         this.bodyEl.update(content, loadScripts);
27235     },
27236
27237     /**
27238      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
27239      * @return {Roo.UpdateManager} The UpdateManager
27240      */
27241     getUpdateManager : function(){
27242         return this.bodyEl.getUpdateManager();
27243     },
27244
27245     /**
27246      * Set a URL to be used to load the content for this TabPanelItem.
27247      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
27248      * @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)
27249      * @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)
27250      * @return {Roo.UpdateManager} The UpdateManager
27251      */
27252     setUrl : function(url, params, loadOnce){
27253         if(this.refreshDelegate){
27254             this.un('activate', this.refreshDelegate);
27255         }
27256         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
27257         this.on("activate", this.refreshDelegate);
27258         return this.bodyEl.getUpdateManager();
27259     },
27260
27261     /** @private */
27262     _handleRefresh : function(url, params, loadOnce){
27263         if(!loadOnce || !this.loaded){
27264             var updater = this.bodyEl.getUpdateManager();
27265             updater.update(url, params, this._setLoaded.createDelegate(this));
27266         }
27267     },
27268
27269     /**
27270      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
27271      *   Will fail silently if the setUrl method has not been called.
27272      *   This does not activate the panel, just updates its content.
27273      */
27274     refresh : function(){
27275         if(this.refreshDelegate){
27276            this.loaded = false;
27277            this.refreshDelegate();
27278         }
27279     },
27280
27281     /** @private */
27282     _setLoaded : function(){
27283         this.loaded = true;
27284     },
27285
27286     /** @private */
27287     closeClick : function(e){
27288         var o = {};
27289         e.stopEvent();
27290         this.fireEvent("beforeclose", this, o);
27291         if(o.cancel !== true){
27292             this.tabPanel.removeTab(this.id);
27293         }
27294     },
27295     /**
27296      * The text displayed in the tooltip for the close icon.
27297      * @type String
27298      */
27299     closeText : "Close this tab"
27300 });
27301
27302 /** @private */
27303 Roo.TabPanel.prototype.createStrip = function(container){
27304     var strip = document.createElement("div");
27305     strip.className = "x-tabs-wrap";
27306     container.appendChild(strip);
27307     return strip;
27308 };
27309 /** @private */
27310 Roo.TabPanel.prototype.createStripList = function(strip){
27311     // div wrapper for retard IE
27312     // returns the "tr" element.
27313     strip.innerHTML = '<div class="x-tabs-strip-wrap">'+
27314         '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
27315         '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
27316     return strip.firstChild.firstChild.firstChild.firstChild;
27317 };
27318 /** @private */
27319 Roo.TabPanel.prototype.createBody = function(container){
27320     var body = document.createElement("div");
27321     Roo.id(body, "tab-body");
27322     Roo.fly(body).addClass("x-tabs-body");
27323     container.appendChild(body);
27324     return body;
27325 };
27326 /** @private */
27327 Roo.TabPanel.prototype.createItemBody = function(bodyEl, id){
27328     var body = Roo.getDom(id);
27329     if(!body){
27330         body = document.createElement("div");
27331         body.id = id;
27332     }
27333     Roo.fly(body).addClass("x-tabs-item-body");
27334     bodyEl.insertBefore(body, bodyEl.firstChild);
27335     return body;
27336 };
27337 /** @private */
27338 Roo.TabPanel.prototype.createStripElements = function(stripEl, text, closable){
27339     var td = document.createElement("td");
27340     stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
27341     //stripEl.appendChild(td);
27342     if(closable){
27343         td.className = "x-tabs-closable";
27344         if(!this.closeTpl){
27345             this.closeTpl = new Roo.Template(
27346                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
27347                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
27348                '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
27349             );
27350         }
27351         var el = this.closeTpl.overwrite(td, {"text": text});
27352         var close = el.getElementsByTagName("div")[0];
27353         var inner = el.getElementsByTagName("em")[0];
27354         return {"el": el, "close": close, "inner": inner};
27355     } else {
27356         if(!this.tabTpl){
27357             this.tabTpl = new Roo.Template(
27358                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
27359                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
27360             );
27361         }
27362         var el = this.tabTpl.overwrite(td, {"text": text});
27363         var inner = el.getElementsByTagName("em")[0];
27364         return {"el": el, "inner": inner};
27365     }
27366 };/*
27367  * Based on:
27368  * Ext JS Library 1.1.1
27369  * Copyright(c) 2006-2007, Ext JS, LLC.
27370  *
27371  * Originally Released Under LGPL - original licence link has changed is not relivant.
27372  *
27373  * Fork - LGPL
27374  * <script type="text/javascript">
27375  */
27376
27377 /**
27378  * @class Roo.Button
27379  * @extends Roo.util.Observable
27380  * Simple Button class
27381  * @cfg {String} text The button text
27382  * @cfg {String} icon The path to an image to display in the button (the image will be set as the background-image
27383  * CSS property of the button by default, so if you want a mixed icon/text button, set cls:"x-btn-text-icon")
27384  * @cfg {Function} handler A function called when the button is clicked (can be used instead of click event)
27385  * @cfg {Object} scope The scope of the handler
27386  * @cfg {Number} minWidth The minimum width for this button (used to give a set of buttons a common width)
27387  * @cfg {String/Object} tooltip The tooltip for the button - can be a string or QuickTips config object
27388  * @cfg {Boolean} hidden True to start hidden (defaults to false)
27389  * @cfg {Boolean} disabled True to start disabled (defaults to false)
27390  * @cfg {Boolean} pressed True to start pressed (only if enableToggle = true)
27391  * @cfg {String} toggleGroup The group this toggle button is a member of (only 1 per group can be pressed, only
27392    applies if enableToggle = true)
27393  * @cfg {String/HTMLElement/Element} renderTo The element to append the button to
27394  * @cfg {Boolean/Object} repeat True to repeat fire the click event while the mouse is down. This can also be
27395   an {@link Roo.util.ClickRepeater} config object (defaults to false).
27396  * @constructor
27397  * Create a new button
27398  * @param {Object} config The config object
27399  */
27400 Roo.Button = function(renderTo, config)
27401 {
27402     if (!config) {
27403         config = renderTo;
27404         renderTo = config.renderTo || false;
27405     }
27406     
27407     Roo.apply(this, config);
27408     this.addEvents({
27409         /**
27410              * @event click
27411              * Fires when this button is clicked
27412              * @param {Button} this
27413              * @param {EventObject} e The click event
27414              */
27415             "click" : true,
27416         /**
27417              * @event toggle
27418              * Fires when the "pressed" state of this button changes (only if enableToggle = true)
27419              * @param {Button} this
27420              * @param {Boolean} pressed
27421              */
27422             "toggle" : true,
27423         /**
27424              * @event mouseover
27425              * Fires when the mouse hovers over the button
27426              * @param {Button} this
27427              * @param {Event} e The event object
27428              */
27429         'mouseover' : true,
27430         /**
27431              * @event mouseout
27432              * Fires when the mouse exits the button
27433              * @param {Button} this
27434              * @param {Event} e The event object
27435              */
27436         'mouseout': true,
27437          /**
27438              * @event render
27439              * Fires when the button is rendered
27440              * @param {Button} this
27441              */
27442         'render': true
27443     });
27444     if(this.menu){
27445         this.menu = Roo.menu.MenuMgr.get(this.menu);
27446     }
27447     // register listeners first!!  - so render can be captured..
27448     Roo.util.Observable.call(this);
27449     if(renderTo){
27450         this.render(renderTo);
27451     }
27452     
27453   
27454 };
27455
27456 Roo.extend(Roo.Button, Roo.util.Observable, {
27457     /**
27458      * 
27459      */
27460     
27461     /**
27462      * Read-only. True if this button is hidden
27463      * @type Boolean
27464      */
27465     hidden : false,
27466     /**
27467      * Read-only. True if this button is disabled
27468      * @type Boolean
27469      */
27470     disabled : false,
27471     /**
27472      * Read-only. True if this button is pressed (only if enableToggle = true)
27473      * @type Boolean
27474      */
27475     pressed : false,
27476
27477     /**
27478      * @cfg {Number} tabIndex 
27479      * The DOM tabIndex for this button (defaults to undefined)
27480      */
27481     tabIndex : undefined,
27482
27483     /**
27484      * @cfg {Boolean} enableToggle
27485      * True to enable pressed/not pressed toggling (defaults to false)
27486      */
27487     enableToggle: false,
27488     /**
27489      * @cfg {Mixed} menu
27490      * Standard menu attribute consisting of a reference to a menu object, a menu id or a menu config blob (defaults to undefined).
27491      */
27492     menu : undefined,
27493     /**
27494      * @cfg {String} menuAlign
27495      * The position to align the menu to (see {@link Roo.Element#alignTo} for more details, defaults to 'tl-bl?').
27496      */
27497     menuAlign : "tl-bl?",
27498
27499     /**
27500      * @cfg {String} iconCls
27501      * A css class which sets a background image to be used as the icon for this button (defaults to undefined).
27502      */
27503     iconCls : undefined,
27504     /**
27505      * @cfg {String} type
27506      * The button's type, corresponding to the DOM input element type attribute.  Either "submit," "reset" or "button" (default).
27507      */
27508     type : 'button',
27509
27510     // private
27511     menuClassTarget: 'tr',
27512
27513     /**
27514      * @cfg {String} clickEvent
27515      * The type of event to map to the button's event handler (defaults to 'click')
27516      */
27517     clickEvent : 'click',
27518
27519     /**
27520      * @cfg {Boolean} handleMouseEvents
27521      * False to disable visual cues on mouseover, mouseout and mousedown (defaults to true)
27522      */
27523     handleMouseEvents : true,
27524
27525     /**
27526      * @cfg {String} tooltipType
27527      * The type of tooltip to use. Either "qtip" (default) for QuickTips or "title" for title attribute.
27528      */
27529     tooltipType : 'qtip',
27530
27531     /**
27532      * @cfg {String} cls
27533      * A CSS class to apply to the button's main element.
27534      */
27535     
27536     /**
27537      * @cfg {Roo.Template} template (Optional)
27538      * An {@link Roo.Template} with which to create the Button's main element. This Template must
27539      * contain numeric substitution parameter 0 if it is to display the tRoo property. Changing the template could
27540      * require code modifications if required elements (e.g. a button) aren't present.
27541      */
27542
27543     // private
27544     render : function(renderTo){
27545         var btn;
27546         if(this.hideParent){
27547             this.parentEl = Roo.get(renderTo);
27548         }
27549         if(!this.dhconfig){
27550             if(!this.template){
27551                 if(!Roo.Button.buttonTemplate){
27552                     // hideous table template
27553                     Roo.Button.buttonTemplate = new Roo.Template(
27554                         '<table border="0" cellpadding="0" cellspacing="0" class="x-btn-wrap"><tbody><tr>',
27555                         '<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>',
27556                         "</tr></tbody></table>");
27557                 }
27558                 this.template = Roo.Button.buttonTemplate;
27559             }
27560             btn = this.template.append(renderTo, [this.text || '&#160;', this.type], true);
27561             var btnEl = btn.child("button:first");
27562             btnEl.on('focus', this.onFocus, this);
27563             btnEl.on('blur', this.onBlur, this);
27564             if(this.cls){
27565                 btn.addClass(this.cls);
27566             }
27567             if(this.icon){
27568                 btnEl.setStyle('background-image', 'url(' +this.icon +')');
27569             }
27570             if(this.iconCls){
27571                 btnEl.addClass(this.iconCls);
27572                 if(!this.cls){
27573                     btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
27574                 }
27575             }
27576             if(this.tabIndex !== undefined){
27577                 btnEl.dom.tabIndex = this.tabIndex;
27578             }
27579             if(this.tooltip){
27580                 if(typeof this.tooltip == 'object'){
27581                     Roo.QuickTips.tips(Roo.apply({
27582                           target: btnEl.id
27583                     }, this.tooltip));
27584                 } else {
27585                     btnEl.dom[this.tooltipType] = this.tooltip;
27586                 }
27587             }
27588         }else{
27589             btn = Roo.DomHelper.append(Roo.get(renderTo).dom, this.dhconfig, true);
27590         }
27591         this.el = btn;
27592         if(this.id){
27593             this.el.dom.id = this.el.id = this.id;
27594         }
27595         if(this.menu){
27596             this.el.child(this.menuClassTarget).addClass("x-btn-with-menu");
27597             this.menu.on("show", this.onMenuShow, this);
27598             this.menu.on("hide", this.onMenuHide, this);
27599         }
27600         btn.addClass("x-btn");
27601         if(Roo.isIE && !Roo.isIE7){
27602             this.autoWidth.defer(1, this);
27603         }else{
27604             this.autoWidth();
27605         }
27606         if(this.handleMouseEvents){
27607             btn.on("mouseover", this.onMouseOver, this);
27608             btn.on("mouseout", this.onMouseOut, this);
27609             btn.on("mousedown", this.onMouseDown, this);
27610         }
27611         btn.on(this.clickEvent, this.onClick, this);
27612         //btn.on("mouseup", this.onMouseUp, this);
27613         if(this.hidden){
27614             this.hide();
27615         }
27616         if(this.disabled){
27617             this.disable();
27618         }
27619         Roo.ButtonToggleMgr.register(this);
27620         if(this.pressed){
27621             this.el.addClass("x-btn-pressed");
27622         }
27623         if(this.repeat){
27624             var repeater = new Roo.util.ClickRepeater(btn,
27625                 typeof this.repeat == "object" ? this.repeat : {}
27626             );
27627             repeater.on("click", this.onClick,  this);
27628         }
27629         
27630         this.fireEvent('render', this);
27631         
27632     },
27633     /**
27634      * Returns the button's underlying element
27635      * @return {Roo.Element} The element
27636      */
27637     getEl : function(){
27638         return this.el;  
27639     },
27640     
27641     /**
27642      * Destroys this Button and removes any listeners.
27643      */
27644     destroy : function(){
27645         Roo.ButtonToggleMgr.unregister(this);
27646         this.el.removeAllListeners();
27647         this.purgeListeners();
27648         this.el.remove();
27649     },
27650
27651     // private
27652     autoWidth : function(){
27653         if(this.el){
27654             this.el.setWidth("auto");
27655             if(Roo.isIE7 && Roo.isStrict){
27656                 var ib = this.el.child('button');
27657                 if(ib && ib.getWidth() > 20){
27658                     ib.clip();
27659                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
27660                 }
27661             }
27662             if(this.minWidth){
27663                 if(this.hidden){
27664                     this.el.beginMeasure();
27665                 }
27666                 if(this.el.getWidth() < this.minWidth){
27667                     this.el.setWidth(this.minWidth);
27668                 }
27669                 if(this.hidden){
27670                     this.el.endMeasure();
27671                 }
27672             }
27673         }
27674     },
27675
27676     /**
27677      * Assigns this button's click handler
27678      * @param {Function} handler The function to call when the button is clicked
27679      * @param {Object} scope (optional) Scope for the function passed in
27680      */
27681     setHandler : function(handler, scope){
27682         this.handler = handler;
27683         this.scope = scope;  
27684     },
27685     
27686     /**
27687      * Sets this button's text
27688      * @param {String} text The button text
27689      */
27690     setText : function(text){
27691         this.text = text;
27692         if(this.el){
27693             this.el.child("td.x-btn-center button.x-btn-text").update(text);
27694         }
27695         this.autoWidth();
27696     },
27697     
27698     /**
27699      * Gets the text for this button
27700      * @return {String} The button text
27701      */
27702     getText : function(){
27703         return this.text;  
27704     },
27705     
27706     /**
27707      * Show this button
27708      */
27709     show: function(){
27710         this.hidden = false;
27711         if(this.el){
27712             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "");
27713         }
27714     },
27715     
27716     /**
27717      * Hide this button
27718      */
27719     hide: function(){
27720         this.hidden = true;
27721         if(this.el){
27722             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "none");
27723         }
27724     },
27725     
27726     /**
27727      * Convenience function for boolean show/hide
27728      * @param {Boolean} visible True to show, false to hide
27729      */
27730     setVisible: function(visible){
27731         if(visible) {
27732             this.show();
27733         }else{
27734             this.hide();
27735         }
27736     },
27737     
27738     /**
27739      * If a state it passed, it becomes the pressed state otherwise the current state is toggled.
27740      * @param {Boolean} state (optional) Force a particular state
27741      */
27742     toggle : function(state){
27743         state = state === undefined ? !this.pressed : state;
27744         if(state != this.pressed){
27745             if(state){
27746                 this.el.addClass("x-btn-pressed");
27747                 this.pressed = true;
27748                 this.fireEvent("toggle", this, true);
27749             }else{
27750                 this.el.removeClass("x-btn-pressed");
27751                 this.pressed = false;
27752                 this.fireEvent("toggle", this, false);
27753             }
27754             if(this.toggleHandler){
27755                 this.toggleHandler.call(this.scope || this, this, state);
27756             }
27757         }
27758     },
27759     
27760     /**
27761      * Focus the button
27762      */
27763     focus : function(){
27764         this.el.child('button:first').focus();
27765     },
27766     
27767     /**
27768      * Disable this button
27769      */
27770     disable : function(){
27771         if(this.el){
27772             this.el.addClass("x-btn-disabled");
27773         }
27774         this.disabled = true;
27775     },
27776     
27777     /**
27778      * Enable this button
27779      */
27780     enable : function(){
27781         if(this.el){
27782             this.el.removeClass("x-btn-disabled");
27783         }
27784         this.disabled = false;
27785     },
27786
27787     /**
27788      * Convenience function for boolean enable/disable
27789      * @param {Boolean} enabled True to enable, false to disable
27790      */
27791     setDisabled : function(v){
27792         this[v !== true ? "enable" : "disable"]();
27793     },
27794
27795     // private
27796     onClick : function(e){
27797         if(e){
27798             e.preventDefault();
27799         }
27800         if(e.button != 0){
27801             return;
27802         }
27803         if(!this.disabled){
27804             if(this.enableToggle){
27805                 this.toggle();
27806             }
27807             if(this.menu && !this.menu.isVisible()){
27808                 this.menu.show(this.el, this.menuAlign);
27809             }
27810             this.fireEvent("click", this, e);
27811             if(this.handler){
27812                 this.el.removeClass("x-btn-over");
27813                 this.handler.call(this.scope || this, this, e);
27814             }
27815         }
27816     },
27817     // private
27818     onMouseOver : function(e){
27819         if(!this.disabled){
27820             this.el.addClass("x-btn-over");
27821             this.fireEvent('mouseover', this, e);
27822         }
27823     },
27824     // private
27825     onMouseOut : function(e){
27826         if(!e.within(this.el,  true)){
27827             this.el.removeClass("x-btn-over");
27828             this.fireEvent('mouseout', this, e);
27829         }
27830     },
27831     // private
27832     onFocus : function(e){
27833         if(!this.disabled){
27834             this.el.addClass("x-btn-focus");
27835         }
27836     },
27837     // private
27838     onBlur : function(e){
27839         this.el.removeClass("x-btn-focus");
27840     },
27841     // private
27842     onMouseDown : function(e){
27843         if(!this.disabled && e.button == 0){
27844             this.el.addClass("x-btn-click");
27845             Roo.get(document).on('mouseup', this.onMouseUp, this);
27846         }
27847     },
27848     // private
27849     onMouseUp : function(e){
27850         if(e.button == 0){
27851             this.el.removeClass("x-btn-click");
27852             Roo.get(document).un('mouseup', this.onMouseUp, this);
27853         }
27854     },
27855     // private
27856     onMenuShow : function(e){
27857         this.el.addClass("x-btn-menu-active");
27858     },
27859     // private
27860     onMenuHide : function(e){
27861         this.el.removeClass("x-btn-menu-active");
27862     }   
27863 });
27864
27865 // Private utility class used by Button
27866 Roo.ButtonToggleMgr = function(){
27867    var groups = {};
27868    
27869    function toggleGroup(btn, state){
27870        if(state){
27871            var g = groups[btn.toggleGroup];
27872            for(var i = 0, l = g.length; i < l; i++){
27873                if(g[i] != btn){
27874                    g[i].toggle(false);
27875                }
27876            }
27877        }
27878    }
27879    
27880    return {
27881        register : function(btn){
27882            if(!btn.toggleGroup){
27883                return;
27884            }
27885            var g = groups[btn.toggleGroup];
27886            if(!g){
27887                g = groups[btn.toggleGroup] = [];
27888            }
27889            g.push(btn);
27890            btn.on("toggle", toggleGroup);
27891        },
27892        
27893        unregister : function(btn){
27894            if(!btn.toggleGroup){
27895                return;
27896            }
27897            var g = groups[btn.toggleGroup];
27898            if(g){
27899                g.remove(btn);
27900                btn.un("toggle", toggleGroup);
27901            }
27902        }
27903    };
27904 }();/*
27905  * Based on:
27906  * Ext JS Library 1.1.1
27907  * Copyright(c) 2006-2007, Ext JS, LLC.
27908  *
27909  * Originally Released Under LGPL - original licence link has changed is not relivant.
27910  *
27911  * Fork - LGPL
27912  * <script type="text/javascript">
27913  */
27914  
27915 /**
27916  * @class Roo.SplitButton
27917  * @extends Roo.Button
27918  * A split button that provides a built-in dropdown arrow that can fire an event separately from the default
27919  * click event of the button.  Typically this would be used to display a dropdown menu that provides additional
27920  * options to the primary button action, but any custom handler can provide the arrowclick implementation.
27921  * @cfg {Function} arrowHandler A function called when the arrow button is clicked (can be used instead of click event)
27922  * @cfg {String} arrowTooltip The title attribute of the arrow
27923  * @constructor
27924  * Create a new menu button
27925  * @param {String/HTMLElement/Element} renderTo The element to append the button to
27926  * @param {Object} config The config object
27927  */
27928 Roo.SplitButton = function(renderTo, config){
27929     Roo.SplitButton.superclass.constructor.call(this, renderTo, config);
27930     /**
27931      * @event arrowclick
27932      * Fires when this button's arrow is clicked
27933      * @param {SplitButton} this
27934      * @param {EventObject} e The click event
27935      */
27936     this.addEvents({"arrowclick":true});
27937 };
27938
27939 Roo.extend(Roo.SplitButton, Roo.Button, {
27940     render : function(renderTo){
27941         // this is one sweet looking template!
27942         var tpl = new Roo.Template(
27943             '<table cellspacing="0" class="x-btn-menu-wrap x-btn"><tr><td>',
27944             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-text-wrap"><tbody>',
27945             '<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>',
27946             "</tbody></table></td><td>",
27947             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-arrow-wrap"><tbody>',
27948             '<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>',
27949             "</tbody></table></td></tr></table>"
27950         );
27951         var btn = tpl.append(renderTo, [this.text, this.type], true);
27952         var btnEl = btn.child("button");
27953         if(this.cls){
27954             btn.addClass(this.cls);
27955         }
27956         if(this.icon){
27957             btnEl.setStyle('background-image', 'url(' +this.icon +')');
27958         }
27959         if(this.iconCls){
27960             btnEl.addClass(this.iconCls);
27961             if(!this.cls){
27962                 btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
27963             }
27964         }
27965         this.el = btn;
27966         if(this.handleMouseEvents){
27967             btn.on("mouseover", this.onMouseOver, this);
27968             btn.on("mouseout", this.onMouseOut, this);
27969             btn.on("mousedown", this.onMouseDown, this);
27970             btn.on("mouseup", this.onMouseUp, this);
27971         }
27972         btn.on(this.clickEvent, this.onClick, this);
27973         if(this.tooltip){
27974             if(typeof this.tooltip == 'object'){
27975                 Roo.QuickTips.tips(Roo.apply({
27976                       target: btnEl.id
27977                 }, this.tooltip));
27978             } else {
27979                 btnEl.dom[this.tooltipType] = this.tooltip;
27980             }
27981         }
27982         if(this.arrowTooltip){
27983             btn.child("button:nth(2)").dom[this.tooltipType] = this.arrowTooltip;
27984         }
27985         if(this.hidden){
27986             this.hide();
27987         }
27988         if(this.disabled){
27989             this.disable();
27990         }
27991         if(this.pressed){
27992             this.el.addClass("x-btn-pressed");
27993         }
27994         if(Roo.isIE && !Roo.isIE7){
27995             this.autoWidth.defer(1, this);
27996         }else{
27997             this.autoWidth();
27998         }
27999         if(this.menu){
28000             this.menu.on("show", this.onMenuShow, this);
28001             this.menu.on("hide", this.onMenuHide, this);
28002         }
28003         this.fireEvent('render', this);
28004     },
28005
28006     // private
28007     autoWidth : function(){
28008         if(this.el){
28009             var tbl = this.el.child("table:first");
28010             var tbl2 = this.el.child("table:last");
28011             this.el.setWidth("auto");
28012             tbl.setWidth("auto");
28013             if(Roo.isIE7 && Roo.isStrict){
28014                 var ib = this.el.child('button:first');
28015                 if(ib && ib.getWidth() > 20){
28016                     ib.clip();
28017                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
28018                 }
28019             }
28020             if(this.minWidth){
28021                 if(this.hidden){
28022                     this.el.beginMeasure();
28023                 }
28024                 if((tbl.getWidth()+tbl2.getWidth()) < this.minWidth){
28025                     tbl.setWidth(this.minWidth-tbl2.getWidth());
28026                 }
28027                 if(this.hidden){
28028                     this.el.endMeasure();
28029                 }
28030             }
28031             this.el.setWidth(tbl.getWidth()+tbl2.getWidth());
28032         } 
28033     },
28034     /**
28035      * Sets this button's click handler
28036      * @param {Function} handler The function to call when the button is clicked
28037      * @param {Object} scope (optional) Scope for the function passed above
28038      */
28039     setHandler : function(handler, scope){
28040         this.handler = handler;
28041         this.scope = scope;  
28042     },
28043     
28044     /**
28045      * Sets this button's arrow click handler
28046      * @param {Function} handler The function to call when the arrow is clicked
28047      * @param {Object} scope (optional) Scope for the function passed above
28048      */
28049     setArrowHandler : function(handler, scope){
28050         this.arrowHandler = handler;
28051         this.scope = scope;  
28052     },
28053     
28054     /**
28055      * Focus the button
28056      */
28057     focus : function(){
28058         if(this.el){
28059             this.el.child("button:first").focus();
28060         }
28061     },
28062
28063     // private
28064     onClick : function(e){
28065         e.preventDefault();
28066         if(!this.disabled){
28067             if(e.getTarget(".x-btn-menu-arrow-wrap")){
28068                 if(this.menu && !this.menu.isVisible()){
28069                     this.menu.show(this.el, this.menuAlign);
28070                 }
28071                 this.fireEvent("arrowclick", this, e);
28072                 if(this.arrowHandler){
28073                     this.arrowHandler.call(this.scope || this, this, e);
28074                 }
28075             }else{
28076                 this.fireEvent("click", this, e);
28077                 if(this.handler){
28078                     this.handler.call(this.scope || this, this, e);
28079                 }
28080             }
28081         }
28082     },
28083     // private
28084     onMouseDown : function(e){
28085         if(!this.disabled){
28086             Roo.fly(e.getTarget("table")).addClass("x-btn-click");
28087         }
28088     },
28089     // private
28090     onMouseUp : function(e){
28091         Roo.fly(e.getTarget("table")).removeClass("x-btn-click");
28092     }   
28093 });
28094
28095
28096 // backwards compat
28097 Roo.MenuButton = Roo.SplitButton;/*
28098  * Based on:
28099  * Ext JS Library 1.1.1
28100  * Copyright(c) 2006-2007, Ext JS, LLC.
28101  *
28102  * Originally Released Under LGPL - original licence link has changed is not relivant.
28103  *
28104  * Fork - LGPL
28105  * <script type="text/javascript">
28106  */
28107
28108 /**
28109  * @class Roo.Toolbar
28110  * Basic Toolbar class.
28111  * @constructor
28112  * Creates a new Toolbar
28113  * @param {Object} container The config object
28114  */ 
28115 Roo.Toolbar = function(container, buttons, config)
28116 {
28117     /// old consturctor format still supported..
28118     if(container instanceof Array){ // omit the container for later rendering
28119         buttons = container;
28120         config = buttons;
28121         container = null;
28122     }
28123     if (typeof(container) == 'object' && container.xtype) {
28124         config = container;
28125         container = config.container;
28126         buttons = config.buttons || []; // not really - use items!!
28127     }
28128     var xitems = [];
28129     if (config && config.items) {
28130         xitems = config.items;
28131         delete config.items;
28132     }
28133     Roo.apply(this, config);
28134     this.buttons = buttons;
28135     
28136     if(container){
28137         this.render(container);
28138     }
28139     this.xitems = xitems;
28140     Roo.each(xitems, function(b) {
28141         this.add(b);
28142     }, this);
28143     
28144 };
28145
28146 Roo.Toolbar.prototype = {
28147     /**
28148      * @cfg {Array} items
28149      * array of button configs or elements to add (will be converted to a MixedCollection)
28150      */
28151     
28152     /**
28153      * @cfg {String/HTMLElement/Element} container
28154      * The id or element that will contain the toolbar
28155      */
28156     // private
28157     render : function(ct){
28158         this.el = Roo.get(ct);
28159         if(this.cls){
28160             this.el.addClass(this.cls);
28161         }
28162         // using a table allows for vertical alignment
28163         // 100% width is needed by Safari...
28164         this.el.update('<div class="x-toolbar x-small-editor"><table cellspacing="0"><tr></tr></table></div>');
28165         this.tr = this.el.child("tr", true);
28166         var autoId = 0;
28167         this.items = new Roo.util.MixedCollection(false, function(o){
28168             return o.id || ("item" + (++autoId));
28169         });
28170         if(this.buttons){
28171             this.add.apply(this, this.buttons);
28172             delete this.buttons;
28173         }
28174     },
28175
28176     /**
28177      * Adds element(s) to the toolbar -- this function takes a variable number of 
28178      * arguments of mixed type and adds them to the toolbar.
28179      * @param {Mixed} arg1 The following types of arguments are all valid:<br />
28180      * <ul>
28181      * <li>{@link Roo.Toolbar.Button} config: A valid button config object (equivalent to {@link #addButton})</li>
28182      * <li>HtmlElement: Any standard HTML element (equivalent to {@link #addElement})</li>
28183      * <li>Field: Any form field (equivalent to {@link #addField})</li>
28184      * <li>Item: Any subclass of {@link Roo.Toolbar.Item} (equivalent to {@link #addItem})</li>
28185      * <li>String: Any generic string (gets wrapped in a {@link Roo.Toolbar.TextItem}, equivalent to {@link #addText}).
28186      * Note that there are a few special strings that are treated differently as explained nRoo.</li>
28187      * <li>'separator' or '-': Creates a separator element (equivalent to {@link #addSeparator})</li>
28188      * <li>' ': Creates a spacer element (equivalent to {@link #addSpacer})</li>
28189      * <li>'->': Creates a fill element (equivalent to {@link #addFill})</li>
28190      * </ul>
28191      * @param {Mixed} arg2
28192      * @param {Mixed} etc.
28193      */
28194     add : function(){
28195         var a = arguments, l = a.length;
28196         for(var i = 0; i < l; i++){
28197             this._add(a[i]);
28198         }
28199     },
28200     // private..
28201     _add : function(el) {
28202         
28203         if (el.xtype) {
28204             el = Roo.factory(el, typeof(Roo.Toolbar[el.xtype]) == 'undefined' ? Roo.form : Roo.Toolbar);
28205         }
28206         
28207         if (el.applyTo){ // some kind of form field
28208             return this.addField(el);
28209         } 
28210         if (el.render){ // some kind of Toolbar.Item
28211             return this.addItem(el);
28212         }
28213         if (typeof el == "string"){ // string
28214             if(el == "separator" || el == "-"){
28215                 return this.addSeparator();
28216             }
28217             if (el == " "){
28218                 return this.addSpacer();
28219             }
28220             if(el == "->"){
28221                 return this.addFill();
28222             }
28223             return this.addText(el);
28224             
28225         }
28226         if(el.tagName){ // element
28227             return this.addElement(el);
28228         }
28229         if(typeof el == "object"){ // must be button config?
28230             return this.addButton(el);
28231         }
28232         // and now what?!?!
28233         return false;
28234         
28235     },
28236     
28237     /**
28238      * Add an Xtype element
28239      * @param {Object} xtype Xtype Object
28240      * @return {Object} created Object
28241      */
28242     addxtype : function(e){
28243         return this.add(e);  
28244     },
28245     
28246     /**
28247      * Returns the Element for this toolbar.
28248      * @return {Roo.Element}
28249      */
28250     getEl : function(){
28251         return this.el;  
28252     },
28253     
28254     /**
28255      * Adds a separator
28256      * @return {Roo.Toolbar.Item} The separator item
28257      */
28258     addSeparator : function(){
28259         return this.addItem(new Roo.Toolbar.Separator());
28260     },
28261
28262     /**
28263      * Adds a spacer element
28264      * @return {Roo.Toolbar.Spacer} The spacer item
28265      */
28266     addSpacer : function(){
28267         return this.addItem(new Roo.Toolbar.Spacer());
28268     },
28269
28270     /**
28271      * Adds a fill element that forces subsequent additions to the right side of the toolbar
28272      * @return {Roo.Toolbar.Fill} The fill item
28273      */
28274     addFill : function(){
28275         return this.addItem(new Roo.Toolbar.Fill());
28276     },
28277
28278     /**
28279      * Adds any standard HTML element to the toolbar
28280      * @param {String/HTMLElement/Element} el The element or id of the element to add
28281      * @return {Roo.Toolbar.Item} The element's item
28282      */
28283     addElement : function(el){
28284         return this.addItem(new Roo.Toolbar.Item(el));
28285     },
28286     /**
28287      * Collection of items on the toolbar.. (only Toolbar Items, so use fields to retrieve fields)
28288      * @type Roo.util.MixedCollection  
28289      */
28290     items : false,
28291      
28292     /**
28293      * Adds any Toolbar.Item or subclass
28294      * @param {Roo.Toolbar.Item} item
28295      * @return {Roo.Toolbar.Item} The item
28296      */
28297     addItem : function(item){
28298         var td = this.nextBlock();
28299         item.render(td);
28300         this.items.add(item);
28301         return item;
28302     },
28303     
28304     /**
28305      * Adds a button (or buttons). See {@link Roo.Toolbar.Button} for more info on the config.
28306      * @param {Object/Array} config A button config or array of configs
28307      * @return {Roo.Toolbar.Button/Array}
28308      */
28309     addButton : function(config){
28310         if(config instanceof Array){
28311             var buttons = [];
28312             for(var i = 0, len = config.length; i < len; i++) {
28313                 buttons.push(this.addButton(config[i]));
28314             }
28315             return buttons;
28316         }
28317         var b = config;
28318         if(!(config instanceof Roo.Toolbar.Button)){
28319             b = config.split ?
28320                 new Roo.Toolbar.SplitButton(config) :
28321                 new Roo.Toolbar.Button(config);
28322         }
28323         var td = this.nextBlock();
28324         b.render(td);
28325         this.items.add(b);
28326         return b;
28327     },
28328     
28329     /**
28330      * Adds text to the toolbar
28331      * @param {String} text The text to add
28332      * @return {Roo.Toolbar.Item} The element's item
28333      */
28334     addText : function(text){
28335         return this.addItem(new Roo.Toolbar.TextItem(text));
28336     },
28337     
28338     /**
28339      * Inserts any {@link Roo.Toolbar.Item}/{@link Roo.Toolbar.Button} at the specified index.
28340      * @param {Number} index The index where the item is to be inserted
28341      * @param {Object/Roo.Toolbar.Item/Roo.Toolbar.Button (may be Array)} item The button, or button config object to be inserted.
28342      * @return {Roo.Toolbar.Button/Item}
28343      */
28344     insertButton : function(index, item){
28345         if(item instanceof Array){
28346             var buttons = [];
28347             for(var i = 0, len = item.length; i < len; i++) {
28348                buttons.push(this.insertButton(index + i, item[i]));
28349             }
28350             return buttons;
28351         }
28352         if (!(item instanceof Roo.Toolbar.Button)){
28353            item = new Roo.Toolbar.Button(item);
28354         }
28355         var td = document.createElement("td");
28356         this.tr.insertBefore(td, this.tr.childNodes[index]);
28357         item.render(td);
28358         this.items.insert(index, item);
28359         return item;
28360     },
28361     
28362     /**
28363      * Adds a new element to the toolbar from the passed {@link Roo.DomHelper} config.
28364      * @param {Object} config
28365      * @return {Roo.Toolbar.Item} The element's item
28366      */
28367     addDom : function(config, returnEl){
28368         var td = this.nextBlock();
28369         Roo.DomHelper.overwrite(td, config);
28370         var ti = new Roo.Toolbar.Item(td.firstChild);
28371         ti.render(td);
28372         this.items.add(ti);
28373         return ti;
28374     },
28375
28376     /**
28377      * Collection of fields on the toolbar.. usefull for quering (value is false if there are no fields)
28378      * @type Roo.util.MixedCollection  
28379      */
28380     fields : false,
28381     
28382     /**
28383      * Adds a dynamically rendered Roo.form field (TextField, ComboBox, etc).
28384      * Note: the field should not have been rendered yet. For a field that has already been
28385      * rendered, use {@link #addElement}.
28386      * @param {Roo.form.Field} field
28387      * @return {Roo.ToolbarItem}
28388      */
28389      
28390       
28391     addField : function(field) {
28392         if (!this.fields) {
28393             var autoId = 0;
28394             this.fields = new Roo.util.MixedCollection(false, function(o){
28395                 return o.id || ("item" + (++autoId));
28396             });
28397
28398         }
28399         
28400         var td = this.nextBlock();
28401         field.render(td);
28402         var ti = new Roo.Toolbar.Item(td.firstChild);
28403         ti.render(td);
28404         this.items.add(ti);
28405         this.fields.add(field);
28406         return ti;
28407     },
28408     /**
28409      * Hide the toolbar
28410      * @method hide
28411      */
28412      
28413       
28414     hide : function()
28415     {
28416         this.el.child('div').setVisibilityMode(Roo.Element.DISPLAY);
28417         this.el.child('div').hide();
28418     },
28419     /**
28420      * Show the toolbar
28421      * @method show
28422      */
28423     show : function()
28424     {
28425         this.el.child('div').show();
28426     },
28427       
28428     // private
28429     nextBlock : function(){
28430         var td = document.createElement("td");
28431         this.tr.appendChild(td);
28432         return td;
28433     },
28434
28435     // private
28436     destroy : function(){
28437         if(this.items){ // rendered?
28438             Roo.destroy.apply(Roo, this.items.items);
28439         }
28440         if(this.fields){ // rendered?
28441             Roo.destroy.apply(Roo, this.fields.items);
28442         }
28443         Roo.Element.uncache(this.el, this.tr);
28444     }
28445 };
28446
28447 /**
28448  * @class Roo.Toolbar.Item
28449  * The base class that other classes should extend in order to get some basic common toolbar item functionality.
28450  * @constructor
28451  * Creates a new Item
28452  * @param {HTMLElement} el 
28453  */
28454 Roo.Toolbar.Item = function(el){
28455     this.el = Roo.getDom(el);
28456     this.id = Roo.id(this.el);
28457     this.hidden = false;
28458 };
28459
28460 Roo.Toolbar.Item.prototype = {
28461     
28462     /**
28463      * Get this item's HTML Element
28464      * @return {HTMLElement}
28465      */
28466     getEl : function(){
28467        return this.el;  
28468     },
28469
28470     // private
28471     render : function(td){
28472         this.td = td;
28473         td.appendChild(this.el);
28474     },
28475     
28476     /**
28477      * Removes and destroys this item.
28478      */
28479     destroy : function(){
28480         this.td.parentNode.removeChild(this.td);
28481     },
28482     
28483     /**
28484      * Shows this item.
28485      */
28486     show: function(){
28487         this.hidden = false;
28488         this.td.style.display = "";
28489     },
28490     
28491     /**
28492      * Hides this item.
28493      */
28494     hide: function(){
28495         this.hidden = true;
28496         this.td.style.display = "none";
28497     },
28498     
28499     /**
28500      * Convenience function for boolean show/hide.
28501      * @param {Boolean} visible true to show/false to hide
28502      */
28503     setVisible: function(visible){
28504         if(visible) {
28505             this.show();
28506         }else{
28507             this.hide();
28508         }
28509     },
28510     
28511     /**
28512      * Try to focus this item.
28513      */
28514     focus : function(){
28515         Roo.fly(this.el).focus();
28516     },
28517     
28518     /**
28519      * Disables this item.
28520      */
28521     disable : function(){
28522         Roo.fly(this.td).addClass("x-item-disabled");
28523         this.disabled = true;
28524         this.el.disabled = true;
28525     },
28526     
28527     /**
28528      * Enables this item.
28529      */
28530     enable : function(){
28531         Roo.fly(this.td).removeClass("x-item-disabled");
28532         this.disabled = false;
28533         this.el.disabled = false;
28534     }
28535 };
28536
28537
28538 /**
28539  * @class Roo.Toolbar.Separator
28540  * @extends Roo.Toolbar.Item
28541  * A simple toolbar separator class
28542  * @constructor
28543  * Creates a new Separator
28544  */
28545 Roo.Toolbar.Separator = function(){
28546     var s = document.createElement("span");
28547     s.className = "ytb-sep";
28548     Roo.Toolbar.Separator.superclass.constructor.call(this, s);
28549 };
28550 Roo.extend(Roo.Toolbar.Separator, Roo.Toolbar.Item, {
28551     enable:Roo.emptyFn,
28552     disable:Roo.emptyFn,
28553     focus:Roo.emptyFn
28554 });
28555
28556 /**
28557  * @class Roo.Toolbar.Spacer
28558  * @extends Roo.Toolbar.Item
28559  * A simple element that adds extra horizontal space to a toolbar.
28560  * @constructor
28561  * Creates a new Spacer
28562  */
28563 Roo.Toolbar.Spacer = function(){
28564     var s = document.createElement("div");
28565     s.className = "ytb-spacer";
28566     Roo.Toolbar.Spacer.superclass.constructor.call(this, s);
28567 };
28568 Roo.extend(Roo.Toolbar.Spacer, Roo.Toolbar.Item, {
28569     enable:Roo.emptyFn,
28570     disable:Roo.emptyFn,
28571     focus:Roo.emptyFn
28572 });
28573
28574 /**
28575  * @class Roo.Toolbar.Fill
28576  * @extends Roo.Toolbar.Spacer
28577  * A simple element that adds a greedy (100% width) horizontal space to a toolbar.
28578  * @constructor
28579  * Creates a new Spacer
28580  */
28581 Roo.Toolbar.Fill = Roo.extend(Roo.Toolbar.Spacer, {
28582     // private
28583     render : function(td){
28584         td.style.width = '100%';
28585         Roo.Toolbar.Fill.superclass.render.call(this, td);
28586     }
28587 });
28588
28589 /**
28590  * @class Roo.Toolbar.TextItem
28591  * @extends Roo.Toolbar.Item
28592  * A simple class that renders text directly into a toolbar.
28593  * @constructor
28594  * Creates a new TextItem
28595  * @param {String} text
28596  */
28597 Roo.Toolbar.TextItem = function(text){
28598     if (typeof(text) == 'object') {
28599         text = text.text;
28600     }
28601     var s = document.createElement("span");
28602     s.className = "ytb-text";
28603     s.innerHTML = text;
28604     Roo.Toolbar.TextItem.superclass.constructor.call(this, s);
28605 };
28606 Roo.extend(Roo.Toolbar.TextItem, Roo.Toolbar.Item, {
28607     enable:Roo.emptyFn,
28608     disable:Roo.emptyFn,
28609     focus:Roo.emptyFn
28610 });
28611
28612 /**
28613  * @class Roo.Toolbar.Button
28614  * @extends Roo.Button
28615  * A button that renders into a toolbar.
28616  * @constructor
28617  * Creates a new Button
28618  * @param {Object} config A standard {@link Roo.Button} config object
28619  */
28620 Roo.Toolbar.Button = function(config){
28621     Roo.Toolbar.Button.superclass.constructor.call(this, null, config);
28622 };
28623 Roo.extend(Roo.Toolbar.Button, Roo.Button, {
28624     render : function(td){
28625         this.td = td;
28626         Roo.Toolbar.Button.superclass.render.call(this, td);
28627     },
28628     
28629     /**
28630      * Removes and destroys this button
28631      */
28632     destroy : function(){
28633         Roo.Toolbar.Button.superclass.destroy.call(this);
28634         this.td.parentNode.removeChild(this.td);
28635     },
28636     
28637     /**
28638      * Shows this button
28639      */
28640     show: function(){
28641         this.hidden = false;
28642         this.td.style.display = "";
28643     },
28644     
28645     /**
28646      * Hides this button
28647      */
28648     hide: function(){
28649         this.hidden = true;
28650         this.td.style.display = "none";
28651     },
28652
28653     /**
28654      * Disables this item
28655      */
28656     disable : function(){
28657         Roo.fly(this.td).addClass("x-item-disabled");
28658         this.disabled = true;
28659     },
28660
28661     /**
28662      * Enables this item
28663      */
28664     enable : function(){
28665         Roo.fly(this.td).removeClass("x-item-disabled");
28666         this.disabled = false;
28667     }
28668 });
28669 // backwards compat
28670 Roo.ToolbarButton = Roo.Toolbar.Button;
28671
28672 /**
28673  * @class Roo.Toolbar.SplitButton
28674  * @extends Roo.SplitButton
28675  * A menu button that renders into a toolbar.
28676  * @constructor
28677  * Creates a new SplitButton
28678  * @param {Object} config A standard {@link Roo.SplitButton} config object
28679  */
28680 Roo.Toolbar.SplitButton = function(config){
28681     Roo.Toolbar.SplitButton.superclass.constructor.call(this, null, config);
28682 };
28683 Roo.extend(Roo.Toolbar.SplitButton, Roo.SplitButton, {
28684     render : function(td){
28685         this.td = td;
28686         Roo.Toolbar.SplitButton.superclass.render.call(this, td);
28687     },
28688     
28689     /**
28690      * Removes and destroys this button
28691      */
28692     destroy : function(){
28693         Roo.Toolbar.SplitButton.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 // backwards compat
28715 Roo.Toolbar.MenuButton = Roo.Toolbar.SplitButton;/*
28716  * Based on:
28717  * Ext JS Library 1.1.1
28718  * Copyright(c) 2006-2007, Ext JS, LLC.
28719  *
28720  * Originally Released Under LGPL - original licence link has changed is not relivant.
28721  *
28722  * Fork - LGPL
28723  * <script type="text/javascript">
28724  */
28725  
28726 /**
28727  * @class Roo.PagingToolbar
28728  * @extends Roo.Toolbar
28729  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
28730  * @constructor
28731  * Create a new PagingToolbar
28732  * @param {Object} config The config object
28733  */
28734 Roo.PagingToolbar = function(el, ds, config)
28735 {
28736     // old args format still supported... - xtype is prefered..
28737     if (typeof(el) == 'object' && el.xtype) {
28738         // created from xtype...
28739         config = el;
28740         ds = el.dataSource;
28741         el = config.container;
28742     }
28743     var items = [];
28744     if (config.items) {
28745         items = config.items;
28746         config.items = [];
28747     }
28748     
28749     Roo.PagingToolbar.superclass.constructor.call(this, el, null, config);
28750     this.ds = ds;
28751     this.cursor = 0;
28752     this.renderButtons(this.el);
28753     this.bind(ds);
28754     
28755     // supprot items array.
28756    
28757     Roo.each(items, function(e) {
28758         this.add(Roo.factory(e));
28759     },this);
28760     
28761 };
28762
28763 Roo.extend(Roo.PagingToolbar, Roo.Toolbar, {
28764     /**
28765      * @cfg {Roo.data.Store} dataSource
28766      * The underlying data store providing the paged data
28767      */
28768     /**
28769      * @cfg {String/HTMLElement/Element} container
28770      * container The id or element that will contain the toolbar
28771      */
28772     /**
28773      * @cfg {Boolean} displayInfo
28774      * True to display the displayMsg (defaults to false)
28775      */
28776     /**
28777      * @cfg {Number} pageSize
28778      * The number of records to display per page (defaults to 20)
28779      */
28780     pageSize: 20,
28781     /**
28782      * @cfg {String} displayMsg
28783      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
28784      */
28785     displayMsg : 'Displaying {0} - {1} of {2}',
28786     /**
28787      * @cfg {String} emptyMsg
28788      * The message to display when no records are found (defaults to "No data to display")
28789      */
28790     emptyMsg : 'No data to display',
28791     /**
28792      * Customizable piece of the default paging text (defaults to "Page")
28793      * @type String
28794      */
28795     beforePageText : "Page",
28796     /**
28797      * Customizable piece of the default paging text (defaults to "of %0")
28798      * @type String
28799      */
28800     afterPageText : "of {0}",
28801     /**
28802      * Customizable piece of the default paging text (defaults to "First Page")
28803      * @type String
28804      */
28805     firstText : "First Page",
28806     /**
28807      * Customizable piece of the default paging text (defaults to "Previous Page")
28808      * @type String
28809      */
28810     prevText : "Previous Page",
28811     /**
28812      * Customizable piece of the default paging text (defaults to "Next Page")
28813      * @type String
28814      */
28815     nextText : "Next Page",
28816     /**
28817      * Customizable piece of the default paging text (defaults to "Last Page")
28818      * @type String
28819      */
28820     lastText : "Last Page",
28821     /**
28822      * Customizable piece of the default paging text (defaults to "Refresh")
28823      * @type String
28824      */
28825     refreshText : "Refresh",
28826
28827     // private
28828     renderButtons : function(el){
28829         Roo.PagingToolbar.superclass.render.call(this, el);
28830         this.first = this.addButton({
28831             tooltip: this.firstText,
28832             cls: "x-btn-icon x-grid-page-first",
28833             disabled: true,
28834             handler: this.onClick.createDelegate(this, ["first"])
28835         });
28836         this.prev = this.addButton({
28837             tooltip: this.prevText,
28838             cls: "x-btn-icon x-grid-page-prev",
28839             disabled: true,
28840             handler: this.onClick.createDelegate(this, ["prev"])
28841         });
28842         //this.addSeparator();
28843         this.add(this.beforePageText);
28844         this.field = Roo.get(this.addDom({
28845            tag: "input",
28846            type: "text",
28847            size: "3",
28848            value: "1",
28849            cls: "x-grid-page-number"
28850         }).el);
28851         this.field.on("keydown", this.onPagingKeydown, this);
28852         this.field.on("focus", function(){this.dom.select();});
28853         this.afterTextEl = this.addText(String.format(this.afterPageText, 1));
28854         this.field.setHeight(18);
28855         //this.addSeparator();
28856         this.next = this.addButton({
28857             tooltip: this.nextText,
28858             cls: "x-btn-icon x-grid-page-next",
28859             disabled: true,
28860             handler: this.onClick.createDelegate(this, ["next"])
28861         });
28862         this.last = this.addButton({
28863             tooltip: this.lastText,
28864             cls: "x-btn-icon x-grid-page-last",
28865             disabled: true,
28866             handler: this.onClick.createDelegate(this, ["last"])
28867         });
28868         //this.addSeparator();
28869         this.loading = this.addButton({
28870             tooltip: this.refreshText,
28871             cls: "x-btn-icon x-grid-loading",
28872             handler: this.onClick.createDelegate(this, ["refresh"])
28873         });
28874
28875         if(this.displayInfo){
28876             this.displayEl = Roo.fly(this.el.dom.firstChild).createChild({cls:'x-paging-info'});
28877         }
28878     },
28879
28880     // private
28881     updateInfo : function(){
28882         if(this.displayEl){
28883             var count = this.ds.getCount();
28884             var msg = count == 0 ?
28885                 this.emptyMsg :
28886                 String.format(
28887                     this.displayMsg,
28888                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
28889                 );
28890             this.displayEl.update(msg);
28891         }
28892     },
28893
28894     // private
28895     onLoad : function(ds, r, o){
28896        this.cursor = o.params ? o.params.start : 0;
28897        var d = this.getPageData(), ap = d.activePage, ps = d.pages;
28898
28899        this.afterTextEl.el.innerHTML = String.format(this.afterPageText, d.pages);
28900        this.field.dom.value = ap;
28901        this.first.setDisabled(ap == 1);
28902        this.prev.setDisabled(ap == 1);
28903        this.next.setDisabled(ap == ps);
28904        this.last.setDisabled(ap == ps);
28905        this.loading.enable();
28906        this.updateInfo();
28907     },
28908
28909     // private
28910     getPageData : function(){
28911         var total = this.ds.getTotalCount();
28912         return {
28913             total : total,
28914             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
28915             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
28916         };
28917     },
28918
28919     // private
28920     onLoadError : function(){
28921         this.loading.enable();
28922     },
28923
28924     // private
28925     onPagingKeydown : function(e){
28926         var k = e.getKey();
28927         var d = this.getPageData();
28928         if(k == e.RETURN){
28929             var v = this.field.dom.value, pageNum;
28930             if(!v || isNaN(pageNum = parseInt(v, 10))){
28931                 this.field.dom.value = d.activePage;
28932                 return;
28933             }
28934             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
28935             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
28936             e.stopEvent();
28937         }
28938         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))
28939         {
28940           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
28941           this.field.dom.value = pageNum;
28942           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
28943           e.stopEvent();
28944         }
28945         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
28946         {
28947           var v = this.field.dom.value, pageNum; 
28948           var increment = (e.shiftKey) ? 10 : 1;
28949           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
28950             increment *= -1;
28951           if(!v || isNaN(pageNum = parseInt(v, 10))) {
28952             this.field.dom.value = d.activePage;
28953             return;
28954           }
28955           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
28956           {
28957             this.field.dom.value = parseInt(v, 10) + increment;
28958             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
28959             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
28960           }
28961           e.stopEvent();
28962         }
28963     },
28964
28965     // private
28966     beforeLoad : function(){
28967         if(this.loading){
28968             this.loading.disable();
28969         }
28970     },
28971
28972     // private
28973     onClick : function(which){
28974         var ds = this.ds;
28975         switch(which){
28976             case "first":
28977                 ds.load({params:{start: 0, limit: this.pageSize}});
28978             break;
28979             case "prev":
28980                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
28981             break;
28982             case "next":
28983                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
28984             break;
28985             case "last":
28986                 var total = ds.getTotalCount();
28987                 var extra = total % this.pageSize;
28988                 var lastStart = extra ? (total - extra) : total-this.pageSize;
28989                 ds.load({params:{start: lastStart, limit: this.pageSize}});
28990             break;
28991             case "refresh":
28992                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
28993             break;
28994         }
28995     },
28996
28997     /**
28998      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
28999      * @param {Roo.data.Store} store The data store to unbind
29000      */
29001     unbind : function(ds){
29002         ds.un("beforeload", this.beforeLoad, this);
29003         ds.un("load", this.onLoad, this);
29004         ds.un("loadexception", this.onLoadError, this);
29005         ds.un("remove", this.updateInfo, this);
29006         ds.un("add", this.updateInfo, this);
29007         this.ds = undefined;
29008     },
29009
29010     /**
29011      * Binds the paging toolbar to the specified {@link Roo.data.Store}
29012      * @param {Roo.data.Store} store The data store to bind
29013      */
29014     bind : function(ds){
29015         ds.on("beforeload", this.beforeLoad, this);
29016         ds.on("load", this.onLoad, this);
29017         ds.on("loadexception", this.onLoadError, this);
29018         ds.on("remove", this.updateInfo, this);
29019         ds.on("add", this.updateInfo, this);
29020         this.ds = ds;
29021     }
29022 });/*
29023  * Based on:
29024  * Ext JS Library 1.1.1
29025  * Copyright(c) 2006-2007, Ext JS, LLC.
29026  *
29027  * Originally Released Under LGPL - original licence link has changed is not relivant.
29028  *
29029  * Fork - LGPL
29030  * <script type="text/javascript">
29031  */
29032
29033 /**
29034  * @class Roo.Resizable
29035  * @extends Roo.util.Observable
29036  * <p>Applies drag handles to an element to make it resizable. The drag handles are inserted into the element
29037  * and positioned absolute. Some elements, such as a textarea or image, don't support this. To overcome that, you can wrap
29038  * 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
29039  * the element will be wrapped for you automatically.</p>
29040  * <p>Here is the list of valid resize handles:</p>
29041  * <pre>
29042 Value   Description
29043 ------  -------------------
29044  'n'     north
29045  's'     south
29046  'e'     east
29047  'w'     west
29048  'nw'    northwest
29049  'sw'    southwest
29050  'se'    southeast
29051  'ne'    northeast
29052  'hd'    horizontal drag
29053  'all'   all
29054 </pre>
29055  * <p>Here's an example showing the creation of a typical Resizable:</p>
29056  * <pre><code>
29057 var resizer = new Roo.Resizable("element-id", {
29058     handles: 'all',
29059     minWidth: 200,
29060     minHeight: 100,
29061     maxWidth: 500,
29062     maxHeight: 400,
29063     pinned: true
29064 });
29065 resizer.on("resize", myHandler);
29066 </code></pre>
29067  * <p>To hide a particular handle, set its display to none in CSS, or through script:<br>
29068  * resizer.east.setDisplayed(false);</p>
29069  * @cfg {Boolean/String/Element} resizeChild True to resize the first child, or id/element to resize (defaults to false)
29070  * @cfg {Array/String} adjustments String "auto" or an array [width, height] with values to be <b>added</b> to the
29071  * resize operation's new size (defaults to [0, 0])
29072  * @cfg {Number} minWidth The minimum width for the element (defaults to 5)
29073  * @cfg {Number} minHeight The minimum height for the element (defaults to 5)
29074  * @cfg {Number} maxWidth The maximum width for the element (defaults to 10000)
29075  * @cfg {Number} maxHeight The maximum height for the element (defaults to 10000)
29076  * @cfg {Boolean} enabled False to disable resizing (defaults to true)
29077  * @cfg {Boolean} wrap True to wrap an element with a div if needed (required for textareas and images, defaults to false)
29078  * @cfg {Number} width The width of the element in pixels (defaults to null)
29079  * @cfg {Number} height The height of the element in pixels (defaults to null)
29080  * @cfg {Boolean} animate True to animate the resize (not compatible with dynamic sizing, defaults to false)
29081  * @cfg {Number} duration Animation duration if animate = true (defaults to .35)
29082  * @cfg {Boolean} dynamic True to resize the element while dragging instead of using a proxy (defaults to false)
29083  * @cfg {String} handles String consisting of the resize handles to display (defaults to undefined)
29084  * @cfg {Boolean} multiDirectional <b>Deprecated</b>.  The old style of adding multi-direction resize handles, deprecated
29085  * in favor of the handles config option (defaults to false)
29086  * @cfg {Boolean} disableTrackOver True to disable mouse tracking. This is only applied at config time. (defaults to false)
29087  * @cfg {String} easing Animation easing if animate = true (defaults to 'easingOutStrong')
29088  * @cfg {Number} widthIncrement The increment to snap the width resize in pixels (dynamic must be true, defaults to 0)
29089  * @cfg {Number} heightIncrement The increment to snap the height resize in pixels (dynamic must be true, defaults to 0)
29090  * @cfg {Boolean} pinned True to ensure that the resize handles are always visible, false to display them only when the
29091  * user mouses over the resizable borders. This is only applied at config time. (defaults to false)
29092  * @cfg {Boolean} preserveRatio True to preserve the original ratio between height and width during resize (defaults to false)
29093  * @cfg {Boolean} transparent True for transparent handles. This is only applied at config time. (defaults to false)
29094  * @cfg {Number} minX The minimum allowed page X for the element (only used for west resizing, defaults to 0)
29095  * @cfg {Number} minY The minimum allowed page Y for the element (only used for north resizing, defaults to 0)
29096  * @cfg {Boolean} draggable Convenience to initialize drag drop (defaults to false)
29097  * @constructor
29098  * Create a new resizable component
29099  * @param {String/HTMLElement/Roo.Element} el The id or element to resize
29100  * @param {Object} config configuration options
29101   */
29102 Roo.Resizable = function(el, config)
29103 {
29104     this.el = Roo.get(el);
29105
29106     if(config && config.wrap){
29107         config.resizeChild = this.el;
29108         this.el = this.el.wrap(typeof config.wrap == "object" ? config.wrap : {cls:"xresizable-wrap"});
29109         this.el.id = this.el.dom.id = config.resizeChild.id + "-rzwrap";
29110         this.el.setStyle("overflow", "hidden");
29111         this.el.setPositioning(config.resizeChild.getPositioning());
29112         config.resizeChild.clearPositioning();
29113         if(!config.width || !config.height){
29114             var csize = config.resizeChild.getSize();
29115             this.el.setSize(csize.width, csize.height);
29116         }
29117         if(config.pinned && !config.adjustments){
29118             config.adjustments = "auto";
29119         }
29120     }
29121
29122     this.proxy = this.el.createProxy({tag: "div", cls: "x-resizable-proxy", id: this.el.id + "-rzproxy"});
29123     this.proxy.unselectable();
29124     this.proxy.enableDisplayMode('block');
29125
29126     Roo.apply(this, config);
29127
29128     if(this.pinned){
29129         this.disableTrackOver = true;
29130         this.el.addClass("x-resizable-pinned");
29131     }
29132     // if the element isn't positioned, make it relative
29133     var position = this.el.getStyle("position");
29134     if(position != "absolute" && position != "fixed"){
29135         this.el.setStyle("position", "relative");
29136     }
29137     if(!this.handles){ // no handles passed, must be legacy style
29138         this.handles = 's,e,se';
29139         if(this.multiDirectional){
29140             this.handles += ',n,w';
29141         }
29142     }
29143     if(this.handles == "all"){
29144         this.handles = "n s e w ne nw se sw";
29145     }
29146     var hs = this.handles.split(/\s*?[,;]\s*?| /);
29147     var ps = Roo.Resizable.positions;
29148     for(var i = 0, len = hs.length; i < len; i++){
29149         if(hs[i] && ps[hs[i]]){
29150             var pos = ps[hs[i]];
29151             this[pos] = new Roo.Resizable.Handle(this, pos, this.disableTrackOver, this.transparent);
29152         }
29153     }
29154     // legacy
29155     this.corner = this.southeast;
29156     
29157     // updateBox = the box can move..
29158     if(this.handles.indexOf("n") != -1 || this.handles.indexOf("w") != -1 || this.handles.indexOf("hd") != -1) {
29159         this.updateBox = true;
29160     }
29161
29162     this.activeHandle = null;
29163
29164     if(this.resizeChild){
29165         if(typeof this.resizeChild == "boolean"){
29166             this.resizeChild = Roo.get(this.el.dom.firstChild, true);
29167         }else{
29168             this.resizeChild = Roo.get(this.resizeChild, true);
29169         }
29170     }
29171     
29172     if(this.adjustments == "auto"){
29173         var rc = this.resizeChild;
29174         var hw = this.west, he = this.east, hn = this.north, hs = this.south;
29175         if(rc && (hw || hn)){
29176             rc.position("relative");
29177             rc.setLeft(hw ? hw.el.getWidth() : 0);
29178             rc.setTop(hn ? hn.el.getHeight() : 0);
29179         }
29180         this.adjustments = [
29181             (he ? -he.el.getWidth() : 0) + (hw ? -hw.el.getWidth() : 0),
29182             (hn ? -hn.el.getHeight() : 0) + (hs ? -hs.el.getHeight() : 0) -1
29183         ];
29184     }
29185
29186     if(this.draggable){
29187         this.dd = this.dynamic ?
29188             this.el.initDD(null) : this.el.initDDProxy(null, {dragElId: this.proxy.id});
29189         this.dd.setHandleElId(this.resizeChild ? this.resizeChild.id : this.el.id);
29190     }
29191
29192     // public events
29193     this.addEvents({
29194         /**
29195          * @event beforeresize
29196          * Fired before resize is allowed. Set enabled to false to cancel resize.
29197          * @param {Roo.Resizable} this
29198          * @param {Roo.EventObject} e The mousedown event
29199          */
29200         "beforeresize" : true,
29201         /**
29202          * @event resizing
29203          * Fired a resizing.
29204          * @param {Roo.Resizable} this
29205          * @param {Number} x The new x position
29206          * @param {Number} y The new y position
29207          * @param {Number} w The new w width
29208          * @param {Number} h The new h hight
29209          * @param {Roo.EventObject} e The mouseup event
29210          */
29211         "resizing" : true,
29212         /**
29213          * @event resize
29214          * Fired after a resize.
29215          * @param {Roo.Resizable} this
29216          * @param {Number} width The new width
29217          * @param {Number} height The new height
29218          * @param {Roo.EventObject} e The mouseup event
29219          */
29220         "resize" : true
29221     });
29222
29223     if(this.width !== null && this.height !== null){
29224         this.resizeTo(this.width, this.height);
29225     }else{
29226         this.updateChildSize();
29227     }
29228     if(Roo.isIE){
29229         this.el.dom.style.zoom = 1;
29230     }
29231     Roo.Resizable.superclass.constructor.call(this);
29232 };
29233
29234 Roo.extend(Roo.Resizable, Roo.util.Observable, {
29235         resizeChild : false,
29236         adjustments : [0, 0],
29237         minWidth : 5,
29238         minHeight : 5,
29239         maxWidth : 10000,
29240         maxHeight : 10000,
29241         enabled : true,
29242         animate : false,
29243         duration : .35,
29244         dynamic : false,
29245         handles : false,
29246         multiDirectional : false,
29247         disableTrackOver : false,
29248         easing : 'easeOutStrong',
29249         widthIncrement : 0,
29250         heightIncrement : 0,
29251         pinned : false,
29252         width : null,
29253         height : null,
29254         preserveRatio : false,
29255         transparent: false,
29256         minX: 0,
29257         minY: 0,
29258         draggable: false,
29259
29260         /**
29261          * @cfg {String/HTMLElement/Element} constrainTo Constrain the resize to a particular element
29262          */
29263         constrainTo: undefined,
29264         /**
29265          * @cfg {Roo.lib.Region} resizeRegion Constrain the resize to a particular region
29266          */
29267         resizeRegion: undefined,
29268
29269
29270     /**
29271      * Perform a manual resize
29272      * @param {Number} width
29273      * @param {Number} height
29274      */
29275     resizeTo : function(width, height){
29276         this.el.setSize(width, height);
29277         this.updateChildSize();
29278         this.fireEvent("resize", this, width, height, null);
29279     },
29280
29281     // private
29282     startSizing : function(e, handle){
29283         this.fireEvent("beforeresize", this, e);
29284         if(this.enabled){ // 2nd enabled check in case disabled before beforeresize handler
29285
29286             if(!this.overlay){
29287                 this.overlay = this.el.createProxy({tag: "div", cls: "x-resizable-overlay", html: "&#160;"});
29288                 this.overlay.unselectable();
29289                 this.overlay.enableDisplayMode("block");
29290                 this.overlay.on("mousemove", this.onMouseMove, this);
29291                 this.overlay.on("mouseup", this.onMouseUp, this);
29292             }
29293             this.overlay.setStyle("cursor", handle.el.getStyle("cursor"));
29294
29295             this.resizing = true;
29296             this.startBox = this.el.getBox();
29297             this.startPoint = e.getXY();
29298             this.offsets = [(this.startBox.x + this.startBox.width) - this.startPoint[0],
29299                             (this.startBox.y + this.startBox.height) - this.startPoint[1]];
29300
29301             this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
29302             this.overlay.show();
29303
29304             if(this.constrainTo) {
29305                 var ct = Roo.get(this.constrainTo);
29306                 this.resizeRegion = ct.getRegion().adjust(
29307                     ct.getFrameWidth('t'),
29308                     ct.getFrameWidth('l'),
29309                     -ct.getFrameWidth('b'),
29310                     -ct.getFrameWidth('r')
29311                 );
29312             }
29313
29314             this.proxy.setStyle('visibility', 'hidden'); // workaround display none
29315             this.proxy.show();
29316             this.proxy.setBox(this.startBox);
29317             if(!this.dynamic){
29318                 this.proxy.setStyle('visibility', 'visible');
29319             }
29320         }
29321     },
29322
29323     // private
29324     onMouseDown : function(handle, e){
29325         if(this.enabled){
29326             e.stopEvent();
29327             this.activeHandle = handle;
29328             this.startSizing(e, handle);
29329         }
29330     },
29331
29332     // private
29333     onMouseUp : function(e){
29334         var size = this.resizeElement();
29335         this.resizing = false;
29336         this.handleOut();
29337         this.overlay.hide();
29338         this.proxy.hide();
29339         this.fireEvent("resize", this, size.width, size.height, e);
29340     },
29341
29342     // private
29343     updateChildSize : function(){
29344         
29345         if(this.resizeChild){
29346             var el = this.el;
29347             var child = this.resizeChild;
29348             var adj = this.adjustments;
29349             if(el.dom.offsetWidth){
29350                 var b = el.getSize(true);
29351                 child.setSize(b.width+adj[0], b.height+adj[1]);
29352             }
29353             // Second call here for IE
29354             // The first call enables instant resizing and
29355             // the second call corrects scroll bars if they
29356             // exist
29357             if(Roo.isIE){
29358                 setTimeout(function(){
29359                     if(el.dom.offsetWidth){
29360                         var b = el.getSize(true);
29361                         child.setSize(b.width+adj[0], b.height+adj[1]);
29362                     }
29363                 }, 10);
29364             }
29365         }
29366     },
29367
29368     // private
29369     snap : function(value, inc, min){
29370         if(!inc || !value) return value;
29371         var newValue = value;
29372         var m = value % inc;
29373         if(m > 0){
29374             if(m > (inc/2)){
29375                 newValue = value + (inc-m);
29376             }else{
29377                 newValue = value - m;
29378             }
29379         }
29380         return Math.max(min, newValue);
29381     },
29382
29383     // private
29384     resizeElement : function(){
29385         var box = this.proxy.getBox();
29386         if(this.updateBox){
29387             this.el.setBox(box, false, this.animate, this.duration, null, this.easing);
29388         }else{
29389             this.el.setSize(box.width, box.height, this.animate, this.duration, null, this.easing);
29390         }
29391         this.updateChildSize();
29392         if(!this.dynamic){
29393             this.proxy.hide();
29394         }
29395         return box;
29396     },
29397
29398     // private
29399     constrain : function(v, diff, m, mx){
29400         if(v - diff < m){
29401             diff = v - m;
29402         }else if(v - diff > mx){
29403             diff = mx - v;
29404         }
29405         return diff;
29406     },
29407
29408     // private
29409     onMouseMove : function(e){
29410         
29411         if(this.enabled){
29412             try{// try catch so if something goes wrong the user doesn't get hung
29413
29414             if(this.resizeRegion && !this.resizeRegion.contains(e.getPoint())) {
29415                 return;
29416             }
29417
29418             //var curXY = this.startPoint;
29419             var curSize = this.curSize || this.startBox;
29420             var x = this.startBox.x, y = this.startBox.y;
29421             var ox = x, oy = y;
29422             var w = curSize.width, h = curSize.height;
29423             var ow = w, oh = h;
29424             var mw = this.minWidth, mh = this.minHeight;
29425             var mxw = this.maxWidth, mxh = this.maxHeight;
29426             var wi = this.widthIncrement;
29427             var hi = this.heightIncrement;
29428
29429             var eventXY = e.getXY();
29430             var diffX = -(this.startPoint[0] - Math.max(this.minX, eventXY[0]));
29431             var diffY = -(this.startPoint[1] - Math.max(this.minY, eventXY[1]));
29432
29433             var pos = this.activeHandle.position;
29434
29435             switch(pos){
29436                 case "east":
29437                     w += diffX;
29438                     w = Math.min(Math.max(mw, w), mxw);
29439                     break;
29440              
29441                 case "south":
29442                     h += diffY;
29443                     h = Math.min(Math.max(mh, h), mxh);
29444                     break;
29445                 case "southeast":
29446                     w += diffX;
29447                     h += diffY;
29448                     w = Math.min(Math.max(mw, w), mxw);
29449                     h = Math.min(Math.max(mh, h), mxh);
29450                     break;
29451                 case "north":
29452                     diffY = this.constrain(h, diffY, mh, mxh);
29453                     y += diffY;
29454                     h -= diffY;
29455                     break;
29456                 case "hdrag":
29457                     
29458                     if (wi) {
29459                         var adiffX = Math.abs(diffX);
29460                         var sub = (adiffX % wi); // how much 
29461                         if (sub > (wi/2)) { // far enough to snap
29462                             diffX = (diffX > 0) ? diffX-sub + wi : diffX+sub - wi;
29463                         } else {
29464                             // remove difference.. 
29465                             diffX = (diffX > 0) ? diffX-sub : diffX+sub;
29466                         }
29467                     }
29468                     x += diffX;
29469                     x = Math.max(this.minX, x);
29470                     break;
29471                 case "west":
29472                     diffX = this.constrain(w, diffX, mw, mxw);
29473                     x += diffX;
29474                     w -= diffX;
29475                     break;
29476                 case "northeast":
29477                     w += diffX;
29478                     w = Math.min(Math.max(mw, w), mxw);
29479                     diffY = this.constrain(h, diffY, mh, mxh);
29480                     y += diffY;
29481                     h -= diffY;
29482                     break;
29483                 case "northwest":
29484                     diffX = this.constrain(w, diffX, mw, mxw);
29485                     diffY = this.constrain(h, diffY, mh, mxh);
29486                     y += diffY;
29487                     h -= diffY;
29488                     x += diffX;
29489                     w -= diffX;
29490                     break;
29491                case "southwest":
29492                     diffX = this.constrain(w, diffX, mw, mxw);
29493                     h += diffY;
29494                     h = Math.min(Math.max(mh, h), mxh);
29495                     x += diffX;
29496                     w -= diffX;
29497                     break;
29498             }
29499
29500             var sw = this.snap(w, wi, mw);
29501             var sh = this.snap(h, hi, mh);
29502             if(sw != w || sh != h){
29503                 switch(pos){
29504                     case "northeast":
29505                         y -= sh - h;
29506                     break;
29507                     case "north":
29508                         y -= sh - h;
29509                         break;
29510                     case "southwest":
29511                         x -= sw - w;
29512                     break;
29513                     case "west":
29514                         x -= sw - w;
29515                         break;
29516                     case "northwest":
29517                         x -= sw - w;
29518                         y -= sh - h;
29519                     break;
29520                 }
29521                 w = sw;
29522                 h = sh;
29523             }
29524
29525             if(this.preserveRatio){
29526                 switch(pos){
29527                     case "southeast":
29528                     case "east":
29529                         h = oh * (w/ow);
29530                         h = Math.min(Math.max(mh, h), mxh);
29531                         w = ow * (h/oh);
29532                        break;
29533                     case "south":
29534                         w = ow * (h/oh);
29535                         w = Math.min(Math.max(mw, w), mxw);
29536                         h = oh * (w/ow);
29537                         break;
29538                     case "northeast":
29539                         w = ow * (h/oh);
29540                         w = Math.min(Math.max(mw, w), mxw);
29541                         h = oh * (w/ow);
29542                     break;
29543                     case "north":
29544                         var tw = w;
29545                         w = ow * (h/oh);
29546                         w = Math.min(Math.max(mw, w), mxw);
29547                         h = oh * (w/ow);
29548                         x += (tw - w) / 2;
29549                         break;
29550                     case "southwest":
29551                         h = oh * (w/ow);
29552                         h = Math.min(Math.max(mh, h), mxh);
29553                         var tw = w;
29554                         w = ow * (h/oh);
29555                         x += tw - w;
29556                         break;
29557                     case "west":
29558                         var th = h;
29559                         h = oh * (w/ow);
29560                         h = Math.min(Math.max(mh, h), mxh);
29561                         y += (th - h) / 2;
29562                         var tw = w;
29563                         w = ow * (h/oh);
29564                         x += tw - w;
29565                        break;
29566                     case "northwest":
29567                         var tw = w;
29568                         var th = h;
29569                         h = oh * (w/ow);
29570                         h = Math.min(Math.max(mh, h), mxh);
29571                         w = ow * (h/oh);
29572                         y += th - h;
29573                         x += tw - w;
29574                        break;
29575
29576                 }
29577             }
29578             if (pos == 'hdrag') {
29579                 w = ow;
29580             }
29581             this.proxy.setBounds(x, y, w, h);
29582             if(this.dynamic){
29583                 this.resizeElement();
29584             }
29585             }catch(e){}
29586         }
29587         this.fireEvent("resizing", this, x, y, w, h, e);
29588     },
29589
29590     // private
29591     handleOver : function(){
29592         if(this.enabled){
29593             this.el.addClass("x-resizable-over");
29594         }
29595     },
29596
29597     // private
29598     handleOut : function(){
29599         if(!this.resizing){
29600             this.el.removeClass("x-resizable-over");
29601         }
29602     },
29603
29604     /**
29605      * Returns the element this component is bound to.
29606      * @return {Roo.Element}
29607      */
29608     getEl : function(){
29609         return this.el;
29610     },
29611
29612     /**
29613      * Returns the resizeChild element (or null).
29614      * @return {Roo.Element}
29615      */
29616     getResizeChild : function(){
29617         return this.resizeChild;
29618     },
29619     groupHandler : function()
29620     {
29621         
29622     },
29623     /**
29624      * Destroys this resizable. If the element was wrapped and
29625      * removeEl is not true then the element remains.
29626      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
29627      */
29628     destroy : function(removeEl){
29629         this.proxy.remove();
29630         if(this.overlay){
29631             this.overlay.removeAllListeners();
29632             this.overlay.remove();
29633         }
29634         var ps = Roo.Resizable.positions;
29635         for(var k in ps){
29636             if(typeof ps[k] != "function" && this[ps[k]]){
29637                 var h = this[ps[k]];
29638                 h.el.removeAllListeners();
29639                 h.el.remove();
29640             }
29641         }
29642         if(removeEl){
29643             this.el.update("");
29644             this.el.remove();
29645         }
29646     }
29647 });
29648
29649 // private
29650 // hash to map config positions to true positions
29651 Roo.Resizable.positions = {
29652     n: "north", s: "south", e: "east", w: "west", se: "southeast", sw: "southwest", nw: "northwest", ne: "northeast", 
29653     hd: "hdrag"
29654 };
29655
29656 // private
29657 Roo.Resizable.Handle = function(rz, pos, disableTrackOver, transparent){
29658     if(!this.tpl){
29659         // only initialize the template if resizable is used
29660         var tpl = Roo.DomHelper.createTemplate(
29661             {tag: "div", cls: "x-resizable-handle x-resizable-handle-{0}"}
29662         );
29663         tpl.compile();
29664         Roo.Resizable.Handle.prototype.tpl = tpl;
29665     }
29666     this.position = pos;
29667     this.rz = rz;
29668     // show north drag fro topdra
29669     var handlepos = pos == 'hdrag' ? 'north' : pos;
29670     
29671     this.el = this.tpl.append(rz.el.dom, [handlepos], true);
29672     if (pos == 'hdrag') {
29673         this.el.setStyle('cursor', 'pointer');
29674     }
29675     this.el.unselectable();
29676     if(transparent){
29677         this.el.setOpacity(0);
29678     }
29679     this.el.on("mousedown", this.onMouseDown, this);
29680     if(!disableTrackOver){
29681         this.el.on("mouseover", this.onMouseOver, this);
29682         this.el.on("mouseout", this.onMouseOut, this);
29683     }
29684 };
29685
29686 // private
29687 Roo.Resizable.Handle.prototype = {
29688     afterResize : function(rz){
29689         Roo.log('after?');
29690         // do nothing
29691     },
29692     // private
29693     onMouseDown : function(e){
29694         this.rz.onMouseDown(this, e);
29695     },
29696     // private
29697     onMouseOver : function(e){
29698         this.rz.handleOver(this, e);
29699     },
29700     // private
29701     onMouseOut : function(e){
29702         this.rz.handleOut(this, e);
29703     }
29704 };/*
29705  * Based on:
29706  * Ext JS Library 1.1.1
29707  * Copyright(c) 2006-2007, Ext JS, LLC.
29708  *
29709  * Originally Released Under LGPL - original licence link has changed is not relivant.
29710  *
29711  * Fork - LGPL
29712  * <script type="text/javascript">
29713  */
29714
29715 /**
29716  * @class Roo.Editor
29717  * @extends Roo.Component
29718  * A base editor field that handles displaying/hiding on demand and has some built-in sizing and event handling logic.
29719  * @constructor
29720  * Create a new Editor
29721  * @param {Roo.form.Field} field The Field object (or descendant)
29722  * @param {Object} config The config object
29723  */
29724 Roo.Editor = function(field, config){
29725     Roo.Editor.superclass.constructor.call(this, config);
29726     this.field = field;
29727     this.addEvents({
29728         /**
29729              * @event beforestartedit
29730              * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
29731              * false from the handler of this event.
29732              * @param {Editor} this
29733              * @param {Roo.Element} boundEl The underlying element bound to this editor
29734              * @param {Mixed} value The field value being set
29735              */
29736         "beforestartedit" : true,
29737         /**
29738              * @event startedit
29739              * Fires when this editor is displayed
29740              * @param {Roo.Element} boundEl The underlying element bound to this editor
29741              * @param {Mixed} value The starting field value
29742              */
29743         "startedit" : true,
29744         /**
29745              * @event beforecomplete
29746              * Fires after a change has been made to the field, but before the change is reflected in the underlying
29747              * field.  Saving the change to the field can be canceled by returning false from the handler of this event.
29748              * Note that if the value has not changed and ignoreNoChange = true, the editing will still end but this
29749              * event will not fire since no edit actually occurred.
29750              * @param {Editor} this
29751              * @param {Mixed} value The current field value
29752              * @param {Mixed} startValue The original field value
29753              */
29754         "beforecomplete" : true,
29755         /**
29756              * @event complete
29757              * Fires after editing is complete and any changed value has been written to the underlying field.
29758              * @param {Editor} this
29759              * @param {Mixed} value The current field value
29760              * @param {Mixed} startValue The original field value
29761              */
29762         "complete" : true,
29763         /**
29764          * @event specialkey
29765          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
29766          * {@link Roo.EventObject#getKey} to determine which key was pressed.
29767          * @param {Roo.form.Field} this
29768          * @param {Roo.EventObject} e The event object
29769          */
29770         "specialkey" : true
29771     });
29772 };
29773
29774 Roo.extend(Roo.Editor, Roo.Component, {
29775     /**
29776      * @cfg {Boolean/String} autosize
29777      * True for the editor to automatically adopt the size of the underlying field, "width" to adopt the width only,
29778      * or "height" to adopt the height only (defaults to false)
29779      */
29780     /**
29781      * @cfg {Boolean} revertInvalid
29782      * True to automatically revert the field value and cancel the edit when the user completes an edit and the field
29783      * validation fails (defaults to true)
29784      */
29785     /**
29786      * @cfg {Boolean} ignoreNoChange
29787      * True to skip the the edit completion process (no save, no events fired) if the user completes an edit and
29788      * the value has not changed (defaults to false).  Applies only to string values - edits for other data types
29789      * will never be ignored.
29790      */
29791     /**
29792      * @cfg {Boolean} hideEl
29793      * False to keep the bound element visible while the editor is displayed (defaults to true)
29794      */
29795     /**
29796      * @cfg {Mixed} value
29797      * The data value of the underlying field (defaults to "")
29798      */
29799     value : "",
29800     /**
29801      * @cfg {String} alignment
29802      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "c-c?").
29803      */
29804     alignment: "c-c?",
29805     /**
29806      * @cfg {Boolean/String} shadow "sides" for sides/bottom only, "frame" for 4-way shadow, and "drop"
29807      * for bottom-right shadow (defaults to "frame")
29808      */
29809     shadow : "frame",
29810     /**
29811      * @cfg {Boolean} constrain True to constrain the editor to the viewport
29812      */
29813     constrain : false,
29814     /**
29815      * @cfg {Boolean} completeOnEnter True to complete the edit when the enter key is pressed (defaults to false)
29816      */
29817     completeOnEnter : false,
29818     /**
29819      * @cfg {Boolean} cancelOnEsc True to cancel the edit when the escape key is pressed (defaults to false)
29820      */
29821     cancelOnEsc : false,
29822     /**
29823      * @cfg {Boolean} updateEl True to update the innerHTML of the bound element when the update completes (defaults to false)
29824      */
29825     updateEl : false,
29826
29827     // private
29828     onRender : function(ct, position){
29829         this.el = new Roo.Layer({
29830             shadow: this.shadow,
29831             cls: "x-editor",
29832             parentEl : ct,
29833             shim : this.shim,
29834             shadowOffset:4,
29835             id: this.id,
29836             constrain: this.constrain
29837         });
29838         this.el.setStyle("overflow", Roo.isGecko ? "auto" : "hidden");
29839         if(this.field.msgTarget != 'title'){
29840             this.field.msgTarget = 'qtip';
29841         }
29842         this.field.render(this.el);
29843         if(Roo.isGecko){
29844             this.field.el.dom.setAttribute('autocomplete', 'off');
29845         }
29846         this.field.on("specialkey", this.onSpecialKey, this);
29847         if(this.swallowKeys){
29848             this.field.el.swallowEvent(['keydown','keypress']);
29849         }
29850         this.field.show();
29851         this.field.on("blur", this.onBlur, this);
29852         if(this.field.grow){
29853             this.field.on("autosize", this.el.sync,  this.el, {delay:1});
29854         }
29855     },
29856
29857     onSpecialKey : function(field, e)
29858     {
29859         //Roo.log('editor onSpecialKey');
29860         if(this.completeOnEnter && e.getKey() == e.ENTER){
29861             e.stopEvent();
29862             this.completeEdit();
29863             return;
29864         }
29865         // do not fire special key otherwise it might hide close the editor...
29866         if(e.getKey() == e.ENTER){    
29867             return;
29868         }
29869         if(this.cancelOnEsc && e.getKey() == e.ESC){
29870             this.cancelEdit();
29871             return;
29872         } 
29873         this.fireEvent('specialkey', field, e);
29874     
29875     },
29876
29877     /**
29878      * Starts the editing process and shows the editor.
29879      * @param {String/HTMLElement/Element} el The element to edit
29880      * @param {String} value (optional) A value to initialize the editor with. If a value is not provided, it defaults
29881       * to the innerHTML of el.
29882      */
29883     startEdit : function(el, value){
29884         if(this.editing){
29885             this.completeEdit();
29886         }
29887         this.boundEl = Roo.get(el);
29888         var v = value !== undefined ? value : this.boundEl.dom.innerHTML;
29889         if(!this.rendered){
29890             this.render(this.parentEl || document.body);
29891         }
29892         if(this.fireEvent("beforestartedit", this, this.boundEl, v) === false){
29893             return;
29894         }
29895         this.startValue = v;
29896         this.field.setValue(v);
29897         if(this.autoSize){
29898             var sz = this.boundEl.getSize();
29899             switch(this.autoSize){
29900                 case "width":
29901                 this.setSize(sz.width,  "");
29902                 break;
29903                 case "height":
29904                 this.setSize("",  sz.height);
29905                 break;
29906                 default:
29907                 this.setSize(sz.width,  sz.height);
29908             }
29909         }
29910         this.el.alignTo(this.boundEl, this.alignment);
29911         this.editing = true;
29912         if(Roo.QuickTips){
29913             Roo.QuickTips.disable();
29914         }
29915         this.show();
29916     },
29917
29918     /**
29919      * Sets the height and width of this editor.
29920      * @param {Number} width The new width
29921      * @param {Number} height The new height
29922      */
29923     setSize : function(w, h){
29924         this.field.setSize(w, h);
29925         if(this.el){
29926             this.el.sync();
29927         }
29928     },
29929
29930     /**
29931      * Realigns the editor to the bound field based on the current alignment config value.
29932      */
29933     realign : function(){
29934         this.el.alignTo(this.boundEl, this.alignment);
29935     },
29936
29937     /**
29938      * Ends the editing process, persists the changed value to the underlying field, and hides the editor.
29939      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after edit (defaults to false)
29940      */
29941     completeEdit : function(remainVisible){
29942         if(!this.editing){
29943             return;
29944         }
29945         var v = this.getValue();
29946         if(this.revertInvalid !== false && !this.field.isValid()){
29947             v = this.startValue;
29948             this.cancelEdit(true);
29949         }
29950         if(String(v) === String(this.startValue) && this.ignoreNoChange){
29951             this.editing = false;
29952             this.hide();
29953             return;
29954         }
29955         if(this.fireEvent("beforecomplete", this, v, this.startValue) !== false){
29956             this.editing = false;
29957             if(this.updateEl && this.boundEl){
29958                 this.boundEl.update(v);
29959             }
29960             if(remainVisible !== true){
29961                 this.hide();
29962             }
29963             this.fireEvent("complete", this, v, this.startValue);
29964         }
29965     },
29966
29967     // private
29968     onShow : function(){
29969         this.el.show();
29970         if(this.hideEl !== false){
29971             this.boundEl.hide();
29972         }
29973         this.field.show();
29974         if(Roo.isIE && !this.fixIEFocus){ // IE has problems with focusing the first time
29975             this.fixIEFocus = true;
29976             this.deferredFocus.defer(50, this);
29977         }else{
29978             this.field.focus();
29979         }
29980         this.fireEvent("startedit", this.boundEl, this.startValue);
29981     },
29982
29983     deferredFocus : function(){
29984         if(this.editing){
29985             this.field.focus();
29986         }
29987     },
29988
29989     /**
29990      * Cancels the editing process and hides the editor without persisting any changes.  The field value will be
29991      * reverted to the original starting value.
29992      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after
29993      * cancel (defaults to false)
29994      */
29995     cancelEdit : function(remainVisible){
29996         if(this.editing){
29997             this.setValue(this.startValue);
29998             if(remainVisible !== true){
29999                 this.hide();
30000             }
30001         }
30002     },
30003
30004     // private
30005     onBlur : function(){
30006         if(this.allowBlur !== true && this.editing){
30007             this.completeEdit();
30008         }
30009     },
30010
30011     // private
30012     onHide : function(){
30013         if(this.editing){
30014             this.completeEdit();
30015             return;
30016         }
30017         this.field.blur();
30018         if(this.field.collapse){
30019             this.field.collapse();
30020         }
30021         this.el.hide();
30022         if(this.hideEl !== false){
30023             this.boundEl.show();
30024         }
30025         if(Roo.QuickTips){
30026             Roo.QuickTips.enable();
30027         }
30028     },
30029
30030     /**
30031      * Sets the data value of the editor
30032      * @param {Mixed} value Any valid value supported by the underlying field
30033      */
30034     setValue : function(v){
30035         this.field.setValue(v);
30036     },
30037
30038     /**
30039      * Gets the data value of the editor
30040      * @return {Mixed} The data value
30041      */
30042     getValue : function(){
30043         return this.field.getValue();
30044     }
30045 });/*
30046  * Based on:
30047  * Ext JS Library 1.1.1
30048  * Copyright(c) 2006-2007, Ext JS, LLC.
30049  *
30050  * Originally Released Under LGPL - original licence link has changed is not relivant.
30051  *
30052  * Fork - LGPL
30053  * <script type="text/javascript">
30054  */
30055  
30056 /**
30057  * @class Roo.BasicDialog
30058  * @extends Roo.util.Observable
30059  * Lightweight Dialog Class.  The code below shows the creation of a typical dialog using existing HTML markup:
30060  * <pre><code>
30061 var dlg = new Roo.BasicDialog("my-dlg", {
30062     height: 200,
30063     width: 300,
30064     minHeight: 100,
30065     minWidth: 150,
30066     modal: true,
30067     proxyDrag: true,
30068     shadow: true
30069 });
30070 dlg.addKeyListener(27, dlg.hide, dlg); // ESC can also close the dialog
30071 dlg.addButton('OK', dlg.hide, dlg);    // Could call a save function instead of hiding
30072 dlg.addButton('Cancel', dlg.hide, dlg);
30073 dlg.show();
30074 </code></pre>
30075   <b>A Dialog should always be a direct child of the body element.</b>
30076  * @cfg {Boolean/DomHelper} autoCreate True to auto create from scratch, or using a DomHelper Object (defaults to false)
30077  * @cfg {String} title Default text to display in the title bar (defaults to null)
30078  * @cfg {Number} width Width of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
30079  * @cfg {Number} height Height of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
30080  * @cfg {Number} x The default left page coordinate of the dialog (defaults to center screen)
30081  * @cfg {Number} y The default top page coordinate of the dialog (defaults to center screen)
30082  * @cfg {String/Element} animateTarget Id or element from which the dialog should animate while opening
30083  * (defaults to null with no animation)
30084  * @cfg {Boolean} resizable False to disable manual dialog resizing (defaults to true)
30085  * @cfg {String} resizeHandles Which resize handles to display - see the {@link Roo.Resizable} handles config
30086  * property for valid values (defaults to 'all')
30087  * @cfg {Number} minHeight The minimum allowable height for a resizable dialog (defaults to 80)
30088  * @cfg {Number} minWidth The minimum allowable width for a resizable dialog (defaults to 200)
30089  * @cfg {Boolean} modal True to show the dialog modally, preventing user interaction with the rest of the page (defaults to false)
30090  * @cfg {Boolean} autoScroll True to allow the dialog body contents to overflow and display scrollbars (defaults to false)
30091  * @cfg {Boolean} closable False to remove the built-in top-right corner close button (defaults to true)
30092  * @cfg {Boolean} collapsible False to remove the built-in top-right corner collapse button (defaults to true)
30093  * @cfg {Boolean} constraintoviewport True to keep the dialog constrained within the visible viewport boundaries (defaults to true)
30094  * @cfg {Boolean} syncHeightBeforeShow True to cause the dimensions to be recalculated before the dialog is shown (defaults to false)
30095  * @cfg {Boolean} draggable False to disable dragging of the dialog within the viewport (defaults to true)
30096  * @cfg {Boolean} autoTabs If true, all elements with class 'x-dlg-tab' will get automatically converted to tabs (defaults to false)
30097  * @cfg {String} tabTag The tag name of tab elements, used when autoTabs = true (defaults to 'div')
30098  * @cfg {Boolean} proxyDrag True to drag a lightweight proxy element rather than the dialog itself, used when
30099  * draggable = true (defaults to false)
30100  * @cfg {Boolean} fixedcenter True to ensure that anytime the dialog is shown or resized it gets centered (defaults to false)
30101  * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
30102  * shadow (defaults to false)
30103  * @cfg {Number} shadowOffset The number of pixels to offset the shadow if displayed (defaults to 5)
30104  * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "right")
30105  * @cfg {Number} minButtonWidth Minimum width of all dialog buttons (defaults to 75)
30106  * @cfg {Array} buttons Array of buttons
30107  * @cfg {Boolean} shim True to create an iframe shim that prevents selects from showing through (defaults to false)
30108  * @constructor
30109  * Create a new BasicDialog.
30110  * @param {String/HTMLElement/Roo.Element} el The container element or DOM node, or its id
30111  * @param {Object} config Configuration options
30112  */
30113 Roo.BasicDialog = function(el, config){
30114     this.el = Roo.get(el);
30115     var dh = Roo.DomHelper;
30116     if(!this.el && config && config.autoCreate){
30117         if(typeof config.autoCreate == "object"){
30118             if(!config.autoCreate.id){
30119                 config.autoCreate.id = el;
30120             }
30121             this.el = dh.append(document.body,
30122                         config.autoCreate, true);
30123         }else{
30124             this.el = dh.append(document.body,
30125                         {tag: "div", id: el, style:'visibility:hidden;'}, true);
30126         }
30127     }
30128     el = this.el;
30129     el.setDisplayed(true);
30130     el.hide = this.hideAction;
30131     this.id = el.id;
30132     el.addClass("x-dlg");
30133
30134     Roo.apply(this, config);
30135
30136     this.proxy = el.createProxy("x-dlg-proxy");
30137     this.proxy.hide = this.hideAction;
30138     this.proxy.setOpacity(.5);
30139     this.proxy.hide();
30140
30141     if(config.width){
30142         el.setWidth(config.width);
30143     }
30144     if(config.height){
30145         el.setHeight(config.height);
30146     }
30147     this.size = el.getSize();
30148     if(typeof config.x != "undefined" && typeof config.y != "undefined"){
30149         this.xy = [config.x,config.y];
30150     }else{
30151         this.xy = el.getCenterXY(true);
30152     }
30153     /** The header element @type Roo.Element */
30154     this.header = el.child("> .x-dlg-hd");
30155     /** The body element @type Roo.Element */
30156     this.body = el.child("> .x-dlg-bd");
30157     /** The footer element @type Roo.Element */
30158     this.footer = el.child("> .x-dlg-ft");
30159
30160     if(!this.header){
30161         this.header = el.createChild({tag: "div", cls:"x-dlg-hd", html: "&#160;"}, this.body ? this.body.dom : null);
30162     }
30163     if(!this.body){
30164         this.body = el.createChild({tag: "div", cls:"x-dlg-bd"});
30165     }
30166
30167     this.header.unselectable();
30168     if(this.title){
30169         this.header.update(this.title);
30170     }
30171     // this element allows the dialog to be focused for keyboard event
30172     this.focusEl = el.createChild({tag: "a", href:"#", cls:"x-dlg-focus", tabIndex:"-1"});
30173     this.focusEl.swallowEvent("click", true);
30174
30175     this.header.wrap({cls:"x-dlg-hd-right"}).wrap({cls:"x-dlg-hd-left"}, true);
30176
30177     // wrap the body and footer for special rendering
30178     this.bwrap = this.body.wrap({tag: "div", cls:"x-dlg-dlg-body"});
30179     if(this.footer){
30180         this.bwrap.dom.appendChild(this.footer.dom);
30181     }
30182
30183     this.bg = this.el.createChild({
30184         tag: "div", cls:"x-dlg-bg",
30185         html: '<div class="x-dlg-bg-left"><div class="x-dlg-bg-right"><div class="x-dlg-bg-center">&#160;</div></div></div>'
30186     });
30187     this.centerBg = this.bg.child("div.x-dlg-bg-center");
30188
30189
30190     if(this.autoScroll !== false && !this.autoTabs){
30191         this.body.setStyle("overflow", "auto");
30192     }
30193
30194     this.toolbox = this.el.createChild({cls: "x-dlg-toolbox"});
30195
30196     if(this.closable !== false){
30197         this.el.addClass("x-dlg-closable");
30198         this.close = this.toolbox.createChild({cls:"x-dlg-close"});
30199         this.close.on("click", this.closeClick, this);
30200         this.close.addClassOnOver("x-dlg-close-over");
30201     }
30202     if(this.collapsible !== false){
30203         this.collapseBtn = this.toolbox.createChild({cls:"x-dlg-collapse"});
30204         this.collapseBtn.on("click", this.collapseClick, this);
30205         this.collapseBtn.addClassOnOver("x-dlg-collapse-over");
30206         this.header.on("dblclick", this.collapseClick, this);
30207     }
30208     if(this.resizable !== false){
30209         this.el.addClass("x-dlg-resizable");
30210         this.resizer = new Roo.Resizable(el, {
30211             minWidth: this.minWidth || 80,
30212             minHeight:this.minHeight || 80,
30213             handles: this.resizeHandles || "all",
30214             pinned: true
30215         });
30216         this.resizer.on("beforeresize", this.beforeResize, this);
30217         this.resizer.on("resize", this.onResize, this);
30218     }
30219     if(this.draggable !== false){
30220         el.addClass("x-dlg-draggable");
30221         if (!this.proxyDrag) {
30222             var dd = new Roo.dd.DD(el.dom.id, "WindowDrag");
30223         }
30224         else {
30225             var dd = new Roo.dd.DDProxy(el.dom.id, "WindowDrag", {dragElId: this.proxy.id});
30226         }
30227         dd.setHandleElId(this.header.id);
30228         dd.endDrag = this.endMove.createDelegate(this);
30229         dd.startDrag = this.startMove.createDelegate(this);
30230         dd.onDrag = this.onDrag.createDelegate(this);
30231         dd.scroll = false;
30232         this.dd = dd;
30233     }
30234     if(this.modal){
30235         this.mask = dh.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
30236         this.mask.enableDisplayMode("block");
30237         this.mask.hide();
30238         this.el.addClass("x-dlg-modal");
30239     }
30240     if(this.shadow){
30241         this.shadow = new Roo.Shadow({
30242             mode : typeof this.shadow == "string" ? this.shadow : "sides",
30243             offset : this.shadowOffset
30244         });
30245     }else{
30246         this.shadowOffset = 0;
30247     }
30248     if(Roo.useShims && this.shim !== false){
30249         this.shim = this.el.createShim();
30250         this.shim.hide = this.hideAction;
30251         this.shim.hide();
30252     }else{
30253         this.shim = false;
30254     }
30255     if(this.autoTabs){
30256         this.initTabs();
30257     }
30258     if (this.buttons) { 
30259         var bts= this.buttons;
30260         this.buttons = [];
30261         Roo.each(bts, function(b) {
30262             this.addButton(b);
30263         }, this);
30264     }
30265     
30266     
30267     this.addEvents({
30268         /**
30269          * @event keydown
30270          * Fires when a key is pressed
30271          * @param {Roo.BasicDialog} this
30272          * @param {Roo.EventObject} e
30273          */
30274         "keydown" : true,
30275         /**
30276          * @event move
30277          * Fires when this dialog is moved by the user.
30278          * @param {Roo.BasicDialog} this
30279          * @param {Number} x The new page X
30280          * @param {Number} y The new page Y
30281          */
30282         "move" : true,
30283         /**
30284          * @event resize
30285          * Fires when this dialog is resized by the user.
30286          * @param {Roo.BasicDialog} this
30287          * @param {Number} width The new width
30288          * @param {Number} height The new height
30289          */
30290         "resize" : true,
30291         /**
30292          * @event beforehide
30293          * Fires before this dialog is hidden.
30294          * @param {Roo.BasicDialog} this
30295          */
30296         "beforehide" : true,
30297         /**
30298          * @event hide
30299          * Fires when this dialog is hidden.
30300          * @param {Roo.BasicDialog} this
30301          */
30302         "hide" : true,
30303         /**
30304          * @event beforeshow
30305          * Fires before this dialog is shown.
30306          * @param {Roo.BasicDialog} this
30307          */
30308         "beforeshow" : true,
30309         /**
30310          * @event show
30311          * Fires when this dialog is shown.
30312          * @param {Roo.BasicDialog} this
30313          */
30314         "show" : true
30315     });
30316     el.on("keydown", this.onKeyDown, this);
30317     el.on("mousedown", this.toFront, this);
30318     Roo.EventManager.onWindowResize(this.adjustViewport, this, true);
30319     this.el.hide();
30320     Roo.DialogManager.register(this);
30321     Roo.BasicDialog.superclass.constructor.call(this);
30322 };
30323
30324 Roo.extend(Roo.BasicDialog, Roo.util.Observable, {
30325     shadowOffset: Roo.isIE ? 6 : 5,
30326     minHeight: 80,
30327     minWidth: 200,
30328     minButtonWidth: 75,
30329     defaultButton: null,
30330     buttonAlign: "right",
30331     tabTag: 'div',
30332     firstShow: true,
30333
30334     /**
30335      * Sets the dialog title text
30336      * @param {String} text The title text to display
30337      * @return {Roo.BasicDialog} this
30338      */
30339     setTitle : function(text){
30340         this.header.update(text);
30341         return this;
30342     },
30343
30344     // private
30345     closeClick : function(){
30346         this.hide();
30347     },
30348
30349     // private
30350     collapseClick : function(){
30351         this[this.collapsed ? "expand" : "collapse"]();
30352     },
30353
30354     /**
30355      * Collapses the dialog to its minimized state (only the title bar is visible).
30356      * Equivalent to the user clicking the collapse dialog button.
30357      */
30358     collapse : function(){
30359         if(!this.collapsed){
30360             this.collapsed = true;
30361             this.el.addClass("x-dlg-collapsed");
30362             this.restoreHeight = this.el.getHeight();
30363             this.resizeTo(this.el.getWidth(), this.header.getHeight());
30364         }
30365     },
30366
30367     /**
30368      * Expands a collapsed dialog back to its normal state.  Equivalent to the user
30369      * clicking the expand dialog button.
30370      */
30371     expand : function(){
30372         if(this.collapsed){
30373             this.collapsed = false;
30374             this.el.removeClass("x-dlg-collapsed");
30375             this.resizeTo(this.el.getWidth(), this.restoreHeight);
30376         }
30377     },
30378
30379     /**
30380      * Reinitializes the tabs component, clearing out old tabs and finding new ones.
30381      * @return {Roo.TabPanel} The tabs component
30382      */
30383     initTabs : function(){
30384         var tabs = this.getTabs();
30385         while(tabs.getTab(0)){
30386             tabs.removeTab(0);
30387         }
30388         this.el.select(this.tabTag+'.x-dlg-tab').each(function(el){
30389             var dom = el.dom;
30390             tabs.addTab(Roo.id(dom), dom.title);
30391             dom.title = "";
30392         });
30393         tabs.activate(0);
30394         return tabs;
30395     },
30396
30397     // private
30398     beforeResize : function(){
30399         this.resizer.minHeight = Math.max(this.minHeight, this.getHeaderFooterHeight(true)+40);
30400     },
30401
30402     // private
30403     onResize : function(){
30404         this.refreshSize();
30405         this.syncBodyHeight();
30406         this.adjustAssets();
30407         this.focus();
30408         this.fireEvent("resize", this, this.size.width, this.size.height);
30409     },
30410
30411     // private
30412     onKeyDown : function(e){
30413         if(this.isVisible()){
30414             this.fireEvent("keydown", this, e);
30415         }
30416     },
30417
30418     /**
30419      * Resizes the dialog.
30420      * @param {Number} width
30421      * @param {Number} height
30422      * @return {Roo.BasicDialog} this
30423      */
30424     resizeTo : function(width, height){
30425         this.el.setSize(width, height);
30426         this.size = {width: width, height: height};
30427         this.syncBodyHeight();
30428         if(this.fixedcenter){
30429             this.center();
30430         }
30431         if(this.isVisible()){
30432             this.constrainXY();
30433             this.adjustAssets();
30434         }
30435         this.fireEvent("resize", this, width, height);
30436         return this;
30437     },
30438
30439
30440     /**
30441      * Resizes the dialog to fit the specified content size.
30442      * @param {Number} width
30443      * @param {Number} height
30444      * @return {Roo.BasicDialog} this
30445      */
30446     setContentSize : function(w, h){
30447         h += this.getHeaderFooterHeight() + this.body.getMargins("tb");
30448         w += this.body.getMargins("lr") + this.bwrap.getMargins("lr") + this.centerBg.getPadding("lr");
30449         //if(!this.el.isBorderBox()){
30450             h +=  this.body.getPadding("tb") + this.bwrap.getBorderWidth("tb") + this.body.getBorderWidth("tb") + this.el.getBorderWidth("tb");
30451             w += this.body.getPadding("lr") + this.bwrap.getBorderWidth("lr") + this.body.getBorderWidth("lr") + this.bwrap.getPadding("lr") + this.el.getBorderWidth("lr");
30452         //}
30453         if(this.tabs){
30454             h += this.tabs.stripWrap.getHeight() + this.tabs.bodyEl.getMargins("tb") + this.tabs.bodyEl.getPadding("tb");
30455             w += this.tabs.bodyEl.getMargins("lr") + this.tabs.bodyEl.getPadding("lr");
30456         }
30457         this.resizeTo(w, h);
30458         return this;
30459     },
30460
30461     /**
30462      * Adds a key listener for when this dialog is displayed.  This allows you to hook in a function that will be
30463      * executed in response to a particular key being pressed while the dialog is active.
30464      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the following options:
30465      *                                  {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
30466      * @param {Function} fn The function to call
30467      * @param {Object} scope (optional) The scope of the function
30468      * @return {Roo.BasicDialog} this
30469      */
30470     addKeyListener : function(key, fn, scope){
30471         var keyCode, shift, ctrl, alt;
30472         if(typeof key == "object" && !(key instanceof Array)){
30473             keyCode = key["key"];
30474             shift = key["shift"];
30475             ctrl = key["ctrl"];
30476             alt = key["alt"];
30477         }else{
30478             keyCode = key;
30479         }
30480         var handler = function(dlg, e){
30481             if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) &&  (!alt || e.altKey)){
30482                 var k = e.getKey();
30483                 if(keyCode instanceof Array){
30484                     for(var i = 0, len = keyCode.length; i < len; i++){
30485                         if(keyCode[i] == k){
30486                           fn.call(scope || window, dlg, k, e);
30487                           return;
30488                         }
30489                     }
30490                 }else{
30491                     if(k == keyCode){
30492                         fn.call(scope || window, dlg, k, e);
30493                     }
30494                 }
30495             }
30496         };
30497         this.on("keydown", handler);
30498         return this;
30499     },
30500
30501     /**
30502      * Returns the TabPanel component (creates it if it doesn't exist).
30503      * Note: If you wish to simply check for the existence of tabs without creating them,
30504      * check for a null 'tabs' property.
30505      * @return {Roo.TabPanel} The tabs component
30506      */
30507     getTabs : function(){
30508         if(!this.tabs){
30509             this.el.addClass("x-dlg-auto-tabs");
30510             this.body.addClass(this.tabPosition == "bottom" ? "x-tabs-bottom" : "x-tabs-top");
30511             this.tabs = new Roo.TabPanel(this.body.dom, this.tabPosition == "bottom");
30512         }
30513         return this.tabs;
30514     },
30515
30516     /**
30517      * Adds a button to the footer section of the dialog.
30518      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
30519      * object or a valid Roo.DomHelper element config
30520      * @param {Function} handler The function called when the button is clicked
30521      * @param {Object} scope (optional) The scope of the handler function (accepts position as a property)
30522      * @return {Roo.Button} The new button
30523      */
30524     addButton : function(config, handler, scope){
30525         var dh = Roo.DomHelper;
30526         if(!this.footer){
30527             this.footer = dh.append(this.bwrap, {tag: "div", cls:"x-dlg-ft"}, true);
30528         }
30529         if(!this.btnContainer){
30530             var tb = this.footer.createChild({
30531
30532                 cls:"x-dlg-btns x-dlg-btns-"+this.buttonAlign,
30533                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
30534             }, null, true);
30535             this.btnContainer = tb.firstChild.firstChild.firstChild;
30536         }
30537         var bconfig = {
30538             handler: handler,
30539             scope: scope,
30540             minWidth: this.minButtonWidth,
30541             hideParent:true
30542         };
30543         if(typeof config == "string"){
30544             bconfig.text = config;
30545         }else{
30546             if(config.tag){
30547                 bconfig.dhconfig = config;
30548             }else{
30549                 Roo.apply(bconfig, config);
30550             }
30551         }
30552         var fc = false;
30553         if ((typeof(bconfig.position) != 'undefined') && bconfig.position < this.btnContainer.childNodes.length-1) {
30554             bconfig.position = Math.max(0, bconfig.position);
30555             fc = this.btnContainer.childNodes[bconfig.position];
30556         }
30557          
30558         var btn = new Roo.Button(
30559             fc ? 
30560                 this.btnContainer.insertBefore(document.createElement("td"),fc)
30561                 : this.btnContainer.appendChild(document.createElement("td")),
30562             //Roo.get(this.btnContainer).createChild( { tag: 'td'},  fc ),
30563             bconfig
30564         );
30565         this.syncBodyHeight();
30566         if(!this.buttons){
30567             /**
30568              * Array of all the buttons that have been added to this dialog via addButton
30569              * @type Array
30570              */
30571             this.buttons = [];
30572         }
30573         this.buttons.push(btn);
30574         return btn;
30575     },
30576
30577     /**
30578      * Sets the default button to be focused when the dialog is displayed.
30579      * @param {Roo.BasicDialog.Button} btn The button object returned by {@link #addButton}
30580      * @return {Roo.BasicDialog} this
30581      */
30582     setDefaultButton : function(btn){
30583         this.defaultButton = btn;
30584         return this;
30585     },
30586
30587     // private
30588     getHeaderFooterHeight : function(safe){
30589         var height = 0;
30590         if(this.header){
30591            height += this.header.getHeight();
30592         }
30593         if(this.footer){
30594            var fm = this.footer.getMargins();
30595             height += (this.footer.getHeight()+fm.top+fm.bottom);
30596         }
30597         height += this.bwrap.getPadding("tb")+this.bwrap.getBorderWidth("tb");
30598         height += this.centerBg.getPadding("tb");
30599         return height;
30600     },
30601
30602     // private
30603     syncBodyHeight : function()
30604     {
30605         var bd = this.body, // the text
30606             cb = this.centerBg, // wrapper around bottom.. but does not seem to be used..
30607             bw = this.bwrap;
30608         var height = this.size.height - this.getHeaderFooterHeight(false);
30609         bd.setHeight(height-bd.getMargins("tb"));
30610         var hh = this.header.getHeight();
30611         var h = this.size.height-hh;
30612         cb.setHeight(h);
30613         
30614         bw.setLeftTop(cb.getPadding("l"), hh+cb.getPadding("t"));
30615         bw.setHeight(h-cb.getPadding("tb"));
30616         
30617         bw.setWidth(this.el.getWidth(true)-cb.getPadding("lr"));
30618         bd.setWidth(bw.getWidth(true));
30619         if(this.tabs){
30620             this.tabs.syncHeight();
30621             if(Roo.isIE){
30622                 this.tabs.el.repaint();
30623             }
30624         }
30625     },
30626
30627     /**
30628      * Restores the previous state of the dialog if Roo.state is configured.
30629      * @return {Roo.BasicDialog} this
30630      */
30631     restoreState : function(){
30632         var box = Roo.state.Manager.get(this.stateId || (this.el.id + "-state"));
30633         if(box && box.width){
30634             this.xy = [box.x, box.y];
30635             this.resizeTo(box.width, box.height);
30636         }
30637         return this;
30638     },
30639
30640     // private
30641     beforeShow : function(){
30642         this.expand();
30643         if(this.fixedcenter){
30644             this.xy = this.el.getCenterXY(true);
30645         }
30646         if(this.modal){
30647             Roo.get(document.body).addClass("x-body-masked");
30648             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
30649             this.mask.show();
30650         }
30651         this.constrainXY();
30652     },
30653
30654     // private
30655     animShow : function(){
30656         var b = Roo.get(this.animateTarget).getBox();
30657         this.proxy.setSize(b.width, b.height);
30658         this.proxy.setLocation(b.x, b.y);
30659         this.proxy.show();
30660         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height,
30661                     true, .35, this.showEl.createDelegate(this));
30662     },
30663
30664     /**
30665      * Shows the dialog.
30666      * @param {String/HTMLElement/Roo.Element} animateTarget (optional) Reset the animation target
30667      * @return {Roo.BasicDialog} this
30668      */
30669     show : function(animateTarget){
30670         if (this.fireEvent("beforeshow", this) === false){
30671             return;
30672         }
30673         if(this.syncHeightBeforeShow){
30674             this.syncBodyHeight();
30675         }else if(this.firstShow){
30676             this.firstShow = false;
30677             this.syncBodyHeight(); // sync the height on the first show instead of in the constructor
30678         }
30679         this.animateTarget = animateTarget || this.animateTarget;
30680         if(!this.el.isVisible()){
30681             this.beforeShow();
30682             if(this.animateTarget && Roo.get(this.animateTarget)){
30683                 this.animShow();
30684             }else{
30685                 this.showEl();
30686             }
30687         }
30688         return this;
30689     },
30690
30691     // private
30692     showEl : function(){
30693         this.proxy.hide();
30694         this.el.setXY(this.xy);
30695         this.el.show();
30696         this.adjustAssets(true);
30697         this.toFront();
30698         this.focus();
30699         // IE peekaboo bug - fix found by Dave Fenwick
30700         if(Roo.isIE){
30701             this.el.repaint();
30702         }
30703         this.fireEvent("show", this);
30704     },
30705
30706     /**
30707      * Focuses the dialog.  If a defaultButton is set, it will receive focus, otherwise the
30708      * dialog itself will receive focus.
30709      */
30710     focus : function(){
30711         if(this.defaultButton){
30712             this.defaultButton.focus();
30713         }else{
30714             this.focusEl.focus();
30715         }
30716     },
30717
30718     // private
30719     constrainXY : function(){
30720         if(this.constraintoviewport !== false){
30721             if(!this.viewSize){
30722                 if(this.container){
30723                     var s = this.container.getSize();
30724                     this.viewSize = [s.width, s.height];
30725                 }else{
30726                     this.viewSize = [Roo.lib.Dom.getViewWidth(),Roo.lib.Dom.getViewHeight()];
30727                 }
30728             }
30729             var s = Roo.get(this.container||document).getScroll();
30730
30731             var x = this.xy[0], y = this.xy[1];
30732             var w = this.size.width, h = this.size.height;
30733             var vw = this.viewSize[0], vh = this.viewSize[1];
30734             // only move it if it needs it
30735             var moved = false;
30736             // first validate right/bottom
30737             if(x + w > vw+s.left){
30738                 x = vw - w;
30739                 moved = true;
30740             }
30741             if(y + h > vh+s.top){
30742                 y = vh - h;
30743                 moved = true;
30744             }
30745             // then make sure top/left isn't negative
30746             if(x < s.left){
30747                 x = s.left;
30748                 moved = true;
30749             }
30750             if(y < s.top){
30751                 y = s.top;
30752                 moved = true;
30753             }
30754             if(moved){
30755                 // cache xy
30756                 this.xy = [x, y];
30757                 if(this.isVisible()){
30758                     this.el.setLocation(x, y);
30759                     this.adjustAssets();
30760                 }
30761             }
30762         }
30763     },
30764
30765     // private
30766     onDrag : function(){
30767         if(!this.proxyDrag){
30768             this.xy = this.el.getXY();
30769             this.adjustAssets();
30770         }
30771     },
30772
30773     // private
30774     adjustAssets : function(doShow){
30775         var x = this.xy[0], y = this.xy[1];
30776         var w = this.size.width, h = this.size.height;
30777         if(doShow === true){
30778             if(this.shadow){
30779                 this.shadow.show(this.el);
30780             }
30781             if(this.shim){
30782                 this.shim.show();
30783             }
30784         }
30785         if(this.shadow && this.shadow.isVisible()){
30786             this.shadow.show(this.el);
30787         }
30788         if(this.shim && this.shim.isVisible()){
30789             this.shim.setBounds(x, y, w, h);
30790         }
30791     },
30792
30793     // private
30794     adjustViewport : function(w, h){
30795         if(!w || !h){
30796             w = Roo.lib.Dom.getViewWidth();
30797             h = Roo.lib.Dom.getViewHeight();
30798         }
30799         // cache the size
30800         this.viewSize = [w, h];
30801         if(this.modal && this.mask.isVisible()){
30802             this.mask.setSize(w, h); // first make sure the mask isn't causing overflow
30803             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
30804         }
30805         if(this.isVisible()){
30806             this.constrainXY();
30807         }
30808     },
30809
30810     /**
30811      * Destroys this dialog and all its supporting elements (including any tabs, shim,
30812      * shadow, proxy, mask, etc.)  Also removes all event listeners.
30813      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
30814      */
30815     destroy : function(removeEl){
30816         if(this.isVisible()){
30817             this.animateTarget = null;
30818             this.hide();
30819         }
30820         Roo.EventManager.removeResizeListener(this.adjustViewport, this);
30821         if(this.tabs){
30822             this.tabs.destroy(removeEl);
30823         }
30824         Roo.destroy(
30825              this.shim,
30826              this.proxy,
30827              this.resizer,
30828              this.close,
30829              this.mask
30830         );
30831         if(this.dd){
30832             this.dd.unreg();
30833         }
30834         if(this.buttons){
30835            for(var i = 0, len = this.buttons.length; i < len; i++){
30836                this.buttons[i].destroy();
30837            }
30838         }
30839         this.el.removeAllListeners();
30840         if(removeEl === true){
30841             this.el.update("");
30842             this.el.remove();
30843         }
30844         Roo.DialogManager.unregister(this);
30845     },
30846
30847     // private
30848     startMove : function(){
30849         if(this.proxyDrag){
30850             this.proxy.show();
30851         }
30852         if(this.constraintoviewport !== false){
30853             this.dd.constrainTo(document.body, {right: this.shadowOffset, bottom: this.shadowOffset});
30854         }
30855     },
30856
30857     // private
30858     endMove : function(){
30859         if(!this.proxyDrag){
30860             Roo.dd.DD.prototype.endDrag.apply(this.dd, arguments);
30861         }else{
30862             Roo.dd.DDProxy.prototype.endDrag.apply(this.dd, arguments);
30863             this.proxy.hide();
30864         }
30865         this.refreshSize();
30866         this.adjustAssets();
30867         this.focus();
30868         this.fireEvent("move", this, this.xy[0], this.xy[1]);
30869     },
30870
30871     /**
30872      * Brings this dialog to the front of any other visible dialogs
30873      * @return {Roo.BasicDialog} this
30874      */
30875     toFront : function(){
30876         Roo.DialogManager.bringToFront(this);
30877         return this;
30878     },
30879
30880     /**
30881      * Sends this dialog to the back (under) of any other visible dialogs
30882      * @return {Roo.BasicDialog} this
30883      */
30884     toBack : function(){
30885         Roo.DialogManager.sendToBack(this);
30886         return this;
30887     },
30888
30889     /**
30890      * Centers this dialog in the viewport
30891      * @return {Roo.BasicDialog} this
30892      */
30893     center : function(){
30894         var xy = this.el.getCenterXY(true);
30895         this.moveTo(xy[0], xy[1]);
30896         return this;
30897     },
30898
30899     /**
30900      * Moves the dialog's top-left corner to the specified point
30901      * @param {Number} x
30902      * @param {Number} y
30903      * @return {Roo.BasicDialog} this
30904      */
30905     moveTo : function(x, y){
30906         this.xy = [x,y];
30907         if(this.isVisible()){
30908             this.el.setXY(this.xy);
30909             this.adjustAssets();
30910         }
30911         return this;
30912     },
30913
30914     /**
30915      * Aligns the dialog to the specified element
30916      * @param {String/HTMLElement/Roo.Element} element The element to align to.
30917      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details).
30918      * @param {Array} offsets (optional) Offset the positioning by [x, y]
30919      * @return {Roo.BasicDialog} this
30920      */
30921     alignTo : function(element, position, offsets){
30922         this.xy = this.el.getAlignToXY(element, position, offsets);
30923         if(this.isVisible()){
30924             this.el.setXY(this.xy);
30925             this.adjustAssets();
30926         }
30927         return this;
30928     },
30929
30930     /**
30931      * Anchors an element to another element and realigns it when the window is resized.
30932      * @param {String/HTMLElement/Roo.Element} element The element to align to.
30933      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details)
30934      * @param {Array} offsets (optional) Offset the positioning by [x, y]
30935      * @param {Boolean/Number} monitorScroll (optional) true to monitor body scroll and reposition. If this parameter
30936      * is a number, it is used as the buffer delay (defaults to 50ms).
30937      * @return {Roo.BasicDialog} this
30938      */
30939     anchorTo : function(el, alignment, offsets, monitorScroll){
30940         var action = function(){
30941             this.alignTo(el, alignment, offsets);
30942         };
30943         Roo.EventManager.onWindowResize(action, this);
30944         var tm = typeof monitorScroll;
30945         if(tm != 'undefined'){
30946             Roo.EventManager.on(window, 'scroll', action, this,
30947                 {buffer: tm == 'number' ? monitorScroll : 50});
30948         }
30949         action.call(this);
30950         return this;
30951     },
30952
30953     /**
30954      * Returns true if the dialog is visible
30955      * @return {Boolean}
30956      */
30957     isVisible : function(){
30958         return this.el.isVisible();
30959     },
30960
30961     // private
30962     animHide : function(callback){
30963         var b = Roo.get(this.animateTarget).getBox();
30964         this.proxy.show();
30965         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height);
30966         this.el.hide();
30967         this.proxy.setBounds(b.x, b.y, b.width, b.height, true, .35,
30968                     this.hideEl.createDelegate(this, [callback]));
30969     },
30970
30971     /**
30972      * Hides the dialog.
30973      * @param {Function} callback (optional) Function to call when the dialog is hidden
30974      * @return {Roo.BasicDialog} this
30975      */
30976     hide : function(callback){
30977         if (this.fireEvent("beforehide", this) === false){
30978             return;
30979         }
30980         if(this.shadow){
30981             this.shadow.hide();
30982         }
30983         if(this.shim) {
30984           this.shim.hide();
30985         }
30986         // sometimes animateTarget seems to get set.. causing problems...
30987         // this just double checks..
30988         if(this.animateTarget && Roo.get(this.animateTarget)) {
30989            this.animHide(callback);
30990         }else{
30991             this.el.hide();
30992             this.hideEl(callback);
30993         }
30994         return this;
30995     },
30996
30997     // private
30998     hideEl : function(callback){
30999         this.proxy.hide();
31000         if(this.modal){
31001             this.mask.hide();
31002             Roo.get(document.body).removeClass("x-body-masked");
31003         }
31004         this.fireEvent("hide", this);
31005         if(typeof callback == "function"){
31006             callback();
31007         }
31008     },
31009
31010     // private
31011     hideAction : function(){
31012         this.setLeft("-10000px");
31013         this.setTop("-10000px");
31014         this.setStyle("visibility", "hidden");
31015     },
31016
31017     // private
31018     refreshSize : function(){
31019         this.size = this.el.getSize();
31020         this.xy = this.el.getXY();
31021         Roo.state.Manager.set(this.stateId || this.el.id + "-state", this.el.getBox());
31022     },
31023
31024     // private
31025     // z-index is managed by the DialogManager and may be overwritten at any time
31026     setZIndex : function(index){
31027         if(this.modal){
31028             this.mask.setStyle("z-index", index);
31029         }
31030         if(this.shim){
31031             this.shim.setStyle("z-index", ++index);
31032         }
31033         if(this.shadow){
31034             this.shadow.setZIndex(++index);
31035         }
31036         this.el.setStyle("z-index", ++index);
31037         if(this.proxy){
31038             this.proxy.setStyle("z-index", ++index);
31039         }
31040         if(this.resizer){
31041             this.resizer.proxy.setStyle("z-index", ++index);
31042         }
31043
31044         this.lastZIndex = index;
31045     },
31046
31047     /**
31048      * Returns the element for this dialog
31049      * @return {Roo.Element} The underlying dialog Element
31050      */
31051     getEl : function(){
31052         return this.el;
31053     }
31054 });
31055
31056 /**
31057  * @class Roo.DialogManager
31058  * Provides global access to BasicDialogs that have been created and
31059  * support for z-indexing (layering) multiple open dialogs.
31060  */
31061 Roo.DialogManager = function(){
31062     var list = {};
31063     var accessList = [];
31064     var front = null;
31065
31066     // private
31067     var sortDialogs = function(d1, d2){
31068         return (!d1._lastAccess || d1._lastAccess < d2._lastAccess) ? -1 : 1;
31069     };
31070
31071     // private
31072     var orderDialogs = function(){
31073         accessList.sort(sortDialogs);
31074         var seed = Roo.DialogManager.zseed;
31075         for(var i = 0, len = accessList.length; i < len; i++){
31076             var dlg = accessList[i];
31077             if(dlg){
31078                 dlg.setZIndex(seed + (i*10));
31079             }
31080         }
31081     };
31082
31083     return {
31084         /**
31085          * The starting z-index for BasicDialogs (defaults to 9000)
31086          * @type Number The z-index value
31087          */
31088         zseed : 9000,
31089
31090         // private
31091         register : function(dlg){
31092             list[dlg.id] = dlg;
31093             accessList.push(dlg);
31094         },
31095
31096         // private
31097         unregister : function(dlg){
31098             delete list[dlg.id];
31099             var i=0;
31100             var len=0;
31101             if(!accessList.indexOf){
31102                 for(  i = 0, len = accessList.length; i < len; i++){
31103                     if(accessList[i] == dlg){
31104                         accessList.splice(i, 1);
31105                         return;
31106                     }
31107                 }
31108             }else{
31109                  i = accessList.indexOf(dlg);
31110                 if(i != -1){
31111                     accessList.splice(i, 1);
31112                 }
31113             }
31114         },
31115
31116         /**
31117          * Gets a registered dialog by id
31118          * @param {String/Object} id The id of the dialog or a dialog
31119          * @return {Roo.BasicDialog} this
31120          */
31121         get : function(id){
31122             return typeof id == "object" ? id : list[id];
31123         },
31124
31125         /**
31126          * Brings the specified dialog to the front
31127          * @param {String/Object} dlg The id of the dialog or a dialog
31128          * @return {Roo.BasicDialog} this
31129          */
31130         bringToFront : function(dlg){
31131             dlg = this.get(dlg);
31132             if(dlg != front){
31133                 front = dlg;
31134                 dlg._lastAccess = new Date().getTime();
31135                 orderDialogs();
31136             }
31137             return dlg;
31138         },
31139
31140         /**
31141          * Sends the specified dialog to the back
31142          * @param {String/Object} dlg The id of the dialog or a dialog
31143          * @return {Roo.BasicDialog} this
31144          */
31145         sendToBack : function(dlg){
31146             dlg = this.get(dlg);
31147             dlg._lastAccess = -(new Date().getTime());
31148             orderDialogs();
31149             return dlg;
31150         },
31151
31152         /**
31153          * Hides all dialogs
31154          */
31155         hideAll : function(){
31156             for(var id in list){
31157                 if(list[id] && typeof list[id] != "function" && list[id].isVisible()){
31158                     list[id].hide();
31159                 }
31160             }
31161         }
31162     };
31163 }();
31164
31165 /**
31166  * @class Roo.LayoutDialog
31167  * @extends Roo.BasicDialog
31168  * Dialog which provides adjustments for working with a layout in a Dialog.
31169  * Add your necessary layout config options to the dialog's config.<br>
31170  * Example usage (including a nested layout):
31171  * <pre><code>
31172 if(!dialog){
31173     dialog = new Roo.LayoutDialog("download-dlg", {
31174         modal: true,
31175         width:600,
31176         height:450,
31177         shadow:true,
31178         minWidth:500,
31179         minHeight:350,
31180         autoTabs:true,
31181         proxyDrag:true,
31182         // layout config merges with the dialog config
31183         center:{
31184             tabPosition: "top",
31185             alwaysShowTabs: true
31186         }
31187     });
31188     dialog.addKeyListener(27, dialog.hide, dialog);
31189     dialog.setDefaultButton(dialog.addButton("Close", dialog.hide, dialog));
31190     dialog.addButton("Build It!", this.getDownload, this);
31191
31192     // we can even add nested layouts
31193     var innerLayout = new Roo.BorderLayout("dl-inner", {
31194         east: {
31195             initialSize: 200,
31196             autoScroll:true,
31197             split:true
31198         },
31199         center: {
31200             autoScroll:true
31201         }
31202     });
31203     innerLayout.beginUpdate();
31204     innerLayout.add("east", new Roo.ContentPanel("dl-details"));
31205     innerLayout.add("center", new Roo.ContentPanel("selection-panel"));
31206     innerLayout.endUpdate(true);
31207
31208     var layout = dialog.getLayout();
31209     layout.beginUpdate();
31210     layout.add("center", new Roo.ContentPanel("standard-panel",
31211                         {title: "Download the Source", fitToFrame:true}));
31212     layout.add("center", new Roo.NestedLayoutPanel(innerLayout,
31213                {title: "Build your own roo.js"}));
31214     layout.getRegion("center").showPanel(sp);
31215     layout.endUpdate();
31216 }
31217 </code></pre>
31218     * @constructor
31219     * @param {String/HTMLElement/Roo.Element} el The id of or container element, or config
31220     * @param {Object} config configuration options
31221   */
31222 Roo.LayoutDialog = function(el, cfg){
31223     
31224     var config=  cfg;
31225     if (typeof(cfg) == 'undefined') {
31226         config = Roo.apply({}, el);
31227         // not sure why we use documentElement here.. - it should always be body.
31228         // IE7 borks horribly if we use documentElement.
31229         // webkit also does not like documentElement - it creates a body element...
31230         el = Roo.get( document.body || document.documentElement ).createChild();
31231         //config.autoCreate = true;
31232     }
31233     
31234     
31235     config.autoTabs = false;
31236     Roo.LayoutDialog.superclass.constructor.call(this, el, config);
31237     this.body.setStyle({overflow:"hidden", position:"relative"});
31238     this.layout = new Roo.BorderLayout(this.body.dom, config);
31239     this.layout.monitorWindowResize = false;
31240     this.el.addClass("x-dlg-auto-layout");
31241     // fix case when center region overwrites center function
31242     this.center = Roo.BasicDialog.prototype.center;
31243     this.on("show", this.layout.layout, this.layout, true);
31244     if (config.items) {
31245         var xitems = config.items;
31246         delete config.items;
31247         Roo.each(xitems, this.addxtype, this);
31248     }
31249     
31250     
31251 };
31252 Roo.extend(Roo.LayoutDialog, Roo.BasicDialog, {
31253     /**
31254      * Ends update of the layout <strike>and resets display to none</strike>. Use standard beginUpdate/endUpdate on the layout.
31255      * @deprecated
31256      */
31257     endUpdate : function(){
31258         this.layout.endUpdate();
31259     },
31260
31261     /**
31262      * Begins an update of the layout <strike>and sets display to block and visibility to hidden</strike>. Use standard beginUpdate/endUpdate on the layout.
31263      *  @deprecated
31264      */
31265     beginUpdate : function(){
31266         this.layout.beginUpdate();
31267     },
31268
31269     /**
31270      * Get the BorderLayout for this dialog
31271      * @return {Roo.BorderLayout}
31272      */
31273     getLayout : function(){
31274         return this.layout;
31275     },
31276
31277     showEl : function(){
31278         Roo.LayoutDialog.superclass.showEl.apply(this, arguments);
31279         if(Roo.isIE7){
31280             this.layout.layout();
31281         }
31282     },
31283
31284     // private
31285     // Use the syncHeightBeforeShow config option to control this automatically
31286     syncBodyHeight : function(){
31287         Roo.LayoutDialog.superclass.syncBodyHeight.call(this);
31288         if(this.layout){this.layout.layout();}
31289     },
31290     
31291       /**
31292      * Add an xtype element (actually adds to the layout.)
31293      * @return {Object} xdata xtype object data.
31294      */
31295     
31296     addxtype : function(c) {
31297         return this.layout.addxtype(c);
31298     }
31299 });/*
31300  * Based on:
31301  * Ext JS Library 1.1.1
31302  * Copyright(c) 2006-2007, Ext JS, LLC.
31303  *
31304  * Originally Released Under LGPL - original licence link has changed is not relivant.
31305  *
31306  * Fork - LGPL
31307  * <script type="text/javascript">
31308  */
31309  
31310 /**
31311  * @class Roo.MessageBox
31312  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
31313  * Example usage:
31314  *<pre><code>
31315 // Basic alert:
31316 Roo.Msg.alert('Status', 'Changes saved successfully.');
31317
31318 // Prompt for user data:
31319 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
31320     if (btn == 'ok'){
31321         // process text value...
31322     }
31323 });
31324
31325 // Show a dialog using config options:
31326 Roo.Msg.show({
31327    title:'Save Changes?',
31328    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
31329    buttons: Roo.Msg.YESNOCANCEL,
31330    fn: processResult,
31331    animEl: 'elId'
31332 });
31333 </code></pre>
31334  * @singleton
31335  */
31336 Roo.MessageBox = function(){
31337     var dlg, opt, mask, waitTimer;
31338     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
31339     var buttons, activeTextEl, bwidth;
31340
31341     // private
31342     var handleButton = function(button){
31343         dlg.hide();
31344         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
31345     };
31346
31347     // private
31348     var handleHide = function(){
31349         if(opt && opt.cls){
31350             dlg.el.removeClass(opt.cls);
31351         }
31352         if(waitTimer){
31353             Roo.TaskMgr.stop(waitTimer);
31354             waitTimer = null;
31355         }
31356     };
31357
31358     // private
31359     var updateButtons = function(b){
31360         var width = 0;
31361         if(!b){
31362             buttons["ok"].hide();
31363             buttons["cancel"].hide();
31364             buttons["yes"].hide();
31365             buttons["no"].hide();
31366             dlg.footer.dom.style.display = 'none';
31367             return width;
31368         }
31369         dlg.footer.dom.style.display = '';
31370         for(var k in buttons){
31371             if(typeof buttons[k] != "function"){
31372                 if(b[k]){
31373                     buttons[k].show();
31374                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.MessageBox.buttonText[k]);
31375                     width += buttons[k].el.getWidth()+15;
31376                 }else{
31377                     buttons[k].hide();
31378                 }
31379             }
31380         }
31381         return width;
31382     };
31383
31384     // private
31385     var handleEsc = function(d, k, e){
31386         if(opt && opt.closable !== false){
31387             dlg.hide();
31388         }
31389         if(e){
31390             e.stopEvent();
31391         }
31392     };
31393
31394     return {
31395         /**
31396          * Returns a reference to the underlying {@link Roo.BasicDialog} element
31397          * @return {Roo.BasicDialog} The BasicDialog element
31398          */
31399         getDialog : function(){
31400            if(!dlg){
31401                 dlg = new Roo.BasicDialog("x-msg-box", {
31402                     autoCreate : true,
31403                     shadow: true,
31404                     draggable: true,
31405                     resizable:false,
31406                     constraintoviewport:false,
31407                     fixedcenter:true,
31408                     collapsible : false,
31409                     shim:true,
31410                     modal: true,
31411                     width:400, height:100,
31412                     buttonAlign:"center",
31413                     closeClick : function(){
31414                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
31415                             handleButton("no");
31416                         }else{
31417                             handleButton("cancel");
31418                         }
31419                     }
31420                 });
31421                 dlg.on("hide", handleHide);
31422                 mask = dlg.mask;
31423                 dlg.addKeyListener(27, handleEsc);
31424                 buttons = {};
31425                 var bt = this.buttonText;
31426                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
31427                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
31428                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
31429                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
31430                 bodyEl = dlg.body.createChild({
31431
31432                     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>'
31433                 });
31434                 msgEl = bodyEl.dom.firstChild;
31435                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
31436                 textboxEl.enableDisplayMode();
31437                 textboxEl.addKeyListener([10,13], function(){
31438                     if(dlg.isVisible() && opt && opt.buttons){
31439                         if(opt.buttons.ok){
31440                             handleButton("ok");
31441                         }else if(opt.buttons.yes){
31442                             handleButton("yes");
31443                         }
31444                     }
31445                 });
31446                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
31447                 textareaEl.enableDisplayMode();
31448                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
31449                 progressEl.enableDisplayMode();
31450                 var pf = progressEl.dom.firstChild;
31451                 if (pf) {
31452                     pp = Roo.get(pf.firstChild);
31453                     pp.setHeight(pf.offsetHeight);
31454                 }
31455                 
31456             }
31457             return dlg;
31458         },
31459
31460         /**
31461          * Updates the message box body text
31462          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
31463          * the XHTML-compliant non-breaking space character '&amp;#160;')
31464          * @return {Roo.MessageBox} This message box
31465          */
31466         updateText : function(text){
31467             if(!dlg.isVisible() && !opt.width){
31468                 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
31469             }
31470             msgEl.innerHTML = text || '&#160;';
31471       
31472             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
31473             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
31474             var w = Math.max(
31475                     Math.min(opt.width || cw , this.maxWidth), 
31476                     Math.max(opt.minWidth || this.minWidth, bwidth)
31477             );
31478             if(opt.prompt){
31479                 activeTextEl.setWidth(w);
31480             }
31481             if(dlg.isVisible()){
31482                 dlg.fixedcenter = false;
31483             }
31484             // to big, make it scroll. = But as usual stupid IE does not support
31485             // !important..
31486             
31487             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
31488                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
31489                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
31490             } else {
31491                 bodyEl.dom.style.height = '';
31492                 bodyEl.dom.style.overflowY = '';
31493             }
31494             if (cw > w) {
31495                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
31496             } else {
31497                 bodyEl.dom.style.overflowX = '';
31498             }
31499             
31500             dlg.setContentSize(w, bodyEl.getHeight());
31501             if(dlg.isVisible()){
31502                 dlg.fixedcenter = true;
31503             }
31504             return this;
31505         },
31506
31507         /**
31508          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
31509          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
31510          * @param {Number} value Any number between 0 and 1 (e.g., .5)
31511          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
31512          * @return {Roo.MessageBox} This message box
31513          */
31514         updateProgress : function(value, text){
31515             if(text){
31516                 this.updateText(text);
31517             }
31518             if (pp) { // weird bug on my firefox - for some reason this is not defined
31519                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
31520             }
31521             return this;
31522         },        
31523
31524         /**
31525          * Returns true if the message box is currently displayed
31526          * @return {Boolean} True if the message box is visible, else false
31527          */
31528         isVisible : function(){
31529             return dlg && dlg.isVisible();  
31530         },
31531
31532         /**
31533          * Hides the message box if it is displayed
31534          */
31535         hide : function(){
31536             if(this.isVisible()){
31537                 dlg.hide();
31538             }  
31539         },
31540
31541         /**
31542          * Displays a new message box, or reinitializes an existing message box, based on the config options
31543          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
31544          * The following config object properties are supported:
31545          * <pre>
31546 Property    Type             Description
31547 ----------  ---------------  ------------------------------------------------------------------------------------
31548 animEl            String/Element   An id or Element from which the message box should animate as it opens and
31549                                    closes (defaults to undefined)
31550 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
31551                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
31552 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
31553                                    progress and wait dialogs will ignore this property and always hide the
31554                                    close button as they can only be closed programmatically.
31555 cls               String           A custom CSS class to apply to the message box element
31556 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
31557                                    displayed (defaults to 75)
31558 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
31559                                    function will be btn (the name of the button that was clicked, if applicable,
31560                                    e.g. "ok"), and text (the value of the active text field, if applicable).
31561                                    Progress and wait dialogs will ignore this option since they do not respond to
31562                                    user actions and can only be closed programmatically, so any required function
31563                                    should be called by the same code after it closes the dialog.
31564 icon              String           A CSS class that provides a background image to be used as an icon for
31565                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
31566 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
31567 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
31568 modal             Boolean          False to allow user interaction with the page while the message box is
31569                                    displayed (defaults to true)
31570 msg               String           A string that will replace the existing message box body text (defaults
31571                                    to the XHTML-compliant non-breaking space character '&#160;')
31572 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
31573 progress          Boolean          True to display a progress bar (defaults to false)
31574 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
31575 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
31576 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
31577 title             String           The title text
31578 value             String           The string value to set into the active textbox element if displayed
31579 wait              Boolean          True to display a progress bar (defaults to false)
31580 width             Number           The width of the dialog in pixels
31581 </pre>
31582          *
31583          * Example usage:
31584          * <pre><code>
31585 Roo.Msg.show({
31586    title: 'Address',
31587    msg: 'Please enter your address:',
31588    width: 300,
31589    buttons: Roo.MessageBox.OKCANCEL,
31590    multiline: true,
31591    fn: saveAddress,
31592    animEl: 'addAddressBtn'
31593 });
31594 </code></pre>
31595          * @param {Object} config Configuration options
31596          * @return {Roo.MessageBox} This message box
31597          */
31598         show : function(options)
31599         {
31600             
31601             // this causes nightmares if you show one dialog after another
31602             // especially on callbacks..
31603              
31604             if(this.isVisible()){
31605                 
31606                 this.hide();
31607                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
31608                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
31609                 Roo.log("New Dialog Message:" +  options.msg )
31610                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
31611                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
31612                 
31613             }
31614             var d = this.getDialog();
31615             opt = options;
31616             d.setTitle(opt.title || "&#160;");
31617             d.close.setDisplayed(opt.closable !== false);
31618             activeTextEl = textboxEl;
31619             opt.prompt = opt.prompt || (opt.multiline ? true : false);
31620             if(opt.prompt){
31621                 if(opt.multiline){
31622                     textboxEl.hide();
31623                     textareaEl.show();
31624                     textareaEl.setHeight(typeof opt.multiline == "number" ?
31625                         opt.multiline : this.defaultTextHeight);
31626                     activeTextEl = textareaEl;
31627                 }else{
31628                     textboxEl.show();
31629                     textareaEl.hide();
31630                 }
31631             }else{
31632                 textboxEl.hide();
31633                 textareaEl.hide();
31634             }
31635             progressEl.setDisplayed(opt.progress === true);
31636             this.updateProgress(0);
31637             activeTextEl.dom.value = opt.value || "";
31638             if(opt.prompt){
31639                 dlg.setDefaultButton(activeTextEl);
31640             }else{
31641                 var bs = opt.buttons;
31642                 var db = null;
31643                 if(bs && bs.ok){
31644                     db = buttons["ok"];
31645                 }else if(bs && bs.yes){
31646                     db = buttons["yes"];
31647                 }
31648                 dlg.setDefaultButton(db);
31649             }
31650             bwidth = updateButtons(opt.buttons);
31651             this.updateText(opt.msg);
31652             if(opt.cls){
31653                 d.el.addClass(opt.cls);
31654             }
31655             d.proxyDrag = opt.proxyDrag === true;
31656             d.modal = opt.modal !== false;
31657             d.mask = opt.modal !== false ? mask : false;
31658             if(!d.isVisible()){
31659                 // force it to the end of the z-index stack so it gets a cursor in FF
31660                 document.body.appendChild(dlg.el.dom);
31661                 d.animateTarget = null;
31662                 d.show(options.animEl);
31663             }
31664             return this;
31665         },
31666
31667         /**
31668          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
31669          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
31670          * and closing the message box when the process is complete.
31671          * @param {String} title The title bar text
31672          * @param {String} msg The message box body text
31673          * @return {Roo.MessageBox} This message box
31674          */
31675         progress : function(title, msg){
31676             this.show({
31677                 title : title,
31678                 msg : msg,
31679                 buttons: false,
31680                 progress:true,
31681                 closable:false,
31682                 minWidth: this.minProgressWidth,
31683                 modal : true
31684             });
31685             return this;
31686         },
31687
31688         /**
31689          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
31690          * If a callback function is passed it will be called after the user clicks the button, and the
31691          * id of the button that was clicked will be passed as the only parameter to the callback
31692          * (could also be the top-right close button).
31693          * @param {String} title The title bar text
31694          * @param {String} msg The message box body text
31695          * @param {Function} fn (optional) The callback function invoked after the message box is closed
31696          * @param {Object} scope (optional) The scope of the callback function
31697          * @return {Roo.MessageBox} This message box
31698          */
31699         alert : function(title, msg, fn, scope){
31700             this.show({
31701                 title : title,
31702                 msg : msg,
31703                 buttons: this.OK,
31704                 fn: fn,
31705                 scope : scope,
31706                 modal : true
31707             });
31708             return this;
31709         },
31710
31711         /**
31712          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
31713          * interaction while waiting for a long-running process to complete that does not have defined intervals.
31714          * You are responsible for closing the message box when the process is complete.
31715          * @param {String} msg The message box body text
31716          * @param {String} title (optional) The title bar text
31717          * @return {Roo.MessageBox} This message box
31718          */
31719         wait : function(msg, title){
31720             this.show({
31721                 title : title,
31722                 msg : msg,
31723                 buttons: false,
31724                 closable:false,
31725                 progress:true,
31726                 modal:true,
31727                 width:300,
31728                 wait:true
31729             });
31730             waitTimer = Roo.TaskMgr.start({
31731                 run: function(i){
31732                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
31733                 },
31734                 interval: 1000
31735             });
31736             return this;
31737         },
31738
31739         /**
31740          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
31741          * If a callback function is passed it will be called after the user clicks either button, and the id of the
31742          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
31743          * @param {String} title The title bar text
31744          * @param {String} msg The message box body text
31745          * @param {Function} fn (optional) The callback function invoked after the message box is closed
31746          * @param {Object} scope (optional) The scope of the callback function
31747          * @return {Roo.MessageBox} This message box
31748          */
31749         confirm : function(title, msg, fn, scope){
31750             this.show({
31751                 title : title,
31752                 msg : msg,
31753                 buttons: this.YESNO,
31754                 fn: fn,
31755                 scope : scope,
31756                 modal : true
31757             });
31758             return this;
31759         },
31760
31761         /**
31762          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
31763          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
31764          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
31765          * (could also be the top-right close button) and the text that was entered will be passed as the two
31766          * parameters to the callback.
31767          * @param {String} title The title bar text
31768          * @param {String} msg The message box body text
31769          * @param {Function} fn (optional) The callback function invoked after the message box is closed
31770          * @param {Object} scope (optional) The scope of the callback function
31771          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
31772          * property, or the height in pixels to create the textbox (defaults to false / single-line)
31773          * @return {Roo.MessageBox} This message box
31774          */
31775         prompt : function(title, msg, fn, scope, multiline){
31776             this.show({
31777                 title : title,
31778                 msg : msg,
31779                 buttons: this.OKCANCEL,
31780                 fn: fn,
31781                 minWidth:250,
31782                 scope : scope,
31783                 prompt:true,
31784                 multiline: multiline,
31785                 modal : true
31786             });
31787             return this;
31788         },
31789
31790         /**
31791          * Button config that displays a single OK button
31792          * @type Object
31793          */
31794         OK : {ok:true},
31795         /**
31796          * Button config that displays Yes and No buttons
31797          * @type Object
31798          */
31799         YESNO : {yes:true, no:true},
31800         /**
31801          * Button config that displays OK and Cancel buttons
31802          * @type Object
31803          */
31804         OKCANCEL : {ok:true, cancel:true},
31805         /**
31806          * Button config that displays Yes, No and Cancel buttons
31807          * @type Object
31808          */
31809         YESNOCANCEL : {yes:true, no:true, cancel:true},
31810
31811         /**
31812          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
31813          * @type Number
31814          */
31815         defaultTextHeight : 75,
31816         /**
31817          * The maximum width in pixels of the message box (defaults to 600)
31818          * @type Number
31819          */
31820         maxWidth : 600,
31821         /**
31822          * The minimum width in pixels of the message box (defaults to 100)
31823          * @type Number
31824          */
31825         minWidth : 100,
31826         /**
31827          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
31828          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
31829          * @type Number
31830          */
31831         minProgressWidth : 250,
31832         /**
31833          * An object containing the default button text strings that can be overriden for localized language support.
31834          * Supported properties are: ok, cancel, yes and no.
31835          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
31836          * @type Object
31837          */
31838         buttonText : {
31839             ok : "OK",
31840             cancel : "Cancel",
31841             yes : "Yes",
31842             no : "No"
31843         }
31844     };
31845 }();
31846
31847 /**
31848  * Shorthand for {@link Roo.MessageBox}
31849  */
31850 Roo.Msg = Roo.MessageBox;/*
31851  * Based on:
31852  * Ext JS Library 1.1.1
31853  * Copyright(c) 2006-2007, Ext JS, LLC.
31854  *
31855  * Originally Released Under LGPL - original licence link has changed is not relivant.
31856  *
31857  * Fork - LGPL
31858  * <script type="text/javascript">
31859  */
31860 /**
31861  * @class Roo.QuickTips
31862  * Provides attractive and customizable tooltips for any element.
31863  * @singleton
31864  */
31865 Roo.QuickTips = function(){
31866     var el, tipBody, tipBodyText, tipTitle, tm, cfg, close, tagEls = {}, esc, removeCls = null, bdLeft, bdRight;
31867     var ce, bd, xy, dd;
31868     var visible = false, disabled = true, inited = false;
31869     var showProc = 1, hideProc = 1, dismissProc = 1, locks = [];
31870     
31871     var onOver = function(e){
31872         if(disabled){
31873             return;
31874         }
31875         var t = e.getTarget();
31876         if(!t || t.nodeType !== 1 || t == document || t == document.body){
31877             return;
31878         }
31879         if(ce && t == ce.el){
31880             clearTimeout(hideProc);
31881             return;
31882         }
31883         if(t && tagEls[t.id]){
31884             tagEls[t.id].el = t;
31885             showProc = show.defer(tm.showDelay, tm, [tagEls[t.id]]);
31886             return;
31887         }
31888         var ttp, et = Roo.fly(t);
31889         var ns = cfg.namespace;
31890         if(tm.interceptTitles && t.title){
31891             ttp = t.title;
31892             t.qtip = ttp;
31893             t.removeAttribute("title");
31894             e.preventDefault();
31895         }else{
31896             ttp = t.qtip || et.getAttributeNS(ns, cfg.attribute);
31897         }
31898         if(ttp){
31899             showProc = show.defer(tm.showDelay, tm, [{
31900                 el: t, 
31901                 text: ttp, 
31902                 width: et.getAttributeNS(ns, cfg.width),
31903                 autoHide: et.getAttributeNS(ns, cfg.hide) != "user",
31904                 title: et.getAttributeNS(ns, cfg.title),
31905                     cls: et.getAttributeNS(ns, cfg.cls)
31906             }]);
31907         }
31908     };
31909     
31910     var onOut = function(e){
31911         clearTimeout(showProc);
31912         var t = e.getTarget();
31913         if(t && ce && ce.el == t && (tm.autoHide && ce.autoHide !== false)){
31914             hideProc = setTimeout(hide, tm.hideDelay);
31915         }
31916     };
31917     
31918     var onMove = function(e){
31919         if(disabled){
31920             return;
31921         }
31922         xy = e.getXY();
31923         xy[1] += 18;
31924         if(tm.trackMouse && ce){
31925             el.setXY(xy);
31926         }
31927     };
31928     
31929     var onDown = function(e){
31930         clearTimeout(showProc);
31931         clearTimeout(hideProc);
31932         if(!e.within(el)){
31933             if(tm.hideOnClick){
31934                 hide();
31935                 tm.disable();
31936                 tm.enable.defer(100, tm);
31937             }
31938         }
31939     };
31940     
31941     var getPad = function(){
31942         return 2;//bdLeft.getPadding('l')+bdRight.getPadding('r');
31943     };
31944
31945     var show = function(o){
31946         if(disabled){
31947             return;
31948         }
31949         clearTimeout(dismissProc);
31950         ce = o;
31951         if(removeCls){ // in case manually hidden
31952             el.removeClass(removeCls);
31953             removeCls = null;
31954         }
31955         if(ce.cls){
31956             el.addClass(ce.cls);
31957             removeCls = ce.cls;
31958         }
31959         if(ce.title){
31960             tipTitle.update(ce.title);
31961             tipTitle.show();
31962         }else{
31963             tipTitle.update('');
31964             tipTitle.hide();
31965         }
31966         el.dom.style.width  = tm.maxWidth+'px';
31967         //tipBody.dom.style.width = '';
31968         tipBodyText.update(o.text);
31969         var p = getPad(), w = ce.width;
31970         if(!w){
31971             var td = tipBodyText.dom;
31972             var aw = Math.max(td.offsetWidth, td.clientWidth, td.scrollWidth);
31973             if(aw > tm.maxWidth){
31974                 w = tm.maxWidth;
31975             }else if(aw < tm.minWidth){
31976                 w = tm.minWidth;
31977             }else{
31978                 w = aw;
31979             }
31980         }
31981         //tipBody.setWidth(w);
31982         el.setWidth(parseInt(w, 10) + p);
31983         if(ce.autoHide === false){
31984             close.setDisplayed(true);
31985             if(dd){
31986                 dd.unlock();
31987             }
31988         }else{
31989             close.setDisplayed(false);
31990             if(dd){
31991                 dd.lock();
31992             }
31993         }
31994         if(xy){
31995             el.avoidY = xy[1]-18;
31996             el.setXY(xy);
31997         }
31998         if(tm.animate){
31999             el.setOpacity(.1);
32000             el.setStyle("visibility", "visible");
32001             el.fadeIn({callback: afterShow});
32002         }else{
32003             afterShow();
32004         }
32005     };
32006     
32007     var afterShow = function(){
32008         if(ce){
32009             el.show();
32010             esc.enable();
32011             if(tm.autoDismiss && ce.autoHide !== false){
32012                 dismissProc = setTimeout(hide, tm.autoDismissDelay);
32013             }
32014         }
32015     };
32016     
32017     var hide = function(noanim){
32018         clearTimeout(dismissProc);
32019         clearTimeout(hideProc);
32020         ce = null;
32021         if(el.isVisible()){
32022             esc.disable();
32023             if(noanim !== true && tm.animate){
32024                 el.fadeOut({callback: afterHide});
32025             }else{
32026                 afterHide();
32027             } 
32028         }
32029     };
32030     
32031     var afterHide = function(){
32032         el.hide();
32033         if(removeCls){
32034             el.removeClass(removeCls);
32035             removeCls = null;
32036         }
32037     };
32038     
32039     return {
32040         /**
32041         * @cfg {Number} minWidth
32042         * The minimum width of the quick tip (defaults to 40)
32043         */
32044        minWidth : 40,
32045         /**
32046         * @cfg {Number} maxWidth
32047         * The maximum width of the quick tip (defaults to 300)
32048         */
32049        maxWidth : 300,
32050         /**
32051         * @cfg {Boolean} interceptTitles
32052         * True to automatically use the element's DOM title value if available (defaults to false)
32053         */
32054        interceptTitles : false,
32055         /**
32056         * @cfg {Boolean} trackMouse
32057         * True to have the quick tip follow the mouse as it moves over the target element (defaults to false)
32058         */
32059        trackMouse : false,
32060         /**
32061         * @cfg {Boolean} hideOnClick
32062         * True to hide the quick tip if the user clicks anywhere in the document (defaults to true)
32063         */
32064        hideOnClick : true,
32065         /**
32066         * @cfg {Number} showDelay
32067         * Delay in milliseconds before the quick tip displays after the mouse enters the target element (defaults to 500)
32068         */
32069        showDelay : 500,
32070         /**
32071         * @cfg {Number} hideDelay
32072         * Delay in milliseconds before the quick tip hides when autoHide = true (defaults to 200)
32073         */
32074        hideDelay : 200,
32075         /**
32076         * @cfg {Boolean} autoHide
32077         * True to automatically hide the quick tip after the mouse exits the target element (defaults to true).
32078         * Used in conjunction with hideDelay.
32079         */
32080        autoHide : true,
32081         /**
32082         * @cfg {Boolean}
32083         * True to automatically hide the quick tip after a set period of time, regardless of the user's actions
32084         * (defaults to true).  Used in conjunction with autoDismissDelay.
32085         */
32086        autoDismiss : true,
32087         /**
32088         * @cfg {Number}
32089         * Delay in milliseconds before the quick tip hides when autoDismiss = true (defaults to 5000)
32090         */
32091        autoDismissDelay : 5000,
32092        /**
32093         * @cfg {Boolean} animate
32094         * True to turn on fade animation. Defaults to false (ClearType/scrollbar flicker issues in IE7).
32095         */
32096        animate : false,
32097
32098        /**
32099         * @cfg {String} title
32100         * Title text to display (defaults to '').  This can be any valid HTML markup.
32101         */
32102         title: '',
32103        /**
32104         * @cfg {String} text
32105         * Body text to display (defaults to '').  This can be any valid HTML markup.
32106         */
32107         text : '',
32108        /**
32109         * @cfg {String} cls
32110         * A CSS class to apply to the base quick tip element (defaults to '').
32111         */
32112         cls : '',
32113        /**
32114         * @cfg {Number} width
32115         * Width in pixels of the quick tip (defaults to auto).  Width will be ignored if it exceeds the bounds of
32116         * minWidth or maxWidth.
32117         */
32118         width : null,
32119
32120     /**
32121      * Initialize and enable QuickTips for first use.  This should be called once before the first attempt to access
32122      * or display QuickTips in a page.
32123      */
32124        init : function(){
32125           tm = Roo.QuickTips;
32126           cfg = tm.tagConfig;
32127           if(!inited){
32128               if(!Roo.isReady){ // allow calling of init() before onReady
32129                   Roo.onReady(Roo.QuickTips.init, Roo.QuickTips);
32130                   return;
32131               }
32132               el = new Roo.Layer({cls:"x-tip", shadow:"drop", shim: true, constrain:true, shadowOffset:4});
32133               el.fxDefaults = {stopFx: true};
32134               // maximum custom styling
32135               //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>');
32136               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>');              
32137               tipTitle = el.child('h3');
32138               tipTitle.enableDisplayMode("block");
32139               tipBody = el.child('div.x-tip-bd');
32140               tipBodyText = el.child('div.x-tip-bd-inner');
32141               //bdLeft = el.child('div.x-tip-bd-left');
32142               //bdRight = el.child('div.x-tip-bd-right');
32143               close = el.child('div.x-tip-close');
32144               close.enableDisplayMode("block");
32145               close.on("click", hide);
32146               var d = Roo.get(document);
32147               d.on("mousedown", onDown);
32148               d.on("mouseover", onOver);
32149               d.on("mouseout", onOut);
32150               d.on("mousemove", onMove);
32151               esc = d.addKeyListener(27, hide);
32152               esc.disable();
32153               if(Roo.dd.DD){
32154                   dd = el.initDD("default", null, {
32155                       onDrag : function(){
32156                           el.sync();  
32157                       }
32158                   });
32159                   dd.setHandleElId(tipTitle.id);
32160                   dd.lock();
32161               }
32162               inited = true;
32163           }
32164           this.enable(); 
32165        },
32166
32167     /**
32168      * Configures a new quick tip instance and assigns it to a target element.  The following config options
32169      * are supported:
32170      * <pre>
32171 Property    Type                   Description
32172 ----------  ---------------------  ------------------------------------------------------------------------
32173 target      Element/String/Array   An Element, id or array of ids that this quick tip should be tied to
32174      * </ul>
32175      * @param {Object} config The config object
32176      */
32177        register : function(config){
32178            var cs = config instanceof Array ? config : arguments;
32179            for(var i = 0, len = cs.length; i < len; i++) {
32180                var c = cs[i];
32181                var target = c.target;
32182                if(target){
32183                    if(target instanceof Array){
32184                        for(var j = 0, jlen = target.length; j < jlen; j++){
32185                            tagEls[target[j]] = c;
32186                        }
32187                    }else{
32188                        tagEls[typeof target == 'string' ? target : Roo.id(target)] = c;
32189                    }
32190                }
32191            }
32192        },
32193
32194     /**
32195      * Removes this quick tip from its element and destroys it.
32196      * @param {String/HTMLElement/Element} el The element from which the quick tip is to be removed.
32197      */
32198        unregister : function(el){
32199            delete tagEls[Roo.id(el)];
32200        },
32201
32202     /**
32203      * Enable this quick tip.
32204      */
32205        enable : function(){
32206            if(inited && disabled){
32207                locks.pop();
32208                if(locks.length < 1){
32209                    disabled = false;
32210                }
32211            }
32212        },
32213
32214     /**
32215      * Disable this quick tip.
32216      */
32217        disable : function(){
32218           disabled = true;
32219           clearTimeout(showProc);
32220           clearTimeout(hideProc);
32221           clearTimeout(dismissProc);
32222           if(ce){
32223               hide(true);
32224           }
32225           locks.push(1);
32226        },
32227
32228     /**
32229      * Returns true if the quick tip is enabled, else false.
32230      */
32231        isEnabled : function(){
32232             return !disabled;
32233        },
32234
32235         // private
32236        tagConfig : {
32237            namespace : "ext",
32238            attribute : "qtip",
32239            width : "width",
32240            target : "target",
32241            title : "qtitle",
32242            hide : "hide",
32243            cls : "qclass"
32244        }
32245    };
32246 }();
32247
32248 // backwards compat
32249 Roo.QuickTips.tips = Roo.QuickTips.register;/*
32250  * Based on:
32251  * Ext JS Library 1.1.1
32252  * Copyright(c) 2006-2007, Ext JS, LLC.
32253  *
32254  * Originally Released Under LGPL - original licence link has changed is not relivant.
32255  *
32256  * Fork - LGPL
32257  * <script type="text/javascript">
32258  */
32259  
32260
32261 /**
32262  * @class Roo.tree.TreePanel
32263  * @extends Roo.data.Tree
32264
32265  * @cfg {Boolean} rootVisible false to hide the root node (defaults to true)
32266  * @cfg {Boolean} lines false to disable tree lines (defaults to true)
32267  * @cfg {Boolean} enableDD true to enable drag and drop
32268  * @cfg {Boolean} enableDrag true to enable just drag
32269  * @cfg {Boolean} enableDrop true to enable just drop
32270  * @cfg {Object} dragConfig Custom config to pass to the {@link Roo.tree.TreeDragZone} instance
32271  * @cfg {Object} dropConfig Custom config to pass to the {@link Roo.tree.TreeDropZone} instance
32272  * @cfg {String} ddGroup The DD group this TreePanel belongs to
32273  * @cfg {String} ddAppendOnly True if the tree should only allow append drops (use for trees which are sorted)
32274  * @cfg {Boolean} ddScroll true to enable YUI body scrolling
32275  * @cfg {Boolean} containerScroll true to register this container with ScrollManager
32276  * @cfg {Boolean} hlDrop false to disable node highlight on drop (defaults to the value of Roo.enableFx)
32277  * @cfg {String} hlColor The color of the node highlight (defaults to C3DAF9)
32278  * @cfg {Boolean} animate true to enable animated expand/collapse (defaults to the value of Roo.enableFx)
32279  * @cfg {Boolean} singleExpand true if only 1 node per branch may be expanded
32280  * @cfg {Boolean} selModel A tree selection model to use with this TreePanel (defaults to a {@link Roo.tree.DefaultSelectionModel})
32281  * @cfg {Boolean} loader A TreeLoader for use with this TreePanel
32282  * @cfg {Object|Roo.tree.TreeEditor} editor The TreeEditor or xtype data to display when clicked.
32283  * @cfg {String} pathSeparator The token used to separate sub-paths in path strings (defaults to '/')
32284  * @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>
32285  * @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>
32286  * 
32287  * @constructor
32288  * @param {String/HTMLElement/Element} el The container element
32289  * @param {Object} config
32290  */
32291 Roo.tree.TreePanel = function(el, config){
32292     var root = false;
32293     var loader = false;
32294     if (config.root) {
32295         root = config.root;
32296         delete config.root;
32297     }
32298     if (config.loader) {
32299         loader = config.loader;
32300         delete config.loader;
32301     }
32302     
32303     Roo.apply(this, config);
32304     Roo.tree.TreePanel.superclass.constructor.call(this);
32305     this.el = Roo.get(el);
32306     this.el.addClass('x-tree');
32307     //console.log(root);
32308     if (root) {
32309         this.setRootNode( Roo.factory(root, Roo.tree));
32310     }
32311     if (loader) {
32312         this.loader = Roo.factory(loader, Roo.tree);
32313     }
32314    /**
32315     * Read-only. The id of the container element becomes this TreePanel's id.
32316     */
32317     this.id = this.el.id;
32318     this.addEvents({
32319         /**
32320         * @event beforeload
32321         * Fires before a node is loaded, return false to cancel
32322         * @param {Node} node The node being loaded
32323         */
32324         "beforeload" : true,
32325         /**
32326         * @event load
32327         * Fires when a node is loaded
32328         * @param {Node} node The node that was loaded
32329         */
32330         "load" : true,
32331         /**
32332         * @event textchange
32333         * Fires when the text for a node is changed
32334         * @param {Node} node The node
32335         * @param {String} text The new text
32336         * @param {String} oldText The old text
32337         */
32338         "textchange" : true,
32339         /**
32340         * @event beforeexpand
32341         * Fires before a node is expanded, return false to cancel.
32342         * @param {Node} node The node
32343         * @param {Boolean} deep
32344         * @param {Boolean} anim
32345         */
32346         "beforeexpand" : true,
32347         /**
32348         * @event beforecollapse
32349         * Fires before a node is collapsed, return false to cancel.
32350         * @param {Node} node The node
32351         * @param {Boolean} deep
32352         * @param {Boolean} anim
32353         */
32354         "beforecollapse" : true,
32355         /**
32356         * @event expand
32357         * Fires when a node is expanded
32358         * @param {Node} node The node
32359         */
32360         "expand" : true,
32361         /**
32362         * @event disabledchange
32363         * Fires when the disabled status of a node changes
32364         * @param {Node} node The node
32365         * @param {Boolean} disabled
32366         */
32367         "disabledchange" : true,
32368         /**
32369         * @event collapse
32370         * Fires when a node is collapsed
32371         * @param {Node} node The node
32372         */
32373         "collapse" : true,
32374         /**
32375         * @event beforeclick
32376         * Fires before click processing on a node. Return false to cancel the default action.
32377         * @param {Node} node The node
32378         * @param {Roo.EventObject} e The event object
32379         */
32380         "beforeclick":true,
32381         /**
32382         * @event checkchange
32383         * Fires when a node with a checkbox's checked property changes
32384         * @param {Node} this This node
32385         * @param {Boolean} checked
32386         */
32387         "checkchange":true,
32388         /**
32389         * @event click
32390         * Fires when a node is clicked
32391         * @param {Node} node The node
32392         * @param {Roo.EventObject} e The event object
32393         */
32394         "click":true,
32395         /**
32396         * @event dblclick
32397         * Fires when a node is double clicked
32398         * @param {Node} node The node
32399         * @param {Roo.EventObject} e The event object
32400         */
32401         "dblclick":true,
32402         /**
32403         * @event contextmenu
32404         * Fires when a node is right clicked
32405         * @param {Node} node The node
32406         * @param {Roo.EventObject} e The event object
32407         */
32408         "contextmenu":true,
32409         /**
32410         * @event beforechildrenrendered
32411         * Fires right before the child nodes for a node are rendered
32412         * @param {Node} node The node
32413         */
32414         "beforechildrenrendered":true,
32415         /**
32416         * @event startdrag
32417         * Fires when a node starts being dragged
32418         * @param {Roo.tree.TreePanel} this
32419         * @param {Roo.tree.TreeNode} node
32420         * @param {event} e The raw browser event
32421         */ 
32422        "startdrag" : true,
32423        /**
32424         * @event enddrag
32425         * Fires when a drag operation is complete
32426         * @param {Roo.tree.TreePanel} this
32427         * @param {Roo.tree.TreeNode} node
32428         * @param {event} e The raw browser event
32429         */
32430        "enddrag" : true,
32431        /**
32432         * @event dragdrop
32433         * Fires when a dragged node is dropped on a valid DD target
32434         * @param {Roo.tree.TreePanel} this
32435         * @param {Roo.tree.TreeNode} node
32436         * @param {DD} dd The dd it was dropped on
32437         * @param {event} e The raw browser event
32438         */
32439        "dragdrop" : true,
32440        /**
32441         * @event beforenodedrop
32442         * Fires when a DD object is dropped on a node in this tree for preprocessing. Return false to cancel the drop. The dropEvent
32443         * passed to handlers has the following properties:<br />
32444         * <ul style="padding:5px;padding-left:16px;">
32445         * <li>tree - The TreePanel</li>
32446         * <li>target - The node being targeted for the drop</li>
32447         * <li>data - The drag data from the drag source</li>
32448         * <li>point - The point of the drop - append, above or below</li>
32449         * <li>source - The drag source</li>
32450         * <li>rawEvent - Raw mouse event</li>
32451         * <li>dropNode - Drop node(s) provided by the source <b>OR</b> you can supply node(s)
32452         * to be inserted by setting them on this object.</li>
32453         * <li>cancel - Set this to true to cancel the drop.</li>
32454         * </ul>
32455         * @param {Object} dropEvent
32456         */
32457        "beforenodedrop" : true,
32458        /**
32459         * @event nodedrop
32460         * Fires after a DD object is dropped on a node in this tree. The dropEvent
32461         * passed to handlers has the following properties:<br />
32462         * <ul style="padding:5px;padding-left:16px;">
32463         * <li>tree - The TreePanel</li>
32464         * <li>target - The node being targeted for the drop</li>
32465         * <li>data - The drag data from the drag source</li>
32466         * <li>point - The point of the drop - append, above or below</li>
32467         * <li>source - The drag source</li>
32468         * <li>rawEvent - Raw mouse event</li>
32469         * <li>dropNode - Dropped node(s).</li>
32470         * </ul>
32471         * @param {Object} dropEvent
32472         */
32473        "nodedrop" : true,
32474         /**
32475         * @event nodedragover
32476         * Fires when a tree node is being targeted for a drag drop, return false to signal drop not allowed. The dragOverEvent
32477         * passed to handlers has the following properties:<br />
32478         * <ul style="padding:5px;padding-left:16px;">
32479         * <li>tree - The TreePanel</li>
32480         * <li>target - The node being targeted for the drop</li>
32481         * <li>data - The drag data from the drag source</li>
32482         * <li>point - The point of the drop - append, above or below</li>
32483         * <li>source - The drag source</li>
32484         * <li>rawEvent - Raw mouse event</li>
32485         * <li>dropNode - Drop node(s) provided by the source.</li>
32486         * <li>cancel - Set this to true to signal drop not allowed.</li>
32487         * </ul>
32488         * @param {Object} dragOverEvent
32489         */
32490        "nodedragover" : true
32491         
32492     });
32493     if(this.singleExpand){
32494        this.on("beforeexpand", this.restrictExpand, this);
32495     }
32496     if (this.editor) {
32497         this.editor.tree = this;
32498         this.editor = Roo.factory(this.editor, Roo.tree);
32499     }
32500     
32501     if (this.selModel) {
32502         this.selModel = Roo.factory(this.selModel, Roo.tree);
32503     }
32504    
32505 };
32506 Roo.extend(Roo.tree.TreePanel, Roo.data.Tree, {
32507     rootVisible : true,
32508     animate: Roo.enableFx,
32509     lines : true,
32510     enableDD : false,
32511     hlDrop : Roo.enableFx,
32512   
32513     renderer: false,
32514     
32515     rendererTip: false,
32516     // private
32517     restrictExpand : function(node){
32518         var p = node.parentNode;
32519         if(p){
32520             if(p.expandedChild && p.expandedChild.parentNode == p){
32521                 p.expandedChild.collapse();
32522             }
32523             p.expandedChild = node;
32524         }
32525     },
32526
32527     // private override
32528     setRootNode : function(node){
32529         Roo.tree.TreePanel.superclass.setRootNode.call(this, node);
32530         if(!this.rootVisible){
32531             node.ui = new Roo.tree.RootTreeNodeUI(node);
32532         }
32533         return node;
32534     },
32535
32536     /**
32537      * Returns the container element for this TreePanel
32538      */
32539     getEl : function(){
32540         return this.el;
32541     },
32542
32543     /**
32544      * Returns the default TreeLoader for this TreePanel
32545      */
32546     getLoader : function(){
32547         return this.loader;
32548     },
32549
32550     /**
32551      * Expand all nodes
32552      */
32553     expandAll : function(){
32554         this.root.expand(true);
32555     },
32556
32557     /**
32558      * Collapse all nodes
32559      */
32560     collapseAll : function(){
32561         this.root.collapse(true);
32562     },
32563
32564     /**
32565      * Returns the selection model used by this TreePanel
32566      */
32567     getSelectionModel : function(){
32568         if(!this.selModel){
32569             this.selModel = new Roo.tree.DefaultSelectionModel();
32570         }
32571         return this.selModel;
32572     },
32573
32574     /**
32575      * Retrieve an array of checked nodes, or an array of a specific attribute of checked nodes (e.g. "id")
32576      * @param {String} attribute (optional) Defaults to null (return the actual nodes)
32577      * @param {TreeNode} startNode (optional) The node to start from, defaults to the root
32578      * @return {Array}
32579      */
32580     getChecked : function(a, startNode){
32581         startNode = startNode || this.root;
32582         var r = [];
32583         var f = function(){
32584             if(this.attributes.checked){
32585                 r.push(!a ? this : (a == 'id' ? this.id : this.attributes[a]));
32586             }
32587         }
32588         startNode.cascade(f);
32589         return r;
32590     },
32591
32592     /**
32593      * Expands a specified path in this TreePanel. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
32594      * @param {String} path
32595      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
32596      * @param {Function} callback (optional) The callback to call when the expand is complete. The callback will be called with
32597      * (bSuccess, oLastNode) where bSuccess is if the expand was successful and oLastNode is the last node that was expanded.
32598      */
32599     expandPath : function(path, attr, callback){
32600         attr = attr || "id";
32601         var keys = path.split(this.pathSeparator);
32602         var curNode = this.root;
32603         if(curNode.attributes[attr] != keys[1]){ // invalid root
32604             if(callback){
32605                 callback(false, null);
32606             }
32607             return;
32608         }
32609         var index = 1;
32610         var f = function(){
32611             if(++index == keys.length){
32612                 if(callback){
32613                     callback(true, curNode);
32614                 }
32615                 return;
32616             }
32617             var c = curNode.findChild(attr, keys[index]);
32618             if(!c){
32619                 if(callback){
32620                     callback(false, curNode);
32621                 }
32622                 return;
32623             }
32624             curNode = c;
32625             c.expand(false, false, f);
32626         };
32627         curNode.expand(false, false, f);
32628     },
32629
32630     /**
32631      * Selects the node in this tree at the specified path. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
32632      * @param {String} path
32633      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
32634      * @param {Function} callback (optional) The callback to call when the selection is complete. The callback will be called with
32635      * (bSuccess, oSelNode) where bSuccess is if the selection was successful and oSelNode is the selected node.
32636      */
32637     selectPath : function(path, attr, callback){
32638         attr = attr || "id";
32639         var keys = path.split(this.pathSeparator);
32640         var v = keys.pop();
32641         if(keys.length > 0){
32642             var f = function(success, node){
32643                 if(success && node){
32644                     var n = node.findChild(attr, v);
32645                     if(n){
32646                         n.select();
32647                         if(callback){
32648                             callback(true, n);
32649                         }
32650                     }else if(callback){
32651                         callback(false, n);
32652                     }
32653                 }else{
32654                     if(callback){
32655                         callback(false, n);
32656                     }
32657                 }
32658             };
32659             this.expandPath(keys.join(this.pathSeparator), attr, f);
32660         }else{
32661             this.root.select();
32662             if(callback){
32663                 callback(true, this.root);
32664             }
32665         }
32666     },
32667
32668     getTreeEl : function(){
32669         return this.el;
32670     },
32671
32672     /**
32673      * Trigger rendering of this TreePanel
32674      */
32675     render : function(){
32676         if (this.innerCt) {
32677             return this; // stop it rendering more than once!!
32678         }
32679         
32680         this.innerCt = this.el.createChild({tag:"ul",
32681                cls:"x-tree-root-ct " +
32682                (this.lines ? "x-tree-lines" : "x-tree-no-lines")});
32683
32684         if(this.containerScroll){
32685             Roo.dd.ScrollManager.register(this.el);
32686         }
32687         if((this.enableDD || this.enableDrop) && !this.dropZone){
32688            /**
32689             * The dropZone used by this tree if drop is enabled
32690             * @type Roo.tree.TreeDropZone
32691             */
32692              this.dropZone = new Roo.tree.TreeDropZone(this, this.dropConfig || {
32693                ddGroup: this.ddGroup || "TreeDD", appendOnly: this.ddAppendOnly === true
32694            });
32695         }
32696         if((this.enableDD || this.enableDrag) && !this.dragZone){
32697            /**
32698             * The dragZone used by this tree if drag is enabled
32699             * @type Roo.tree.TreeDragZone
32700             */
32701             this.dragZone = new Roo.tree.TreeDragZone(this, this.dragConfig || {
32702                ddGroup: this.ddGroup || "TreeDD",
32703                scroll: this.ddScroll
32704            });
32705         }
32706         this.getSelectionModel().init(this);
32707         if (!this.root) {
32708             Roo.log("ROOT not set in tree");
32709             return this;
32710         }
32711         this.root.render();
32712         if(!this.rootVisible){
32713             this.root.renderChildren();
32714         }
32715         return this;
32716     }
32717 });/*
32718  * Based on:
32719  * Ext JS Library 1.1.1
32720  * Copyright(c) 2006-2007, Ext JS, LLC.
32721  *
32722  * Originally Released Under LGPL - original licence link has changed is not relivant.
32723  *
32724  * Fork - LGPL
32725  * <script type="text/javascript">
32726  */
32727  
32728
32729 /**
32730  * @class Roo.tree.DefaultSelectionModel
32731  * @extends Roo.util.Observable
32732  * The default single selection for a TreePanel.
32733  * @param {Object} cfg Configuration
32734  */
32735 Roo.tree.DefaultSelectionModel = function(cfg){
32736    this.selNode = null;
32737    
32738    
32739    
32740    this.addEvents({
32741        /**
32742         * @event selectionchange
32743         * Fires when the selected node changes
32744         * @param {DefaultSelectionModel} this
32745         * @param {TreeNode} node the new selection
32746         */
32747        "selectionchange" : true,
32748
32749        /**
32750         * @event beforeselect
32751         * Fires before the selected node changes, return false to cancel the change
32752         * @param {DefaultSelectionModel} this
32753         * @param {TreeNode} node the new selection
32754         * @param {TreeNode} node the old selection
32755         */
32756        "beforeselect" : true
32757    });
32758    
32759     Roo.tree.DefaultSelectionModel.superclass.constructor.call(this,cfg);
32760 };
32761
32762 Roo.extend(Roo.tree.DefaultSelectionModel, Roo.util.Observable, {
32763     init : function(tree){
32764         this.tree = tree;
32765         tree.getTreeEl().on("keydown", this.onKeyDown, this);
32766         tree.on("click", this.onNodeClick, this);
32767     },
32768     
32769     onNodeClick : function(node, e){
32770         if (e.ctrlKey && this.selNode == node)  {
32771             this.unselect(node);
32772             return;
32773         }
32774         this.select(node);
32775     },
32776     
32777     /**
32778      * Select a node.
32779      * @param {TreeNode} node The node to select
32780      * @return {TreeNode} The selected node
32781      */
32782     select : function(node){
32783         var last = this.selNode;
32784         if(last != node && this.fireEvent('beforeselect', this, node, last) !== false){
32785             if(last){
32786                 last.ui.onSelectedChange(false);
32787             }
32788             this.selNode = node;
32789             node.ui.onSelectedChange(true);
32790             this.fireEvent("selectionchange", this, node, last);
32791         }
32792         return node;
32793     },
32794     
32795     /**
32796      * Deselect a node.
32797      * @param {TreeNode} node The node to unselect
32798      */
32799     unselect : function(node){
32800         if(this.selNode == node){
32801             this.clearSelections();
32802         }    
32803     },
32804     
32805     /**
32806      * Clear all selections
32807      */
32808     clearSelections : function(){
32809         var n = this.selNode;
32810         if(n){
32811             n.ui.onSelectedChange(false);
32812             this.selNode = null;
32813             this.fireEvent("selectionchange", this, null);
32814         }
32815         return n;
32816     },
32817     
32818     /**
32819      * Get the selected node
32820      * @return {TreeNode} The selected node
32821      */
32822     getSelectedNode : function(){
32823         return this.selNode;    
32824     },
32825     
32826     /**
32827      * Returns true if the node is selected
32828      * @param {TreeNode} node The node to check
32829      * @return {Boolean}
32830      */
32831     isSelected : function(node){
32832         return this.selNode == node;  
32833     },
32834
32835     /**
32836      * Selects the node above the selected node in the tree, intelligently walking the nodes
32837      * @return TreeNode The new selection
32838      */
32839     selectPrevious : function(){
32840         var s = this.selNode || this.lastSelNode;
32841         if(!s){
32842             return null;
32843         }
32844         var ps = s.previousSibling;
32845         if(ps){
32846             if(!ps.isExpanded() || ps.childNodes.length < 1){
32847                 return this.select(ps);
32848             } else{
32849                 var lc = ps.lastChild;
32850                 while(lc && lc.isExpanded() && lc.childNodes.length > 0){
32851                     lc = lc.lastChild;
32852                 }
32853                 return this.select(lc);
32854             }
32855         } else if(s.parentNode && (this.tree.rootVisible || !s.parentNode.isRoot)){
32856             return this.select(s.parentNode);
32857         }
32858         return null;
32859     },
32860
32861     /**
32862      * Selects the node above the selected node in the tree, intelligently walking the nodes
32863      * @return TreeNode The new selection
32864      */
32865     selectNext : function(){
32866         var s = this.selNode || this.lastSelNode;
32867         if(!s){
32868             return null;
32869         }
32870         if(s.firstChild && s.isExpanded()){
32871              return this.select(s.firstChild);
32872          }else if(s.nextSibling){
32873              return this.select(s.nextSibling);
32874          }else if(s.parentNode){
32875             var newS = null;
32876             s.parentNode.bubble(function(){
32877                 if(this.nextSibling){
32878                     newS = this.getOwnerTree().selModel.select(this.nextSibling);
32879                     return false;
32880                 }
32881             });
32882             return newS;
32883          }
32884         return null;
32885     },
32886
32887     onKeyDown : function(e){
32888         var s = this.selNode || this.lastSelNode;
32889         // undesirable, but required
32890         var sm = this;
32891         if(!s){
32892             return;
32893         }
32894         var k = e.getKey();
32895         switch(k){
32896              case e.DOWN:
32897                  e.stopEvent();
32898                  this.selectNext();
32899              break;
32900              case e.UP:
32901                  e.stopEvent();
32902                  this.selectPrevious();
32903              break;
32904              case e.RIGHT:
32905                  e.preventDefault();
32906                  if(s.hasChildNodes()){
32907                      if(!s.isExpanded()){
32908                          s.expand();
32909                      }else if(s.firstChild){
32910                          this.select(s.firstChild, e);
32911                      }
32912                  }
32913              break;
32914              case e.LEFT:
32915                  e.preventDefault();
32916                  if(s.hasChildNodes() && s.isExpanded()){
32917                      s.collapse();
32918                  }else if(s.parentNode && (this.tree.rootVisible || s.parentNode != this.tree.getRootNode())){
32919                      this.select(s.parentNode, e);
32920                  }
32921              break;
32922         };
32923     }
32924 });
32925
32926 /**
32927  * @class Roo.tree.MultiSelectionModel
32928  * @extends Roo.util.Observable
32929  * Multi selection for a TreePanel.
32930  * @param {Object} cfg Configuration
32931  */
32932 Roo.tree.MultiSelectionModel = function(){
32933    this.selNodes = [];
32934    this.selMap = {};
32935    this.addEvents({
32936        /**
32937         * @event selectionchange
32938         * Fires when the selected nodes change
32939         * @param {MultiSelectionModel} this
32940         * @param {Array} nodes Array of the selected nodes
32941         */
32942        "selectionchange" : true
32943    });
32944    Roo.tree.MultiSelectionModel.superclass.constructor.call(this,cfg);
32945    
32946 };
32947
32948 Roo.extend(Roo.tree.MultiSelectionModel, Roo.util.Observable, {
32949     init : function(tree){
32950         this.tree = tree;
32951         tree.getTreeEl().on("keydown", this.onKeyDown, this);
32952         tree.on("click", this.onNodeClick, this);
32953     },
32954     
32955     onNodeClick : function(node, e){
32956         this.select(node, e, e.ctrlKey);
32957     },
32958     
32959     /**
32960      * Select a node.
32961      * @param {TreeNode} node The node to select
32962      * @param {EventObject} e (optional) An event associated with the selection
32963      * @param {Boolean} keepExisting True to retain existing selections
32964      * @return {TreeNode} The selected node
32965      */
32966     select : function(node, e, keepExisting){
32967         if(keepExisting !== true){
32968             this.clearSelections(true);
32969         }
32970         if(this.isSelected(node)){
32971             this.lastSelNode = node;
32972             return node;
32973         }
32974         this.selNodes.push(node);
32975         this.selMap[node.id] = node;
32976         this.lastSelNode = node;
32977         node.ui.onSelectedChange(true);
32978         this.fireEvent("selectionchange", this, this.selNodes);
32979         return node;
32980     },
32981     
32982     /**
32983      * Deselect a node.
32984      * @param {TreeNode} node The node to unselect
32985      */
32986     unselect : function(node){
32987         if(this.selMap[node.id]){
32988             node.ui.onSelectedChange(false);
32989             var sn = this.selNodes;
32990             var index = -1;
32991             if(sn.indexOf){
32992                 index = sn.indexOf(node);
32993             }else{
32994                 for(var i = 0, len = sn.length; i < len; i++){
32995                     if(sn[i] == node){
32996                         index = i;
32997                         break;
32998                     }
32999                 }
33000             }
33001             if(index != -1){
33002                 this.selNodes.splice(index, 1);
33003             }
33004             delete this.selMap[node.id];
33005             this.fireEvent("selectionchange", this, this.selNodes);
33006         }
33007     },
33008     
33009     /**
33010      * Clear all selections
33011      */
33012     clearSelections : function(suppressEvent){
33013         var sn = this.selNodes;
33014         if(sn.length > 0){
33015             for(var i = 0, len = sn.length; i < len; i++){
33016                 sn[i].ui.onSelectedChange(false);
33017             }
33018             this.selNodes = [];
33019             this.selMap = {};
33020             if(suppressEvent !== true){
33021                 this.fireEvent("selectionchange", this, this.selNodes);
33022             }
33023         }
33024     },
33025     
33026     /**
33027      * Returns true if the node is selected
33028      * @param {TreeNode} node The node to check
33029      * @return {Boolean}
33030      */
33031     isSelected : function(node){
33032         return this.selMap[node.id] ? true : false;  
33033     },
33034     
33035     /**
33036      * Returns an array of the selected nodes
33037      * @return {Array}
33038      */
33039     getSelectedNodes : function(){
33040         return this.selNodes;    
33041     },
33042
33043     onKeyDown : Roo.tree.DefaultSelectionModel.prototype.onKeyDown,
33044
33045     selectNext : Roo.tree.DefaultSelectionModel.prototype.selectNext,
33046
33047     selectPrevious : Roo.tree.DefaultSelectionModel.prototype.selectPrevious
33048 });/*
33049  * Based on:
33050  * Ext JS Library 1.1.1
33051  * Copyright(c) 2006-2007, Ext JS, LLC.
33052  *
33053  * Originally Released Under LGPL - original licence link has changed is not relivant.
33054  *
33055  * Fork - LGPL
33056  * <script type="text/javascript">
33057  */
33058  
33059 /**
33060  * @class Roo.tree.TreeNode
33061  * @extends Roo.data.Node
33062  * @cfg {String} text The text for this node
33063  * @cfg {Boolean} expanded true to start the node expanded
33064  * @cfg {Boolean} allowDrag false to make this node undraggable if DD is on (defaults to true)
33065  * @cfg {Boolean} allowDrop false if this node cannot be drop on
33066  * @cfg {Boolean} disabled true to start the node disabled
33067  * @cfg {String} icon The path to an icon for the node. The preferred way to do this
33068  * is to use the cls or iconCls attributes and add the icon via a CSS background image.
33069  * @cfg {String} cls A css class to be added to the node
33070  * @cfg {String} iconCls A css class to be added to the nodes icon element for applying css background images
33071  * @cfg {String} href URL of the link used for the node (defaults to #)
33072  * @cfg {String} hrefTarget target frame for the link
33073  * @cfg {String} qtip An Ext QuickTip for the node
33074  * @cfg {String} qtipCfg An Ext QuickTip config for the node (used instead of qtip)
33075  * @cfg {Boolean} singleClickExpand True for single click expand on this node
33076  * @cfg {Function} uiProvider A UI <b>class</b> to use for this node (defaults to Roo.tree.TreeNodeUI)
33077  * @cfg {Boolean} checked True to render a checked checkbox for this node, false to render an unchecked checkbox
33078  * (defaults to undefined with no checkbox rendered)
33079  * @constructor
33080  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node
33081  */
33082 Roo.tree.TreeNode = function(attributes){
33083     attributes = attributes || {};
33084     if(typeof attributes == "string"){
33085         attributes = {text: attributes};
33086     }
33087     this.childrenRendered = false;
33088     this.rendered = false;
33089     Roo.tree.TreeNode.superclass.constructor.call(this, attributes);
33090     this.expanded = attributes.expanded === true;
33091     this.isTarget = attributes.isTarget !== false;
33092     this.draggable = attributes.draggable !== false && attributes.allowDrag !== false;
33093     this.allowChildren = attributes.allowChildren !== false && attributes.allowDrop !== false;
33094
33095     /**
33096      * Read-only. The text for this node. To change it use setText().
33097      * @type String
33098      */
33099     this.text = attributes.text;
33100     /**
33101      * True if this node is disabled.
33102      * @type Boolean
33103      */
33104     this.disabled = attributes.disabled === true;
33105
33106     this.addEvents({
33107         /**
33108         * @event textchange
33109         * Fires when the text for this node is changed
33110         * @param {Node} this This node
33111         * @param {String} text The new text
33112         * @param {String} oldText The old text
33113         */
33114         "textchange" : true,
33115         /**
33116         * @event beforeexpand
33117         * Fires before this node is expanded, return false to cancel.
33118         * @param {Node} this This node
33119         * @param {Boolean} deep
33120         * @param {Boolean} anim
33121         */
33122         "beforeexpand" : true,
33123         /**
33124         * @event beforecollapse
33125         * Fires before this node is collapsed, return false to cancel.
33126         * @param {Node} this This node
33127         * @param {Boolean} deep
33128         * @param {Boolean} anim
33129         */
33130         "beforecollapse" : true,
33131         /**
33132         * @event expand
33133         * Fires when this node is expanded
33134         * @param {Node} this This node
33135         */
33136         "expand" : true,
33137         /**
33138         * @event disabledchange
33139         * Fires when the disabled status of this node changes
33140         * @param {Node} this This node
33141         * @param {Boolean} disabled
33142         */
33143         "disabledchange" : true,
33144         /**
33145         * @event collapse
33146         * Fires when this node is collapsed
33147         * @param {Node} this This node
33148         */
33149         "collapse" : true,
33150         /**
33151         * @event beforeclick
33152         * Fires before click processing. Return false to cancel the default action.
33153         * @param {Node} this This node
33154         * @param {Roo.EventObject} e The event object
33155         */
33156         "beforeclick":true,
33157         /**
33158         * @event checkchange
33159         * Fires when a node with a checkbox's checked property changes
33160         * @param {Node} this This node
33161         * @param {Boolean} checked
33162         */
33163         "checkchange":true,
33164         /**
33165         * @event click
33166         * Fires when this node is clicked
33167         * @param {Node} this This node
33168         * @param {Roo.EventObject} e The event object
33169         */
33170         "click":true,
33171         /**
33172         * @event dblclick
33173         * Fires when this node is double clicked
33174         * @param {Node} this This node
33175         * @param {Roo.EventObject} e The event object
33176         */
33177         "dblclick":true,
33178         /**
33179         * @event contextmenu
33180         * Fires when this node is right clicked
33181         * @param {Node} this This node
33182         * @param {Roo.EventObject} e The event object
33183         */
33184         "contextmenu":true,
33185         /**
33186         * @event beforechildrenrendered
33187         * Fires right before the child nodes for this node are rendered
33188         * @param {Node} this This node
33189         */
33190         "beforechildrenrendered":true
33191     });
33192
33193     var uiClass = this.attributes.uiProvider || Roo.tree.TreeNodeUI;
33194
33195     /**
33196      * Read-only. The UI for this node
33197      * @type TreeNodeUI
33198      */
33199     this.ui = new uiClass(this);
33200     
33201     // finally support items[]
33202     if (typeof(this.attributes.items) == 'undefined' || !this.attributes.items) {
33203         return;
33204     }
33205     
33206     
33207     Roo.each(this.attributes.items, function(c) {
33208         this.appendChild(Roo.factory(c,Roo.Tree));
33209     }, this);
33210     delete this.attributes.items;
33211     
33212     
33213     
33214 };
33215 Roo.extend(Roo.tree.TreeNode, Roo.data.Node, {
33216     preventHScroll: true,
33217     /**
33218      * Returns true if this node is expanded
33219      * @return {Boolean}
33220      */
33221     isExpanded : function(){
33222         return this.expanded;
33223     },
33224
33225     /**
33226      * Returns the UI object for this node
33227      * @return {TreeNodeUI}
33228      */
33229     getUI : function(){
33230         return this.ui;
33231     },
33232
33233     // private override
33234     setFirstChild : function(node){
33235         var of = this.firstChild;
33236         Roo.tree.TreeNode.superclass.setFirstChild.call(this, node);
33237         if(this.childrenRendered && of && node != of){
33238             of.renderIndent(true, true);
33239         }
33240         if(this.rendered){
33241             this.renderIndent(true, true);
33242         }
33243     },
33244
33245     // private override
33246     setLastChild : function(node){
33247         var ol = this.lastChild;
33248         Roo.tree.TreeNode.superclass.setLastChild.call(this, node);
33249         if(this.childrenRendered && ol && node != ol){
33250             ol.renderIndent(true, true);
33251         }
33252         if(this.rendered){
33253             this.renderIndent(true, true);
33254         }
33255     },
33256
33257     // these methods are overridden to provide lazy rendering support
33258     // private override
33259     appendChild : function()
33260     {
33261         var node = Roo.tree.TreeNode.superclass.appendChild.apply(this, arguments);
33262         if(node && this.childrenRendered){
33263             node.render();
33264         }
33265         this.ui.updateExpandIcon();
33266         return node;
33267     },
33268
33269     // private override
33270     removeChild : function(node){
33271         this.ownerTree.getSelectionModel().unselect(node);
33272         Roo.tree.TreeNode.superclass.removeChild.apply(this, arguments);
33273         // if it's been rendered remove dom node
33274         if(this.childrenRendered){
33275             node.ui.remove();
33276         }
33277         if(this.childNodes.length < 1){
33278             this.collapse(false, false);
33279         }else{
33280             this.ui.updateExpandIcon();
33281         }
33282         if(!this.firstChild) {
33283             this.childrenRendered = false;
33284         }
33285         return node;
33286     },
33287
33288     // private override
33289     insertBefore : function(node, refNode){
33290         var newNode = Roo.tree.TreeNode.superclass.insertBefore.apply(this, arguments);
33291         if(newNode && refNode && this.childrenRendered){
33292             node.render();
33293         }
33294         this.ui.updateExpandIcon();
33295         return newNode;
33296     },
33297
33298     /**
33299      * Sets the text for this node
33300      * @param {String} text
33301      */
33302     setText : function(text){
33303         var oldText = this.text;
33304         this.text = text;
33305         this.attributes.text = text;
33306         if(this.rendered){ // event without subscribing
33307             this.ui.onTextChange(this, text, oldText);
33308         }
33309         this.fireEvent("textchange", this, text, oldText);
33310     },
33311
33312     /**
33313      * Triggers selection of this node
33314      */
33315     select : function(){
33316         this.getOwnerTree().getSelectionModel().select(this);
33317     },
33318
33319     /**
33320      * Triggers deselection of this node
33321      */
33322     unselect : function(){
33323         this.getOwnerTree().getSelectionModel().unselect(this);
33324     },
33325
33326     /**
33327      * Returns true if this node is selected
33328      * @return {Boolean}
33329      */
33330     isSelected : function(){
33331         return this.getOwnerTree().getSelectionModel().isSelected(this);
33332     },
33333
33334     /**
33335      * Expand this node.
33336      * @param {Boolean} deep (optional) True to expand all children as well
33337      * @param {Boolean} anim (optional) false to cancel the default animation
33338      * @param {Function} callback (optional) A callback to be called when
33339      * expanding this node completes (does not wait for deep expand to complete).
33340      * Called with 1 parameter, this node.
33341      */
33342     expand : function(deep, anim, callback){
33343         if(!this.expanded){
33344             if(this.fireEvent("beforeexpand", this, deep, anim) === false){
33345                 return;
33346             }
33347             if(!this.childrenRendered){
33348                 this.renderChildren();
33349             }
33350             this.expanded = true;
33351             if(!this.isHiddenRoot() && (this.getOwnerTree().animate && anim !== false) || anim){
33352                 this.ui.animExpand(function(){
33353                     this.fireEvent("expand", this);
33354                     if(typeof callback == "function"){
33355                         callback(this);
33356                     }
33357                     if(deep === true){
33358                         this.expandChildNodes(true);
33359                     }
33360                 }.createDelegate(this));
33361                 return;
33362             }else{
33363                 this.ui.expand();
33364                 this.fireEvent("expand", this);
33365                 if(typeof callback == "function"){
33366                     callback(this);
33367                 }
33368             }
33369         }else{
33370            if(typeof callback == "function"){
33371                callback(this);
33372            }
33373         }
33374         if(deep === true){
33375             this.expandChildNodes(true);
33376         }
33377     },
33378
33379     isHiddenRoot : function(){
33380         return this.isRoot && !this.getOwnerTree().rootVisible;
33381     },
33382
33383     /**
33384      * Collapse this node.
33385      * @param {Boolean} deep (optional) True to collapse all children as well
33386      * @param {Boolean} anim (optional) false to cancel the default animation
33387      */
33388     collapse : function(deep, anim){
33389         if(this.expanded && !this.isHiddenRoot()){
33390             if(this.fireEvent("beforecollapse", this, deep, anim) === false){
33391                 return;
33392             }
33393             this.expanded = false;
33394             if((this.getOwnerTree().animate && anim !== false) || anim){
33395                 this.ui.animCollapse(function(){
33396                     this.fireEvent("collapse", this);
33397                     if(deep === true){
33398                         this.collapseChildNodes(true);
33399                     }
33400                 }.createDelegate(this));
33401                 return;
33402             }else{
33403                 this.ui.collapse();
33404                 this.fireEvent("collapse", this);
33405             }
33406         }
33407         if(deep === true){
33408             var cs = this.childNodes;
33409             for(var i = 0, len = cs.length; i < len; i++) {
33410                 cs[i].collapse(true, false);
33411             }
33412         }
33413     },
33414
33415     // private
33416     delayedExpand : function(delay){
33417         if(!this.expandProcId){
33418             this.expandProcId = this.expand.defer(delay, this);
33419         }
33420     },
33421
33422     // private
33423     cancelExpand : function(){
33424         if(this.expandProcId){
33425             clearTimeout(this.expandProcId);
33426         }
33427         this.expandProcId = false;
33428     },
33429
33430     /**
33431      * Toggles expanded/collapsed state of the node
33432      */
33433     toggle : function(){
33434         if(this.expanded){
33435             this.collapse();
33436         }else{
33437             this.expand();
33438         }
33439     },
33440
33441     /**
33442      * Ensures all parent nodes are expanded
33443      */
33444     ensureVisible : function(callback){
33445         var tree = this.getOwnerTree();
33446         tree.expandPath(this.parentNode.getPath(), false, function(){
33447             tree.getTreeEl().scrollChildIntoView(this.ui.anchor);
33448             Roo.callback(callback);
33449         }.createDelegate(this));
33450     },
33451
33452     /**
33453      * Expand all child nodes
33454      * @param {Boolean} deep (optional) true if the child nodes should also expand their child nodes
33455      */
33456     expandChildNodes : function(deep){
33457         var cs = this.childNodes;
33458         for(var i = 0, len = cs.length; i < len; i++) {
33459                 cs[i].expand(deep);
33460         }
33461     },
33462
33463     /**
33464      * Collapse all child nodes
33465      * @param {Boolean} deep (optional) true if the child nodes should also collapse their child nodes
33466      */
33467     collapseChildNodes : function(deep){
33468         var cs = this.childNodes;
33469         for(var i = 0, len = cs.length; i < len; i++) {
33470                 cs[i].collapse(deep);
33471         }
33472     },
33473
33474     /**
33475      * Disables this node
33476      */
33477     disable : function(){
33478         this.disabled = true;
33479         this.unselect();
33480         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
33481             this.ui.onDisableChange(this, true);
33482         }
33483         this.fireEvent("disabledchange", this, true);
33484     },
33485
33486     /**
33487      * Enables this node
33488      */
33489     enable : function(){
33490         this.disabled = false;
33491         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
33492             this.ui.onDisableChange(this, false);
33493         }
33494         this.fireEvent("disabledchange", this, false);
33495     },
33496
33497     // private
33498     renderChildren : function(suppressEvent){
33499         if(suppressEvent !== false){
33500             this.fireEvent("beforechildrenrendered", this);
33501         }
33502         var cs = this.childNodes;
33503         for(var i = 0, len = cs.length; i < len; i++){
33504             cs[i].render(true);
33505         }
33506         this.childrenRendered = true;
33507     },
33508
33509     // private
33510     sort : function(fn, scope){
33511         Roo.tree.TreeNode.superclass.sort.apply(this, arguments);
33512         if(this.childrenRendered){
33513             var cs = this.childNodes;
33514             for(var i = 0, len = cs.length; i < len; i++){
33515                 cs[i].render(true);
33516             }
33517         }
33518     },
33519
33520     // private
33521     render : function(bulkRender){
33522         this.ui.render(bulkRender);
33523         if(!this.rendered){
33524             this.rendered = true;
33525             if(this.expanded){
33526                 this.expanded = false;
33527                 this.expand(false, false);
33528             }
33529         }
33530     },
33531
33532     // private
33533     renderIndent : function(deep, refresh){
33534         if(refresh){
33535             this.ui.childIndent = null;
33536         }
33537         this.ui.renderIndent();
33538         if(deep === true && this.childrenRendered){
33539             var cs = this.childNodes;
33540             for(var i = 0, len = cs.length; i < len; i++){
33541                 cs[i].renderIndent(true, refresh);
33542             }
33543         }
33544     }
33545 });/*
33546  * Based on:
33547  * Ext JS Library 1.1.1
33548  * Copyright(c) 2006-2007, Ext JS, LLC.
33549  *
33550  * Originally Released Under LGPL - original licence link has changed is not relivant.
33551  *
33552  * Fork - LGPL
33553  * <script type="text/javascript">
33554  */
33555  
33556 /**
33557  * @class Roo.tree.AsyncTreeNode
33558  * @extends Roo.tree.TreeNode
33559  * @cfg {TreeLoader} loader A TreeLoader to be used by this node (defaults to the loader defined on the tree)
33560  * @constructor
33561  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node 
33562  */
33563  Roo.tree.AsyncTreeNode = function(config){
33564     this.loaded = false;
33565     this.loading = false;
33566     Roo.tree.AsyncTreeNode.superclass.constructor.apply(this, arguments);
33567     /**
33568     * @event beforeload
33569     * Fires before this node is loaded, return false to cancel
33570     * @param {Node} this This node
33571     */
33572     this.addEvents({'beforeload':true, 'load': true});
33573     /**
33574     * @event load
33575     * Fires when this node is loaded
33576     * @param {Node} this This node
33577     */
33578     /**
33579      * The loader used by this node (defaults to using the tree's defined loader)
33580      * @type TreeLoader
33581      * @property loader
33582      */
33583 };
33584 Roo.extend(Roo.tree.AsyncTreeNode, Roo.tree.TreeNode, {
33585     expand : function(deep, anim, callback){
33586         if(this.loading){ // if an async load is already running, waiting til it's done
33587             var timer;
33588             var f = function(){
33589                 if(!this.loading){ // done loading
33590                     clearInterval(timer);
33591                     this.expand(deep, anim, callback);
33592                 }
33593             }.createDelegate(this);
33594             timer = setInterval(f, 200);
33595             return;
33596         }
33597         if(!this.loaded){
33598             if(this.fireEvent("beforeload", this) === false){
33599                 return;
33600             }
33601             this.loading = true;
33602             this.ui.beforeLoad(this);
33603             var loader = this.loader || this.attributes.loader || this.getOwnerTree().getLoader();
33604             if(loader){
33605                 loader.load(this, this.loadComplete.createDelegate(this, [deep, anim, callback]));
33606                 return;
33607             }
33608         }
33609         Roo.tree.AsyncTreeNode.superclass.expand.call(this, deep, anim, callback);
33610     },
33611     
33612     /**
33613      * Returns true if this node is currently loading
33614      * @return {Boolean}
33615      */
33616     isLoading : function(){
33617         return this.loading;  
33618     },
33619     
33620     loadComplete : function(deep, anim, callback){
33621         this.loading = false;
33622         this.loaded = true;
33623         this.ui.afterLoad(this);
33624         this.fireEvent("load", this);
33625         this.expand(deep, anim, callback);
33626     },
33627     
33628     /**
33629      * Returns true if this node has been loaded
33630      * @return {Boolean}
33631      */
33632     isLoaded : function(){
33633         return this.loaded;
33634     },
33635     
33636     hasChildNodes : function(){
33637         if(!this.isLeaf() && !this.loaded){
33638             return true;
33639         }else{
33640             return Roo.tree.AsyncTreeNode.superclass.hasChildNodes.call(this);
33641         }
33642     },
33643
33644     /**
33645      * Trigger a reload for this node
33646      * @param {Function} callback
33647      */
33648     reload : function(callback){
33649         this.collapse(false, false);
33650         while(this.firstChild){
33651             this.removeChild(this.firstChild);
33652         }
33653         this.childrenRendered = false;
33654         this.loaded = false;
33655         if(this.isHiddenRoot()){
33656             this.expanded = false;
33657         }
33658         this.expand(false, false, callback);
33659     }
33660 });/*
33661  * Based on:
33662  * Ext JS Library 1.1.1
33663  * Copyright(c) 2006-2007, Ext JS, LLC.
33664  *
33665  * Originally Released Under LGPL - original licence link has changed is not relivant.
33666  *
33667  * Fork - LGPL
33668  * <script type="text/javascript">
33669  */
33670  
33671 /**
33672  * @class Roo.tree.TreeNodeUI
33673  * @constructor
33674  * @param {Object} node The node to render
33675  * The TreeNode UI implementation is separate from the
33676  * tree implementation. Unless you are customizing the tree UI,
33677  * you should never have to use this directly.
33678  */
33679 Roo.tree.TreeNodeUI = function(node){
33680     this.node = node;
33681     this.rendered = false;
33682     this.animating = false;
33683     this.emptyIcon = Roo.BLANK_IMAGE_URL;
33684 };
33685
33686 Roo.tree.TreeNodeUI.prototype = {
33687     removeChild : function(node){
33688         if(this.rendered){
33689             this.ctNode.removeChild(node.ui.getEl());
33690         }
33691     },
33692
33693     beforeLoad : function(){
33694          this.addClass("x-tree-node-loading");
33695     },
33696
33697     afterLoad : function(){
33698          this.removeClass("x-tree-node-loading");
33699     },
33700
33701     onTextChange : function(node, text, oldText){
33702         if(this.rendered){
33703             this.textNode.innerHTML = text;
33704         }
33705     },
33706
33707     onDisableChange : function(node, state){
33708         this.disabled = state;
33709         if(state){
33710             this.addClass("x-tree-node-disabled");
33711         }else{
33712             this.removeClass("x-tree-node-disabled");
33713         }
33714     },
33715
33716     onSelectedChange : function(state){
33717         if(state){
33718             this.focus();
33719             this.addClass("x-tree-selected");
33720         }else{
33721             //this.blur();
33722             this.removeClass("x-tree-selected");
33723         }
33724     },
33725
33726     onMove : function(tree, node, oldParent, newParent, index, refNode){
33727         this.childIndent = null;
33728         if(this.rendered){
33729             var targetNode = newParent.ui.getContainer();
33730             if(!targetNode){//target not rendered
33731                 this.holder = document.createElement("div");
33732                 this.holder.appendChild(this.wrap);
33733                 return;
33734             }
33735             var insertBefore = refNode ? refNode.ui.getEl() : null;
33736             if(insertBefore){
33737                 targetNode.insertBefore(this.wrap, insertBefore);
33738             }else{
33739                 targetNode.appendChild(this.wrap);
33740             }
33741             this.node.renderIndent(true);
33742         }
33743     },
33744
33745     addClass : function(cls){
33746         if(this.elNode){
33747             Roo.fly(this.elNode).addClass(cls);
33748         }
33749     },
33750
33751     removeClass : function(cls){
33752         if(this.elNode){
33753             Roo.fly(this.elNode).removeClass(cls);
33754         }
33755     },
33756
33757     remove : function(){
33758         if(this.rendered){
33759             this.holder = document.createElement("div");
33760             this.holder.appendChild(this.wrap);
33761         }
33762     },
33763
33764     fireEvent : function(){
33765         return this.node.fireEvent.apply(this.node, arguments);
33766     },
33767
33768     initEvents : function(){
33769         this.node.on("move", this.onMove, this);
33770         var E = Roo.EventManager;
33771         var a = this.anchor;
33772
33773         var el = Roo.fly(a, '_treeui');
33774
33775         if(Roo.isOpera){ // opera render bug ignores the CSS
33776             el.setStyle("text-decoration", "none");
33777         }
33778
33779         el.on("click", this.onClick, this);
33780         el.on("dblclick", this.onDblClick, this);
33781
33782         if(this.checkbox){
33783             Roo.EventManager.on(this.checkbox,
33784                     Roo.isIE ? 'click' : 'change', this.onCheckChange, this);
33785         }
33786
33787         el.on("contextmenu", this.onContextMenu, this);
33788
33789         var icon = Roo.fly(this.iconNode);
33790         icon.on("click", this.onClick, this);
33791         icon.on("dblclick", this.onDblClick, this);
33792         icon.on("contextmenu", this.onContextMenu, this);
33793         E.on(this.ecNode, "click", this.ecClick, this, true);
33794
33795         if(this.node.disabled){
33796             this.addClass("x-tree-node-disabled");
33797         }
33798         if(this.node.hidden){
33799             this.addClass("x-tree-node-disabled");
33800         }
33801         var ot = this.node.getOwnerTree();
33802         var dd = ot.enableDD || ot.enableDrag || ot.enableDrop;
33803         if(dd && (!this.node.isRoot || ot.rootVisible)){
33804             Roo.dd.Registry.register(this.elNode, {
33805                 node: this.node,
33806                 handles: this.getDDHandles(),
33807                 isHandle: false
33808             });
33809         }
33810     },
33811
33812     getDDHandles : function(){
33813         return [this.iconNode, this.textNode];
33814     },
33815
33816     hide : function(){
33817         if(this.rendered){
33818             this.wrap.style.display = "none";
33819         }
33820     },
33821
33822     show : function(){
33823         if(this.rendered){
33824             this.wrap.style.display = "";
33825         }
33826     },
33827
33828     onContextMenu : function(e){
33829         if (this.node.hasListener("contextmenu") || this.node.getOwnerTree().hasListener("contextmenu")) {
33830             e.preventDefault();
33831             this.focus();
33832             this.fireEvent("contextmenu", this.node, e);
33833         }
33834     },
33835
33836     onClick : function(e){
33837         if(this.dropping){
33838             e.stopEvent();
33839             return;
33840         }
33841         if(this.fireEvent("beforeclick", this.node, e) !== false){
33842             if(!this.disabled && this.node.attributes.href){
33843                 this.fireEvent("click", this.node, e);
33844                 return;
33845             }
33846             e.preventDefault();
33847             if(this.disabled){
33848                 return;
33849             }
33850
33851             if(this.node.attributes.singleClickExpand && !this.animating && this.node.hasChildNodes()){
33852                 this.node.toggle();
33853             }
33854
33855             this.fireEvent("click", this.node, e);
33856         }else{
33857             e.stopEvent();
33858         }
33859     },
33860
33861     onDblClick : function(e){
33862         e.preventDefault();
33863         if(this.disabled){
33864             return;
33865         }
33866         if(this.checkbox){
33867             this.toggleCheck();
33868         }
33869         if(!this.animating && this.node.hasChildNodes()){
33870             this.node.toggle();
33871         }
33872         this.fireEvent("dblclick", this.node, e);
33873     },
33874
33875     onCheckChange : function(){
33876         var checked = this.checkbox.checked;
33877         this.node.attributes.checked = checked;
33878         this.fireEvent('checkchange', this.node, checked);
33879     },
33880
33881     ecClick : function(e){
33882         if(!this.animating && this.node.hasChildNodes()){
33883             this.node.toggle();
33884         }
33885     },
33886
33887     startDrop : function(){
33888         this.dropping = true;
33889     },
33890
33891     // delayed drop so the click event doesn't get fired on a drop
33892     endDrop : function(){
33893        setTimeout(function(){
33894            this.dropping = false;
33895        }.createDelegate(this), 50);
33896     },
33897
33898     expand : function(){
33899         this.updateExpandIcon();
33900         this.ctNode.style.display = "";
33901     },
33902
33903     focus : function(){
33904         if(!this.node.preventHScroll){
33905             try{this.anchor.focus();
33906             }catch(e){}
33907         }else if(!Roo.isIE){
33908             try{
33909                 var noscroll = this.node.getOwnerTree().getTreeEl().dom;
33910                 var l = noscroll.scrollLeft;
33911                 this.anchor.focus();
33912                 noscroll.scrollLeft = l;
33913             }catch(e){}
33914         }
33915     },
33916
33917     toggleCheck : function(value){
33918         var cb = this.checkbox;
33919         if(cb){
33920             cb.checked = (value === undefined ? !cb.checked : value);
33921         }
33922     },
33923
33924     blur : function(){
33925         try{
33926             this.anchor.blur();
33927         }catch(e){}
33928     },
33929
33930     animExpand : function(callback){
33931         var ct = Roo.get(this.ctNode);
33932         ct.stopFx();
33933         if(!this.node.hasChildNodes()){
33934             this.updateExpandIcon();
33935             this.ctNode.style.display = "";
33936             Roo.callback(callback);
33937             return;
33938         }
33939         this.animating = true;
33940         this.updateExpandIcon();
33941
33942         ct.slideIn('t', {
33943            callback : function(){
33944                this.animating = false;
33945                Roo.callback(callback);
33946             },
33947             scope: this,
33948             duration: this.node.ownerTree.duration || .25
33949         });
33950     },
33951
33952     highlight : function(){
33953         var tree = this.node.getOwnerTree();
33954         Roo.fly(this.wrap).highlight(
33955             tree.hlColor || "C3DAF9",
33956             {endColor: tree.hlBaseColor}
33957         );
33958     },
33959
33960     collapse : function(){
33961         this.updateExpandIcon();
33962         this.ctNode.style.display = "none";
33963     },
33964
33965     animCollapse : function(callback){
33966         var ct = Roo.get(this.ctNode);
33967         ct.enableDisplayMode('block');
33968         ct.stopFx();
33969
33970         this.animating = true;
33971         this.updateExpandIcon();
33972
33973         ct.slideOut('t', {
33974             callback : function(){
33975                this.animating = false;
33976                Roo.callback(callback);
33977             },
33978             scope: this,
33979             duration: this.node.ownerTree.duration || .25
33980         });
33981     },
33982
33983     getContainer : function(){
33984         return this.ctNode;
33985     },
33986
33987     getEl : function(){
33988         return this.wrap;
33989     },
33990
33991     appendDDGhost : function(ghostNode){
33992         ghostNode.appendChild(this.elNode.cloneNode(true));
33993     },
33994
33995     getDDRepairXY : function(){
33996         return Roo.lib.Dom.getXY(this.iconNode);
33997     },
33998
33999     onRender : function(){
34000         this.render();
34001     },
34002
34003     render : function(bulkRender){
34004         var n = this.node, a = n.attributes;
34005         var targetNode = n.parentNode ?
34006               n.parentNode.ui.getContainer() : n.ownerTree.innerCt.dom;
34007
34008         if(!this.rendered){
34009             this.rendered = true;
34010
34011             this.renderElements(n, a, targetNode, bulkRender);
34012
34013             if(a.qtip){
34014                if(this.textNode.setAttributeNS){
34015                    this.textNode.setAttributeNS("ext", "qtip", a.qtip);
34016                    if(a.qtipTitle){
34017                        this.textNode.setAttributeNS("ext", "qtitle", a.qtipTitle);
34018                    }
34019                }else{
34020                    this.textNode.setAttribute("ext:qtip", a.qtip);
34021                    if(a.qtipTitle){
34022                        this.textNode.setAttribute("ext:qtitle", a.qtipTitle);
34023                    }
34024                }
34025             }else if(a.qtipCfg){
34026                 a.qtipCfg.target = Roo.id(this.textNode);
34027                 Roo.QuickTips.register(a.qtipCfg);
34028             }
34029             this.initEvents();
34030             if(!this.node.expanded){
34031                 this.updateExpandIcon();
34032             }
34033         }else{
34034             if(bulkRender === true) {
34035                 targetNode.appendChild(this.wrap);
34036             }
34037         }
34038     },
34039
34040     renderElements : function(n, a, targetNode, bulkRender)
34041     {
34042         // add some indent caching, this helps performance when rendering a large tree
34043         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
34044         var t = n.getOwnerTree();
34045         var txt = t.renderer ? t.renderer(n.attributes) : Roo.util.Format.htmlEncode(n.text);
34046         if (typeof(n.attributes.html) != 'undefined') {
34047             txt = n.attributes.html;
34048         }
34049         var tip = t.rendererTip ? t.rendererTip(n.attributes) : txt;
34050         var cb = typeof a.checked == 'boolean';
34051         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
34052         var buf = ['<li class="x-tree-node"><div class="x-tree-node-el ', a.cls,'">',
34053             '<span class="x-tree-node-indent">',this.indentMarkup,"</span>",
34054             '<img src="', this.emptyIcon, '" class="x-tree-ec-icon" />',
34055             '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',(a.icon ? " x-tree-node-inline-icon" : ""),(a.iconCls ? " "+a.iconCls : ""),'" unselectable="on" />',
34056             cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + (a.checked ? 'checked="checked" />' : ' />')) : '',
34057             '<a hidefocus="on" href="',href,'" tabIndex="1" ',
34058              a.hrefTarget ? ' target="'+a.hrefTarget+'"' : "", 
34059                 '><span unselectable="on" qtip="' , tip ,'">',txt,"</span></a></div>",
34060             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
34061             "</li>"];
34062
34063         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
34064             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
34065                                 n.nextSibling.ui.getEl(), buf.join(""));
34066         }else{
34067             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
34068         }
34069
34070         this.elNode = this.wrap.childNodes[0];
34071         this.ctNode = this.wrap.childNodes[1];
34072         var cs = this.elNode.childNodes;
34073         this.indentNode = cs[0];
34074         this.ecNode = cs[1];
34075         this.iconNode = cs[2];
34076         var index = 3;
34077         if(cb){
34078             this.checkbox = cs[3];
34079             index++;
34080         }
34081         this.anchor = cs[index];
34082         this.textNode = cs[index].firstChild;
34083     },
34084
34085     getAnchor : function(){
34086         return this.anchor;
34087     },
34088
34089     getTextEl : function(){
34090         return this.textNode;
34091     },
34092
34093     getIconEl : function(){
34094         return this.iconNode;
34095     },
34096
34097     isChecked : function(){
34098         return this.checkbox ? this.checkbox.checked : false;
34099     },
34100
34101     updateExpandIcon : function(){
34102         if(this.rendered){
34103             var n = this.node, c1, c2;
34104             var cls = n.isLast() ? "x-tree-elbow-end" : "x-tree-elbow";
34105             var hasChild = n.hasChildNodes();
34106             if(hasChild){
34107                 if(n.expanded){
34108                     cls += "-minus";
34109                     c1 = "x-tree-node-collapsed";
34110                     c2 = "x-tree-node-expanded";
34111                 }else{
34112                     cls += "-plus";
34113                     c1 = "x-tree-node-expanded";
34114                     c2 = "x-tree-node-collapsed";
34115                 }
34116                 if(this.wasLeaf){
34117                     this.removeClass("x-tree-node-leaf");
34118                     this.wasLeaf = false;
34119                 }
34120                 if(this.c1 != c1 || this.c2 != c2){
34121                     Roo.fly(this.elNode).replaceClass(c1, c2);
34122                     this.c1 = c1; this.c2 = c2;
34123                 }
34124             }else{
34125                 // this changes non-leafs into leafs if they have no children.
34126                 // it's not very rational behaviour..
34127                 
34128                 if(!this.wasLeaf && this.node.leaf){
34129                     Roo.fly(this.elNode).replaceClass("x-tree-node-expanded", "x-tree-node-leaf");
34130                     delete this.c1;
34131                     delete this.c2;
34132                     this.wasLeaf = true;
34133                 }
34134             }
34135             var ecc = "x-tree-ec-icon "+cls;
34136             if(this.ecc != ecc){
34137                 this.ecNode.className = ecc;
34138                 this.ecc = ecc;
34139             }
34140         }
34141     },
34142
34143     getChildIndent : function(){
34144         if(!this.childIndent){
34145             var buf = [];
34146             var p = this.node;
34147             while(p){
34148                 if(!p.isRoot || (p.isRoot && p.ownerTree.rootVisible)){
34149                     if(!p.isLast()) {
34150                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-elbow-line" />');
34151                     } else {
34152                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-icon" />');
34153                     }
34154                 }
34155                 p = p.parentNode;
34156             }
34157             this.childIndent = buf.join("");
34158         }
34159         return this.childIndent;
34160     },
34161
34162     renderIndent : function(){
34163         if(this.rendered){
34164             var indent = "";
34165             var p = this.node.parentNode;
34166             if(p){
34167                 indent = p.ui.getChildIndent();
34168             }
34169             if(this.indentMarkup != indent){ // don't rerender if not required
34170                 this.indentNode.innerHTML = indent;
34171                 this.indentMarkup = indent;
34172             }
34173             this.updateExpandIcon();
34174         }
34175     }
34176 };
34177
34178 Roo.tree.RootTreeNodeUI = function(){
34179     Roo.tree.RootTreeNodeUI.superclass.constructor.apply(this, arguments);
34180 };
34181 Roo.extend(Roo.tree.RootTreeNodeUI, Roo.tree.TreeNodeUI, {
34182     render : function(){
34183         if(!this.rendered){
34184             var targetNode = this.node.ownerTree.innerCt.dom;
34185             this.node.expanded = true;
34186             targetNode.innerHTML = '<div class="x-tree-root-node"></div>';
34187             this.wrap = this.ctNode = targetNode.firstChild;
34188         }
34189     },
34190     collapse : function(){
34191     },
34192     expand : function(){
34193     }
34194 });/*
34195  * Based on:
34196  * Ext JS Library 1.1.1
34197  * Copyright(c) 2006-2007, Ext JS, LLC.
34198  *
34199  * Originally Released Under LGPL - original licence link has changed is not relivant.
34200  *
34201  * Fork - LGPL
34202  * <script type="text/javascript">
34203  */
34204 /**
34205  * @class Roo.tree.TreeLoader
34206  * @extends Roo.util.Observable
34207  * A TreeLoader provides for lazy loading of an {@link Roo.tree.TreeNode}'s child
34208  * nodes from a specified URL. The response must be a javascript Array definition
34209  * who's elements are node definition objects. eg:
34210  * <pre><code>
34211 {  success : true,
34212    data :      [
34213    
34214     { 'id': 1, 'text': 'A folder Node', 'leaf': false },
34215     { 'id': 2, 'text': 'A leaf Node', 'leaf': true }
34216     ]
34217 }
34218
34219
34220 </code></pre>
34221  * <br><br>
34222  * The old style respose with just an array is still supported, but not recommended.
34223  * <br><br>
34224  *
34225  * A server request is sent, and child nodes are loaded only when a node is expanded.
34226  * The loading node's id is passed to the server under the parameter name "node" to
34227  * enable the server to produce the correct child nodes.
34228  * <br><br>
34229  * To pass extra parameters, an event handler may be attached to the "beforeload"
34230  * event, and the parameters specified in the TreeLoader's baseParams property:
34231  * <pre><code>
34232     myTreeLoader.on("beforeload", function(treeLoader, node) {
34233         this.baseParams.category = node.attributes.category;
34234     }, this);
34235 </code></pre><
34236  * This would pass an HTTP parameter called "category" to the server containing
34237  * the value of the Node's "category" attribute.
34238  * @constructor
34239  * Creates a new Treeloader.
34240  * @param {Object} config A config object containing config properties.
34241  */
34242 Roo.tree.TreeLoader = function(config){
34243     this.baseParams = {};
34244     this.requestMethod = "POST";
34245     Roo.apply(this, config);
34246
34247     this.addEvents({
34248     
34249         /**
34250          * @event beforeload
34251          * Fires before a network request is made to retrieve the Json text which specifies a node's children.
34252          * @param {Object} This TreeLoader object.
34253          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
34254          * @param {Object} callback The callback function specified in the {@link #load} call.
34255          */
34256         beforeload : true,
34257         /**
34258          * @event load
34259          * Fires when the node has been successfuly loaded.
34260          * @param {Object} This TreeLoader object.
34261          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
34262          * @param {Object} response The response object containing the data from the server.
34263          */
34264         load : true,
34265         /**
34266          * @event loadexception
34267          * Fires if the network request failed.
34268          * @param {Object} This TreeLoader object.
34269          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
34270          * @param {Object} response The response object containing the data from the server.
34271          */
34272         loadexception : true,
34273         /**
34274          * @event create
34275          * Fires before a node is created, enabling you to return custom Node types 
34276          * @param {Object} This TreeLoader object.
34277          * @param {Object} attr - the data returned from the AJAX call (modify it to suit)
34278          */
34279         create : true
34280     });
34281
34282     Roo.tree.TreeLoader.superclass.constructor.call(this);
34283 };
34284
34285 Roo.extend(Roo.tree.TreeLoader, Roo.util.Observable, {
34286     /**
34287     * @cfg {String} dataUrl The URL from which to request a Json string which
34288     * specifies an array of node definition object representing the child nodes
34289     * to be loaded.
34290     */
34291     /**
34292     * @cfg {String} requestMethod either GET or POST
34293     * defaults to POST (due to BC)
34294     * to be loaded.
34295     */
34296     /**
34297     * @cfg {Object} baseParams (optional) An object containing properties which
34298     * specify HTTP parameters to be passed to each request for child nodes.
34299     */
34300     /**
34301     * @cfg {Object} baseAttrs (optional) An object containing attributes to be added to all nodes
34302     * created by this loader. If the attributes sent by the server have an attribute in this object,
34303     * they take priority.
34304     */
34305     /**
34306     * @cfg {Object} uiProviders (optional) An object containing properties which
34307     * 
34308     * DEPRECATED - use 'create' event handler to modify attributes - which affect creation.
34309     * specify custom {@link Roo.tree.TreeNodeUI} implementations. If the optional
34310     * <i>uiProvider</i> attribute of a returned child node is a string rather
34311     * than a reference to a TreeNodeUI implementation, this that string value
34312     * is used as a property name in the uiProviders object. You can define the provider named
34313     * 'default' , and this will be used for all nodes (if no uiProvider is delivered by the node data)
34314     */
34315     uiProviders : {},
34316
34317     /**
34318     * @cfg {Boolean} clearOnLoad (optional) Default to true. Remove previously existing
34319     * child nodes before loading.
34320     */
34321     clearOnLoad : true,
34322
34323     /**
34324     * @cfg {String} root (optional) Default to false. Use this to read data from an object 
34325     * property on loading, rather than expecting an array. (eg. more compatible to a standard
34326     * Grid query { data : [ .....] }
34327     */
34328     
34329     root : false,
34330      /**
34331     * @cfg {String} queryParam (optional) 
34332     * Name of the query as it will be passed on the querystring (defaults to 'node')
34333     * eg. the request will be ?node=[id]
34334     */
34335     
34336     
34337     queryParam: false,
34338     
34339     /**
34340      * Load an {@link Roo.tree.TreeNode} from the URL specified in the constructor.
34341      * This is called automatically when a node is expanded, but may be used to reload
34342      * a node (or append new children if the {@link #clearOnLoad} option is false.)
34343      * @param {Roo.tree.TreeNode} node
34344      * @param {Function} callback
34345      */
34346     load : function(node, callback){
34347         if(this.clearOnLoad){
34348             while(node.firstChild){
34349                 node.removeChild(node.firstChild);
34350             }
34351         }
34352         if(node.attributes.children){ // preloaded json children
34353             var cs = node.attributes.children;
34354             for(var i = 0, len = cs.length; i < len; i++){
34355                 node.appendChild(this.createNode(cs[i]));
34356             }
34357             if(typeof callback == "function"){
34358                 callback();
34359             }
34360         }else if(this.dataUrl){
34361             this.requestData(node, callback);
34362         }
34363     },
34364
34365     getParams: function(node){
34366         var buf = [], bp = this.baseParams;
34367         for(var key in bp){
34368             if(typeof bp[key] != "function"){
34369                 buf.push(encodeURIComponent(key), "=", encodeURIComponent(bp[key]), "&");
34370             }
34371         }
34372         var n = this.queryParam === false ? 'node' : this.queryParam;
34373         buf.push(n + "=", encodeURIComponent(node.id));
34374         return buf.join("");
34375     },
34376
34377     requestData : function(node, callback){
34378         if(this.fireEvent("beforeload", this, node, callback) !== false){
34379             this.transId = Roo.Ajax.request({
34380                 method:this.requestMethod,
34381                 url: this.dataUrl||this.url,
34382                 success: this.handleResponse,
34383                 failure: this.handleFailure,
34384                 scope: this,
34385                 argument: {callback: callback, node: node},
34386                 params: this.getParams(node)
34387             });
34388         }else{
34389             // if the load is cancelled, make sure we notify
34390             // the node that we are done
34391             if(typeof callback == "function"){
34392                 callback();
34393             }
34394         }
34395     },
34396
34397     isLoading : function(){
34398         return this.transId ? true : false;
34399     },
34400
34401     abort : function(){
34402         if(this.isLoading()){
34403             Roo.Ajax.abort(this.transId);
34404         }
34405     },
34406
34407     // private
34408     createNode : function(attr)
34409     {
34410         // apply baseAttrs, nice idea Corey!
34411         if(this.baseAttrs){
34412             Roo.applyIf(attr, this.baseAttrs);
34413         }
34414         if(this.applyLoader !== false){
34415             attr.loader = this;
34416         }
34417         // uiProvider = depreciated..
34418         
34419         if(typeof(attr.uiProvider) == 'string'){
34420            attr.uiProvider = this.uiProviders[attr.uiProvider] || 
34421                 /**  eval:var:attr */ eval(attr.uiProvider);
34422         }
34423         if(typeof(this.uiProviders['default']) != 'undefined') {
34424             attr.uiProvider = this.uiProviders['default'];
34425         }
34426         
34427         this.fireEvent('create', this, attr);
34428         
34429         attr.leaf  = typeof(attr.leaf) == 'string' ? attr.leaf * 1 : attr.leaf;
34430         return(attr.leaf ?
34431                         new Roo.tree.TreeNode(attr) :
34432                         new Roo.tree.AsyncTreeNode(attr));
34433     },
34434
34435     processResponse : function(response, node, callback)
34436     {
34437         var json = response.responseText;
34438         try {
34439             
34440             var o = Roo.decode(json);
34441             
34442             if (this.root === false && typeof(o.success) != undefined) {
34443                 this.root = 'data'; // the default behaviour for list like data..
34444                 }
34445                 
34446             if (this.root !== false &&  !o.success) {
34447                 // it's a failure condition.
34448                 var a = response.argument;
34449                 this.fireEvent("loadexception", this, a.node, response);
34450                 Roo.log("Load failed - should have a handler really");
34451                 return;
34452             }
34453             
34454             
34455             
34456             if (this.root !== false) {
34457                  o = o[this.root];
34458             }
34459             
34460             for(var i = 0, len = o.length; i < len; i++){
34461                 var n = this.createNode(o[i]);
34462                 if(n){
34463                     node.appendChild(n);
34464                 }
34465             }
34466             if(typeof callback == "function"){
34467                 callback(this, node);
34468             }
34469         }catch(e){
34470             this.handleFailure(response);
34471         }
34472     },
34473
34474     handleResponse : function(response){
34475         this.transId = false;
34476         var a = response.argument;
34477         this.processResponse(response, a.node, a.callback);
34478         this.fireEvent("load", this, a.node, response);
34479     },
34480
34481     handleFailure : function(response)
34482     {
34483         // should handle failure better..
34484         this.transId = false;
34485         var a = response.argument;
34486         this.fireEvent("loadexception", this, a.node, response);
34487         if(typeof a.callback == "function"){
34488             a.callback(this, a.node);
34489         }
34490     }
34491 });/*
34492  * Based on:
34493  * Ext JS Library 1.1.1
34494  * Copyright(c) 2006-2007, Ext JS, LLC.
34495  *
34496  * Originally Released Under LGPL - original licence link has changed is not relivant.
34497  *
34498  * Fork - LGPL
34499  * <script type="text/javascript">
34500  */
34501
34502 /**
34503 * @class Roo.tree.TreeFilter
34504 * Note this class is experimental and doesn't update the indent (lines) or expand collapse icons of the nodes
34505 * @param {TreePanel} tree
34506 * @param {Object} config (optional)
34507  */
34508 Roo.tree.TreeFilter = function(tree, config){
34509     this.tree = tree;
34510     this.filtered = {};
34511     Roo.apply(this, config);
34512 };
34513
34514 Roo.tree.TreeFilter.prototype = {
34515     clearBlank:false,
34516     reverse:false,
34517     autoClear:false,
34518     remove:false,
34519
34520      /**
34521      * Filter the data by a specific attribute.
34522      * @param {String/RegExp} value Either string that the attribute value
34523      * should start with or a RegExp to test against the attribute
34524      * @param {String} attr (optional) The attribute passed in your node's attributes collection. Defaults to "text".
34525      * @param {TreeNode} startNode (optional) The node to start the filter at.
34526      */
34527     filter : function(value, attr, startNode){
34528         attr = attr || "text";
34529         var f;
34530         if(typeof value == "string"){
34531             var vlen = value.length;
34532             // auto clear empty filter
34533             if(vlen == 0 && this.clearBlank){
34534                 this.clear();
34535                 return;
34536             }
34537             value = value.toLowerCase();
34538             f = function(n){
34539                 return n.attributes[attr].substr(0, vlen).toLowerCase() == value;
34540             };
34541         }else if(value.exec){ // regex?
34542             f = function(n){
34543                 return value.test(n.attributes[attr]);
34544             };
34545         }else{
34546             throw 'Illegal filter type, must be string or regex';
34547         }
34548         this.filterBy(f, null, startNode);
34549         },
34550
34551     /**
34552      * Filter by a function. The passed function will be called with each
34553      * node in the tree (or from the startNode). If the function returns true, the node is kept
34554      * otherwise it is filtered. If a node is filtered, its children are also filtered.
34555      * @param {Function} fn The filter function
34556      * @param {Object} scope (optional) The scope of the function (defaults to the current node)
34557      */
34558     filterBy : function(fn, scope, startNode){
34559         startNode = startNode || this.tree.root;
34560         if(this.autoClear){
34561             this.clear();
34562         }
34563         var af = this.filtered, rv = this.reverse;
34564         var f = function(n){
34565             if(n == startNode){
34566                 return true;
34567             }
34568             if(af[n.id]){
34569                 return false;
34570             }
34571             var m = fn.call(scope || n, n);
34572             if(!m || rv){
34573                 af[n.id] = n;
34574                 n.ui.hide();
34575                 return false;
34576             }
34577             return true;
34578         };
34579         startNode.cascade(f);
34580         if(this.remove){
34581            for(var id in af){
34582                if(typeof id != "function"){
34583                    var n = af[id];
34584                    if(n && n.parentNode){
34585                        n.parentNode.removeChild(n);
34586                    }
34587                }
34588            }
34589         }
34590     },
34591
34592     /**
34593      * Clears the current filter. Note: with the "remove" option
34594      * set a filter cannot be cleared.
34595      */
34596     clear : function(){
34597         var t = this.tree;
34598         var af = this.filtered;
34599         for(var id in af){
34600             if(typeof id != "function"){
34601                 var n = af[id];
34602                 if(n){
34603                     n.ui.show();
34604                 }
34605             }
34606         }
34607         this.filtered = {};
34608     }
34609 };
34610 /*
34611  * Based on:
34612  * Ext JS Library 1.1.1
34613  * Copyright(c) 2006-2007, Ext JS, LLC.
34614  *
34615  * Originally Released Under LGPL - original licence link has changed is not relivant.
34616  *
34617  * Fork - LGPL
34618  * <script type="text/javascript">
34619  */
34620  
34621
34622 /**
34623  * @class Roo.tree.TreeSorter
34624  * Provides sorting of nodes in a TreePanel
34625  * 
34626  * @cfg {Boolean} folderSort True to sort leaf nodes under non leaf nodes
34627  * @cfg {String} property The named attribute on the node to sort by (defaults to text)
34628  * @cfg {String} dir The direction to sort (asc or desc) (defaults to asc)
34629  * @cfg {String} leafAttr The attribute used to determine leaf nodes in folder sort (defaults to "leaf")
34630  * @cfg {Boolean} caseSensitive true for case sensitive sort (defaults to false)
34631  * @cfg {Function} sortType A custom "casting" function used to convert node values before sorting
34632  * @constructor
34633  * @param {TreePanel} tree
34634  * @param {Object} config
34635  */
34636 Roo.tree.TreeSorter = function(tree, config){
34637     Roo.apply(this, config);
34638     tree.on("beforechildrenrendered", this.doSort, this);
34639     tree.on("append", this.updateSort, this);
34640     tree.on("insert", this.updateSort, this);
34641     
34642     var dsc = this.dir && this.dir.toLowerCase() == "desc";
34643     var p = this.property || "text";
34644     var sortType = this.sortType;
34645     var fs = this.folderSort;
34646     var cs = this.caseSensitive === true;
34647     var leafAttr = this.leafAttr || 'leaf';
34648
34649     this.sortFn = function(n1, n2){
34650         if(fs){
34651             if(n1.attributes[leafAttr] && !n2.attributes[leafAttr]){
34652                 return 1;
34653             }
34654             if(!n1.attributes[leafAttr] && n2.attributes[leafAttr]){
34655                 return -1;
34656             }
34657         }
34658         var v1 = sortType ? sortType(n1) : (cs ? n1.attributes[p] : n1.attributes[p].toUpperCase());
34659         var v2 = sortType ? sortType(n2) : (cs ? n2.attributes[p] : n2.attributes[p].toUpperCase());
34660         if(v1 < v2){
34661                         return dsc ? +1 : -1;
34662                 }else if(v1 > v2){
34663                         return dsc ? -1 : +1;
34664         }else{
34665                 return 0;
34666         }
34667     };
34668 };
34669
34670 Roo.tree.TreeSorter.prototype = {
34671     doSort : function(node){
34672         node.sort(this.sortFn);
34673     },
34674     
34675     compareNodes : function(n1, n2){
34676         return (n1.text.toUpperCase() > n2.text.toUpperCase() ? 1 : -1);
34677     },
34678     
34679     updateSort : function(tree, node){
34680         if(node.childrenRendered){
34681             this.doSort.defer(1, this, [node]);
34682         }
34683     }
34684 };/*
34685  * Based on:
34686  * Ext JS Library 1.1.1
34687  * Copyright(c) 2006-2007, Ext JS, LLC.
34688  *
34689  * Originally Released Under LGPL - original licence link has changed is not relivant.
34690  *
34691  * Fork - LGPL
34692  * <script type="text/javascript">
34693  */
34694
34695 if(Roo.dd.DropZone){
34696     
34697 Roo.tree.TreeDropZone = function(tree, config){
34698     this.allowParentInsert = false;
34699     this.allowContainerDrop = false;
34700     this.appendOnly = false;
34701     Roo.tree.TreeDropZone.superclass.constructor.call(this, tree.innerCt, config);
34702     this.tree = tree;
34703     this.lastInsertClass = "x-tree-no-status";
34704     this.dragOverData = {};
34705 };
34706
34707 Roo.extend(Roo.tree.TreeDropZone, Roo.dd.DropZone, {
34708     ddGroup : "TreeDD",
34709     scroll:  true,
34710     
34711     expandDelay : 1000,
34712     
34713     expandNode : function(node){
34714         if(node.hasChildNodes() && !node.isExpanded()){
34715             node.expand(false, null, this.triggerCacheRefresh.createDelegate(this));
34716         }
34717     },
34718     
34719     queueExpand : function(node){
34720         this.expandProcId = this.expandNode.defer(this.expandDelay, this, [node]);
34721     },
34722     
34723     cancelExpand : function(){
34724         if(this.expandProcId){
34725             clearTimeout(this.expandProcId);
34726             this.expandProcId = false;
34727         }
34728     },
34729     
34730     isValidDropPoint : function(n, pt, dd, e, data){
34731         if(!n || !data){ return false; }
34732         var targetNode = n.node;
34733         var dropNode = data.node;
34734         // default drop rules
34735         if(!(targetNode && targetNode.isTarget && pt)){
34736             return false;
34737         }
34738         if(pt == "append" && targetNode.allowChildren === false){
34739             return false;
34740         }
34741         if((pt == "above" || pt == "below") && (targetNode.parentNode && targetNode.parentNode.allowChildren === false)){
34742             return false;
34743         }
34744         if(dropNode && (targetNode == dropNode || dropNode.contains(targetNode))){
34745             return false;
34746         }
34747         // reuse the object
34748         var overEvent = this.dragOverData;
34749         overEvent.tree = this.tree;
34750         overEvent.target = targetNode;
34751         overEvent.data = data;
34752         overEvent.point = pt;
34753         overEvent.source = dd;
34754         overEvent.rawEvent = e;
34755         overEvent.dropNode = dropNode;
34756         overEvent.cancel = false;  
34757         var result = this.tree.fireEvent("nodedragover", overEvent);
34758         return overEvent.cancel === false && result !== false;
34759     },
34760     
34761     getDropPoint : function(e, n, dd)
34762     {
34763         var tn = n.node;
34764         if(tn.isRoot){
34765             return tn.allowChildren !== false ? "append" : false; // always append for root
34766         }
34767         var dragEl = n.ddel;
34768         var t = Roo.lib.Dom.getY(dragEl), b = t + dragEl.offsetHeight;
34769         var y = Roo.lib.Event.getPageY(e);
34770         //var noAppend = tn.allowChildren === false || tn.isLeaf();
34771         
34772         // we may drop nodes anywhere, as long as allowChildren has not been set to false..
34773         var noAppend = tn.allowChildren === false;
34774         if(this.appendOnly || tn.parentNode.allowChildren === false){
34775             return noAppend ? false : "append";
34776         }
34777         var noBelow = false;
34778         if(!this.allowParentInsert){
34779             noBelow = tn.hasChildNodes() && tn.isExpanded();
34780         }
34781         var q = (b - t) / (noAppend ? 2 : 3);
34782         if(y >= t && y < (t + q)){
34783             return "above";
34784         }else if(!noBelow && (noAppend || y >= b-q && y <= b)){
34785             return "below";
34786         }else{
34787             return "append";
34788         }
34789     },
34790     
34791     onNodeEnter : function(n, dd, e, data)
34792     {
34793         this.cancelExpand();
34794     },
34795     
34796     onNodeOver : function(n, dd, e, data)
34797     {
34798        
34799         var pt = this.getDropPoint(e, n, dd);
34800         var node = n.node;
34801         
34802         // auto node expand check
34803         if(!this.expandProcId && pt == "append" && node.hasChildNodes() && !n.node.isExpanded()){
34804             this.queueExpand(node);
34805         }else if(pt != "append"){
34806             this.cancelExpand();
34807         }
34808         
34809         // set the insert point style on the target node
34810         var returnCls = this.dropNotAllowed;
34811         if(this.isValidDropPoint(n, pt, dd, e, data)){
34812            if(pt){
34813                var el = n.ddel;
34814                var cls;
34815                if(pt == "above"){
34816                    returnCls = n.node.isFirst() ? "x-tree-drop-ok-above" : "x-tree-drop-ok-between";
34817                    cls = "x-tree-drag-insert-above";
34818                }else if(pt == "below"){
34819                    returnCls = n.node.isLast() ? "x-tree-drop-ok-below" : "x-tree-drop-ok-between";
34820                    cls = "x-tree-drag-insert-below";
34821                }else{
34822                    returnCls = "x-tree-drop-ok-append";
34823                    cls = "x-tree-drag-append";
34824                }
34825                if(this.lastInsertClass != cls){
34826                    Roo.fly(el).replaceClass(this.lastInsertClass, cls);
34827                    this.lastInsertClass = cls;
34828                }
34829            }
34830        }
34831        return returnCls;
34832     },
34833     
34834     onNodeOut : function(n, dd, e, data){
34835         
34836         this.cancelExpand();
34837         this.removeDropIndicators(n);
34838     },
34839     
34840     onNodeDrop : function(n, dd, e, data){
34841         var point = this.getDropPoint(e, n, dd);
34842         var targetNode = n.node;
34843         targetNode.ui.startDrop();
34844         if(!this.isValidDropPoint(n, point, dd, e, data)){
34845             targetNode.ui.endDrop();
34846             return false;
34847         }
34848         // first try to find the drop node
34849         var dropNode = data.node || (dd.getTreeNode ? dd.getTreeNode(data, targetNode, point, e) : null);
34850         var dropEvent = {
34851             tree : this.tree,
34852             target: targetNode,
34853             data: data,
34854             point: point,
34855             source: dd,
34856             rawEvent: e,
34857             dropNode: dropNode,
34858             cancel: !dropNode   
34859         };
34860         var retval = this.tree.fireEvent("beforenodedrop", dropEvent);
34861         if(retval === false || dropEvent.cancel === true || !dropEvent.dropNode){
34862             targetNode.ui.endDrop();
34863             return false;
34864         }
34865         // allow target changing
34866         targetNode = dropEvent.target;
34867         if(point == "append" && !targetNode.isExpanded()){
34868             targetNode.expand(false, null, function(){
34869                 this.completeDrop(dropEvent);
34870             }.createDelegate(this));
34871         }else{
34872             this.completeDrop(dropEvent);
34873         }
34874         return true;
34875     },
34876     
34877     completeDrop : function(de){
34878         var ns = de.dropNode, p = de.point, t = de.target;
34879         if(!(ns instanceof Array)){
34880             ns = [ns];
34881         }
34882         var n;
34883         for(var i = 0, len = ns.length; i < len; i++){
34884             n = ns[i];
34885             if(p == "above"){
34886                 t.parentNode.insertBefore(n, t);
34887             }else if(p == "below"){
34888                 t.parentNode.insertBefore(n, t.nextSibling);
34889             }else{
34890                 t.appendChild(n);
34891             }
34892         }
34893         n.ui.focus();
34894         if(this.tree.hlDrop){
34895             n.ui.highlight();
34896         }
34897         t.ui.endDrop();
34898         this.tree.fireEvent("nodedrop", de);
34899     },
34900     
34901     afterNodeMoved : function(dd, data, e, targetNode, dropNode){
34902         if(this.tree.hlDrop){
34903             dropNode.ui.focus();
34904             dropNode.ui.highlight();
34905         }
34906         this.tree.fireEvent("nodedrop", this.tree, targetNode, data, dd, e);
34907     },
34908     
34909     getTree : function(){
34910         return this.tree;
34911     },
34912     
34913     removeDropIndicators : function(n){
34914         if(n && n.ddel){
34915             var el = n.ddel;
34916             Roo.fly(el).removeClass([
34917                     "x-tree-drag-insert-above",
34918                     "x-tree-drag-insert-below",
34919                     "x-tree-drag-append"]);
34920             this.lastInsertClass = "_noclass";
34921         }
34922     },
34923     
34924     beforeDragDrop : function(target, e, id){
34925         this.cancelExpand();
34926         return true;
34927     },
34928     
34929     afterRepair : function(data){
34930         if(data && Roo.enableFx){
34931             data.node.ui.highlight();
34932         }
34933         this.hideProxy();
34934     } 
34935     
34936 });
34937
34938 }
34939 /*
34940  * Based on:
34941  * Ext JS Library 1.1.1
34942  * Copyright(c) 2006-2007, Ext JS, LLC.
34943  *
34944  * Originally Released Under LGPL - original licence link has changed is not relivant.
34945  *
34946  * Fork - LGPL
34947  * <script type="text/javascript">
34948  */
34949  
34950
34951 if(Roo.dd.DragZone){
34952 Roo.tree.TreeDragZone = function(tree, config){
34953     Roo.tree.TreeDragZone.superclass.constructor.call(this, tree.getTreeEl(), config);
34954     this.tree = tree;
34955 };
34956
34957 Roo.extend(Roo.tree.TreeDragZone, Roo.dd.DragZone, {
34958     ddGroup : "TreeDD",
34959    
34960     onBeforeDrag : function(data, e){
34961         var n = data.node;
34962         return n && n.draggable && !n.disabled;
34963     },
34964      
34965     
34966     onInitDrag : function(e){
34967         var data = this.dragData;
34968         this.tree.getSelectionModel().select(data.node);
34969         this.proxy.update("");
34970         data.node.ui.appendDDGhost(this.proxy.ghost.dom);
34971         this.tree.fireEvent("startdrag", this.tree, data.node, e);
34972     },
34973     
34974     getRepairXY : function(e, data){
34975         return data.node.ui.getDDRepairXY();
34976     },
34977     
34978     onEndDrag : function(data, e){
34979         this.tree.fireEvent("enddrag", this.tree, data.node, e);
34980         
34981         
34982     },
34983     
34984     onValidDrop : function(dd, e, id){
34985         this.tree.fireEvent("dragdrop", this.tree, this.dragData.node, dd, e);
34986         this.hideProxy();
34987     },
34988     
34989     beforeInvalidDrop : function(e, id){
34990         // this scrolls the original position back into view
34991         var sm = this.tree.getSelectionModel();
34992         sm.clearSelections();
34993         sm.select(this.dragData.node);
34994     }
34995 });
34996 }/*
34997  * Based on:
34998  * Ext JS Library 1.1.1
34999  * Copyright(c) 2006-2007, Ext JS, LLC.
35000  *
35001  * Originally Released Under LGPL - original licence link has changed is not relivant.
35002  *
35003  * Fork - LGPL
35004  * <script type="text/javascript">
35005  */
35006 /**
35007  * @class Roo.tree.TreeEditor
35008  * @extends Roo.Editor
35009  * Provides editor functionality for inline tree node editing.  Any valid {@link Roo.form.Field} can be used
35010  * as the editor field.
35011  * @constructor
35012  * @param {Object} config (used to be the tree panel.)
35013  * @param {Object} oldconfig DEPRECIATED Either a prebuilt {@link Roo.form.Field} instance or a Field config object
35014  * 
35015  * @cfg {Roo.tree.TreePanel} tree The tree to bind to.
35016  * @cfg {Roo.form.TextField|Object} field The field configuration
35017  *
35018  * 
35019  */
35020 Roo.tree.TreeEditor = function(config, oldconfig) { // was -- (tree, config){
35021     var tree = config;
35022     var field;
35023     if (oldconfig) { // old style..
35024         field = oldconfig.events ? oldconfig : new Roo.form.TextField(oldconfig);
35025     } else {
35026         // new style..
35027         tree = config.tree;
35028         config.field = config.field  || {};
35029         config.field.xtype = 'TextField';
35030         field = Roo.factory(config.field, Roo.form);
35031     }
35032     config = config || {};
35033     
35034     
35035     this.addEvents({
35036         /**
35037          * @event beforenodeedit
35038          * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
35039          * false from the handler of this event.
35040          * @param {Editor} this
35041          * @param {Roo.tree.Node} node 
35042          */
35043         "beforenodeedit" : true
35044     });
35045     
35046     //Roo.log(config);
35047     Roo.tree.TreeEditor.superclass.constructor.call(this, field, config);
35048
35049     this.tree = tree;
35050
35051     tree.on('beforeclick', this.beforeNodeClick, this);
35052     tree.getTreeEl().on('mousedown', this.hide, this);
35053     this.on('complete', this.updateNode, this);
35054     this.on('beforestartedit', this.fitToTree, this);
35055     this.on('startedit', this.bindScroll, this, {delay:10});
35056     this.on('specialkey', this.onSpecialKey, this);
35057 };
35058
35059 Roo.extend(Roo.tree.TreeEditor, Roo.Editor, {
35060     /**
35061      * @cfg {String} alignment
35062      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "l-l").
35063      */
35064     alignment: "l-l",
35065     // inherit
35066     autoSize: false,
35067     /**
35068      * @cfg {Boolean} hideEl
35069      * True to hide the bound element while the editor is displayed (defaults to false)
35070      */
35071     hideEl : false,
35072     /**
35073      * @cfg {String} cls
35074      * CSS class to apply to the editor (defaults to "x-small-editor x-tree-editor")
35075      */
35076     cls: "x-small-editor x-tree-editor",
35077     /**
35078      * @cfg {Boolean} shim
35079      * True to shim the editor if selects/iframes could be displayed beneath it (defaults to false)
35080      */
35081     shim:false,
35082     // inherit
35083     shadow:"frame",
35084     /**
35085      * @cfg {Number} maxWidth
35086      * The maximum width in pixels of the editor field (defaults to 250).  Note that if the maxWidth would exceed
35087      * the containing tree element's size, it will be automatically limited for you to the container width, taking
35088      * scroll and client offsets into account prior to each edit.
35089      */
35090     maxWidth: 250,
35091
35092     editDelay : 350,
35093
35094     // private
35095     fitToTree : function(ed, el){
35096         var td = this.tree.getTreeEl().dom, nd = el.dom;
35097         if(td.scrollLeft >  nd.offsetLeft){ // ensure the node left point is visible
35098             td.scrollLeft = nd.offsetLeft;
35099         }
35100         var w = Math.min(
35101                 this.maxWidth,
35102                 (td.clientWidth > 20 ? td.clientWidth : td.offsetWidth) - Math.max(0, nd.offsetLeft-td.scrollLeft) - /*cushion*/5);
35103         this.setSize(w, '');
35104         
35105         return this.fireEvent('beforenodeedit', this, this.editNode);
35106         
35107     },
35108
35109     // private
35110     triggerEdit : function(node){
35111         this.completeEdit();
35112         this.editNode = node;
35113         this.startEdit(node.ui.textNode, node.text);
35114     },
35115
35116     // private
35117     bindScroll : function(){
35118         this.tree.getTreeEl().on('scroll', this.cancelEdit, this);
35119     },
35120
35121     // private
35122     beforeNodeClick : function(node, e){
35123         var sinceLast = (this.lastClick ? this.lastClick.getElapsed() : 0);
35124         this.lastClick = new Date();
35125         if(sinceLast > this.editDelay && this.tree.getSelectionModel().isSelected(node)){
35126             e.stopEvent();
35127             this.triggerEdit(node);
35128             return false;
35129         }
35130         return true;
35131     },
35132
35133     // private
35134     updateNode : function(ed, value){
35135         this.tree.getTreeEl().un('scroll', this.cancelEdit, this);
35136         this.editNode.setText(value);
35137     },
35138
35139     // private
35140     onHide : function(){
35141         Roo.tree.TreeEditor.superclass.onHide.call(this);
35142         if(this.editNode){
35143             this.editNode.ui.focus();
35144         }
35145     },
35146
35147     // private
35148     onSpecialKey : function(field, e){
35149         var k = e.getKey();
35150         if(k == e.ESC){
35151             e.stopEvent();
35152             this.cancelEdit();
35153         }else if(k == e.ENTER && !e.hasModifier()){
35154             e.stopEvent();
35155             this.completeEdit();
35156         }
35157     }
35158 });//<Script type="text/javascript">
35159 /*
35160  * Based on:
35161  * Ext JS Library 1.1.1
35162  * Copyright(c) 2006-2007, Ext JS, LLC.
35163  *
35164  * Originally Released Under LGPL - original licence link has changed is not relivant.
35165  *
35166  * Fork - LGPL
35167  * <script type="text/javascript">
35168  */
35169  
35170 /**
35171  * Not documented??? - probably should be...
35172  */
35173
35174 Roo.tree.ColumnNodeUI = Roo.extend(Roo.tree.TreeNodeUI, {
35175     //focus: Roo.emptyFn, // prevent odd scrolling behavior
35176     
35177     renderElements : function(n, a, targetNode, bulkRender){
35178         //consel.log("renderElements?");
35179         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
35180
35181         var t = n.getOwnerTree();
35182         var tid = Pman.Tab.Document_TypesTree.tree.el.id;
35183         
35184         var cols = t.columns;
35185         var bw = t.borderWidth;
35186         var c = cols[0];
35187         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
35188          var cb = typeof a.checked == "boolean";
35189         var tx = String.format('{0}',n.text || (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
35190         var colcls = 'x-t-' + tid + '-c0';
35191         var buf = [
35192             '<li class="x-tree-node">',
35193             
35194                 
35195                 '<div class="x-tree-node-el ', a.cls,'">',
35196                     // extran...
35197                     '<div class="x-tree-col ', colcls, '" style="width:', c.width-bw, 'px;">',
35198                 
35199                 
35200                         '<span class="x-tree-node-indent">',this.indentMarkup,'</span>',
35201                         '<img src="', this.emptyIcon, '" class="x-tree-ec-icon  " />',
35202                         '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',
35203                            (a.icon ? ' x-tree-node-inline-icon' : ''),
35204                            (a.iconCls ? ' '+a.iconCls : ''),
35205                            '" unselectable="on" />',
35206                         (cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + 
35207                              (a.checked ? 'checked="checked" />' : ' />')) : ''),
35208                              
35209                         '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
35210                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>',
35211                             '<span unselectable="on" qtip="' + tx + '">',
35212                              tx,
35213                              '</span></a>' ,
35214                     '</div>',
35215                      '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
35216                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>'
35217                  ];
35218         for(var i = 1, len = cols.length; i < len; i++){
35219             c = cols[i];
35220             colcls = 'x-t-' + tid + '-c' +i;
35221             tx = String.format('{0}', (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
35222             buf.push('<div class="x-tree-col ', colcls, ' ' ,(c.cls?c.cls:''),'" style="width:',c.width-bw,'px;">',
35223                         '<div class="x-tree-col-text" qtip="' + tx +'">',tx,"</div>",
35224                       "</div>");
35225          }
35226          
35227          buf.push(
35228             '</a>',
35229             '<div class="x-clear"></div></div>',
35230             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
35231             "</li>");
35232         
35233         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
35234             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
35235                                 n.nextSibling.ui.getEl(), buf.join(""));
35236         }else{
35237             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
35238         }
35239         var el = this.wrap.firstChild;
35240         this.elRow = el;
35241         this.elNode = el.firstChild;
35242         this.ranchor = el.childNodes[1];
35243         this.ctNode = this.wrap.childNodes[1];
35244         var cs = el.firstChild.childNodes;
35245         this.indentNode = cs[0];
35246         this.ecNode = cs[1];
35247         this.iconNode = cs[2];
35248         var index = 3;
35249         if(cb){
35250             this.checkbox = cs[3];
35251             index++;
35252         }
35253         this.anchor = cs[index];
35254         
35255         this.textNode = cs[index].firstChild;
35256         
35257         //el.on("click", this.onClick, this);
35258         //el.on("dblclick", this.onDblClick, this);
35259         
35260         
35261        // console.log(this);
35262     },
35263     initEvents : function(){
35264         Roo.tree.ColumnNodeUI.superclass.initEvents.call(this);
35265         
35266             
35267         var a = this.ranchor;
35268
35269         var el = Roo.get(a);
35270
35271         if(Roo.isOpera){ // opera render bug ignores the CSS
35272             el.setStyle("text-decoration", "none");
35273         }
35274
35275         el.on("click", this.onClick, this);
35276         el.on("dblclick", this.onDblClick, this);
35277         el.on("contextmenu", this.onContextMenu, this);
35278         
35279     },
35280     
35281     /*onSelectedChange : function(state){
35282         if(state){
35283             this.focus();
35284             this.addClass("x-tree-selected");
35285         }else{
35286             //this.blur();
35287             this.removeClass("x-tree-selected");
35288         }
35289     },*/
35290     addClass : function(cls){
35291         if(this.elRow){
35292             Roo.fly(this.elRow).addClass(cls);
35293         }
35294         
35295     },
35296     
35297     
35298     removeClass : function(cls){
35299         if(this.elRow){
35300             Roo.fly(this.elRow).removeClass(cls);
35301         }
35302     }
35303
35304     
35305     
35306 });//<Script type="text/javascript">
35307
35308 /*
35309  * Based on:
35310  * Ext JS Library 1.1.1
35311  * Copyright(c) 2006-2007, Ext JS, LLC.
35312  *
35313  * Originally Released Under LGPL - original licence link has changed is not relivant.
35314  *
35315  * Fork - LGPL
35316  * <script type="text/javascript">
35317  */
35318  
35319
35320 /**
35321  * @class Roo.tree.ColumnTree
35322  * @extends Roo.data.TreePanel
35323  * @cfg {Object} columns  Including width, header, renderer, cls, dataIndex 
35324  * @cfg {int} borderWidth  compined right/left border allowance
35325  * @constructor
35326  * @param {String/HTMLElement/Element} el The container element
35327  * @param {Object} config
35328  */
35329 Roo.tree.ColumnTree =  function(el, config)
35330 {
35331    Roo.tree.ColumnTree.superclass.constructor.call(this, el , config);
35332    this.addEvents({
35333         /**
35334         * @event resize
35335         * Fire this event on a container when it resizes
35336         * @param {int} w Width
35337         * @param {int} h Height
35338         */
35339        "resize" : true
35340     });
35341     this.on('resize', this.onResize, this);
35342 };
35343
35344 Roo.extend(Roo.tree.ColumnTree, Roo.tree.TreePanel, {
35345     //lines:false,
35346     
35347     
35348     borderWidth: Roo.isBorderBox ? 0 : 2, 
35349     headEls : false,
35350     
35351     render : function(){
35352         // add the header.....
35353        
35354         Roo.tree.ColumnTree.superclass.render.apply(this);
35355         
35356         this.el.addClass('x-column-tree');
35357         
35358         this.headers = this.el.createChild(
35359             {cls:'x-tree-headers'},this.innerCt.dom);
35360    
35361         var cols = this.columns, c;
35362         var totalWidth = 0;
35363         this.headEls = [];
35364         var  len = cols.length;
35365         for(var i = 0; i < len; i++){
35366              c = cols[i];
35367              totalWidth += c.width;
35368             this.headEls.push(this.headers.createChild({
35369                  cls:'x-tree-hd ' + (c.cls?c.cls+'-hd':''),
35370                  cn: {
35371                      cls:'x-tree-hd-text',
35372                      html: c.header
35373                  },
35374                  style:'width:'+(c.width-this.borderWidth)+'px;'
35375              }));
35376         }
35377         this.headers.createChild({cls:'x-clear'});
35378         // prevent floats from wrapping when clipped
35379         this.headers.setWidth(totalWidth);
35380         //this.innerCt.setWidth(totalWidth);
35381         this.innerCt.setStyle({ overflow: 'auto' });
35382         this.onResize(this.width, this.height);
35383              
35384         
35385     },
35386     onResize : function(w,h)
35387     {
35388         this.height = h;
35389         this.width = w;
35390         // resize cols..
35391         this.innerCt.setWidth(this.width);
35392         this.innerCt.setHeight(this.height-20);
35393         
35394         // headers...
35395         var cols = this.columns, c;
35396         var totalWidth = 0;
35397         var expEl = false;
35398         var len = cols.length;
35399         for(var i = 0; i < len; i++){
35400             c = cols[i];
35401             if (this.autoExpandColumn !== false && c.dataIndex == this.autoExpandColumn) {
35402                 // it's the expander..
35403                 expEl  = this.headEls[i];
35404                 continue;
35405             }
35406             totalWidth += c.width;
35407             
35408         }
35409         if (expEl) {
35410             expEl.setWidth(  ((w - totalWidth)-this.borderWidth - 20));
35411         }
35412         this.headers.setWidth(w-20);
35413
35414         
35415         
35416         
35417     }
35418 });
35419 /*
35420  * Based on:
35421  * Ext JS Library 1.1.1
35422  * Copyright(c) 2006-2007, Ext JS, LLC.
35423  *
35424  * Originally Released Under LGPL - original licence link has changed is not relivant.
35425  *
35426  * Fork - LGPL
35427  * <script type="text/javascript">
35428  */
35429  
35430 /**
35431  * @class Roo.menu.Menu
35432  * @extends Roo.util.Observable
35433  * A menu object.  This is the container to which you add all other menu items.  Menu can also serve a as a base class
35434  * when you want a specialzed menu based off of another component (like {@link Roo.menu.DateMenu} for example).
35435  * @constructor
35436  * Creates a new Menu
35437  * @param {Object} config Configuration options
35438  */
35439 Roo.menu.Menu = function(config){
35440     Roo.apply(this, config);
35441     this.id = this.id || Roo.id();
35442     this.addEvents({
35443         /**
35444          * @event beforeshow
35445          * Fires before this menu is displayed
35446          * @param {Roo.menu.Menu} this
35447          */
35448         beforeshow : true,
35449         /**
35450          * @event beforehide
35451          * Fires before this menu is hidden
35452          * @param {Roo.menu.Menu} this
35453          */
35454         beforehide : true,
35455         /**
35456          * @event show
35457          * Fires after this menu is displayed
35458          * @param {Roo.menu.Menu} this
35459          */
35460         show : true,
35461         /**
35462          * @event hide
35463          * Fires after this menu is hidden
35464          * @param {Roo.menu.Menu} this
35465          */
35466         hide : true,
35467         /**
35468          * @event click
35469          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
35470          * @param {Roo.menu.Menu} this
35471          * @param {Roo.menu.Item} menuItem The menu item that was clicked
35472          * @param {Roo.EventObject} e
35473          */
35474         click : true,
35475         /**
35476          * @event mouseover
35477          * Fires when the mouse is hovering over this menu
35478          * @param {Roo.menu.Menu} this
35479          * @param {Roo.EventObject} e
35480          * @param {Roo.menu.Item} menuItem The menu item that was clicked
35481          */
35482         mouseover : true,
35483         /**
35484          * @event mouseout
35485          * Fires when the mouse exits this menu
35486          * @param {Roo.menu.Menu} this
35487          * @param {Roo.EventObject} e
35488          * @param {Roo.menu.Item} menuItem The menu item that was clicked
35489          */
35490         mouseout : true,
35491         /**
35492          * @event itemclick
35493          * Fires when a menu item contained in this menu is clicked
35494          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
35495          * @param {Roo.EventObject} e
35496          */
35497         itemclick: true
35498     });
35499     if (this.registerMenu) {
35500         Roo.menu.MenuMgr.register(this);
35501     }
35502     
35503     var mis = this.items;
35504     this.items = new Roo.util.MixedCollection();
35505     if(mis){
35506         this.add.apply(this, mis);
35507     }
35508 };
35509
35510 Roo.extend(Roo.menu.Menu, Roo.util.Observable, {
35511     /**
35512      * @cfg {Number} minWidth The minimum width of the menu in pixels (defaults to 120)
35513      */
35514     minWidth : 120,
35515     /**
35516      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop"
35517      * for bottom-right shadow (defaults to "sides")
35518      */
35519     shadow : "sides",
35520     /**
35521      * @cfg {String} subMenuAlign The {@link Roo.Element#alignTo} anchor position value to use for submenus of
35522      * this menu (defaults to "tl-tr?")
35523      */
35524     subMenuAlign : "tl-tr?",
35525     /**
35526      * @cfg {String} defaultAlign The default {@link Roo.Element#alignTo) anchor position value for this menu
35527      * relative to its element of origin (defaults to "tl-bl?")
35528      */
35529     defaultAlign : "tl-bl?",
35530     /**
35531      * @cfg {Boolean} allowOtherMenus True to allow multiple menus to be displayed at the same time (defaults to false)
35532      */
35533     allowOtherMenus : false,
35534     /**
35535      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
35536      */
35537     registerMenu : true,
35538
35539     hidden:true,
35540
35541     // private
35542     render : function(){
35543         if(this.el){
35544             return;
35545         }
35546         var el = this.el = new Roo.Layer({
35547             cls: "x-menu",
35548             shadow:this.shadow,
35549             constrain: false,
35550             parentEl: this.parentEl || document.body,
35551             zindex:15000
35552         });
35553
35554         this.keyNav = new Roo.menu.MenuNav(this);
35555
35556         if(this.plain){
35557             el.addClass("x-menu-plain");
35558         }
35559         if(this.cls){
35560             el.addClass(this.cls);
35561         }
35562         // generic focus element
35563         this.focusEl = el.createChild({
35564             tag: "a", cls: "x-menu-focus", href: "#", onclick: "return false;", tabIndex:"-1"
35565         });
35566         var ul = el.createChild({tag: "ul", cls: "x-menu-list"});
35567         ul.on(Roo.isTouch ? 'touchstart' : 'click'   , this.onClick, this);
35568         
35569         ul.on("mouseover", this.onMouseOver, this);
35570         ul.on("mouseout", this.onMouseOut, this);
35571         this.items.each(function(item){
35572             if (item.hidden) {
35573                 return;
35574             }
35575             
35576             var li = document.createElement("li");
35577             li.className = "x-menu-list-item";
35578             ul.dom.appendChild(li);
35579             item.render(li, this);
35580         }, this);
35581         this.ul = ul;
35582         this.autoWidth();
35583     },
35584
35585     // private
35586     autoWidth : function(){
35587         var el = this.el, ul = this.ul;
35588         if(!el){
35589             return;
35590         }
35591         var w = this.width;
35592         if(w){
35593             el.setWidth(w);
35594         }else if(Roo.isIE){
35595             el.setWidth(this.minWidth);
35596             var t = el.dom.offsetWidth; // force recalc
35597             el.setWidth(ul.getWidth()+el.getFrameWidth("lr"));
35598         }
35599     },
35600
35601     // private
35602     delayAutoWidth : function(){
35603         if(this.rendered){
35604             if(!this.awTask){
35605                 this.awTask = new Roo.util.DelayedTask(this.autoWidth, this);
35606             }
35607             this.awTask.delay(20);
35608         }
35609     },
35610
35611     // private
35612     findTargetItem : function(e){
35613         var t = e.getTarget(".x-menu-list-item", this.ul,  true);
35614         if(t && t.menuItemId){
35615             return this.items.get(t.menuItemId);
35616         }
35617     },
35618
35619     // private
35620     onClick : function(e){
35621         Roo.log("menu.onClick");
35622         var t = this.findTargetItem(e);
35623         if(!t){
35624             return;
35625         }
35626         Roo.log(e);
35627         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
35628             if(t == this.activeItem && t.shouldDeactivate(e)){
35629                 this.activeItem.deactivate();
35630                 delete this.activeItem;
35631                 return;
35632             }
35633             if(t.canActivate){
35634                 this.setActiveItem(t, true);
35635             }
35636             return;
35637             
35638             
35639         }
35640         
35641         t.onClick(e);
35642         this.fireEvent("click", this, t, e);
35643     },
35644
35645     // private
35646     setActiveItem : function(item, autoExpand){
35647         if(item != this.activeItem){
35648             if(this.activeItem){
35649                 this.activeItem.deactivate();
35650             }
35651             this.activeItem = item;
35652             item.activate(autoExpand);
35653         }else if(autoExpand){
35654             item.expandMenu();
35655         }
35656     },
35657
35658     // private
35659     tryActivate : function(start, step){
35660         var items = this.items;
35661         for(var i = start, len = items.length; i >= 0 && i < len; i+= step){
35662             var item = items.get(i);
35663             if(!item.disabled && item.canActivate){
35664                 this.setActiveItem(item, false);
35665                 return item;
35666             }
35667         }
35668         return false;
35669     },
35670
35671     // private
35672     onMouseOver : function(e){
35673         var t;
35674         if(t = this.findTargetItem(e)){
35675             if(t.canActivate && !t.disabled){
35676                 this.setActiveItem(t, true);
35677             }
35678         }
35679         this.fireEvent("mouseover", this, e, t);
35680     },
35681
35682     // private
35683     onMouseOut : function(e){
35684         var t;
35685         if(t = this.findTargetItem(e)){
35686             if(t == this.activeItem && t.shouldDeactivate(e)){
35687                 this.activeItem.deactivate();
35688                 delete this.activeItem;
35689             }
35690         }
35691         this.fireEvent("mouseout", this, e, t);
35692     },
35693
35694     /**
35695      * Read-only.  Returns true if the menu is currently displayed, else false.
35696      * @type Boolean
35697      */
35698     isVisible : function(){
35699         return this.el && !this.hidden;
35700     },
35701
35702     /**
35703      * Displays this menu relative to another element
35704      * @param {String/HTMLElement/Roo.Element} element The element to align to
35705      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
35706      * the element (defaults to this.defaultAlign)
35707      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
35708      */
35709     show : function(el, pos, parentMenu){
35710         this.parentMenu = parentMenu;
35711         if(!this.el){
35712             this.render();
35713         }
35714         this.fireEvent("beforeshow", this);
35715         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
35716     },
35717
35718     /**
35719      * Displays this menu at a specific xy position
35720      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
35721      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
35722      */
35723     showAt : function(xy, parentMenu, /* private: */_e){
35724         this.parentMenu = parentMenu;
35725         if(!this.el){
35726             this.render();
35727         }
35728         if(_e !== false){
35729             this.fireEvent("beforeshow", this);
35730             xy = this.el.adjustForConstraints(xy);
35731         }
35732         this.el.setXY(xy);
35733         this.el.show();
35734         this.hidden = false;
35735         this.focus();
35736         this.fireEvent("show", this);
35737     },
35738
35739     focus : function(){
35740         if(!this.hidden){
35741             this.doFocus.defer(50, this);
35742         }
35743     },
35744
35745     doFocus : function(){
35746         if(!this.hidden){
35747             this.focusEl.focus();
35748         }
35749     },
35750
35751     /**
35752      * Hides this menu and optionally all parent menus
35753      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
35754      */
35755     hide : function(deep){
35756         if(this.el && this.isVisible()){
35757             this.fireEvent("beforehide", this);
35758             if(this.activeItem){
35759                 this.activeItem.deactivate();
35760                 this.activeItem = null;
35761             }
35762             this.el.hide();
35763             this.hidden = true;
35764             this.fireEvent("hide", this);
35765         }
35766         if(deep === true && this.parentMenu){
35767             this.parentMenu.hide(true);
35768         }
35769     },
35770
35771     /**
35772      * Addds one or more items of any type supported by the Menu class, or that can be converted into menu items.
35773      * Any of the following are valid:
35774      * <ul>
35775      * <li>Any menu item object based on {@link Roo.menu.Item}</li>
35776      * <li>An HTMLElement object which will be converted to a menu item</li>
35777      * <li>A menu item config object that will be created as a new menu item</li>
35778      * <li>A string, which can either be '-' or 'separator' to add a menu separator, otherwise
35779      * it will be converted into a {@link Roo.menu.TextItem} and added</li>
35780      * </ul>
35781      * Usage:
35782      * <pre><code>
35783 // Create the menu
35784 var menu = new Roo.menu.Menu();
35785
35786 // Create a menu item to add by reference
35787 var menuItem = new Roo.menu.Item({ text: 'New Item!' });
35788
35789 // Add a bunch of items at once using different methods.
35790 // Only the last item added will be returned.
35791 var item = menu.add(
35792     menuItem,                // add existing item by ref
35793     'Dynamic Item',          // new TextItem
35794     '-',                     // new separator
35795     { text: 'Config Item' }  // new item by config
35796 );
35797 </code></pre>
35798      * @param {Mixed} args One or more menu items, menu item configs or other objects that can be converted to menu items
35799      * @return {Roo.menu.Item} The menu item that was added, or the last one if multiple items were added
35800      */
35801     add : function(){
35802         var a = arguments, l = a.length, item;
35803         for(var i = 0; i < l; i++){
35804             var el = a[i];
35805             if ((typeof(el) == "object") && el.xtype && el.xns) {
35806                 el = Roo.factory(el, Roo.menu);
35807             }
35808             
35809             if(el.render){ // some kind of Item
35810                 item = this.addItem(el);
35811             }else if(typeof el == "string"){ // string
35812                 if(el == "separator" || el == "-"){
35813                     item = this.addSeparator();
35814                 }else{
35815                     item = this.addText(el);
35816                 }
35817             }else if(el.tagName || el.el){ // element
35818                 item = this.addElement(el);
35819             }else if(typeof el == "object"){ // must be menu item config?
35820                 item = this.addMenuItem(el);
35821             }
35822         }
35823         return item;
35824     },
35825
35826     /**
35827      * Returns this menu's underlying {@link Roo.Element} object
35828      * @return {Roo.Element} The element
35829      */
35830     getEl : function(){
35831         if(!this.el){
35832             this.render();
35833         }
35834         return this.el;
35835     },
35836
35837     /**
35838      * Adds a separator bar to the menu
35839      * @return {Roo.menu.Item} The menu item that was added
35840      */
35841     addSeparator : function(){
35842         return this.addItem(new Roo.menu.Separator());
35843     },
35844
35845     /**
35846      * Adds an {@link Roo.Element} object to the menu
35847      * @param {String/HTMLElement/Roo.Element} el The element or DOM node to add, or its id
35848      * @return {Roo.menu.Item} The menu item that was added
35849      */
35850     addElement : function(el){
35851         return this.addItem(new Roo.menu.BaseItem(el));
35852     },
35853
35854     /**
35855      * Adds an existing object based on {@link Roo.menu.Item} to the menu
35856      * @param {Roo.menu.Item} item The menu item to add
35857      * @return {Roo.menu.Item} The menu item that was added
35858      */
35859     addItem : function(item){
35860         this.items.add(item);
35861         if(this.ul){
35862             var li = document.createElement("li");
35863             li.className = "x-menu-list-item";
35864             this.ul.dom.appendChild(li);
35865             item.render(li, this);
35866             this.delayAutoWidth();
35867         }
35868         return item;
35869     },
35870
35871     /**
35872      * Creates a new {@link Roo.menu.Item} based an the supplied config object and adds it to the menu
35873      * @param {Object} config A MenuItem config object
35874      * @return {Roo.menu.Item} The menu item that was added
35875      */
35876     addMenuItem : function(config){
35877         if(!(config instanceof Roo.menu.Item)){
35878             if(typeof config.checked == "boolean"){ // must be check menu item config?
35879                 config = new Roo.menu.CheckItem(config);
35880             }else{
35881                 config = new Roo.menu.Item(config);
35882             }
35883         }
35884         return this.addItem(config);
35885     },
35886
35887     /**
35888      * Creates a new {@link Roo.menu.TextItem} with the supplied text and adds it to the menu
35889      * @param {String} text The text to display in the menu item
35890      * @return {Roo.menu.Item} The menu item that was added
35891      */
35892     addText : function(text){
35893         return this.addItem(new Roo.menu.TextItem({ text : text }));
35894     },
35895
35896     /**
35897      * Inserts an existing object based on {@link Roo.menu.Item} to the menu at a specified index
35898      * @param {Number} index The index in the menu's list of current items where the new item should be inserted
35899      * @param {Roo.menu.Item} item The menu item to add
35900      * @return {Roo.menu.Item} The menu item that was added
35901      */
35902     insert : function(index, item){
35903         this.items.insert(index, item);
35904         if(this.ul){
35905             var li = document.createElement("li");
35906             li.className = "x-menu-list-item";
35907             this.ul.dom.insertBefore(li, this.ul.dom.childNodes[index]);
35908             item.render(li, this);
35909             this.delayAutoWidth();
35910         }
35911         return item;
35912     },
35913
35914     /**
35915      * Removes an {@link Roo.menu.Item} from the menu and destroys the object
35916      * @param {Roo.menu.Item} item The menu item to remove
35917      */
35918     remove : function(item){
35919         this.items.removeKey(item.id);
35920         item.destroy();
35921     },
35922
35923     /**
35924      * Removes and destroys all items in the menu
35925      */
35926     removeAll : function(){
35927         var f;
35928         while(f = this.items.first()){
35929             this.remove(f);
35930         }
35931     }
35932 });
35933
35934 // MenuNav is a private utility class used internally by the Menu
35935 Roo.menu.MenuNav = function(menu){
35936     Roo.menu.MenuNav.superclass.constructor.call(this, menu.el);
35937     this.scope = this.menu = menu;
35938 };
35939
35940 Roo.extend(Roo.menu.MenuNav, Roo.KeyNav, {
35941     doRelay : function(e, h){
35942         var k = e.getKey();
35943         if(!this.menu.activeItem && e.isNavKeyPress() && k != e.SPACE && k != e.RETURN){
35944             this.menu.tryActivate(0, 1);
35945             return false;
35946         }
35947         return h.call(this.scope || this, e, this.menu);
35948     },
35949
35950     up : function(e, m){
35951         if(!m.tryActivate(m.items.indexOf(m.activeItem)-1, -1)){
35952             m.tryActivate(m.items.length-1, -1);
35953         }
35954     },
35955
35956     down : function(e, m){
35957         if(!m.tryActivate(m.items.indexOf(m.activeItem)+1, 1)){
35958             m.tryActivate(0, 1);
35959         }
35960     },
35961
35962     right : function(e, m){
35963         if(m.activeItem){
35964             m.activeItem.expandMenu(true);
35965         }
35966     },
35967
35968     left : function(e, m){
35969         m.hide();
35970         if(m.parentMenu && m.parentMenu.activeItem){
35971             m.parentMenu.activeItem.activate();
35972         }
35973     },
35974
35975     enter : function(e, m){
35976         if(m.activeItem){
35977             e.stopPropagation();
35978             m.activeItem.onClick(e);
35979             m.fireEvent("click", this, m.activeItem);
35980             return true;
35981         }
35982     }
35983 });/*
35984  * Based on:
35985  * Ext JS Library 1.1.1
35986  * Copyright(c) 2006-2007, Ext JS, LLC.
35987  *
35988  * Originally Released Under LGPL - original licence link has changed is not relivant.
35989  *
35990  * Fork - LGPL
35991  * <script type="text/javascript">
35992  */
35993  
35994 /**
35995  * @class Roo.menu.MenuMgr
35996  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
35997  * @singleton
35998  */
35999 Roo.menu.MenuMgr = function(){
36000    var menus, active, groups = {}, attached = false, lastShow = new Date();
36001
36002    // private - called when first menu is created
36003    function init(){
36004        menus = {};
36005        active = new Roo.util.MixedCollection();
36006        Roo.get(document).addKeyListener(27, function(){
36007            if(active.length > 0){
36008                hideAll();
36009            }
36010        });
36011    }
36012
36013    // private
36014    function hideAll(){
36015        if(active && active.length > 0){
36016            var c = active.clone();
36017            c.each(function(m){
36018                m.hide();
36019            });
36020        }
36021    }
36022
36023    // private
36024    function onHide(m){
36025        active.remove(m);
36026        if(active.length < 1){
36027            Roo.get(document).un("mousedown", onMouseDown);
36028            attached = false;
36029        }
36030    }
36031
36032    // private
36033    function onShow(m){
36034        var last = active.last();
36035        lastShow = new Date();
36036        active.add(m);
36037        if(!attached){
36038            Roo.get(document).on("mousedown", onMouseDown);
36039            attached = true;
36040        }
36041        if(m.parentMenu){
36042           m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
36043           m.parentMenu.activeChild = m;
36044        }else if(last && last.isVisible()){
36045           m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
36046        }
36047    }
36048
36049    // private
36050    function onBeforeHide(m){
36051        if(m.activeChild){
36052            m.activeChild.hide();
36053        }
36054        if(m.autoHideTimer){
36055            clearTimeout(m.autoHideTimer);
36056            delete m.autoHideTimer;
36057        }
36058    }
36059
36060    // private
36061    function onBeforeShow(m){
36062        var pm = m.parentMenu;
36063        if(!pm && !m.allowOtherMenus){
36064            hideAll();
36065        }else if(pm && pm.activeChild && active != m){
36066            pm.activeChild.hide();
36067        }
36068    }
36069
36070    // private
36071    function onMouseDown(e){
36072        if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".x-menu")){
36073            hideAll();
36074        }
36075    }
36076
36077    // private
36078    function onBeforeCheck(mi, state){
36079        if(state){
36080            var g = groups[mi.group];
36081            for(var i = 0, l = g.length; i < l; i++){
36082                if(g[i] != mi){
36083                    g[i].setChecked(false);
36084                }
36085            }
36086        }
36087    }
36088
36089    return {
36090
36091        /**
36092         * Hides all menus that are currently visible
36093         */
36094        hideAll : function(){
36095             hideAll();  
36096        },
36097
36098        // private
36099        register : function(menu){
36100            if(!menus){
36101                init();
36102            }
36103            menus[menu.id] = menu;
36104            menu.on("beforehide", onBeforeHide);
36105            menu.on("hide", onHide);
36106            menu.on("beforeshow", onBeforeShow);
36107            menu.on("show", onShow);
36108            var g = menu.group;
36109            if(g && menu.events["checkchange"]){
36110                if(!groups[g]){
36111                    groups[g] = [];
36112                }
36113                groups[g].push(menu);
36114                menu.on("checkchange", onCheck);
36115            }
36116        },
36117
36118         /**
36119          * Returns a {@link Roo.menu.Menu} object
36120          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
36121          * be used to generate and return a new Menu instance.
36122          */
36123        get : function(menu){
36124            if(typeof menu == "string"){ // menu id
36125                return menus[menu];
36126            }else if(menu.events){  // menu instance
36127                return menu;
36128            }else if(typeof menu.length == 'number'){ // array of menu items?
36129                return new Roo.menu.Menu({items:menu});
36130            }else{ // otherwise, must be a config
36131                return new Roo.menu.Menu(menu);
36132            }
36133        },
36134
36135        // private
36136        unregister : function(menu){
36137            delete menus[menu.id];
36138            menu.un("beforehide", onBeforeHide);
36139            menu.un("hide", onHide);
36140            menu.un("beforeshow", onBeforeShow);
36141            menu.un("show", onShow);
36142            var g = menu.group;
36143            if(g && menu.events["checkchange"]){
36144                groups[g].remove(menu);
36145                menu.un("checkchange", onCheck);
36146            }
36147        },
36148
36149        // private
36150        registerCheckable : function(menuItem){
36151            var g = menuItem.group;
36152            if(g){
36153                if(!groups[g]){
36154                    groups[g] = [];
36155                }
36156                groups[g].push(menuItem);
36157                menuItem.on("beforecheckchange", onBeforeCheck);
36158            }
36159        },
36160
36161        // private
36162        unregisterCheckable : function(menuItem){
36163            var g = menuItem.group;
36164            if(g){
36165                groups[g].remove(menuItem);
36166                menuItem.un("beforecheckchange", onBeforeCheck);
36167            }
36168        }
36169    };
36170 }();/*
36171  * Based on:
36172  * Ext JS Library 1.1.1
36173  * Copyright(c) 2006-2007, Ext JS, LLC.
36174  *
36175  * Originally Released Under LGPL - original licence link has changed is not relivant.
36176  *
36177  * Fork - LGPL
36178  * <script type="text/javascript">
36179  */
36180  
36181
36182 /**
36183  * @class Roo.menu.BaseItem
36184  * @extends Roo.Component
36185  * The base class for all items that render into menus.  BaseItem provides default rendering, activated state
36186  * management and base configuration options shared by all menu components.
36187  * @constructor
36188  * Creates a new BaseItem
36189  * @param {Object} config Configuration options
36190  */
36191 Roo.menu.BaseItem = function(config){
36192     Roo.menu.BaseItem.superclass.constructor.call(this, config);
36193
36194     this.addEvents({
36195         /**
36196          * @event click
36197          * Fires when this item is clicked
36198          * @param {Roo.menu.BaseItem} this
36199          * @param {Roo.EventObject} e
36200          */
36201         click: true,
36202         /**
36203          * @event activate
36204          * Fires when this item is activated
36205          * @param {Roo.menu.BaseItem} this
36206          */
36207         activate : true,
36208         /**
36209          * @event deactivate
36210          * Fires when this item is deactivated
36211          * @param {Roo.menu.BaseItem} this
36212          */
36213         deactivate : true
36214     });
36215
36216     if(this.handler){
36217         this.on("click", this.handler, this.scope, true);
36218     }
36219 };
36220
36221 Roo.extend(Roo.menu.BaseItem, Roo.Component, {
36222     /**
36223      * @cfg {Function} handler
36224      * A function that will handle the click event of this menu item (defaults to undefined)
36225      */
36226     /**
36227      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to false)
36228      */
36229     canActivate : false,
36230     
36231      /**
36232      * @cfg {Boolean} hidden True to prevent creation of this menu item (defaults to false)
36233      */
36234     hidden: false,
36235     
36236     /**
36237      * @cfg {String} activeClass The CSS class to use when the item becomes activated (defaults to "x-menu-item-active")
36238      */
36239     activeClass : "x-menu-item-active",
36240     /**
36241      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to true)
36242      */
36243     hideOnClick : true,
36244     /**
36245      * @cfg {Number} hideDelay Length of time in milliseconds to wait before hiding after a click (defaults to 100)
36246      */
36247     hideDelay : 100,
36248
36249     // private
36250     ctype: "Roo.menu.BaseItem",
36251
36252     // private
36253     actionMode : "container",
36254
36255     // private
36256     render : function(container, parentMenu){
36257         this.parentMenu = parentMenu;
36258         Roo.menu.BaseItem.superclass.render.call(this, container);
36259         this.container.menuItemId = this.id;
36260     },
36261
36262     // private
36263     onRender : function(container, position){
36264         this.el = Roo.get(this.el);
36265         container.dom.appendChild(this.el.dom);
36266     },
36267
36268     // private
36269     onClick : function(e){
36270         if(!this.disabled && this.fireEvent("click", this, e) !== false
36271                 && this.parentMenu.fireEvent("itemclick", this, e) !== false){
36272             this.handleClick(e);
36273         }else{
36274             e.stopEvent();
36275         }
36276     },
36277
36278     // private
36279     activate : function(){
36280         if(this.disabled){
36281             return false;
36282         }
36283         var li = this.container;
36284         li.addClass(this.activeClass);
36285         this.region = li.getRegion().adjust(2, 2, -2, -2);
36286         this.fireEvent("activate", this);
36287         return true;
36288     },
36289
36290     // private
36291     deactivate : function(){
36292         this.container.removeClass(this.activeClass);
36293         this.fireEvent("deactivate", this);
36294     },
36295
36296     // private
36297     shouldDeactivate : function(e){
36298         return !this.region || !this.region.contains(e.getPoint());
36299     },
36300
36301     // private
36302     handleClick : function(e){
36303         if(this.hideOnClick){
36304             this.parentMenu.hide.defer(this.hideDelay, this.parentMenu, [true]);
36305         }
36306     },
36307
36308     // private
36309     expandMenu : function(autoActivate){
36310         // do nothing
36311     },
36312
36313     // private
36314     hideMenu : function(){
36315         // do nothing
36316     }
36317 });/*
36318  * Based on:
36319  * Ext JS Library 1.1.1
36320  * Copyright(c) 2006-2007, Ext JS, LLC.
36321  *
36322  * Originally Released Under LGPL - original licence link has changed is not relivant.
36323  *
36324  * Fork - LGPL
36325  * <script type="text/javascript">
36326  */
36327  
36328 /**
36329  * @class Roo.menu.Adapter
36330  * @extends Roo.menu.BaseItem
36331  * 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.
36332  * It provides basic rendering, activation management and enable/disable logic required to work in menus.
36333  * @constructor
36334  * Creates a new Adapter
36335  * @param {Object} config Configuration options
36336  */
36337 Roo.menu.Adapter = function(component, config){
36338     Roo.menu.Adapter.superclass.constructor.call(this, config);
36339     this.component = component;
36340 };
36341 Roo.extend(Roo.menu.Adapter, Roo.menu.BaseItem, {
36342     // private
36343     canActivate : true,
36344
36345     // private
36346     onRender : function(container, position){
36347         this.component.render(container);
36348         this.el = this.component.getEl();
36349     },
36350
36351     // private
36352     activate : function(){
36353         if(this.disabled){
36354             return false;
36355         }
36356         this.component.focus();
36357         this.fireEvent("activate", this);
36358         return true;
36359     },
36360
36361     // private
36362     deactivate : function(){
36363         this.fireEvent("deactivate", this);
36364     },
36365
36366     // private
36367     disable : function(){
36368         this.component.disable();
36369         Roo.menu.Adapter.superclass.disable.call(this);
36370     },
36371
36372     // private
36373     enable : function(){
36374         this.component.enable();
36375         Roo.menu.Adapter.superclass.enable.call(this);
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.TextItem
36390  * @extends Roo.menu.BaseItem
36391  * Adds a static text string to a menu, usually used as either a heading or group separator.
36392  * Note: old style constructor with text is still supported.
36393  * 
36394  * @constructor
36395  * Creates a new TextItem
36396  * @param {Object} cfg Configuration
36397  */
36398 Roo.menu.TextItem = function(cfg){
36399     if (typeof(cfg) == 'string') {
36400         this.text = cfg;
36401     } else {
36402         Roo.apply(this,cfg);
36403     }
36404     
36405     Roo.menu.TextItem.superclass.constructor.call(this);
36406 };
36407
36408 Roo.extend(Roo.menu.TextItem, Roo.menu.BaseItem, {
36409     /**
36410      * @cfg {Boolean} text Text to show on item.
36411      */
36412     text : '',
36413     
36414     /**
36415      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
36416      */
36417     hideOnClick : false,
36418     /**
36419      * @cfg {String} itemCls The default CSS class to use for text items (defaults to "x-menu-text")
36420      */
36421     itemCls : "x-menu-text",
36422
36423     // private
36424     onRender : function(){
36425         var s = document.createElement("span");
36426         s.className = this.itemCls;
36427         s.innerHTML = this.text;
36428         this.el = s;
36429         Roo.menu.TextItem.superclass.onRender.apply(this, arguments);
36430     }
36431 });/*
36432  * Based on:
36433  * Ext JS Library 1.1.1
36434  * Copyright(c) 2006-2007, Ext JS, LLC.
36435  *
36436  * Originally Released Under LGPL - original licence link has changed is not relivant.
36437  *
36438  * Fork - LGPL
36439  * <script type="text/javascript">
36440  */
36441
36442 /**
36443  * @class Roo.menu.Separator
36444  * @extends Roo.menu.BaseItem
36445  * Adds a separator bar to a menu, used to divide logical groups of menu items. Generally you will
36446  * add one of these by using "-" in you call to add() or in your items config rather than creating one directly.
36447  * @constructor
36448  * @param {Object} config Configuration options
36449  */
36450 Roo.menu.Separator = function(config){
36451     Roo.menu.Separator.superclass.constructor.call(this, config);
36452 };
36453
36454 Roo.extend(Roo.menu.Separator, Roo.menu.BaseItem, {
36455     /**
36456      * @cfg {String} itemCls The default CSS class to use for separators (defaults to "x-menu-sep")
36457      */
36458     itemCls : "x-menu-sep",
36459     /**
36460      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
36461      */
36462     hideOnClick : false,
36463
36464     // private
36465     onRender : function(li){
36466         var s = document.createElement("span");
36467         s.className = this.itemCls;
36468         s.innerHTML = "&#160;";
36469         this.el = s;
36470         li.addClass("x-menu-sep-li");
36471         Roo.menu.Separator.superclass.onRender.apply(this, arguments);
36472     }
36473 });/*
36474  * Based on:
36475  * Ext JS Library 1.1.1
36476  * Copyright(c) 2006-2007, Ext JS, LLC.
36477  *
36478  * Originally Released Under LGPL - original licence link has changed is not relivant.
36479  *
36480  * Fork - LGPL
36481  * <script type="text/javascript">
36482  */
36483 /**
36484  * @class Roo.menu.Item
36485  * @extends Roo.menu.BaseItem
36486  * A base class for all menu items that require menu-related functionality (like sub-menus) and are not static
36487  * display items.  Item extends the base functionality of {@link Roo.menu.BaseItem} by adding menu-specific
36488  * activation and click handling.
36489  * @constructor
36490  * Creates a new Item
36491  * @param {Object} config Configuration options
36492  */
36493 Roo.menu.Item = function(config){
36494     Roo.menu.Item.superclass.constructor.call(this, config);
36495     if(this.menu){
36496         this.menu = Roo.menu.MenuMgr.get(this.menu);
36497     }
36498 };
36499 Roo.extend(Roo.menu.Item, Roo.menu.BaseItem, {
36500     
36501     /**
36502      * @cfg {String} text
36503      * The text to show on the menu item.
36504      */
36505     text: '',
36506      /**
36507      * @cfg {String} HTML to render in menu
36508      * The text to show on the menu item (HTML version).
36509      */
36510     html: '',
36511     /**
36512      * @cfg {String} icon
36513      * The path to an icon to display in this menu item (defaults to Roo.BLANK_IMAGE_URL)
36514      */
36515     icon: undefined,
36516     /**
36517      * @cfg {String} itemCls The default CSS class to use for menu items (defaults to "x-menu-item")
36518      */
36519     itemCls : "x-menu-item",
36520     /**
36521      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to true)
36522      */
36523     canActivate : true,
36524     /**
36525      * @cfg {Number} showDelay Length of time in milliseconds to wait before showing this item (defaults to 200)
36526      */
36527     showDelay: 200,
36528     // doc'd in BaseItem
36529     hideDelay: 200,
36530
36531     // private
36532     ctype: "Roo.menu.Item",
36533     
36534     // private
36535     onRender : function(container, position){
36536         var el = document.createElement("a");
36537         el.hideFocus = true;
36538         el.unselectable = "on";
36539         el.href = this.href || "#";
36540         if(this.hrefTarget){
36541             el.target = this.hrefTarget;
36542         }
36543         el.className = this.itemCls + (this.menu ?  " x-menu-item-arrow" : "") + (this.cls ?  " " + this.cls : "");
36544         
36545         var html = this.html.length ? this.html  : String.format('{0}',this.text);
36546         
36547         el.innerHTML = String.format(
36548                 '<img src="{0}" class="x-menu-item-icon {1}" />' + html,
36549                 this.icon || Roo.BLANK_IMAGE_URL, this.iconCls || '');
36550         this.el = el;
36551         Roo.menu.Item.superclass.onRender.call(this, container, position);
36552     },
36553
36554     /**
36555      * Sets the text to display in this menu item
36556      * @param {String} text The text to display
36557      * @param {Boolean} isHTML true to indicate text is pure html.
36558      */
36559     setText : function(text, isHTML){
36560         if (isHTML) {
36561             this.html = text;
36562         } else {
36563             this.text = text;
36564             this.html = '';
36565         }
36566         if(this.rendered){
36567             var html = this.html.length ? this.html  : String.format('{0}',this.text);
36568      
36569             this.el.update(String.format(
36570                 '<img src="{0}" class="x-menu-item-icon {2}">' + html,
36571                 this.icon || Roo.BLANK_IMAGE_URL, this.text, this.iconCls || ''));
36572             this.parentMenu.autoWidth();
36573         }
36574     },
36575
36576     // private
36577     handleClick : function(e){
36578         if(!this.href){ // if no link defined, stop the event automatically
36579             e.stopEvent();
36580         }
36581         Roo.menu.Item.superclass.handleClick.apply(this, arguments);
36582     },
36583
36584     // private
36585     activate : function(autoExpand){
36586         if(Roo.menu.Item.superclass.activate.apply(this, arguments)){
36587             this.focus();
36588             if(autoExpand){
36589                 this.expandMenu();
36590             }
36591         }
36592         return true;
36593     },
36594
36595     // private
36596     shouldDeactivate : function(e){
36597         if(Roo.menu.Item.superclass.shouldDeactivate.call(this, e)){
36598             if(this.menu && this.menu.isVisible()){
36599                 return !this.menu.getEl().getRegion().contains(e.getPoint());
36600             }
36601             return true;
36602         }
36603         return false;
36604     },
36605
36606     // private
36607     deactivate : function(){
36608         Roo.menu.Item.superclass.deactivate.apply(this, arguments);
36609         this.hideMenu();
36610     },
36611
36612     // private
36613     expandMenu : function(autoActivate){
36614         if(!this.disabled && this.menu){
36615             clearTimeout(this.hideTimer);
36616             delete this.hideTimer;
36617             if(!this.menu.isVisible() && !this.showTimer){
36618                 this.showTimer = this.deferExpand.defer(this.showDelay, this, [autoActivate]);
36619             }else if (this.menu.isVisible() && autoActivate){
36620                 this.menu.tryActivate(0, 1);
36621             }
36622         }
36623     },
36624
36625     // private
36626     deferExpand : function(autoActivate){
36627         delete this.showTimer;
36628         this.menu.show(this.container, this.parentMenu.subMenuAlign || "tl-tr?", this.parentMenu);
36629         if(autoActivate){
36630             this.menu.tryActivate(0, 1);
36631         }
36632     },
36633
36634     // private
36635     hideMenu : function(){
36636         clearTimeout(this.showTimer);
36637         delete this.showTimer;
36638         if(!this.hideTimer && this.menu && this.menu.isVisible()){
36639             this.hideTimer = this.deferHide.defer(this.hideDelay, this);
36640         }
36641     },
36642
36643     // private
36644     deferHide : function(){
36645         delete this.hideTimer;
36646         this.menu.hide();
36647     }
36648 });/*
36649  * Based on:
36650  * Ext JS Library 1.1.1
36651  * Copyright(c) 2006-2007, Ext JS, LLC.
36652  *
36653  * Originally Released Under LGPL - original licence link has changed is not relivant.
36654  *
36655  * Fork - LGPL
36656  * <script type="text/javascript">
36657  */
36658  
36659 /**
36660  * @class Roo.menu.CheckItem
36661  * @extends Roo.menu.Item
36662  * Adds a menu item that contains a checkbox by default, but can also be part of a radio group.
36663  * @constructor
36664  * Creates a new CheckItem
36665  * @param {Object} config Configuration options
36666  */
36667 Roo.menu.CheckItem = function(config){
36668     Roo.menu.CheckItem.superclass.constructor.call(this, config);
36669     this.addEvents({
36670         /**
36671          * @event beforecheckchange
36672          * Fires before the checked value is set, providing an opportunity to cancel if needed
36673          * @param {Roo.menu.CheckItem} this
36674          * @param {Boolean} checked The new checked value that will be set
36675          */
36676         "beforecheckchange" : true,
36677         /**
36678          * @event checkchange
36679          * Fires after the checked value has been set
36680          * @param {Roo.menu.CheckItem} this
36681          * @param {Boolean} checked The checked value that was set
36682          */
36683         "checkchange" : true
36684     });
36685     if(this.checkHandler){
36686         this.on('checkchange', this.checkHandler, this.scope);
36687     }
36688 };
36689 Roo.extend(Roo.menu.CheckItem, Roo.menu.Item, {
36690     /**
36691      * @cfg {String} group
36692      * All check items with the same group name will automatically be grouped into a single-select
36693      * radio button group (defaults to '')
36694      */
36695     /**
36696      * @cfg {String} itemCls The default CSS class to use for check items (defaults to "x-menu-item x-menu-check-item")
36697      */
36698     itemCls : "x-menu-item x-menu-check-item",
36699     /**
36700      * @cfg {String} groupClass The default CSS class to use for radio group check items (defaults to "x-menu-group-item")
36701      */
36702     groupClass : "x-menu-group-item",
36703
36704     /**
36705      * @cfg {Boolean} checked True to initialize this checkbox as checked (defaults to false).  Note that
36706      * if this checkbox is part of a radio group (group = true) only the last item in the group that is
36707      * initialized with checked = true will be rendered as checked.
36708      */
36709     checked: false,
36710
36711     // private
36712     ctype: "Roo.menu.CheckItem",
36713
36714     // private
36715     onRender : function(c){
36716         Roo.menu.CheckItem.superclass.onRender.apply(this, arguments);
36717         if(this.group){
36718             this.el.addClass(this.groupClass);
36719         }
36720         Roo.menu.MenuMgr.registerCheckable(this);
36721         if(this.checked){
36722             this.checked = false;
36723             this.setChecked(true, true);
36724         }
36725     },
36726
36727     // private
36728     destroy : function(){
36729         if(this.rendered){
36730             Roo.menu.MenuMgr.unregisterCheckable(this);
36731         }
36732         Roo.menu.CheckItem.superclass.destroy.apply(this, arguments);
36733     },
36734
36735     /**
36736      * Set the checked state of this item
36737      * @param {Boolean} checked The new checked value
36738      * @param {Boolean} suppressEvent (optional) True to prevent the checkchange event from firing (defaults to false)
36739      */
36740     setChecked : function(state, suppressEvent){
36741         if(this.checked != state && this.fireEvent("beforecheckchange", this, state) !== false){
36742             if(this.container){
36743                 this.container[state ? "addClass" : "removeClass"]("x-menu-item-checked");
36744             }
36745             this.checked = state;
36746             if(suppressEvent !== true){
36747                 this.fireEvent("checkchange", this, state);
36748             }
36749         }
36750     },
36751
36752     // private
36753     handleClick : function(e){
36754        if(!this.disabled && !(this.checked && this.group)){// disable unselect on radio item
36755            this.setChecked(!this.checked);
36756        }
36757        Roo.menu.CheckItem.superclass.handleClick.apply(this, arguments);
36758     }
36759 });/*
36760  * Based on:
36761  * Ext JS Library 1.1.1
36762  * Copyright(c) 2006-2007, Ext JS, LLC.
36763  *
36764  * Originally Released Under LGPL - original licence link has changed is not relivant.
36765  *
36766  * Fork - LGPL
36767  * <script type="text/javascript">
36768  */
36769  
36770 /**
36771  * @class Roo.menu.DateItem
36772  * @extends Roo.menu.Adapter
36773  * A menu item that wraps the {@link Roo.DatPicker} component.
36774  * @constructor
36775  * Creates a new DateItem
36776  * @param {Object} config Configuration options
36777  */
36778 Roo.menu.DateItem = function(config){
36779     Roo.menu.DateItem.superclass.constructor.call(this, new Roo.DatePicker(config), config);
36780     /** The Roo.DatePicker object @type Roo.DatePicker */
36781     this.picker = this.component;
36782     this.addEvents({select: true});
36783     
36784     this.picker.on("render", function(picker){
36785         picker.getEl().swallowEvent("click");
36786         picker.container.addClass("x-menu-date-item");
36787     });
36788
36789     this.picker.on("select", this.onSelect, this);
36790 };
36791
36792 Roo.extend(Roo.menu.DateItem, Roo.menu.Adapter, {
36793     // private
36794     onSelect : function(picker, date){
36795         this.fireEvent("select", this, date, picker);
36796         Roo.menu.DateItem.superclass.handleClick.call(this);
36797     }
36798 });/*
36799  * Based on:
36800  * Ext JS Library 1.1.1
36801  * Copyright(c) 2006-2007, Ext JS, LLC.
36802  *
36803  * Originally Released Under LGPL - original licence link has changed is not relivant.
36804  *
36805  * Fork - LGPL
36806  * <script type="text/javascript">
36807  */
36808  
36809 /**
36810  * @class Roo.menu.ColorItem
36811  * @extends Roo.menu.Adapter
36812  * A menu item that wraps the {@link Roo.ColorPalette} component.
36813  * @constructor
36814  * Creates a new ColorItem
36815  * @param {Object} config Configuration options
36816  */
36817 Roo.menu.ColorItem = function(config){
36818     Roo.menu.ColorItem.superclass.constructor.call(this, new Roo.ColorPalette(config), config);
36819     /** The Roo.ColorPalette object @type Roo.ColorPalette */
36820     this.palette = this.component;
36821     this.relayEvents(this.palette, ["select"]);
36822     if(this.selectHandler){
36823         this.on('select', this.selectHandler, this.scope);
36824     }
36825 };
36826 Roo.extend(Roo.menu.ColorItem, Roo.menu.Adapter);/*
36827  * Based on:
36828  * Ext JS Library 1.1.1
36829  * Copyright(c) 2006-2007, Ext JS, LLC.
36830  *
36831  * Originally Released Under LGPL - original licence link has changed is not relivant.
36832  *
36833  * Fork - LGPL
36834  * <script type="text/javascript">
36835  */
36836  
36837
36838 /**
36839  * @class Roo.menu.DateMenu
36840  * @extends Roo.menu.Menu
36841  * A menu containing a {@link Roo.menu.DateItem} component (which provides a date picker).
36842  * @constructor
36843  * Creates a new DateMenu
36844  * @param {Object} config Configuration options
36845  */
36846 Roo.menu.DateMenu = function(config){
36847     Roo.menu.DateMenu.superclass.constructor.call(this, config);
36848     this.plain = true;
36849     var di = new Roo.menu.DateItem(config);
36850     this.add(di);
36851     /**
36852      * The {@link Roo.DatePicker} instance for this DateMenu
36853      * @type DatePicker
36854      */
36855     this.picker = di.picker;
36856     /**
36857      * @event select
36858      * @param {DatePicker} picker
36859      * @param {Date} date
36860      */
36861     this.relayEvents(di, ["select"]);
36862     this.on('beforeshow', function(){
36863         if(this.picker){
36864             this.picker.hideMonthPicker(false);
36865         }
36866     }, this);
36867 };
36868 Roo.extend(Roo.menu.DateMenu, Roo.menu.Menu, {
36869     cls:'x-date-menu'
36870 });/*
36871  * Based on:
36872  * Ext JS Library 1.1.1
36873  * Copyright(c) 2006-2007, Ext JS, LLC.
36874  *
36875  * Originally Released Under LGPL - original licence link has changed is not relivant.
36876  *
36877  * Fork - LGPL
36878  * <script type="text/javascript">
36879  */
36880  
36881
36882 /**
36883  * @class Roo.menu.ColorMenu
36884  * @extends Roo.menu.Menu
36885  * A menu containing a {@link Roo.menu.ColorItem} component (which provides a basic color picker).
36886  * @constructor
36887  * Creates a new ColorMenu
36888  * @param {Object} config Configuration options
36889  */
36890 Roo.menu.ColorMenu = function(config){
36891     Roo.menu.ColorMenu.superclass.constructor.call(this, config);
36892     this.plain = true;
36893     var ci = new Roo.menu.ColorItem(config);
36894     this.add(ci);
36895     /**
36896      * The {@link Roo.ColorPalette} instance for this ColorMenu
36897      * @type ColorPalette
36898      */
36899     this.palette = ci.palette;
36900     /**
36901      * @event select
36902      * @param {ColorPalette} palette
36903      * @param {String} color
36904      */
36905     this.relayEvents(ci, ["select"]);
36906 };
36907 Roo.extend(Roo.menu.ColorMenu, Roo.menu.Menu);/*
36908  * Based on:
36909  * Ext JS Library 1.1.1
36910  * Copyright(c) 2006-2007, Ext JS, LLC.
36911  *
36912  * Originally Released Under LGPL - original licence link has changed is not relivant.
36913  *
36914  * Fork - LGPL
36915  * <script type="text/javascript">
36916  */
36917  
36918 /**
36919  * @class Roo.form.Field
36920  * @extends Roo.BoxComponent
36921  * Base class for form fields that provides default event handling, sizing, value handling and other functionality.
36922  * @constructor
36923  * Creates a new Field
36924  * @param {Object} config Configuration options
36925  */
36926 Roo.form.Field = function(config){
36927     Roo.form.Field.superclass.constructor.call(this, config);
36928 };
36929
36930 Roo.extend(Roo.form.Field, Roo.BoxComponent,  {
36931     /**
36932      * @cfg {String} fieldLabel Label to use when rendering a form.
36933      */
36934        /**
36935      * @cfg {String} qtip Mouse over tip
36936      */
36937      
36938     /**
36939      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
36940      */
36941     invalidClass : "x-form-invalid",
36942     /**
36943      * @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")
36944      */
36945     invalidText : "The value in this field is invalid",
36946     /**
36947      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
36948      */
36949     focusClass : "x-form-focus",
36950     /**
36951      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
36952       automatic validation (defaults to "keyup").
36953      */
36954     validationEvent : "keyup",
36955     /**
36956      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
36957      */
36958     validateOnBlur : true,
36959     /**
36960      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
36961      */
36962     validationDelay : 250,
36963     /**
36964      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
36965      * {tag: "input", type: "text", size: "20", autocomplete: "off"})
36966      */
36967     defaultAutoCreate : {tag: "input", type: "text", size: "20", autocomplete: "off"},
36968     /**
36969      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field")
36970      */
36971     fieldClass : "x-form-field",
36972     /**
36973      * @cfg {String} msgTarget The location where error text should display.  Should be one of the following values (defaults to 'qtip'):
36974      *<pre>
36975 Value         Description
36976 -----------   ----------------------------------------------------------------------
36977 qtip          Display a quick tip when the user hovers over the field
36978 title         Display a default browser title attribute popup
36979 under         Add a block div beneath the field containing the error text
36980 side          Add an error icon to the right of the field with a popup on hover
36981 [element id]  Add the error text directly to the innerHTML of the specified element
36982 </pre>
36983      */
36984     msgTarget : 'qtip',
36985     /**
36986      * @cfg {String} msgFx <b>Experimental</b> The effect used when displaying a validation message under the field (defaults to 'normal').
36987      */
36988     msgFx : 'normal',
36989
36990     /**
36991      * @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.
36992      */
36993     readOnly : false,
36994
36995     /**
36996      * @cfg {Boolean} disabled True to disable the field (defaults to false).
36997      */
36998     disabled : false,
36999
37000     /**
37001      * @cfg {String} inputType The type attribute for input fields -- e.g. radio, text, password (defaults to "text").
37002      */
37003     inputType : undefined,
37004     
37005     /**
37006      * @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).
37007          */
37008         tabIndex : undefined,
37009         
37010     // private
37011     isFormField : true,
37012
37013     // private
37014     hasFocus : false,
37015     /**
37016      * @property {Roo.Element} fieldEl
37017      * Element Containing the rendered Field (with label etc.)
37018      */
37019     /**
37020      * @cfg {Mixed} value A value to initialize this field with.
37021      */
37022     value : undefined,
37023
37024     /**
37025      * @cfg {String} name The field's HTML name attribute.
37026      */
37027     /**
37028      * @cfg {String} cls A CSS class to apply to the field's underlying element.
37029      */
37030
37031         // private ??
37032         initComponent : function(){
37033         Roo.form.Field.superclass.initComponent.call(this);
37034         this.addEvents({
37035             /**
37036              * @event focus
37037              * Fires when this field receives input focus.
37038              * @param {Roo.form.Field} this
37039              */
37040             focus : true,
37041             /**
37042              * @event blur
37043              * Fires when this field loses input focus.
37044              * @param {Roo.form.Field} this
37045              */
37046             blur : true,
37047             /**
37048              * @event specialkey
37049              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
37050              * {@link Roo.EventObject#getKey} to determine which key was pressed.
37051              * @param {Roo.form.Field} this
37052              * @param {Roo.EventObject} e The event object
37053              */
37054             specialkey : true,
37055             /**
37056              * @event change
37057              * Fires just before the field blurs if the field value has changed.
37058              * @param {Roo.form.Field} this
37059              * @param {Mixed} newValue The new value
37060              * @param {Mixed} oldValue The original value
37061              */
37062             change : true,
37063             /**
37064              * @event invalid
37065              * Fires after the field has been marked as invalid.
37066              * @param {Roo.form.Field} this
37067              * @param {String} msg The validation message
37068              */
37069             invalid : true,
37070             /**
37071              * @event valid
37072              * Fires after the field has been validated with no errors.
37073              * @param {Roo.form.Field} this
37074              */
37075             valid : true,
37076              /**
37077              * @event keyup
37078              * Fires after the key up
37079              * @param {Roo.form.Field} this
37080              * @param {Roo.EventObject}  e The event Object
37081              */
37082             keyup : true
37083         });
37084     },
37085
37086     /**
37087      * Returns the name attribute of the field if available
37088      * @return {String} name The field name
37089      */
37090     getName: function(){
37091          return this.rendered && this.el.dom.name ? this.el.dom.name : (this.hiddenName || '');
37092     },
37093
37094     // private
37095     onRender : function(ct, position){
37096         Roo.form.Field.superclass.onRender.call(this, ct, position);
37097         if(!this.el){
37098             var cfg = this.getAutoCreate();
37099             if(!cfg.name){
37100                 cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
37101             }
37102             if (!cfg.name.length) {
37103                 delete cfg.name;
37104             }
37105             if(this.inputType){
37106                 cfg.type = this.inputType;
37107             }
37108             this.el = ct.createChild(cfg, position);
37109         }
37110         var type = this.el.dom.type;
37111         if(type){
37112             if(type == 'password'){
37113                 type = 'text';
37114             }
37115             this.el.addClass('x-form-'+type);
37116         }
37117         if(this.readOnly){
37118             this.el.dom.readOnly = true;
37119         }
37120         if(this.tabIndex !== undefined){
37121             this.el.dom.setAttribute('tabIndex', this.tabIndex);
37122         }
37123
37124         this.el.addClass([this.fieldClass, this.cls]);
37125         this.initValue();
37126     },
37127
37128     /**
37129      * Apply the behaviors of this component to an existing element. <b>This is used instead of render().</b>
37130      * @param {String/HTMLElement/Element} el The id of the node, a DOM node or an existing Element
37131      * @return {Roo.form.Field} this
37132      */
37133     applyTo : function(target){
37134         this.allowDomMove = false;
37135         this.el = Roo.get(target);
37136         this.render(this.el.dom.parentNode);
37137         return this;
37138     },
37139
37140     // private
37141     initValue : function(){
37142         if(this.value !== undefined){
37143             this.setValue(this.value);
37144         }else if(this.el.dom.value.length > 0){
37145             this.setValue(this.el.dom.value);
37146         }
37147     },
37148
37149     /**
37150      * Returns true if this field has been changed since it was originally loaded and is not disabled.
37151      */
37152     isDirty : function() {
37153         if(this.disabled) {
37154             return false;
37155         }
37156         return String(this.getValue()) !== String(this.originalValue);
37157     },
37158
37159     // private
37160     afterRender : function(){
37161         Roo.form.Field.superclass.afterRender.call(this);
37162         this.initEvents();
37163     },
37164
37165     // private
37166     fireKey : function(e){
37167         //Roo.log('field ' + e.getKey());
37168         if(e.isNavKeyPress()){
37169             this.fireEvent("specialkey", this, e);
37170         }
37171     },
37172
37173     /**
37174      * Resets the current field value to the originally loaded value and clears any validation messages
37175      */
37176     reset : function(){
37177         this.setValue(this.resetValue);
37178         this.clearInvalid();
37179     },
37180
37181     // private
37182     initEvents : function(){
37183         // safari killled keypress - so keydown is now used..
37184         this.el.on("keydown" , this.fireKey,  this);
37185         this.el.on("focus", this.onFocus,  this);
37186         this.el.on("blur", this.onBlur,  this);
37187         this.el.relayEvent('keyup', this);
37188
37189         // reference to original value for reset
37190         this.originalValue = this.getValue();
37191         this.resetValue =  this.getValue();
37192     },
37193
37194     // private
37195     onFocus : function(){
37196         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
37197             this.el.addClass(this.focusClass);
37198         }
37199         if(!this.hasFocus){
37200             this.hasFocus = true;
37201             this.startValue = this.getValue();
37202             this.fireEvent("focus", this);
37203         }
37204     },
37205
37206     beforeBlur : Roo.emptyFn,
37207
37208     // private
37209     onBlur : function(){
37210         this.beforeBlur();
37211         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
37212             this.el.removeClass(this.focusClass);
37213         }
37214         this.hasFocus = false;
37215         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
37216             this.validate();
37217         }
37218         var v = this.getValue();
37219         if(String(v) !== String(this.startValue)){
37220             this.fireEvent('change', this, v, this.startValue);
37221         }
37222         this.fireEvent("blur", this);
37223     },
37224
37225     /**
37226      * Returns whether or not the field value is currently valid
37227      * @param {Boolean} preventMark True to disable marking the field invalid
37228      * @return {Boolean} True if the value is valid, else false
37229      */
37230     isValid : function(preventMark){
37231         if(this.disabled){
37232             return true;
37233         }
37234         var restore = this.preventMark;
37235         this.preventMark = preventMark === true;
37236         var v = this.validateValue(this.processValue(this.getRawValue()));
37237         this.preventMark = restore;
37238         return v;
37239     },
37240
37241     /**
37242      * Validates the field value
37243      * @return {Boolean} True if the value is valid, else false
37244      */
37245     validate : function(){
37246         if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
37247             this.clearInvalid();
37248             return true;
37249         }
37250         return false;
37251     },
37252
37253     processValue : function(value){
37254         return value;
37255     },
37256
37257     // private
37258     // Subclasses should provide the validation implementation by overriding this
37259     validateValue : function(value){
37260         return true;
37261     },
37262
37263     /**
37264      * Mark this field as invalid
37265      * @param {String} msg The validation message
37266      */
37267     markInvalid : function(msg){
37268         if(!this.rendered || this.preventMark){ // not rendered
37269             return;
37270         }
37271         
37272         var obj = (typeof(this.combo) != 'undefined') ? this.combo : this; // fix the combox array!!
37273         
37274         obj.el.addClass(this.invalidClass);
37275         msg = msg || this.invalidText;
37276         switch(this.msgTarget){
37277             case 'qtip':
37278                 obj.el.dom.qtip = msg;
37279                 obj.el.dom.qclass = 'x-form-invalid-tip';
37280                 if(Roo.QuickTips){ // fix for floating editors interacting with DND
37281                     Roo.QuickTips.enable();
37282                 }
37283                 break;
37284             case 'title':
37285                 this.el.dom.title = msg;
37286                 break;
37287             case 'under':
37288                 if(!this.errorEl){
37289                     var elp = this.el.findParent('.x-form-element', 5, true);
37290                     this.errorEl = elp.createChild({cls:'x-form-invalid-msg'});
37291                     this.errorEl.setWidth(elp.getWidth(true)-20);
37292                 }
37293                 this.errorEl.update(msg);
37294                 Roo.form.Field.msgFx[this.msgFx].show(this.errorEl, this);
37295                 break;
37296             case 'side':
37297                 if(!this.errorIcon){
37298                     var elp = this.el.findParent('.x-form-element', 5, true);
37299                     this.errorIcon = elp.createChild({cls:'x-form-invalid-icon'});
37300                 }
37301                 this.alignErrorIcon();
37302                 this.errorIcon.dom.qtip = msg;
37303                 this.errorIcon.dom.qclass = 'x-form-invalid-tip';
37304                 this.errorIcon.show();
37305                 this.on('resize', this.alignErrorIcon, this);
37306                 break;
37307             default:
37308                 var t = Roo.getDom(this.msgTarget);
37309                 t.innerHTML = msg;
37310                 t.style.display = this.msgDisplay;
37311                 break;
37312         }
37313         this.fireEvent('invalid', this, msg);
37314     },
37315
37316     // private
37317     alignErrorIcon : function(){
37318         this.errorIcon.alignTo(this.el, 'tl-tr', [2, 0]);
37319     },
37320
37321     /**
37322      * Clear any invalid styles/messages for this field
37323      */
37324     clearInvalid : function(){
37325         if(!this.rendered || this.preventMark){ // not rendered
37326             return;
37327         }
37328         var obj = (typeof(this.combo) != 'undefined') ? this.combo : this; // fix the combox array!!
37329         
37330         obj.el.removeClass(this.invalidClass);
37331         switch(this.msgTarget){
37332             case 'qtip':
37333                 obj.el.dom.qtip = '';
37334                 break;
37335             case 'title':
37336                 this.el.dom.title = '';
37337                 break;
37338             case 'under':
37339                 if(this.errorEl){
37340                     Roo.form.Field.msgFx[this.msgFx].hide(this.errorEl, this);
37341                 }
37342                 break;
37343             case 'side':
37344                 if(this.errorIcon){
37345                     this.errorIcon.dom.qtip = '';
37346                     this.errorIcon.hide();
37347                     this.un('resize', this.alignErrorIcon, this);
37348                 }
37349                 break;
37350             default:
37351                 var t = Roo.getDom(this.msgTarget);
37352                 t.innerHTML = '';
37353                 t.style.display = 'none';
37354                 break;
37355         }
37356         this.fireEvent('valid', this);
37357     },
37358
37359     /**
37360      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
37361      * @return {Mixed} value The field value
37362      */
37363     getRawValue : function(){
37364         var v = this.el.getValue();
37365         
37366         return v;
37367     },
37368
37369     /**
37370      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
37371      * @return {Mixed} value The field value
37372      */
37373     getValue : function(){
37374         var v = this.el.getValue();
37375          
37376         return v;
37377     },
37378
37379     /**
37380      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
37381      * @param {Mixed} value The value to set
37382      */
37383     setRawValue : function(v){
37384         return this.el.dom.value = (v === null || v === undefined ? '' : v);
37385     },
37386
37387     /**
37388      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
37389      * @param {Mixed} value The value to set
37390      */
37391     setValue : function(v){
37392         this.value = v;
37393         if(this.rendered){
37394             this.el.dom.value = (v === null || v === undefined ? '' : v);
37395              this.validate();
37396         }
37397     },
37398
37399     adjustSize : function(w, h){
37400         var s = Roo.form.Field.superclass.adjustSize.call(this, w, h);
37401         s.width = this.adjustWidth(this.el.dom.tagName, s.width);
37402         return s;
37403     },
37404
37405     adjustWidth : function(tag, w){
37406         tag = tag.toLowerCase();
37407         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
37408             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
37409                 if(tag == 'input'){
37410                     return w + 2;
37411                 }
37412                 if(tag == 'textarea'){
37413                     return w-2;
37414                 }
37415             }else if(Roo.isOpera){
37416                 if(tag == 'input'){
37417                     return w + 2;
37418                 }
37419                 if(tag == 'textarea'){
37420                     return w-2;
37421                 }
37422             }
37423         }
37424         return w;
37425     }
37426 });
37427
37428
37429 // anything other than normal should be considered experimental
37430 Roo.form.Field.msgFx = {
37431     normal : {
37432         show: function(msgEl, f){
37433             msgEl.setDisplayed('block');
37434         },
37435
37436         hide : function(msgEl, f){
37437             msgEl.setDisplayed(false).update('');
37438         }
37439     },
37440
37441     slide : {
37442         show: function(msgEl, f){
37443             msgEl.slideIn('t', {stopFx:true});
37444         },
37445
37446         hide : function(msgEl, f){
37447             msgEl.slideOut('t', {stopFx:true,useDisplay:true});
37448         }
37449     },
37450
37451     slideRight : {
37452         show: function(msgEl, f){
37453             msgEl.fixDisplay();
37454             msgEl.alignTo(f.el, 'tl-tr');
37455             msgEl.slideIn('l', {stopFx:true});
37456         },
37457
37458         hide : function(msgEl, f){
37459             msgEl.slideOut('l', {stopFx:true,useDisplay:true});
37460         }
37461     }
37462 };/*
37463  * Based on:
37464  * Ext JS Library 1.1.1
37465  * Copyright(c) 2006-2007, Ext JS, LLC.
37466  *
37467  * Originally Released Under LGPL - original licence link has changed is not relivant.
37468  *
37469  * Fork - LGPL
37470  * <script type="text/javascript">
37471  */
37472  
37473
37474 /**
37475  * @class Roo.form.TextField
37476  * @extends Roo.form.Field
37477  * Basic text field.  Can be used as a direct replacement for traditional text inputs, or as the base
37478  * class for more sophisticated input controls (like {@link Roo.form.TextArea} and {@link Roo.form.ComboBox}).
37479  * @constructor
37480  * Creates a new TextField
37481  * @param {Object} config Configuration options
37482  */
37483 Roo.form.TextField = function(config){
37484     Roo.form.TextField.superclass.constructor.call(this, config);
37485     this.addEvents({
37486         /**
37487          * @event autosize
37488          * Fires when the autosize function is triggered.  The field may or may not have actually changed size
37489          * according to the default logic, but this event provides a hook for the developer to apply additional
37490          * logic at runtime to resize the field if needed.
37491              * @param {Roo.form.Field} this This text field
37492              * @param {Number} width The new field width
37493              */
37494         autosize : true
37495     });
37496 };
37497
37498 Roo.extend(Roo.form.TextField, Roo.form.Field,  {
37499     /**
37500      * @cfg {Boolean} grow True if this field should automatically grow and shrink to its content
37501      */
37502     grow : false,
37503     /**
37504      * @cfg {Number} growMin The minimum width to allow when grow = true (defaults to 30)
37505      */
37506     growMin : 30,
37507     /**
37508      * @cfg {Number} growMax The maximum width to allow when grow = true (defaults to 800)
37509      */
37510     growMax : 800,
37511     /**
37512      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
37513      */
37514     vtype : null,
37515     /**
37516      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
37517      */
37518     maskRe : null,
37519     /**
37520      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
37521      */
37522     disableKeyFilter : false,
37523     /**
37524      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
37525      */
37526     allowBlank : true,
37527     /**
37528      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
37529      */
37530     minLength : 0,
37531     /**
37532      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
37533      */
37534     maxLength : Number.MAX_VALUE,
37535     /**
37536      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
37537      */
37538     minLengthText : "The minimum length for this field is {0}",
37539     /**
37540      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
37541      */
37542     maxLengthText : "The maximum length for this field is {0}",
37543     /**
37544      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
37545      */
37546     selectOnFocus : false,
37547     /**
37548      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
37549      */
37550     blankText : "This field is required",
37551     /**
37552      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
37553      * If available, this function will be called only after the basic validators all return true, and will be passed the
37554      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
37555      */
37556     validator : null,
37557     /**
37558      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
37559      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
37560      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
37561      */
37562     regex : null,
37563     /**
37564      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
37565      */
37566     regexText : "",
37567     /**
37568      * @cfg {String} emptyText The default text to display in an empty field - placeholder... (defaults to null).
37569      */
37570     emptyText : null,
37571    
37572
37573     // private
37574     initEvents : function()
37575     {
37576         if (this.emptyText) {
37577             this.el.attr('placeholder', this.emptyText);
37578         }
37579         
37580         Roo.form.TextField.superclass.initEvents.call(this);
37581         if(this.validationEvent == 'keyup'){
37582             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
37583             this.el.on('keyup', this.filterValidation, this);
37584         }
37585         else if(this.validationEvent !== false){
37586             this.el.on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
37587         }
37588         
37589         if(this.selectOnFocus){
37590             this.on("focus", this.preFocus, this);
37591             
37592         }
37593         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
37594             this.el.on("keypress", this.filterKeys, this);
37595         }
37596         if(this.grow){
37597             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
37598             this.el.on("click", this.autoSize,  this);
37599         }
37600         if(this.el.is('input[type=password]') && Roo.isSafari){
37601             this.el.on('keydown', this.SafariOnKeyDown, this);
37602         }
37603     },
37604
37605     processValue : function(value){
37606         if(this.stripCharsRe){
37607             var newValue = value.replace(this.stripCharsRe, '');
37608             if(newValue !== value){
37609                 this.setRawValue(newValue);
37610                 return newValue;
37611             }
37612         }
37613         return value;
37614     },
37615
37616     filterValidation : function(e){
37617         if(!e.isNavKeyPress()){
37618             this.validationTask.delay(this.validationDelay);
37619         }
37620     },
37621
37622     // private
37623     onKeyUp : function(e){
37624         if(!e.isNavKeyPress()){
37625             this.autoSize();
37626         }
37627     },
37628
37629     /**
37630      * Resets the current field value to the originally-loaded value and clears any validation messages.
37631      *  
37632      */
37633     reset : function(){
37634         Roo.form.TextField.superclass.reset.call(this);
37635        
37636     },
37637
37638     
37639     // private
37640     preFocus : function(){
37641         
37642         if(this.selectOnFocus){
37643             this.el.dom.select();
37644         }
37645     },
37646
37647     
37648     // private
37649     filterKeys : function(e){
37650         var k = e.getKey();
37651         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
37652             return;
37653         }
37654         var c = e.getCharCode(), cc = String.fromCharCode(c);
37655         if(Roo.isIE && (e.isSpecialKey() || !cc)){
37656             return;
37657         }
37658         if(!this.maskRe.test(cc)){
37659             e.stopEvent();
37660         }
37661     },
37662
37663     setValue : function(v){
37664         
37665         Roo.form.TextField.superclass.setValue.apply(this, arguments);
37666         
37667         this.autoSize();
37668     },
37669
37670     /**
37671      * Validates a value according to the field's validation rules and marks the field as invalid
37672      * if the validation fails
37673      * @param {Mixed} value The value to validate
37674      * @return {Boolean} True if the value is valid, else false
37675      */
37676     validateValue : function(value){
37677         if(value.length < 1)  { // if it's blank
37678              if(this.allowBlank){
37679                 this.clearInvalid();
37680                 return true;
37681              }else{
37682                 this.markInvalid(this.blankText);
37683                 return false;
37684              }
37685         }
37686         if(value.length < this.minLength){
37687             this.markInvalid(String.format(this.minLengthText, this.minLength));
37688             return false;
37689         }
37690         if(value.length > this.maxLength){
37691             this.markInvalid(String.format(this.maxLengthText, this.maxLength));
37692             return false;
37693         }
37694         if(this.vtype){
37695             var vt = Roo.form.VTypes;
37696             if(!vt[this.vtype](value, this)){
37697                 this.markInvalid(this.vtypeText || vt[this.vtype +'Text']);
37698                 return false;
37699             }
37700         }
37701         if(typeof this.validator == "function"){
37702             var msg = this.validator(value);
37703             if(msg !== true){
37704                 this.markInvalid(msg);
37705                 return false;
37706             }
37707         }
37708         if(this.regex && !this.regex.test(value)){
37709             this.markInvalid(this.regexText);
37710             return false;
37711         }
37712         return true;
37713     },
37714
37715     /**
37716      * Selects text in this field
37717      * @param {Number} start (optional) The index where the selection should start (defaults to 0)
37718      * @param {Number} end (optional) The index where the selection should end (defaults to the text length)
37719      */
37720     selectText : function(start, end){
37721         var v = this.getRawValue();
37722         if(v.length > 0){
37723             start = start === undefined ? 0 : start;
37724             end = end === undefined ? v.length : end;
37725             var d = this.el.dom;
37726             if(d.setSelectionRange){
37727                 d.setSelectionRange(start, end);
37728             }else if(d.createTextRange){
37729                 var range = d.createTextRange();
37730                 range.moveStart("character", start);
37731                 range.moveEnd("character", v.length-end);
37732                 range.select();
37733             }
37734         }
37735     },
37736
37737     /**
37738      * Automatically grows the field to accomodate the width of the text up to the maximum field width allowed.
37739      * This only takes effect if grow = true, and fires the autosize event.
37740      */
37741     autoSize : function(){
37742         if(!this.grow || !this.rendered){
37743             return;
37744         }
37745         if(!this.metrics){
37746             this.metrics = Roo.util.TextMetrics.createInstance(this.el);
37747         }
37748         var el = this.el;
37749         var v = el.dom.value;
37750         var d = document.createElement('div');
37751         d.appendChild(document.createTextNode(v));
37752         v = d.innerHTML;
37753         d = null;
37754         v += "&#160;";
37755         var w = Math.min(this.growMax, Math.max(this.metrics.getWidth(v) + /* add extra padding */ 10, this.growMin));
37756         this.el.setWidth(w);
37757         this.fireEvent("autosize", this, w);
37758     },
37759     
37760     // private
37761     SafariOnKeyDown : function(event)
37762     {
37763         // this is a workaround for a password hang bug on chrome/ webkit.
37764         
37765         var isSelectAll = false;
37766         
37767         if(this.el.dom.selectionEnd > 0){
37768             isSelectAll = (this.el.dom.selectionEnd - this.el.dom.selectionStart - this.getValue().length == 0) ? true : false;
37769         }
37770         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
37771             event.preventDefault();
37772             this.setValue('');
37773             return;
37774         }
37775         
37776         if(isSelectAll){ // backspace and delete key
37777             
37778             event.preventDefault();
37779             // this is very hacky as keydown always get's upper case.
37780             //
37781             var cc = String.fromCharCode(event.getCharCode());
37782             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
37783             
37784         }
37785         
37786         
37787     }
37788 });/*
37789  * Based on:
37790  * Ext JS Library 1.1.1
37791  * Copyright(c) 2006-2007, Ext JS, LLC.
37792  *
37793  * Originally Released Under LGPL - original licence link has changed is not relivant.
37794  *
37795  * Fork - LGPL
37796  * <script type="text/javascript">
37797  */
37798  
37799 /**
37800  * @class Roo.form.Hidden
37801  * @extends Roo.form.TextField
37802  * Simple Hidden element used on forms 
37803  * 
37804  * usage: form.add(new Roo.form.HiddenField({ 'name' : 'test1' }));
37805  * 
37806  * @constructor
37807  * Creates a new Hidden form element.
37808  * @param {Object} config Configuration options
37809  */
37810
37811
37812
37813 // easy hidden field...
37814 Roo.form.Hidden = function(config){
37815     Roo.form.Hidden.superclass.constructor.call(this, config);
37816 };
37817   
37818 Roo.extend(Roo.form.Hidden, Roo.form.TextField, {
37819     fieldLabel:      '',
37820     inputType:      'hidden',
37821     width:          50,
37822     allowBlank:     true,
37823     labelSeparator: '',
37824     hidden:         true,
37825     itemCls :       'x-form-item-display-none'
37826
37827
37828 });
37829
37830
37831 /*
37832  * Based on:
37833  * Ext JS Library 1.1.1
37834  * Copyright(c) 2006-2007, Ext JS, LLC.
37835  *
37836  * Originally Released Under LGPL - original licence link has changed is not relivant.
37837  *
37838  * Fork - LGPL
37839  * <script type="text/javascript">
37840  */
37841  
37842 /**
37843  * @class Roo.form.TriggerField
37844  * @extends Roo.form.TextField
37845  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
37846  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
37847  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
37848  * for which you can provide a custom implementation.  For example:
37849  * <pre><code>
37850 var trigger = new Roo.form.TriggerField();
37851 trigger.onTriggerClick = myTriggerFn;
37852 trigger.applyTo('my-field');
37853 </code></pre>
37854  *
37855  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
37856  * {@link Roo.form.DateField} and {@link Roo.form.ComboBox} are perfect examples of this.
37857  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
37858  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
37859  * @constructor
37860  * Create a new TriggerField.
37861  * @param {Object} config Configuration options (valid {@Roo.form.TextField} config options will also be applied
37862  * to the base TextField)
37863  */
37864 Roo.form.TriggerField = function(config){
37865     this.mimicing = false;
37866     Roo.form.TriggerField.superclass.constructor.call(this, config);
37867 };
37868
37869 Roo.extend(Roo.form.TriggerField, Roo.form.TextField,  {
37870     /**
37871      * @cfg {String} triggerClass A CSS class to apply to the trigger
37872      */
37873     /**
37874      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
37875      * {tag: "input", type: "text", size: "16", autocomplete: "off"})
37876      */
37877     defaultAutoCreate : {tag: "input", type: "text", size: "16", autocomplete: "off"},
37878     /**
37879      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
37880      */
37881     hideTrigger:false,
37882
37883     /** @cfg {Boolean} grow @hide */
37884     /** @cfg {Number} growMin @hide */
37885     /** @cfg {Number} growMax @hide */
37886
37887     /**
37888      * @hide 
37889      * @method
37890      */
37891     autoSize: Roo.emptyFn,
37892     // private
37893     monitorTab : true,
37894     // private
37895     deferHeight : true,
37896
37897     
37898     actionMode : 'wrap',
37899     // private
37900     onResize : function(w, h){
37901         Roo.form.TriggerField.superclass.onResize.apply(this, arguments);
37902         if(typeof w == 'number'){
37903             var x = w - this.trigger.getWidth();
37904             this.el.setWidth(this.adjustWidth('input', x));
37905             this.trigger.setStyle('left', x+'px');
37906         }
37907     },
37908
37909     // private
37910     adjustSize : Roo.BoxComponent.prototype.adjustSize,
37911
37912     // private
37913     getResizeEl : function(){
37914         return this.wrap;
37915     },
37916
37917     // private
37918     getPositionEl : function(){
37919         return this.wrap;
37920     },
37921
37922     // private
37923     alignErrorIcon : function(){
37924         this.errorIcon.alignTo(this.wrap, 'tl-tr', [2, 0]);
37925     },
37926
37927     // private
37928     onRender : function(ct, position){
37929         Roo.form.TriggerField.superclass.onRender.call(this, ct, position);
37930         this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
37931         this.trigger = this.wrap.createChild(this.triggerConfig ||
37932                 {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.triggerClass});
37933         if(this.hideTrigger){
37934             this.trigger.setDisplayed(false);
37935         }
37936         this.initTrigger();
37937         if(!this.width){
37938             this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
37939         }
37940     },
37941
37942     // private
37943     initTrigger : function(){
37944         this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
37945         this.trigger.addClassOnOver('x-form-trigger-over');
37946         this.trigger.addClassOnClick('x-form-trigger-click');
37947     },
37948
37949     // private
37950     onDestroy : function(){
37951         if(this.trigger){
37952             this.trigger.removeAllListeners();
37953             this.trigger.remove();
37954         }
37955         if(this.wrap){
37956             this.wrap.remove();
37957         }
37958         Roo.form.TriggerField.superclass.onDestroy.call(this);
37959     },
37960
37961     // private
37962     onFocus : function(){
37963         Roo.form.TriggerField.superclass.onFocus.call(this);
37964         if(!this.mimicing){
37965             this.wrap.addClass('x-trigger-wrap-focus');
37966             this.mimicing = true;
37967             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
37968             if(this.monitorTab){
37969                 this.el.on("keydown", this.checkTab, this);
37970             }
37971         }
37972     },
37973
37974     // private
37975     checkTab : function(e){
37976         if(e.getKey() == e.TAB){
37977             this.triggerBlur();
37978         }
37979     },
37980
37981     // private
37982     onBlur : function(){
37983         // do nothing
37984     },
37985
37986     // private
37987     mimicBlur : function(e, t){
37988         if(!this.wrap.contains(t) && this.validateBlur()){
37989             this.triggerBlur();
37990         }
37991     },
37992
37993     // private
37994     triggerBlur : function(){
37995         this.mimicing = false;
37996         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
37997         if(this.monitorTab){
37998             this.el.un("keydown", this.checkTab, this);
37999         }
38000         this.wrap.removeClass('x-trigger-wrap-focus');
38001         Roo.form.TriggerField.superclass.onBlur.call(this);
38002     },
38003
38004     // private
38005     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
38006     validateBlur : function(e, t){
38007         return true;
38008     },
38009
38010     // private
38011     onDisable : function(){
38012         Roo.form.TriggerField.superclass.onDisable.call(this);
38013         if(this.wrap){
38014             this.wrap.addClass('x-item-disabled');
38015         }
38016     },
38017
38018     // private
38019     onEnable : function(){
38020         Roo.form.TriggerField.superclass.onEnable.call(this);
38021         if(this.wrap){
38022             this.wrap.removeClass('x-item-disabled');
38023         }
38024     },
38025
38026     // private
38027     onShow : function(){
38028         var ae = this.getActionEl();
38029         
38030         if(ae){
38031             ae.dom.style.display = '';
38032             ae.dom.style.visibility = 'visible';
38033         }
38034     },
38035
38036     // private
38037     
38038     onHide : function(){
38039         var ae = this.getActionEl();
38040         ae.dom.style.display = 'none';
38041     },
38042
38043     /**
38044      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
38045      * by an implementing function.
38046      * @method
38047      * @param {EventObject} e
38048      */
38049     onTriggerClick : Roo.emptyFn
38050 });
38051
38052 // TwinTriggerField is not a public class to be used directly.  It is meant as an abstract base class
38053 // to be extended by an implementing class.  For an example of implementing this class, see the custom
38054 // SearchField implementation here: http://extjs.com/deploy/ext/examples/form/custom.html
38055 Roo.form.TwinTriggerField = Roo.extend(Roo.form.TriggerField, {
38056     initComponent : function(){
38057         Roo.form.TwinTriggerField.superclass.initComponent.call(this);
38058
38059         this.triggerConfig = {
38060             tag:'span', cls:'x-form-twin-triggers', cn:[
38061             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger1Class},
38062             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger2Class}
38063         ]};
38064     },
38065
38066     getTrigger : function(index){
38067         return this.triggers[index];
38068     },
38069
38070     initTrigger : function(){
38071         var ts = this.trigger.select('.x-form-trigger', true);
38072         this.wrap.setStyle('overflow', 'hidden');
38073         var triggerField = this;
38074         ts.each(function(t, all, index){
38075             t.hide = function(){
38076                 var w = triggerField.wrap.getWidth();
38077                 this.dom.style.display = 'none';
38078                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
38079             };
38080             t.show = function(){
38081                 var w = triggerField.wrap.getWidth();
38082                 this.dom.style.display = '';
38083                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
38084             };
38085             var triggerIndex = 'Trigger'+(index+1);
38086
38087             if(this['hide'+triggerIndex]){
38088                 t.dom.style.display = 'none';
38089             }
38090             t.on("click", this['on'+triggerIndex+'Click'], this, {preventDefault:true});
38091             t.addClassOnOver('x-form-trigger-over');
38092             t.addClassOnClick('x-form-trigger-click');
38093         }, this);
38094         this.triggers = ts.elements;
38095     },
38096
38097     onTrigger1Click : Roo.emptyFn,
38098     onTrigger2Click : Roo.emptyFn
38099 });/*
38100  * Based on:
38101  * Ext JS Library 1.1.1
38102  * Copyright(c) 2006-2007, Ext JS, LLC.
38103  *
38104  * Originally Released Under LGPL - original licence link has changed is not relivant.
38105  *
38106  * Fork - LGPL
38107  * <script type="text/javascript">
38108  */
38109  
38110 /**
38111  * @class Roo.form.TextArea
38112  * @extends Roo.form.TextField
38113  * Multiline text field.  Can be used as a direct replacement for traditional textarea fields, plus adds
38114  * support for auto-sizing.
38115  * @constructor
38116  * Creates a new TextArea
38117  * @param {Object} config Configuration options
38118  */
38119 Roo.form.TextArea = function(config){
38120     Roo.form.TextArea.superclass.constructor.call(this, config);
38121     // these are provided exchanges for backwards compat
38122     // minHeight/maxHeight were replaced by growMin/growMax to be
38123     // compatible with TextField growing config values
38124     if(this.minHeight !== undefined){
38125         this.growMin = this.minHeight;
38126     }
38127     if(this.maxHeight !== undefined){
38128         this.growMax = this.maxHeight;
38129     }
38130 };
38131
38132 Roo.extend(Roo.form.TextArea, Roo.form.TextField,  {
38133     /**
38134      * @cfg {Number} growMin The minimum height to allow when grow = true (defaults to 60)
38135      */
38136     growMin : 60,
38137     /**
38138      * @cfg {Number} growMax The maximum height to allow when grow = true (defaults to 1000)
38139      */
38140     growMax: 1000,
38141     /**
38142      * @cfg {Boolean} preventScrollbars True to prevent scrollbars from appearing regardless of how much text is
38143      * in the field (equivalent to setting overflow: hidden, defaults to false)
38144      */
38145     preventScrollbars: false,
38146     /**
38147      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
38148      * {tag: "textarea", style: "width:300px;height:60px;", autocomplete: "off"})
38149      */
38150
38151     // private
38152     onRender : function(ct, position){
38153         if(!this.el){
38154             this.defaultAutoCreate = {
38155                 tag: "textarea",
38156                 style:"width:300px;height:60px;",
38157                 autocomplete: "off"
38158             };
38159         }
38160         Roo.form.TextArea.superclass.onRender.call(this, ct, position);
38161         if(this.grow){
38162             this.textSizeEl = Roo.DomHelper.append(document.body, {
38163                 tag: "pre", cls: "x-form-grow-sizer"
38164             });
38165             if(this.preventScrollbars){
38166                 this.el.setStyle("overflow", "hidden");
38167             }
38168             this.el.setHeight(this.growMin);
38169         }
38170     },
38171
38172     onDestroy : function(){
38173         if(this.textSizeEl){
38174             this.textSizeEl.parentNode.removeChild(this.textSizeEl);
38175         }
38176         Roo.form.TextArea.superclass.onDestroy.call(this);
38177     },
38178
38179     // private
38180     onKeyUp : function(e){
38181         if(!e.isNavKeyPress() || e.getKey() == e.ENTER){
38182             this.autoSize();
38183         }
38184     },
38185
38186     /**
38187      * Automatically grows the field to accomodate the height of the text up to the maximum field height allowed.
38188      * This only takes effect if grow = true, and fires the autosize event if the height changes.
38189      */
38190     autoSize : function(){
38191         if(!this.grow || !this.textSizeEl){
38192             return;
38193         }
38194         var el = this.el;
38195         var v = el.dom.value;
38196         var ts = this.textSizeEl;
38197
38198         ts.innerHTML = '';
38199         ts.appendChild(document.createTextNode(v));
38200         v = ts.innerHTML;
38201
38202         Roo.fly(ts).setWidth(this.el.getWidth());
38203         if(v.length < 1){
38204             v = "&#160;&#160;";
38205         }else{
38206             if(Roo.isIE){
38207                 v = v.replace(/\n/g, '<p>&#160;</p>');
38208             }
38209             v += "&#160;\n&#160;";
38210         }
38211         ts.innerHTML = v;
38212         var h = Math.min(this.growMax, Math.max(ts.offsetHeight, this.growMin));
38213         if(h != this.lastHeight){
38214             this.lastHeight = h;
38215             this.el.setHeight(h);
38216             this.fireEvent("autosize", this, h);
38217         }
38218     }
38219 });/*
38220  * Based on:
38221  * Ext JS Library 1.1.1
38222  * Copyright(c) 2006-2007, Ext JS, LLC.
38223  *
38224  * Originally Released Under LGPL - original licence link has changed is not relivant.
38225  *
38226  * Fork - LGPL
38227  * <script type="text/javascript">
38228  */
38229  
38230
38231 /**
38232  * @class Roo.form.NumberField
38233  * @extends Roo.form.TextField
38234  * Numeric text field that provides automatic keystroke filtering and numeric validation.
38235  * @constructor
38236  * Creates a new NumberField
38237  * @param {Object} config Configuration options
38238  */
38239 Roo.form.NumberField = function(config){
38240     Roo.form.NumberField.superclass.constructor.call(this, config);
38241 };
38242
38243 Roo.extend(Roo.form.NumberField, Roo.form.TextField,  {
38244     /**
38245      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field x-form-num-field")
38246      */
38247     fieldClass: "x-form-field x-form-num-field",
38248     /**
38249      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
38250      */
38251     allowDecimals : true,
38252     /**
38253      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
38254      */
38255     decimalSeparator : ".",
38256     /**
38257      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
38258      */
38259     decimalPrecision : 2,
38260     /**
38261      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
38262      */
38263     allowNegative : true,
38264     /**
38265      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
38266      */
38267     minValue : Number.NEGATIVE_INFINITY,
38268     /**
38269      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
38270      */
38271     maxValue : Number.MAX_VALUE,
38272     /**
38273      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
38274      */
38275     minText : "The minimum value for this field is {0}",
38276     /**
38277      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
38278      */
38279     maxText : "The maximum value for this field is {0}",
38280     /**
38281      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
38282      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
38283      */
38284     nanText : "{0} is not a valid number",
38285
38286     // private
38287     initEvents : function(){
38288         Roo.form.NumberField.superclass.initEvents.call(this);
38289         var allowed = "0123456789";
38290         if(this.allowDecimals){
38291             allowed += this.decimalSeparator;
38292         }
38293         if(this.allowNegative){
38294             allowed += "-";
38295         }
38296         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
38297         var keyPress = function(e){
38298             var k = e.getKey();
38299             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
38300                 return;
38301             }
38302             var c = e.getCharCode();
38303             if(allowed.indexOf(String.fromCharCode(c)) === -1){
38304                 e.stopEvent();
38305             }
38306         };
38307         this.el.on("keypress", keyPress, this);
38308     },
38309
38310     // private
38311     validateValue : function(value){
38312         if(!Roo.form.NumberField.superclass.validateValue.call(this, value)){
38313             return false;
38314         }
38315         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
38316              return true;
38317         }
38318         var num = this.parseValue(value);
38319         if(isNaN(num)){
38320             this.markInvalid(String.format(this.nanText, value));
38321             return false;
38322         }
38323         if(num < this.minValue){
38324             this.markInvalid(String.format(this.minText, this.minValue));
38325             return false;
38326         }
38327         if(num > this.maxValue){
38328             this.markInvalid(String.format(this.maxText, this.maxValue));
38329             return false;
38330         }
38331         return true;
38332     },
38333
38334     getValue : function(){
38335         return this.fixPrecision(this.parseValue(Roo.form.NumberField.superclass.getValue.call(this)));
38336     },
38337
38338     // private
38339     parseValue : function(value){
38340         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
38341         return isNaN(value) ? '' : value;
38342     },
38343
38344     // private
38345     fixPrecision : function(value){
38346         var nan = isNaN(value);
38347         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
38348             return nan ? '' : value;
38349         }
38350         return parseFloat(value).toFixed(this.decimalPrecision);
38351     },
38352
38353     setValue : function(v){
38354         v = this.fixPrecision(v);
38355         Roo.form.NumberField.superclass.setValue.call(this, String(v).replace(".", this.decimalSeparator));
38356     },
38357
38358     // private
38359     decimalPrecisionFcn : function(v){
38360         return Math.floor(v);
38361     },
38362
38363     beforeBlur : function(){
38364         var v = this.parseValue(this.getRawValue());
38365         if(v){
38366             this.setValue(v);
38367         }
38368     }
38369 });/*
38370  * Based on:
38371  * Ext JS Library 1.1.1
38372  * Copyright(c) 2006-2007, Ext JS, LLC.
38373  *
38374  * Originally Released Under LGPL - original licence link has changed is not relivant.
38375  *
38376  * Fork - LGPL
38377  * <script type="text/javascript">
38378  */
38379  
38380 /**
38381  * @class Roo.form.DateField
38382  * @extends Roo.form.TriggerField
38383  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
38384 * @constructor
38385 * Create a new DateField
38386 * @param {Object} config
38387  */
38388 Roo.form.DateField = function(config){
38389     Roo.form.DateField.superclass.constructor.call(this, config);
38390     
38391       this.addEvents({
38392          
38393         /**
38394          * @event select
38395          * Fires when a date is selected
38396              * @param {Roo.form.DateField} combo This combo box
38397              * @param {Date} date The date selected
38398              */
38399         'select' : true
38400          
38401     });
38402     
38403     
38404     if(typeof this.minValue == "string") this.minValue = this.parseDate(this.minValue);
38405     if(typeof this.maxValue == "string") this.maxValue = this.parseDate(this.maxValue);
38406     this.ddMatch = null;
38407     if(this.disabledDates){
38408         var dd = this.disabledDates;
38409         var re = "(?:";
38410         for(var i = 0; i < dd.length; i++){
38411             re += dd[i];
38412             if(i != dd.length-1) re += "|";
38413         }
38414         this.ddMatch = new RegExp(re + ")");
38415     }
38416 };
38417
38418 Roo.extend(Roo.form.DateField, Roo.form.TriggerField,  {
38419     /**
38420      * @cfg {String} format
38421      * The default date format string which can be overriden for localization support.  The format must be
38422      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
38423      */
38424     format : "m/d/y",
38425     /**
38426      * @cfg {String} altFormats
38427      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
38428      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
38429      */
38430     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
38431     /**
38432      * @cfg {Array} disabledDays
38433      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
38434      */
38435     disabledDays : null,
38436     /**
38437      * @cfg {String} disabledDaysText
38438      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
38439      */
38440     disabledDaysText : "Disabled",
38441     /**
38442      * @cfg {Array} disabledDates
38443      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
38444      * expression so they are very powerful. Some examples:
38445      * <ul>
38446      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
38447      * <li>["03/08", "09/16"] would disable those days for every year</li>
38448      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
38449      * <li>["03/../2006"] would disable every day in March 2006</li>
38450      * <li>["^03"] would disable every day in every March</li>
38451      * </ul>
38452      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
38453      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
38454      */
38455     disabledDates : null,
38456     /**
38457      * @cfg {String} disabledDatesText
38458      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
38459      */
38460     disabledDatesText : "Disabled",
38461     /**
38462      * @cfg {Date/String} minValue
38463      * The minimum allowed date. Can be either a Javascript date object or a string date in a
38464      * valid format (defaults to null).
38465      */
38466     minValue : null,
38467     /**
38468      * @cfg {Date/String} maxValue
38469      * The maximum allowed date. Can be either a Javascript date object or a string date in a
38470      * valid format (defaults to null).
38471      */
38472     maxValue : null,
38473     /**
38474      * @cfg {String} minText
38475      * The error text to display when the date in the cell is before minValue (defaults to
38476      * 'The date in this field must be after {minValue}').
38477      */
38478     minText : "The date in this field must be equal to or after {0}",
38479     /**
38480      * @cfg {String} maxText
38481      * The error text to display when the date in the cell is after maxValue (defaults to
38482      * 'The date in this field must be before {maxValue}').
38483      */
38484     maxText : "The date in this field must be equal to or before {0}",
38485     /**
38486      * @cfg {String} invalidText
38487      * The error text to display when the date in the field is invalid (defaults to
38488      * '{value} is not a valid date - it must be in the format {format}').
38489      */
38490     invalidText : "{0} is not a valid date - it must be in the format {1}",
38491     /**
38492      * @cfg {String} triggerClass
38493      * An additional CSS class used to style the trigger button.  The trigger will always get the
38494      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
38495      * which displays a calendar icon).
38496      */
38497     triggerClass : 'x-form-date-trigger',
38498     
38499
38500     /**
38501      * @cfg {Boolean} useIso
38502      * if enabled, then the date field will use a hidden field to store the 
38503      * real value as iso formated date. default (false)
38504      */ 
38505     useIso : false,
38506     /**
38507      * @cfg {String/Object} autoCreate
38508      * A DomHelper element spec, or true for a default element spec (defaults to
38509      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
38510      */ 
38511     // private
38512     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "off"},
38513     
38514     // private
38515     hiddenField: false,
38516     
38517     onRender : function(ct, position)
38518     {
38519         Roo.form.DateField.superclass.onRender.call(this, ct, position);
38520         if (this.useIso) {
38521             //this.el.dom.removeAttribute('name'); 
38522             Roo.log("Changing name?");
38523             this.el.dom.setAttribute('name', this.name + '____hidden___' ); 
38524             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
38525                     'before', true);
38526             this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
38527             // prevent input submission
38528             this.hiddenName = this.name;
38529         }
38530             
38531             
38532     },
38533     
38534     // private
38535     validateValue : function(value)
38536     {
38537         value = this.formatDate(value);
38538         if(!Roo.form.DateField.superclass.validateValue.call(this, value)){
38539             Roo.log('super failed');
38540             return false;
38541         }
38542         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
38543              return true;
38544         }
38545         var svalue = value;
38546         value = this.parseDate(value);
38547         if(!value){
38548             Roo.log('parse date failed' + svalue);
38549             this.markInvalid(String.format(this.invalidText, svalue, this.format));
38550             return false;
38551         }
38552         var time = value.getTime();
38553         if(this.minValue && time < this.minValue.getTime()){
38554             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
38555             return false;
38556         }
38557         if(this.maxValue && time > this.maxValue.getTime()){
38558             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
38559             return false;
38560         }
38561         if(this.disabledDays){
38562             var day = value.getDay();
38563             for(var i = 0; i < this.disabledDays.length; i++) {
38564                 if(day === this.disabledDays[i]){
38565                     this.markInvalid(this.disabledDaysText);
38566                     return false;
38567                 }
38568             }
38569         }
38570         var fvalue = this.formatDate(value);
38571         if(this.ddMatch && this.ddMatch.test(fvalue)){
38572             this.markInvalid(String.format(this.disabledDatesText, fvalue));
38573             return false;
38574         }
38575         return true;
38576     },
38577
38578     // private
38579     // Provides logic to override the default TriggerField.validateBlur which just returns true
38580     validateBlur : function(){
38581         return !this.menu || !this.menu.isVisible();
38582     },
38583     
38584     getName: function()
38585     {
38586         // returns hidden if it's set..
38587         if (!this.rendered) {return ''};
38588         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
38589         
38590     },
38591
38592     /**
38593      * Returns the current date value of the date field.
38594      * @return {Date} The date value
38595      */
38596     getValue : function(){
38597         
38598         return  this.hiddenField ?
38599                 this.hiddenField.value :
38600                 this.parseDate(Roo.form.DateField.superclass.getValue.call(this)) || "";
38601     },
38602
38603     /**
38604      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
38605      * date, using DateField.format as the date format, according to the same rules as {@link Date#parseDate}
38606      * (the default format used is "m/d/y").
38607      * <br />Usage:
38608      * <pre><code>
38609 //All of these calls set the same date value (May 4, 2006)
38610
38611 //Pass a date object:
38612 var dt = new Date('5/4/06');
38613 dateField.setValue(dt);
38614
38615 //Pass a date string (default format):
38616 dateField.setValue('5/4/06');
38617
38618 //Pass a date string (custom format):
38619 dateField.format = 'Y-m-d';
38620 dateField.setValue('2006-5-4');
38621 </code></pre>
38622      * @param {String/Date} date The date or valid date string
38623      */
38624     setValue : function(date){
38625         if (this.hiddenField) {
38626             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
38627         }
38628         Roo.form.DateField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
38629         // make sure the value field is always stored as a date..
38630         this.value = this.parseDate(date);
38631         
38632         
38633     },
38634
38635     // private
38636     parseDate : function(value){
38637         if(!value || value instanceof Date){
38638             return value;
38639         }
38640         var v = Date.parseDate(value, this.format);
38641          if (!v && this.useIso) {
38642             v = Date.parseDate(value, 'Y-m-d');
38643         }
38644         if(!v && this.altFormats){
38645             if(!this.altFormatsArray){
38646                 this.altFormatsArray = this.altFormats.split("|");
38647             }
38648             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
38649                 v = Date.parseDate(value, this.altFormatsArray[i]);
38650             }
38651         }
38652         return v;
38653     },
38654
38655     // private
38656     formatDate : function(date, fmt){
38657         return (!date || !(date instanceof Date)) ?
38658                date : date.dateFormat(fmt || this.format);
38659     },
38660
38661     // private
38662     menuListeners : {
38663         select: function(m, d){
38664             
38665             this.setValue(d);
38666             this.fireEvent('select', this, d);
38667         },
38668         show : function(){ // retain focus styling
38669             this.onFocus();
38670         },
38671         hide : function(){
38672             this.focus.defer(10, this);
38673             var ml = this.menuListeners;
38674             this.menu.un("select", ml.select,  this);
38675             this.menu.un("show", ml.show,  this);
38676             this.menu.un("hide", ml.hide,  this);
38677         }
38678     },
38679
38680     // private
38681     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
38682     onTriggerClick : function(){
38683         if(this.disabled){
38684             return;
38685         }
38686         if(this.menu == null){
38687             this.menu = new Roo.menu.DateMenu();
38688         }
38689         Roo.apply(this.menu.picker,  {
38690             showClear: this.allowBlank,
38691             minDate : this.minValue,
38692             maxDate : this.maxValue,
38693             disabledDatesRE : this.ddMatch,
38694             disabledDatesText : this.disabledDatesText,
38695             disabledDays : this.disabledDays,
38696             disabledDaysText : this.disabledDaysText,
38697             format : this.useIso ? 'Y-m-d' : this.format,
38698             minText : String.format(this.minText, this.formatDate(this.minValue)),
38699             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
38700         });
38701         this.menu.on(Roo.apply({}, this.menuListeners, {
38702             scope:this
38703         }));
38704         this.menu.picker.setValue(this.getValue() || new Date());
38705         this.menu.show(this.el, "tl-bl?");
38706     },
38707
38708     beforeBlur : function(){
38709         var v = this.parseDate(this.getRawValue());
38710         if(v){
38711             this.setValue(v);
38712         }
38713     },
38714
38715     /*@
38716      * overide
38717      * 
38718      */
38719     isDirty : function() {
38720         if(this.disabled) {
38721             return false;
38722         }
38723         
38724         if(typeof(this.startValue) === 'undefined'){
38725             return false;
38726         }
38727         
38728         return String(this.getValue()) !== String(this.startValue);
38729         
38730     }
38731 });/*
38732  * Based on:
38733  * Ext JS Library 1.1.1
38734  * Copyright(c) 2006-2007, Ext JS, LLC.
38735  *
38736  * Originally Released Under LGPL - original licence link has changed is not relivant.
38737  *
38738  * Fork - LGPL
38739  * <script type="text/javascript">
38740  */
38741  
38742 /**
38743  * @class Roo.form.MonthField
38744  * @extends Roo.form.TriggerField
38745  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
38746 * @constructor
38747 * Create a new MonthField
38748 * @param {Object} config
38749  */
38750 Roo.form.MonthField = function(config){
38751     
38752     Roo.form.MonthField.superclass.constructor.call(this, config);
38753     
38754       this.addEvents({
38755          
38756         /**
38757          * @event select
38758          * Fires when a date is selected
38759              * @param {Roo.form.MonthFieeld} combo This combo box
38760              * @param {Date} date The date selected
38761              */
38762         'select' : true
38763          
38764     });
38765     
38766     
38767     if(typeof this.minValue == "string") this.minValue = this.parseDate(this.minValue);
38768     if(typeof this.maxValue == "string") this.maxValue = this.parseDate(this.maxValue);
38769     this.ddMatch = null;
38770     if(this.disabledDates){
38771         var dd = this.disabledDates;
38772         var re = "(?:";
38773         for(var i = 0; i < dd.length; i++){
38774             re += dd[i];
38775             if(i != dd.length-1) re += "|";
38776         }
38777         this.ddMatch = new RegExp(re + ")");
38778     }
38779 };
38780
38781 Roo.extend(Roo.form.MonthField, Roo.form.TriggerField,  {
38782     /**
38783      * @cfg {String} format
38784      * The default date format string which can be overriden for localization support.  The format must be
38785      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
38786      */
38787     format : "M Y",
38788     /**
38789      * @cfg {String} altFormats
38790      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
38791      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
38792      */
38793     altFormats : "M Y|m/Y|m-y|m-Y|my|mY",
38794     /**
38795      * @cfg {Array} disabledDays
38796      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
38797      */
38798     disabledDays : [0,1,2,3,4,5,6],
38799     /**
38800      * @cfg {String} disabledDaysText
38801      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
38802      */
38803     disabledDaysText : "Disabled",
38804     /**
38805      * @cfg {Array} disabledDates
38806      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
38807      * expression so they are very powerful. Some examples:
38808      * <ul>
38809      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
38810      * <li>["03/08", "09/16"] would disable those days for every year</li>
38811      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
38812      * <li>["03/../2006"] would disable every day in March 2006</li>
38813      * <li>["^03"] would disable every day in every March</li>
38814      * </ul>
38815      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
38816      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
38817      */
38818     disabledDates : null,
38819     /**
38820      * @cfg {String} disabledDatesText
38821      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
38822      */
38823     disabledDatesText : "Disabled",
38824     /**
38825      * @cfg {Date/String} minValue
38826      * The minimum allowed date. Can be either a Javascript date object or a string date in a
38827      * valid format (defaults to null).
38828      */
38829     minValue : null,
38830     /**
38831      * @cfg {Date/String} maxValue
38832      * The maximum allowed date. Can be either a Javascript date object or a string date in a
38833      * valid format (defaults to null).
38834      */
38835     maxValue : null,
38836     /**
38837      * @cfg {String} minText
38838      * The error text to display when the date in the cell is before minValue (defaults to
38839      * 'The date in this field must be after {minValue}').
38840      */
38841     minText : "The date in this field must be equal to or after {0}",
38842     /**
38843      * @cfg {String} maxTextf
38844      * The error text to display when the date in the cell is after maxValue (defaults to
38845      * 'The date in this field must be before {maxValue}').
38846      */
38847     maxText : "The date in this field must be equal to or before {0}",
38848     /**
38849      * @cfg {String} invalidText
38850      * The error text to display when the date in the field is invalid (defaults to
38851      * '{value} is not a valid date - it must be in the format {format}').
38852      */
38853     invalidText : "{0} is not a valid date - it must be in the format {1}",
38854     /**
38855      * @cfg {String} triggerClass
38856      * An additional CSS class used to style the trigger button.  The trigger will always get the
38857      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
38858      * which displays a calendar icon).
38859      */
38860     triggerClass : 'x-form-date-trigger',
38861     
38862
38863     /**
38864      * @cfg {Boolean} useIso
38865      * if enabled, then the date field will use a hidden field to store the 
38866      * real value as iso formated date. default (true)
38867      */ 
38868     useIso : true,
38869     /**
38870      * @cfg {String/Object} autoCreate
38871      * A DomHelper element spec, or true for a default element spec (defaults to
38872      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
38873      */ 
38874     // private
38875     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "off"},
38876     
38877     // private
38878     hiddenField: false,
38879     
38880     hideMonthPicker : false,
38881     
38882     onRender : function(ct, position)
38883     {
38884         Roo.form.MonthField.superclass.onRender.call(this, ct, position);
38885         if (this.useIso) {
38886             this.el.dom.removeAttribute('name'); 
38887             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
38888                     'before', true);
38889             this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
38890             // prevent input submission
38891             this.hiddenName = this.name;
38892         }
38893             
38894             
38895     },
38896     
38897     // private
38898     validateValue : function(value)
38899     {
38900         value = this.formatDate(value);
38901         if(!Roo.form.MonthField.superclass.validateValue.call(this, value)){
38902             return false;
38903         }
38904         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
38905              return true;
38906         }
38907         var svalue = value;
38908         value = this.parseDate(value);
38909         if(!value){
38910             this.markInvalid(String.format(this.invalidText, svalue, this.format));
38911             return false;
38912         }
38913         var time = value.getTime();
38914         if(this.minValue && time < this.minValue.getTime()){
38915             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
38916             return false;
38917         }
38918         if(this.maxValue && time > this.maxValue.getTime()){
38919             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
38920             return false;
38921         }
38922         /*if(this.disabledDays){
38923             var day = value.getDay();
38924             for(var i = 0; i < this.disabledDays.length; i++) {
38925                 if(day === this.disabledDays[i]){
38926                     this.markInvalid(this.disabledDaysText);
38927                     return false;
38928                 }
38929             }
38930         }
38931         */
38932         var fvalue = this.formatDate(value);
38933         /*if(this.ddMatch && this.ddMatch.test(fvalue)){
38934             this.markInvalid(String.format(this.disabledDatesText, fvalue));
38935             return false;
38936         }
38937         */
38938         return true;
38939     },
38940
38941     // private
38942     // Provides logic to override the default TriggerField.validateBlur which just returns true
38943     validateBlur : function(){
38944         return !this.menu || !this.menu.isVisible();
38945     },
38946
38947     /**
38948      * Returns the current date value of the date field.
38949      * @return {Date} The date value
38950      */
38951     getValue : function(){
38952         
38953         
38954         
38955         return  this.hiddenField ?
38956                 this.hiddenField.value :
38957                 this.parseDate(Roo.form.MonthField.superclass.getValue.call(this)) || "";
38958     },
38959
38960     /**
38961      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
38962      * date, using MonthField.format as the date format, according to the same rules as {@link Date#parseDate}
38963      * (the default format used is "m/d/y").
38964      * <br />Usage:
38965      * <pre><code>
38966 //All of these calls set the same date value (May 4, 2006)
38967
38968 //Pass a date object:
38969 var dt = new Date('5/4/06');
38970 monthField.setValue(dt);
38971
38972 //Pass a date string (default format):
38973 monthField.setValue('5/4/06');
38974
38975 //Pass a date string (custom format):
38976 monthField.format = 'Y-m-d';
38977 monthField.setValue('2006-5-4');
38978 </code></pre>
38979      * @param {String/Date} date The date or valid date string
38980      */
38981     setValue : function(date){
38982         Roo.log('month setValue' + date);
38983         // can only be first of month..
38984         
38985         var val = this.parseDate(date);
38986         
38987         if (this.hiddenField) {
38988             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
38989         }
38990         Roo.form.MonthField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
38991         this.value = this.parseDate(date);
38992     },
38993
38994     // private
38995     parseDate : function(value){
38996         if(!value || value instanceof Date){
38997             value = value ? Date.parseDate(value.format('Y-m') + '-01', 'Y-m-d') : null;
38998             return value;
38999         }
39000         var v = Date.parseDate(value, this.format);
39001         if (!v && this.useIso) {
39002             v = Date.parseDate(value, 'Y-m-d');
39003         }
39004         if (v) {
39005             // 
39006             v = Date.parseDate(v.format('Y-m') +'-01', 'Y-m-d');
39007         }
39008         
39009         
39010         if(!v && this.altFormats){
39011             if(!this.altFormatsArray){
39012                 this.altFormatsArray = this.altFormats.split("|");
39013             }
39014             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
39015                 v = Date.parseDate(value, this.altFormatsArray[i]);
39016             }
39017         }
39018         return v;
39019     },
39020
39021     // private
39022     formatDate : function(date, fmt){
39023         return (!date || !(date instanceof Date)) ?
39024                date : date.dateFormat(fmt || this.format);
39025     },
39026
39027     // private
39028     menuListeners : {
39029         select: function(m, d){
39030             this.setValue(d);
39031             this.fireEvent('select', this, d);
39032         },
39033         show : function(){ // retain focus styling
39034             this.onFocus();
39035         },
39036         hide : function(){
39037             this.focus.defer(10, this);
39038             var ml = this.menuListeners;
39039             this.menu.un("select", ml.select,  this);
39040             this.menu.un("show", ml.show,  this);
39041             this.menu.un("hide", ml.hide,  this);
39042         }
39043     },
39044     // private
39045     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
39046     onTriggerClick : function(){
39047         if(this.disabled){
39048             return;
39049         }
39050         if(this.menu == null){
39051             this.menu = new Roo.menu.DateMenu();
39052            
39053         }
39054         
39055         Roo.apply(this.menu.picker,  {
39056             
39057             showClear: this.allowBlank,
39058             minDate : this.minValue,
39059             maxDate : this.maxValue,
39060             disabledDatesRE : this.ddMatch,
39061             disabledDatesText : this.disabledDatesText,
39062             
39063             format : this.useIso ? 'Y-m-d' : this.format,
39064             minText : String.format(this.minText, this.formatDate(this.minValue)),
39065             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
39066             
39067         });
39068          this.menu.on(Roo.apply({}, this.menuListeners, {
39069             scope:this
39070         }));
39071        
39072         
39073         var m = this.menu;
39074         var p = m.picker;
39075         
39076         // hide month picker get's called when we called by 'before hide';
39077         
39078         var ignorehide = true;
39079         p.hideMonthPicker  = function(disableAnim){
39080             if (ignorehide) {
39081                 return;
39082             }
39083              if(this.monthPicker){
39084                 Roo.log("hideMonthPicker called");
39085                 if(disableAnim === true){
39086                     this.monthPicker.hide();
39087                 }else{
39088                     this.monthPicker.slideOut('t', {duration:.2});
39089                     p.setValue(new Date(m.picker.mpSelYear, m.picker.mpSelMonth, 1));
39090                     p.fireEvent("select", this, this.value);
39091                     m.hide();
39092                 }
39093             }
39094         }
39095         
39096         Roo.log('picker set value');
39097         Roo.log(this.getValue());
39098         p.setValue(this.getValue() ? this.parseDate(this.getValue()) : new Date());
39099         m.show(this.el, 'tl-bl?');
39100         ignorehide  = false;
39101         // this will trigger hideMonthPicker..
39102         
39103         
39104         // hidden the day picker
39105         Roo.select('.x-date-picker table', true).first().dom.style.visibility = "hidden";
39106         
39107         
39108         
39109       
39110         
39111         p.showMonthPicker.defer(100, p);
39112     
39113         
39114        
39115     },
39116
39117     beforeBlur : function(){
39118         var v = this.parseDate(this.getRawValue());
39119         if(v){
39120             this.setValue(v);
39121         }
39122     }
39123
39124     /** @cfg {Boolean} grow @hide */
39125     /** @cfg {Number} growMin @hide */
39126     /** @cfg {Number} growMax @hide */
39127     /**
39128      * @hide
39129      * @method autoSize
39130      */
39131 });/*
39132  * Based on:
39133  * Ext JS Library 1.1.1
39134  * Copyright(c) 2006-2007, Ext JS, LLC.
39135  *
39136  * Originally Released Under LGPL - original licence link has changed is not relivant.
39137  *
39138  * Fork - LGPL
39139  * <script type="text/javascript">
39140  */
39141  
39142
39143 /**
39144  * @class Roo.form.ComboBox
39145  * @extends Roo.form.TriggerField
39146  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
39147  * @constructor
39148  * Create a new ComboBox.
39149  * @param {Object} config Configuration options
39150  */
39151 Roo.form.ComboBox = function(config){
39152     Roo.form.ComboBox.superclass.constructor.call(this, config);
39153     this.addEvents({
39154         /**
39155          * @event expand
39156          * Fires when the dropdown list is expanded
39157              * @param {Roo.form.ComboBox} combo This combo box
39158              */
39159         'expand' : true,
39160         /**
39161          * @event collapse
39162          * Fires when the dropdown list is collapsed
39163              * @param {Roo.form.ComboBox} combo This combo box
39164              */
39165         'collapse' : true,
39166         /**
39167          * @event beforeselect
39168          * Fires before a list item is selected. Return false to cancel the selection.
39169              * @param {Roo.form.ComboBox} combo This combo box
39170              * @param {Roo.data.Record} record The data record returned from the underlying store
39171              * @param {Number} index The index of the selected item in the dropdown list
39172              */
39173         'beforeselect' : true,
39174         /**
39175          * @event select
39176          * Fires when a list item is selected
39177              * @param {Roo.form.ComboBox} combo This combo box
39178              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
39179              * @param {Number} index The index of the selected item in the dropdown list
39180              */
39181         'select' : true,
39182         /**
39183          * @event beforequery
39184          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
39185          * The event object passed has these properties:
39186              * @param {Roo.form.ComboBox} combo This combo box
39187              * @param {String} query The query
39188              * @param {Boolean} forceAll true to force "all" query
39189              * @param {Boolean} cancel true to cancel the query
39190              * @param {Object} e The query event object
39191              */
39192         'beforequery': true,
39193          /**
39194          * @event add
39195          * Fires when the 'add' icon is pressed (add a listener to enable add button)
39196              * @param {Roo.form.ComboBox} combo This combo box
39197              */
39198         'add' : true,
39199         /**
39200          * @event edit
39201          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
39202              * @param {Roo.form.ComboBox} combo This combo box
39203              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
39204              */
39205         'edit' : true
39206         
39207         
39208     });
39209     if(this.transform){
39210         this.allowDomMove = false;
39211         var s = Roo.getDom(this.transform);
39212         if(!this.hiddenName){
39213             this.hiddenName = s.name;
39214         }
39215         if(!this.store){
39216             this.mode = 'local';
39217             var d = [], opts = s.options;
39218             for(var i = 0, len = opts.length;i < len; i++){
39219                 var o = opts[i];
39220                 var value = (Roo.isIE ? o.getAttributeNode('value').specified : o.hasAttribute('value')) ? o.value : o.text;
39221                 if(o.selected) {
39222                     this.value = value;
39223                 }
39224                 d.push([value, o.text]);
39225             }
39226             this.store = new Roo.data.SimpleStore({
39227                 'id': 0,
39228                 fields: ['value', 'text'],
39229                 data : d
39230             });
39231             this.valueField = 'value';
39232             this.displayField = 'text';
39233         }
39234         s.name = Roo.id(); // wipe out the name in case somewhere else they have a reference
39235         if(!this.lazyRender){
39236             this.target = true;
39237             this.el = Roo.DomHelper.insertBefore(s, this.autoCreate || this.defaultAutoCreate);
39238             s.parentNode.removeChild(s); // remove it
39239             this.render(this.el.parentNode);
39240         }else{
39241             s.parentNode.removeChild(s); // remove it
39242         }
39243
39244     }
39245     if (this.store) {
39246         this.store = Roo.factory(this.store, Roo.data);
39247     }
39248     
39249     this.selectedIndex = -1;
39250     if(this.mode == 'local'){
39251         if(config.queryDelay === undefined){
39252             this.queryDelay = 10;
39253         }
39254         if(config.minChars === undefined){
39255             this.minChars = 0;
39256         }
39257     }
39258 };
39259
39260 Roo.extend(Roo.form.ComboBox, Roo.form.TriggerField, {
39261     /**
39262      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
39263      */
39264     /**
39265      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
39266      * rendering into an Roo.Editor, defaults to false)
39267      */
39268     /**
39269      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
39270      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
39271      */
39272     /**
39273      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
39274      */
39275     /**
39276      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
39277      * the dropdown list (defaults to undefined, with no header element)
39278      */
39279
39280      /**
39281      * @cfg {String/Roo.Template} tpl The template to use to render the output
39282      */
39283      
39284     // private
39285     defaultAutoCreate : {tag: "input", type: "text", size: "24", autocomplete: "off"},
39286     /**
39287      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
39288      */
39289     listWidth: undefined,
39290     /**
39291      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
39292      * mode = 'remote' or 'text' if mode = 'local')
39293      */
39294     displayField: undefined,
39295     /**
39296      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
39297      * mode = 'remote' or 'value' if mode = 'local'). 
39298      * Note: use of a valueField requires the user make a selection
39299      * in order for a value to be mapped.
39300      */
39301     valueField: undefined,
39302     
39303     
39304     /**
39305      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
39306      * field's data value (defaults to the underlying DOM element's name)
39307      */
39308     hiddenName: undefined,
39309     /**
39310      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
39311      */
39312     listClass: '',
39313     /**
39314      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
39315      */
39316     selectedClass: 'x-combo-selected',
39317     /**
39318      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
39319      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
39320      * which displays a downward arrow icon).
39321      */
39322     triggerClass : 'x-form-arrow-trigger',
39323     /**
39324      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
39325      */
39326     shadow:'sides',
39327     /**
39328      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
39329      * anchor positions (defaults to 'tl-bl')
39330      */
39331     listAlign: 'tl-bl?',
39332     /**
39333      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
39334      */
39335     maxHeight: 300,
39336     /**
39337      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
39338      * query specified by the allQuery config option (defaults to 'query')
39339      */
39340     triggerAction: 'query',
39341     /**
39342      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
39343      * (defaults to 4, does not apply if editable = false)
39344      */
39345     minChars : 4,
39346     /**
39347      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
39348      * delay (typeAheadDelay) if it matches a known value (defaults to false)
39349      */
39350     typeAhead: false,
39351     /**
39352      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
39353      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
39354      */
39355     queryDelay: 500,
39356     /**
39357      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
39358      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
39359      */
39360     pageSize: 0,
39361     /**
39362      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
39363      * when editable = true (defaults to false)
39364      */
39365     selectOnFocus:false,
39366     /**
39367      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
39368      */
39369     queryParam: 'query',
39370     /**
39371      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
39372      * when mode = 'remote' (defaults to 'Loading...')
39373      */
39374     loadingText: 'Loading...',
39375     /**
39376      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
39377      */
39378     resizable: false,
39379     /**
39380      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
39381      */
39382     handleHeight : 8,
39383     /**
39384      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
39385      * traditional select (defaults to true)
39386      */
39387     editable: true,
39388     /**
39389      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
39390      */
39391     allQuery: '',
39392     /**
39393      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
39394      */
39395     mode: 'remote',
39396     /**
39397      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
39398      * listWidth has a higher value)
39399      */
39400     minListWidth : 70,
39401     /**
39402      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
39403      * allow the user to set arbitrary text into the field (defaults to false)
39404      */
39405     forceSelection:false,
39406     /**
39407      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
39408      * if typeAhead = true (defaults to 250)
39409      */
39410     typeAheadDelay : 250,
39411     /**
39412      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
39413      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
39414      */
39415     valueNotFoundText : undefined,
39416     /**
39417      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
39418      */
39419     blockFocus : false,
39420     
39421     /**
39422      * @cfg {Boolean} disableClear Disable showing of clear button.
39423      */
39424     disableClear : false,
39425     /**
39426      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
39427      */
39428     alwaysQuery : false,
39429     
39430     //private
39431     addicon : false,
39432     editicon: false,
39433     
39434     // element that contains real text value.. (when hidden is used..)
39435      
39436     // private
39437     onRender : function(ct, position){
39438         Roo.form.ComboBox.superclass.onRender.call(this, ct, position);
39439         if(this.hiddenName){
39440             this.hiddenField = this.el.insertSibling({tag:'input', type:'hidden', name: this.hiddenName, id:  (this.hiddenId||this.hiddenName)},
39441                     'before', true);
39442             this.hiddenField.value =
39443                 this.hiddenValue !== undefined ? this.hiddenValue :
39444                 this.value !== undefined ? this.value : '';
39445
39446             // prevent input submission
39447             this.el.dom.removeAttribute('name');
39448              
39449              
39450         }
39451         if(Roo.isGecko){
39452             this.el.dom.setAttribute('autocomplete', 'off');
39453         }
39454
39455         var cls = 'x-combo-list';
39456
39457         this.list = new Roo.Layer({
39458             shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
39459         });
39460
39461         var lw = this.listWidth || Math.max(this.wrap.getWidth(), this.minListWidth);
39462         this.list.setWidth(lw);
39463         this.list.swallowEvent('mousewheel');
39464         this.assetHeight = 0;
39465
39466         if(this.title){
39467             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
39468             this.assetHeight += this.header.getHeight();
39469         }
39470
39471         this.innerList = this.list.createChild({cls:cls+'-inner'});
39472         this.innerList.on('mouseover', this.onViewOver, this);
39473         this.innerList.on('mousemove', this.onViewMove, this);
39474         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
39475         
39476         if(this.allowBlank && !this.pageSize && !this.disableClear){
39477             this.footer = this.list.createChild({cls:cls+'-ft'});
39478             this.pageTb = new Roo.Toolbar(this.footer);
39479            
39480         }
39481         if(this.pageSize){
39482             this.footer = this.list.createChild({cls:cls+'-ft'});
39483             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
39484                     {pageSize: this.pageSize});
39485             
39486         }
39487         
39488         if (this.pageTb && this.allowBlank && !this.disableClear) {
39489             var _this = this;
39490             this.pageTb.add(new Roo.Toolbar.Fill(), {
39491                 cls: 'x-btn-icon x-btn-clear',
39492                 text: '&#160;',
39493                 handler: function()
39494                 {
39495                     _this.collapse();
39496                     _this.clearValue();
39497                     _this.onSelect(false, -1);
39498                 }
39499             });
39500         }
39501         if (this.footer) {
39502             this.assetHeight += this.footer.getHeight();
39503         }
39504         
39505
39506         if(!this.tpl){
39507             this.tpl = '<div class="'+cls+'-item">{' + this.displayField + '}</div>';
39508         }
39509
39510         this.view = new Roo.View(this.innerList, this.tpl, {
39511             singleSelect:true, store: this.store, selectedClass: this.selectedClass
39512         });
39513
39514         this.view.on('click', this.onViewClick, this);
39515
39516         this.store.on('beforeload', this.onBeforeLoad, this);
39517         this.store.on('load', this.onLoad, this);
39518         this.store.on('loadexception', this.onLoadException, this);
39519
39520         if(this.resizable){
39521             this.resizer = new Roo.Resizable(this.list,  {
39522                pinned:true, handles:'se'
39523             });
39524             this.resizer.on('resize', function(r, w, h){
39525                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
39526                 this.listWidth = w;
39527                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
39528                 this.restrictHeight();
39529             }, this);
39530             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
39531         }
39532         if(!this.editable){
39533             this.editable = true;
39534             this.setEditable(false);
39535         }  
39536         
39537         
39538         if (typeof(this.events.add.listeners) != 'undefined') {
39539             
39540             this.addicon = this.wrap.createChild(
39541                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
39542        
39543             this.addicon.on('click', function(e) {
39544                 this.fireEvent('add', this);
39545             }, this);
39546         }
39547         if (typeof(this.events.edit.listeners) != 'undefined') {
39548             
39549             this.editicon = this.wrap.createChild(
39550                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
39551             if (this.addicon) {
39552                 this.editicon.setStyle('margin-left', '40px');
39553             }
39554             this.editicon.on('click', function(e) {
39555                 
39556                 // we fire even  if inothing is selected..
39557                 this.fireEvent('edit', this, this.lastData );
39558                 
39559             }, this);
39560         }
39561         
39562         
39563         
39564     },
39565
39566     // private
39567     initEvents : function(){
39568         Roo.form.ComboBox.superclass.initEvents.call(this);
39569
39570         this.keyNav = new Roo.KeyNav(this.el, {
39571             "up" : function(e){
39572                 this.inKeyMode = true;
39573                 this.selectPrev();
39574             },
39575
39576             "down" : function(e){
39577                 if(!this.isExpanded()){
39578                     this.onTriggerClick();
39579                 }else{
39580                     this.inKeyMode = true;
39581                     this.selectNext();
39582                 }
39583             },
39584
39585             "enter" : function(e){
39586                 this.onViewClick();
39587                 //return true;
39588             },
39589
39590             "esc" : function(e){
39591                 this.collapse();
39592             },
39593
39594             "tab" : function(e){
39595                 this.onViewClick(false);
39596                 this.fireEvent("specialkey", this, e);
39597                 return true;
39598             },
39599
39600             scope : this,
39601
39602             doRelay : function(foo, bar, hname){
39603                 if(hname == 'down' || this.scope.isExpanded()){
39604                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
39605                 }
39606                 return true;
39607             },
39608
39609             forceKeyDown: true
39610         });
39611         this.queryDelay = Math.max(this.queryDelay || 10,
39612                 this.mode == 'local' ? 10 : 250);
39613         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
39614         if(this.typeAhead){
39615             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
39616         }
39617         if(this.editable !== false){
39618             this.el.on("keyup", this.onKeyUp, this);
39619         }
39620         if(this.forceSelection){
39621             this.on('blur', this.doForce, this);
39622         }
39623     },
39624
39625     onDestroy : function(){
39626         if(this.view){
39627             this.view.setStore(null);
39628             this.view.el.removeAllListeners();
39629             this.view.el.remove();
39630             this.view.purgeListeners();
39631         }
39632         if(this.list){
39633             this.list.destroy();
39634         }
39635         if(this.store){
39636             this.store.un('beforeload', this.onBeforeLoad, this);
39637             this.store.un('load', this.onLoad, this);
39638             this.store.un('loadexception', this.onLoadException, this);
39639         }
39640         Roo.form.ComboBox.superclass.onDestroy.call(this);
39641     },
39642
39643     // private
39644     fireKey : function(e){
39645         if(e.isNavKeyPress() && !this.list.isVisible()){
39646             this.fireEvent("specialkey", this, e);
39647         }
39648     },
39649
39650     // private
39651     onResize: function(w, h){
39652         Roo.form.ComboBox.superclass.onResize.apply(this, arguments);
39653         
39654         if(typeof w != 'number'){
39655             // we do not handle it!?!?
39656             return;
39657         }
39658         var tw = this.trigger.getWidth();
39659         tw += this.addicon ? this.addicon.getWidth() : 0;
39660         tw += this.editicon ? this.editicon.getWidth() : 0;
39661         var x = w - tw;
39662         this.el.setWidth( this.adjustWidth('input', x));
39663             
39664         this.trigger.setStyle('left', x+'px');
39665         
39666         if(this.list && this.listWidth === undefined){
39667             var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
39668             this.list.setWidth(lw);
39669             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
39670         }
39671         
39672     
39673         
39674     },
39675
39676     /**
39677      * Allow or prevent the user from directly editing the field text.  If false is passed,
39678      * the user will only be able to select from the items defined in the dropdown list.  This method
39679      * is the runtime equivalent of setting the 'editable' config option at config time.
39680      * @param {Boolean} value True to allow the user to directly edit the field text
39681      */
39682     setEditable : function(value){
39683         if(value == this.editable){
39684             return;
39685         }
39686         this.editable = value;
39687         if(!value){
39688             this.el.dom.setAttribute('readOnly', true);
39689             this.el.on('mousedown', this.onTriggerClick,  this);
39690             this.el.addClass('x-combo-noedit');
39691         }else{
39692             this.el.dom.setAttribute('readOnly', false);
39693             this.el.un('mousedown', this.onTriggerClick,  this);
39694             this.el.removeClass('x-combo-noedit');
39695         }
39696     },
39697
39698     // private
39699     onBeforeLoad : function(){
39700         if(!this.hasFocus){
39701             return;
39702         }
39703         this.innerList.update(this.loadingText ?
39704                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
39705         this.restrictHeight();
39706         this.selectedIndex = -1;
39707     },
39708
39709     // private
39710     onLoad : function(){
39711         if(!this.hasFocus){
39712             return;
39713         }
39714         if(this.store.getCount() > 0){
39715             this.expand();
39716             this.restrictHeight();
39717             if(this.lastQuery == this.allQuery){
39718                 if(this.editable){
39719                     this.el.dom.select();
39720                 }
39721                 if(!this.selectByValue(this.value, true)){
39722                     this.select(0, true);
39723                 }
39724             }else{
39725                 this.selectNext();
39726                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
39727                     this.taTask.delay(this.typeAheadDelay);
39728                 }
39729             }
39730         }else{
39731             this.onEmptyResults();
39732         }
39733         //this.el.focus();
39734     },
39735     // private
39736     onLoadException : function()
39737     {
39738         this.collapse();
39739         Roo.log(this.store.reader.jsonData);
39740         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
39741             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
39742         }
39743         
39744         
39745     },
39746     // private
39747     onTypeAhead : function(){
39748         if(this.store.getCount() > 0){
39749             var r = this.store.getAt(0);
39750             var newValue = r.data[this.displayField];
39751             var len = newValue.length;
39752             var selStart = this.getRawValue().length;
39753             if(selStart != len){
39754                 this.setRawValue(newValue);
39755                 this.selectText(selStart, newValue.length);
39756             }
39757         }
39758     },
39759
39760     // private
39761     onSelect : function(record, index){
39762         if(this.fireEvent('beforeselect', this, record, index) !== false){
39763             this.setFromData(index > -1 ? record.data : false);
39764             this.collapse();
39765             this.fireEvent('select', this, record, index);
39766         }
39767     },
39768
39769     /**
39770      * Returns the currently selected field value or empty string if no value is set.
39771      * @return {String} value The selected value
39772      */
39773     getValue : function(){
39774         if(this.valueField){
39775             return typeof this.value != 'undefined' ? this.value : '';
39776         }else{
39777             return Roo.form.ComboBox.superclass.getValue.call(this);
39778         }
39779     },
39780
39781     /**
39782      * Clears any text/value currently set in the field
39783      */
39784     clearValue : function(){
39785         if(this.hiddenField){
39786             this.hiddenField.value = '';
39787         }
39788         this.value = '';
39789         this.setRawValue('');
39790         this.lastSelectionText = '';
39791         
39792     },
39793
39794     /**
39795      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
39796      * will be displayed in the field.  If the value does not match the data value of an existing item,
39797      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
39798      * Otherwise the field will be blank (although the value will still be set).
39799      * @param {String} value The value to match
39800      */
39801     setValue : function(v){
39802         var text = v;
39803         if(this.valueField){
39804             var r = this.findRecord(this.valueField, v);
39805             if(r){
39806                 text = r.data[this.displayField];
39807             }else if(this.valueNotFoundText !== undefined){
39808                 text = this.valueNotFoundText;
39809             }
39810         }
39811         this.lastSelectionText = text;
39812         if(this.hiddenField){
39813             this.hiddenField.value = v;
39814         }
39815         Roo.form.ComboBox.superclass.setValue.call(this, text);
39816         this.value = v;
39817     },
39818     /**
39819      * @property {Object} the last set data for the element
39820      */
39821     
39822     lastData : false,
39823     /**
39824      * Sets the value of the field based on a object which is related to the record format for the store.
39825      * @param {Object} value the value to set as. or false on reset?
39826      */
39827     setFromData : function(o){
39828         var dv = ''; // display value
39829         var vv = ''; // value value..
39830         this.lastData = o;
39831         if (this.displayField) {
39832             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
39833         } else {
39834             // this is an error condition!!!
39835             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
39836         }
39837         
39838         if(this.valueField){
39839             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
39840         }
39841         if(this.hiddenField){
39842             this.hiddenField.value = vv;
39843             
39844             this.lastSelectionText = dv;
39845             Roo.form.ComboBox.superclass.setValue.call(this, dv);
39846             this.value = vv;
39847             return;
39848         }
39849         // no hidden field.. - we store the value in 'value', but still display
39850         // display field!!!!
39851         this.lastSelectionText = dv;
39852         Roo.form.ComboBox.superclass.setValue.call(this, dv);
39853         this.value = vv;
39854         
39855         
39856     },
39857     // private
39858     reset : function(){
39859         // overridden so that last data is reset..
39860         this.setValue(this.resetValue);
39861         this.clearInvalid();
39862         this.lastData = false;
39863         if (this.view) {
39864             this.view.clearSelections();
39865         }
39866     },
39867     // private
39868     findRecord : function(prop, value){
39869         var record;
39870         if(this.store.getCount() > 0){
39871             this.store.each(function(r){
39872                 if(r.data[prop] == value){
39873                     record = r;
39874                     return false;
39875                 }
39876                 return true;
39877             });
39878         }
39879         return record;
39880     },
39881     
39882     getName: function()
39883     {
39884         // returns hidden if it's set..
39885         if (!this.rendered) {return ''};
39886         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
39887         
39888     },
39889     // private
39890     onViewMove : function(e, t){
39891         this.inKeyMode = false;
39892     },
39893
39894     // private
39895     onViewOver : function(e, t){
39896         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
39897             return;
39898         }
39899         var item = this.view.findItemFromChild(t);
39900         if(item){
39901             var index = this.view.indexOf(item);
39902             this.select(index, false);
39903         }
39904     },
39905
39906     // private
39907     onViewClick : function(doFocus)
39908     {
39909         var index = this.view.getSelectedIndexes()[0];
39910         var r = this.store.getAt(index);
39911         if(r){
39912             this.onSelect(r, index);
39913         }
39914         if(doFocus !== false && !this.blockFocus){
39915             this.el.focus();
39916         }
39917     },
39918
39919     // private
39920     restrictHeight : function(){
39921         this.innerList.dom.style.height = '';
39922         var inner = this.innerList.dom;
39923         var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
39924         this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
39925         this.list.beginUpdate();
39926         this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
39927         this.list.alignTo(this.el, this.listAlign);
39928         this.list.endUpdate();
39929     },
39930
39931     // private
39932     onEmptyResults : function(){
39933         this.collapse();
39934     },
39935
39936     /**
39937      * Returns true if the dropdown list is expanded, else false.
39938      */
39939     isExpanded : function(){
39940         return this.list.isVisible();
39941     },
39942
39943     /**
39944      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
39945      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
39946      * @param {String} value The data value of the item to select
39947      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
39948      * selected item if it is not currently in view (defaults to true)
39949      * @return {Boolean} True if the value matched an item in the list, else false
39950      */
39951     selectByValue : function(v, scrollIntoView){
39952         if(v !== undefined && v !== null){
39953             var r = this.findRecord(this.valueField || this.displayField, v);
39954             if(r){
39955                 this.select(this.store.indexOf(r), scrollIntoView);
39956                 return true;
39957             }
39958         }
39959         return false;
39960     },
39961
39962     /**
39963      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
39964      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
39965      * @param {Number} index The zero-based index of the list item to select
39966      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
39967      * selected item if it is not currently in view (defaults to true)
39968      */
39969     select : function(index, scrollIntoView){
39970         this.selectedIndex = index;
39971         this.view.select(index);
39972         if(scrollIntoView !== false){
39973             var el = this.view.getNode(index);
39974             if(el){
39975                 this.innerList.scrollChildIntoView(el, false);
39976             }
39977         }
39978     },
39979
39980     // private
39981     selectNext : function(){
39982         var ct = this.store.getCount();
39983         if(ct > 0){
39984             if(this.selectedIndex == -1){
39985                 this.select(0);
39986             }else if(this.selectedIndex < ct-1){
39987                 this.select(this.selectedIndex+1);
39988             }
39989         }
39990     },
39991
39992     // private
39993     selectPrev : function(){
39994         var ct = this.store.getCount();
39995         if(ct > 0){
39996             if(this.selectedIndex == -1){
39997                 this.select(0);
39998             }else if(this.selectedIndex != 0){
39999                 this.select(this.selectedIndex-1);
40000             }
40001         }
40002     },
40003
40004     // private
40005     onKeyUp : function(e){
40006         if(this.editable !== false && !e.isSpecialKey()){
40007             this.lastKey = e.getKey();
40008             this.dqTask.delay(this.queryDelay);
40009         }
40010     },
40011
40012     // private
40013     validateBlur : function(){
40014         return !this.list || !this.list.isVisible();   
40015     },
40016
40017     // private
40018     initQuery : function(){
40019         this.doQuery(this.getRawValue());
40020     },
40021
40022     // private
40023     doForce : function(){
40024         if(this.el.dom.value.length > 0){
40025             this.el.dom.value =
40026                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
40027              
40028         }
40029     },
40030
40031     /**
40032      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
40033      * query allowing the query action to be canceled if needed.
40034      * @param {String} query The SQL query to execute
40035      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
40036      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
40037      * saved in the current store (defaults to false)
40038      */
40039     doQuery : function(q, forceAll){
40040         if(q === undefined || q === null){
40041             q = '';
40042         }
40043         var qe = {
40044             query: q,
40045             forceAll: forceAll,
40046             combo: this,
40047             cancel:false
40048         };
40049         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
40050             return false;
40051         }
40052         q = qe.query;
40053         forceAll = qe.forceAll;
40054         if(forceAll === true || (q.length >= this.minChars)){
40055             if(this.lastQuery != q || this.alwaysQuery){
40056                 this.lastQuery = q;
40057                 if(this.mode == 'local'){
40058                     this.selectedIndex = -1;
40059                     if(forceAll){
40060                         this.store.clearFilter();
40061                     }else{
40062                         this.store.filter(this.displayField, q);
40063                     }
40064                     this.onLoad();
40065                 }else{
40066                     this.store.baseParams[this.queryParam] = q;
40067                     this.store.load({
40068                         params: this.getParams(q)
40069                     });
40070                     this.expand();
40071                 }
40072             }else{
40073                 this.selectedIndex = -1;
40074                 this.onLoad();   
40075             }
40076         }
40077     },
40078
40079     // private
40080     getParams : function(q){
40081         var p = {};
40082         //p[this.queryParam] = q;
40083         if(this.pageSize){
40084             p.start = 0;
40085             p.limit = this.pageSize;
40086         }
40087         return p;
40088     },
40089
40090     /**
40091      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
40092      */
40093     collapse : function(){
40094         if(!this.isExpanded()){
40095             return;
40096         }
40097         this.list.hide();
40098         Roo.get(document).un('mousedown', this.collapseIf, this);
40099         Roo.get(document).un('mousewheel', this.collapseIf, this);
40100         if (!this.editable) {
40101             Roo.get(document).un('keydown', this.listKeyPress, this);
40102         }
40103         this.fireEvent('collapse', this);
40104     },
40105
40106     // private
40107     collapseIf : function(e){
40108         if(!e.within(this.wrap) && !e.within(this.list)){
40109             this.collapse();
40110         }
40111     },
40112
40113     /**
40114      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
40115      */
40116     expand : function(){
40117         if(this.isExpanded() || !this.hasFocus){
40118             return;
40119         }
40120         this.list.alignTo(this.el, this.listAlign);
40121         this.list.show();
40122         Roo.get(document).on('mousedown', this.collapseIf, this);
40123         Roo.get(document).on('mousewheel', this.collapseIf, this);
40124         if (!this.editable) {
40125             Roo.get(document).on('keydown', this.listKeyPress, this);
40126         }
40127         
40128         this.fireEvent('expand', this);
40129     },
40130
40131     // private
40132     // Implements the default empty TriggerField.onTriggerClick function
40133     onTriggerClick : function(){
40134         if(this.disabled){
40135             return;
40136         }
40137         if(this.isExpanded()){
40138             this.collapse();
40139             if (!this.blockFocus) {
40140                 this.el.focus();
40141             }
40142             
40143         }else {
40144             this.hasFocus = true;
40145             if(this.triggerAction == 'all') {
40146                 this.doQuery(this.allQuery, true);
40147             } else {
40148                 this.doQuery(this.getRawValue());
40149             }
40150             if (!this.blockFocus) {
40151                 this.el.focus();
40152             }
40153         }
40154     },
40155     listKeyPress : function(e)
40156     {
40157         //Roo.log('listkeypress');
40158         // scroll to first matching element based on key pres..
40159         if (e.isSpecialKey()) {
40160             return false;
40161         }
40162         var k = String.fromCharCode(e.getKey()).toUpperCase();
40163         //Roo.log(k);
40164         var match  = false;
40165         var csel = this.view.getSelectedNodes();
40166         var cselitem = false;
40167         if (csel.length) {
40168             var ix = this.view.indexOf(csel[0]);
40169             cselitem  = this.store.getAt(ix);
40170             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
40171                 cselitem = false;
40172             }
40173             
40174         }
40175         
40176         this.store.each(function(v) { 
40177             if (cselitem) {
40178                 // start at existing selection.
40179                 if (cselitem.id == v.id) {
40180                     cselitem = false;
40181                 }
40182                 return;
40183             }
40184                 
40185             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
40186                 match = this.store.indexOf(v);
40187                 return false;
40188             }
40189         }, this);
40190         
40191         if (match === false) {
40192             return true; // no more action?
40193         }
40194         // scroll to?
40195         this.view.select(match);
40196         var sn = Roo.get(this.view.getSelectedNodes()[0])
40197         sn.scrollIntoView(sn.dom.parentNode, false);
40198     }
40199
40200     /** 
40201     * @cfg {Boolean} grow 
40202     * @hide 
40203     */
40204     /** 
40205     * @cfg {Number} growMin 
40206     * @hide 
40207     */
40208     /** 
40209     * @cfg {Number} growMax 
40210     * @hide 
40211     */
40212     /**
40213      * @hide
40214      * @method autoSize
40215      */
40216 });/*
40217  * Copyright(c) 2010-2012, Roo J Solutions Limited
40218  *
40219  * Licence LGPL
40220  *
40221  */
40222
40223 /**
40224  * @class Roo.form.ComboBoxArray
40225  * @extends Roo.form.TextField
40226  * A facebook style adder... for lists of email / people / countries  etc...
40227  * pick multiple items from a combo box, and shows each one.
40228  *
40229  *  Fred [x]  Brian [x]  [Pick another |v]
40230  *
40231  *
40232  *  For this to work: it needs various extra information
40233  *    - normal combo problay has
40234  *      name, hiddenName
40235  *    + displayField, valueField
40236  *
40237  *    For our purpose...
40238  *
40239  *
40240  *   If we change from 'extends' to wrapping...
40241  *   
40242  *  
40243  *
40244  
40245  
40246  * @constructor
40247  * Create a new ComboBoxArray.
40248  * @param {Object} config Configuration options
40249  */
40250  
40251
40252 Roo.form.ComboBoxArray = function(config)
40253 {
40254     this.addEvents({
40255         /**
40256          * @event remove
40257          * Fires when remove the value from the list
40258              * @param {Roo.form.ComboBoxArray} _self This combo box array
40259              * @param {Roo.form.ComboBoxArray.Item} item removed item
40260              */
40261         'remove' : true
40262         
40263         
40264     });
40265     
40266     Roo.form.ComboBoxArray.superclass.constructor.call(this, config);
40267     
40268     this.items = new Roo.util.MixedCollection(false);
40269     
40270     // construct the child combo...
40271     
40272     
40273     
40274     
40275    
40276     
40277 }
40278
40279  
40280 Roo.extend(Roo.form.ComboBoxArray, Roo.form.TextField,
40281
40282     /**
40283      * @cfg {Roo.form.Combo} combo The combo box that is wrapped
40284      */
40285     
40286     lastData : false,
40287     
40288     // behavies liek a hiddne field
40289     inputType:      'hidden',
40290     /**
40291      * @cfg {Number} width The width of the box that displays the selected element
40292      */ 
40293     width:          300,
40294
40295     
40296     
40297     /**
40298      * @cfg {String} name    The name of the visable items on this form (eg. titles not ids)
40299      */
40300     name : false,
40301     /**
40302      * @cfg {String} hiddenName    The hidden name of the field, often contains an comma seperated list of names
40303      */
40304     hiddenName : false,
40305     
40306     
40307     // private the array of items that are displayed..
40308     items  : false,
40309     // private - the hidden field el.
40310     hiddenEl : false,
40311     // private - the filed el..
40312     el : false,
40313     
40314     //validateValue : function() { return true; }, // all values are ok!
40315     //onAddClick: function() { },
40316     
40317     onRender : function(ct, position) 
40318     {
40319         
40320         // create the standard hidden element
40321         //Roo.form.ComboBoxArray.superclass.onRender.call(this, ct, position);
40322         
40323         
40324         // give fake names to child combo;
40325         this.combo.hiddenName = this.hiddenName ? (this.hiddenName+'-subcombo') : this.hiddenName;
40326         this.combo.name = this.name? (this.name+'-subcombo') : this.name;
40327         
40328         this.combo = Roo.factory(this.combo, Roo.form);
40329         this.combo.onRender(ct, position);
40330         if (typeof(this.combo.width) != 'undefined') {
40331             this.combo.onResize(this.combo.width,0);
40332         }
40333         
40334         this.combo.initEvents();
40335         
40336         // assigned so form know we need to do this..
40337         this.store          = this.combo.store;
40338         this.valueField     = this.combo.valueField;
40339         this.displayField   = this.combo.displayField ;
40340         
40341         
40342         this.combo.wrap.addClass('x-cbarray-grp');
40343         
40344         var cbwrap = this.combo.wrap.createChild(
40345             {tag: 'div', cls: 'x-cbarray-cb'},
40346             this.combo.el.dom
40347         );
40348         
40349              
40350         this.hiddenEl = this.combo.wrap.createChild({
40351             tag: 'input',  type:'hidden' , name: this.hiddenName, value : ''
40352         });
40353         this.el = this.combo.wrap.createChild({
40354             tag: 'input',  type:'hidden' , name: this.name, value : ''
40355         });
40356          //   this.el.dom.removeAttribute("name");
40357         
40358         
40359         this.outerWrap = this.combo.wrap;
40360         this.wrap = cbwrap;
40361         
40362         this.outerWrap.setWidth(this.width);
40363         this.outerWrap.dom.removeChild(this.el.dom);
40364         
40365         this.wrap.dom.appendChild(this.el.dom);
40366         this.outerWrap.dom.removeChild(this.combo.trigger.dom);
40367         this.combo.wrap.dom.appendChild(this.combo.trigger.dom);
40368         
40369         this.combo.trigger.setStyle('position','relative');
40370         this.combo.trigger.setStyle('left', '0px');
40371         this.combo.trigger.setStyle('top', '2px');
40372         
40373         this.combo.el.setStyle('vertical-align', 'text-bottom');
40374         
40375         //this.trigger.setStyle('vertical-align', 'top');
40376         
40377         // this should use the code from combo really... on('add' ....)
40378         if (this.adder) {
40379             
40380         
40381             this.adder = this.outerWrap.createChild(
40382                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-adder', style: 'margin-left:2px'});  
40383             var _t = this;
40384             this.adder.on('click', function(e) {
40385                 _t.fireEvent('adderclick', this, e);
40386             }, _t);
40387         }
40388         //var _t = this;
40389         //this.adder.on('click', this.onAddClick, _t);
40390         
40391         
40392         this.combo.on('select', function(cb, rec, ix) {
40393             this.addItem(rec.data);
40394             
40395             cb.setValue('');
40396             cb.el.dom.value = '';
40397             //cb.lastData = rec.data;
40398             // add to list
40399             
40400         }, this);
40401         
40402         
40403     },
40404     
40405     
40406     getName: function()
40407     {
40408         // returns hidden if it's set..
40409         if (!this.rendered) {return ''};
40410         return  this.hiddenName ? this.hiddenName : this.name;
40411         
40412     },
40413     
40414     
40415     onResize: function(w, h){
40416         
40417         return;
40418         // not sure if this is needed..
40419         //this.combo.onResize(w,h);
40420         
40421         if(typeof w != 'number'){
40422             // we do not handle it!?!?
40423             return;
40424         }
40425         var tw = this.combo.trigger.getWidth();
40426         tw += this.addicon ? this.addicon.getWidth() : 0;
40427         tw += this.editicon ? this.editicon.getWidth() : 0;
40428         var x = w - tw;
40429         this.combo.el.setWidth( this.combo.adjustWidth('input', x));
40430             
40431         this.combo.trigger.setStyle('left', '0px');
40432         
40433         if(this.list && this.listWidth === undefined){
40434             var lw = Math.max(x + this.combo.trigger.getWidth(), this.combo.minListWidth);
40435             this.list.setWidth(lw);
40436             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
40437         }
40438         
40439     
40440         
40441     },
40442     
40443     addItem: function(rec)
40444     {
40445         var valueField = this.combo.valueField;
40446         var displayField = this.combo.displayField;
40447         if (this.items.indexOfKey(rec[valueField]) > -1) {
40448             //console.log("GOT " + rec.data.id);
40449             return;
40450         }
40451         
40452         var x = new Roo.form.ComboBoxArray.Item({
40453             //id : rec[this.idField],
40454             data : rec,
40455             displayField : displayField ,
40456             tipField : displayField ,
40457             cb : this
40458         });
40459         // use the 
40460         this.items.add(rec[valueField],x);
40461         // add it before the element..
40462         this.updateHiddenEl();
40463         x.render(this.outerWrap, this.wrap.dom);
40464         // add the image handler..
40465     },
40466     
40467     updateHiddenEl : function()
40468     {
40469         this.validate();
40470         if (!this.hiddenEl) {
40471             return;
40472         }
40473         var ar = [];
40474         var idField = this.combo.valueField;
40475         
40476         this.items.each(function(f) {
40477             ar.push(f.data[idField]);
40478            
40479         });
40480         this.hiddenEl.dom.value = ar.join(',');
40481         this.validate();
40482     },
40483     
40484     reset : function()
40485     {
40486         //Roo.form.ComboBoxArray.superclass.reset.call(this); 
40487         this.items.each(function(f) {
40488            f.remove(); 
40489         });
40490         this.el.dom.value = '';
40491         if (this.hiddenEl) {
40492             this.hiddenEl.dom.value = '';
40493         }
40494         
40495     },
40496     getValue: function()
40497     {
40498         return this.hiddenEl ? this.hiddenEl.dom.value : '';
40499     },
40500     setValue: function(v) // not a valid action - must use addItems..
40501     {
40502          
40503         this.reset();
40504         
40505         
40506         
40507         if (this.store.isLocal && (typeof(v) == 'string')) {
40508             // then we can use the store to find the values..
40509             // comma seperated at present.. this needs to allow JSON based encoding..
40510             this.hiddenEl.value  = v;
40511             var v_ar = [];
40512             Roo.each(v.split(','), function(k) {
40513                 Roo.log("CHECK " + this.valueField + ',' + k);
40514                 var li = this.store.query(this.valueField, k);
40515                 if (!li.length) {
40516                     return;
40517                 }
40518                 var add = {};
40519                 add[this.valueField] = k;
40520                 add[this.displayField] = li.item(0).data[this.displayField];
40521                 
40522                 this.addItem(add);
40523             }, this) 
40524              
40525         }
40526         if (typeof(v) == 'object') {
40527             // then let's assume it's an array of objects..
40528             Roo.each(v, function(l) {
40529                 this.addItem(l);
40530             }, this);
40531              
40532         }
40533         
40534         
40535     },
40536     setFromData: function(v)
40537     {
40538         // this recieves an object, if setValues is called.
40539         this.reset();
40540         this.el.dom.value = v[this.displayField];
40541         this.hiddenEl.dom.value = v[this.valueField];
40542         if (typeof(v[this.valueField]) != 'string' || !v[this.valueField].length) {
40543             return;
40544         }
40545         var kv = v[this.valueField];
40546         var dv = v[this.displayField];
40547         kv = typeof(kv) != 'string' ? '' : kv;
40548         dv = typeof(dv) != 'string' ? '' : dv;
40549         
40550         
40551         var keys = kv.split(',');
40552         var display = dv.split(',');
40553         for (var i = 0 ; i < keys.length; i++) {
40554             
40555             add = {};
40556             add[this.valueField] = keys[i];
40557             add[this.displayField] = display[i];
40558             this.addItem(add);
40559         }
40560       
40561         
40562     },
40563     
40564     /**
40565      * Validates the combox array value
40566      * @return {Boolean} True if the value is valid, else false
40567      */
40568     validate : function(){
40569         if(this.disabled || this.validateValue(this.processValue(this.getValue()))){
40570             this.clearInvalid();
40571             return true;
40572         }
40573         return false;
40574     },
40575     
40576     validateValue : function(value){
40577         return Roo.form.ComboBoxArray.superclass.validateValue.call(this, this.getValue());
40578         
40579     },
40580     
40581     /*@
40582      * overide
40583      * 
40584      */
40585     isDirty : function() {
40586         if(this.disabled) {
40587             return false;
40588         }
40589         
40590         try {
40591             var d = Roo.decode(String(this.originalValue));
40592         } catch (e) {
40593             return String(this.getValue()) !== String(this.originalValue);
40594         }
40595         
40596         var originalValue = [];
40597         
40598         for (var i = 0; i < d.length; i++){
40599             originalValue.push(d[i][this.valueField]);
40600         }
40601         
40602         return String(this.getValue()) !== String(originalValue.join(','));
40603         
40604     }
40605     
40606 });
40607
40608
40609
40610 /**
40611  * @class Roo.form.ComboBoxArray.Item
40612  * @extends Roo.BoxComponent
40613  * A selected item in the list
40614  *  Fred [x]  Brian [x]  [Pick another |v]
40615  * 
40616  * @constructor
40617  * Create a new item.
40618  * @param {Object} config Configuration options
40619  */
40620  
40621 Roo.form.ComboBoxArray.Item = function(config) {
40622     config.id = Roo.id();
40623     Roo.form.ComboBoxArray.Item.superclass.constructor.call(this, config);
40624 }
40625
40626 Roo.extend(Roo.form.ComboBoxArray.Item, Roo.BoxComponent, {
40627     data : {},
40628     cb: false,
40629     displayField : false,
40630     tipField : false,
40631     
40632     
40633     defaultAutoCreate : {
40634         tag: 'div',
40635         cls: 'x-cbarray-item',
40636         cn : [ 
40637             { tag: 'div' },
40638             {
40639                 tag: 'img',
40640                 width:16,
40641                 height : 16,
40642                 src : Roo.BLANK_IMAGE_URL ,
40643                 align: 'center'
40644             }
40645         ]
40646         
40647     },
40648     
40649  
40650     onRender : function(ct, position)
40651     {
40652         Roo.form.Field.superclass.onRender.call(this, ct, position);
40653         
40654         if(!this.el){
40655             var cfg = this.getAutoCreate();
40656             this.el = ct.createChild(cfg, position);
40657         }
40658         
40659         this.el.child('img').dom.setAttribute('src', Roo.BLANK_IMAGE_URL);
40660         
40661         this.el.child('div').dom.innerHTML = this.cb.renderer ? 
40662             this.cb.renderer(this.data) :
40663             String.format('{0}',this.data[this.displayField]);
40664         
40665             
40666         this.el.child('div').dom.setAttribute('qtip',
40667                         String.format('{0}',this.data[this.tipField])
40668         );
40669         
40670         this.el.child('img').on('click', this.remove, this);
40671         
40672     },
40673    
40674     remove : function()
40675     {
40676         this.cb.items.remove(this);
40677         this.el.child('img').un('click', this.remove, this);
40678         this.el.remove();
40679         this.cb.updateHiddenEl();
40680         
40681         this.cb.fireEvent('remove', this.cb, this);
40682     }
40683 });/*
40684  * Based on:
40685  * Ext JS Library 1.1.1
40686  * Copyright(c) 2006-2007, Ext JS, LLC.
40687  *
40688  * Originally Released Under LGPL - original licence link has changed is not relivant.
40689  *
40690  * Fork - LGPL
40691  * <script type="text/javascript">
40692  */
40693 /**
40694  * @class Roo.form.Checkbox
40695  * @extends Roo.form.Field
40696  * Single checkbox field.  Can be used as a direct replacement for traditional checkbox fields.
40697  * @constructor
40698  * Creates a new Checkbox
40699  * @param {Object} config Configuration options
40700  */
40701 Roo.form.Checkbox = function(config){
40702     Roo.form.Checkbox.superclass.constructor.call(this, config);
40703     this.addEvents({
40704         /**
40705          * @event check
40706          * Fires when the checkbox is checked or unchecked.
40707              * @param {Roo.form.Checkbox} this This checkbox
40708              * @param {Boolean} checked The new checked value
40709              */
40710         check : true
40711     });
40712 };
40713
40714 Roo.extend(Roo.form.Checkbox, Roo.form.Field,  {
40715     /**
40716      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
40717      */
40718     focusClass : undefined,
40719     /**
40720      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
40721      */
40722     fieldClass: "x-form-field",
40723     /**
40724      * @cfg {Boolean} checked True if the the checkbox should render already checked (defaults to false)
40725      */
40726     checked: false,
40727     /**
40728      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
40729      * {tag: "input", type: "checkbox", autocomplete: "off"})
40730      */
40731     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "off"},
40732     /**
40733      * @cfg {String} boxLabel The text that appears beside the checkbox
40734      */
40735     boxLabel : "",
40736     /**
40737      * @cfg {String} inputValue The value that should go into the generated input element's value attribute
40738      */  
40739     inputValue : '1',
40740     /**
40741      * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
40742      */
40743      valueOff: '0', // value when not checked..
40744
40745     actionMode : 'viewEl', 
40746     //
40747     // private
40748     itemCls : 'x-menu-check-item x-form-item',
40749     groupClass : 'x-menu-group-item',
40750     inputType : 'hidden',
40751     
40752     
40753     inSetChecked: false, // check that we are not calling self...
40754     
40755     inputElement: false, // real input element?
40756     basedOn: false, // ????
40757     
40758     isFormField: true, // not sure where this is needed!!!!
40759
40760     onResize : function(){
40761         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
40762         if(!this.boxLabel){
40763             this.el.alignTo(this.wrap, 'c-c');
40764         }
40765     },
40766
40767     initEvents : function(){
40768         Roo.form.Checkbox.superclass.initEvents.call(this);
40769         this.el.on("click", this.onClick,  this);
40770         this.el.on("change", this.onClick,  this);
40771     },
40772
40773
40774     getResizeEl : function(){
40775         return this.wrap;
40776     },
40777
40778     getPositionEl : function(){
40779         return this.wrap;
40780     },
40781
40782     // private
40783     onRender : function(ct, position){
40784         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
40785         /*
40786         if(this.inputValue !== undefined){
40787             this.el.dom.value = this.inputValue;
40788         }
40789         */
40790         //this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
40791         this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
40792         var viewEl = this.wrap.createChild({ 
40793             tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
40794         this.viewEl = viewEl;   
40795         this.wrap.on('click', this.onClick,  this); 
40796         
40797         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
40798         this.el.on('propertychange', this.setFromHidden,  this);  //ie
40799         
40800         
40801         
40802         if(this.boxLabel){
40803             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
40804         //    viewEl.on('click', this.onClick,  this); 
40805         }
40806         //if(this.checked){
40807             this.setChecked(this.checked);
40808         //}else{
40809             //this.checked = this.el.dom;
40810         //}
40811
40812     },
40813
40814     // private
40815     initValue : Roo.emptyFn,
40816
40817     /**
40818      * Returns the checked state of the checkbox.
40819      * @return {Boolean} True if checked, else false
40820      */
40821     getValue : function(){
40822         if(this.el){
40823             return String(this.el.dom.value) == String(this.inputValue ) ? this.inputValue : this.valueOff;
40824         }
40825         return this.valueOff;
40826         
40827     },
40828
40829         // private
40830     onClick : function(){ 
40831         this.setChecked(!this.checked);
40832
40833         //if(this.el.dom.checked != this.checked){
40834         //    this.setValue(this.el.dom.checked);
40835        // }
40836     },
40837
40838     /**
40839      * Sets the checked state of the checkbox.
40840      * On is always based on a string comparison between inputValue and the param.
40841      * @param {Boolean/String} value - the value to set 
40842      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
40843      */
40844     setValue : function(v,suppressEvent){
40845         
40846         
40847         //this.checked = (v === true || v === 'true' || v == '1' || String(v).toLowerCase() == 'on');
40848         //if(this.el && this.el.dom){
40849         //    this.el.dom.checked = this.checked;
40850         //    this.el.dom.defaultChecked = this.checked;
40851         //}
40852         this.setChecked(String(v) === String(this.inputValue), suppressEvent);
40853         //this.fireEvent("check", this, this.checked);
40854     },
40855     // private..
40856     setChecked : function(state,suppressEvent)
40857     {
40858         if (this.inSetChecked) {
40859             this.checked = state;
40860             return;
40861         }
40862         
40863     
40864         if(this.wrap){
40865             this.wrap[state ? 'addClass' : 'removeClass']('x-menu-item-checked');
40866         }
40867         this.checked = state;
40868         if(suppressEvent !== true){
40869             this.fireEvent('check', this, state);
40870         }
40871         this.inSetChecked = true;
40872         this.el.dom.value = state ? this.inputValue : this.valueOff;
40873         this.inSetChecked = false;
40874         
40875     },
40876     // handle setting of hidden value by some other method!!?!?
40877     setFromHidden: function()
40878     {
40879         if(!this.el){
40880             return;
40881         }
40882         //console.log("SET FROM HIDDEN");
40883         //alert('setFrom hidden');
40884         this.setValue(this.el.dom.value);
40885     },
40886     
40887     onDestroy : function()
40888     {
40889         if(this.viewEl){
40890             Roo.get(this.viewEl).remove();
40891         }
40892          
40893         Roo.form.Checkbox.superclass.onDestroy.call(this);
40894     }
40895
40896 });/*
40897  * Based on:
40898  * Ext JS Library 1.1.1
40899  * Copyright(c) 2006-2007, Ext JS, LLC.
40900  *
40901  * Originally Released Under LGPL - original licence link has changed is not relivant.
40902  *
40903  * Fork - LGPL
40904  * <script type="text/javascript">
40905  */
40906  
40907 /**
40908  * @class Roo.form.Radio
40909  * @extends Roo.form.Checkbox
40910  * Single radio field.  Same as Checkbox, but provided as a convenience for automatically setting the input type.
40911  * Radio grouping is handled automatically by the browser if you give each radio in a group the same name.
40912  * @constructor
40913  * Creates a new Radio
40914  * @param {Object} config Configuration options
40915  */
40916 Roo.form.Radio = function(){
40917     Roo.form.Radio.superclass.constructor.apply(this, arguments);
40918 };
40919 Roo.extend(Roo.form.Radio, Roo.form.Checkbox, {
40920     inputType: 'radio',
40921
40922     /**
40923      * If this radio is part of a group, it will return the selected value
40924      * @return {String}
40925      */
40926     getGroupValue : function(){
40927         return this.el.up('form').child('input[name='+this.el.dom.name+']:checked', true).value;
40928     },
40929     
40930     
40931     onRender : function(ct, position){
40932         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
40933         
40934         if(this.inputValue !== undefined){
40935             this.el.dom.value = this.inputValue;
40936         }
40937          
40938         this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
40939         //this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
40940         //var viewEl = this.wrap.createChild({ 
40941         //    tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
40942         //this.viewEl = viewEl;   
40943         //this.wrap.on('click', this.onClick,  this); 
40944         
40945         //this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
40946         //this.el.on('propertychange', this.setFromHidden,  this);  //ie
40947         
40948         
40949         
40950         if(this.boxLabel){
40951             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
40952         //    viewEl.on('click', this.onClick,  this); 
40953         }
40954          if(this.checked){
40955             this.el.dom.checked =   'checked' ;
40956         }
40957          
40958     } 
40959     
40960     
40961 });//<script type="text/javascript">
40962
40963 /*
40964  * Based  Ext JS Library 1.1.1
40965  * Copyright(c) 2006-2007, Ext JS, LLC.
40966  * LGPL
40967  *
40968  */
40969  
40970 /**
40971  * @class Roo.HtmlEditorCore
40972  * @extends Roo.Component
40973  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
40974  *
40975  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
40976  */
40977
40978 Roo.HtmlEditorCore = function(config){
40979     
40980     
40981     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
40982     this.addEvents({
40983         /**
40984          * @event initialize
40985          * Fires when the editor is fully initialized (including the iframe)
40986          * @param {Roo.HtmlEditorCore} this
40987          */
40988         initialize: true,
40989         /**
40990          * @event activate
40991          * Fires when the editor is first receives the focus. Any insertion must wait
40992          * until after this event.
40993          * @param {Roo.HtmlEditorCore} this
40994          */
40995         activate: true,
40996          /**
40997          * @event beforesync
40998          * Fires before the textarea is updated with content from the editor iframe. Return false
40999          * to cancel the sync.
41000          * @param {Roo.HtmlEditorCore} this
41001          * @param {String} html
41002          */
41003         beforesync: true,
41004          /**
41005          * @event beforepush
41006          * Fires before the iframe editor is updated with content from the textarea. Return false
41007          * to cancel the push.
41008          * @param {Roo.HtmlEditorCore} this
41009          * @param {String} html
41010          */
41011         beforepush: true,
41012          /**
41013          * @event sync
41014          * Fires when the textarea is updated with content from the editor iframe.
41015          * @param {Roo.HtmlEditorCore} this
41016          * @param {String} html
41017          */
41018         sync: true,
41019          /**
41020          * @event push
41021          * Fires when the iframe editor is updated with content from the textarea.
41022          * @param {Roo.HtmlEditorCore} this
41023          * @param {String} html
41024          */
41025         push: true,
41026         
41027         /**
41028          * @event editorevent
41029          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
41030          * @param {Roo.HtmlEditorCore} this
41031          */
41032         editorevent: true
41033     });
41034      
41035 };
41036
41037
41038 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
41039
41040
41041      /**
41042      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
41043      */
41044     
41045     owner : false,
41046     
41047      /**
41048      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
41049      *                        Roo.resizable.
41050      */
41051     resizable : false,
41052      /**
41053      * @cfg {Number} height (in pixels)
41054      */   
41055     height: 300,
41056    /**
41057      * @cfg {Number} width (in pixels)
41058      */   
41059     width: 500,
41060     
41061     /**
41062      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
41063      * 
41064      */
41065     stylesheets: false,
41066     
41067     // id of frame..
41068     frameId: false,
41069     
41070     // private properties
41071     validationEvent : false,
41072     deferHeight: true,
41073     initialized : false,
41074     activated : false,
41075     sourceEditMode : false,
41076     onFocus : Roo.emptyFn,
41077     iframePad:3,
41078     hideMode:'offsets',
41079     
41080     clearUp: true,
41081     
41082      
41083     
41084
41085     /**
41086      * Protected method that will not generally be called directly. It
41087      * is called when the editor initializes the iframe with HTML contents. Override this method if you
41088      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
41089      */
41090     getDocMarkup : function(){
41091         // body styles..
41092         var st = '';
41093         Roo.log(this.stylesheets);
41094         
41095         // inherit styels from page...?? 
41096         if (this.stylesheets === false) {
41097             
41098             Roo.get(document.head).select('style').each(function(node) {
41099                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
41100             });
41101             
41102             Roo.get(document.head).select('link').each(function(node) { 
41103                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
41104             });
41105             
41106         } else if (!this.stylesheets.length) {
41107                 // simple..
41108                 st = '<style type="text/css">' +
41109                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
41110                    '</style>';
41111         } else {
41112             Roo.each(this.stylesheets, function(s) {
41113                 st += '<link rel="stylesheet" type="text/css" href="' + s +'" />'
41114             });
41115             
41116         }
41117         
41118         st +=  '<style type="text/css">' +
41119             'IMG { cursor: pointer } ' +
41120         '</style>';
41121
41122         
41123         return '<html><head>' + st  +
41124             //<style type="text/css">' +
41125             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
41126             //'</style>' +
41127             ' </head><body class="roo-htmleditor-body"></body></html>';
41128     },
41129
41130     // private
41131     onRender : function(ct, position)
41132     {
41133         var _t = this;
41134         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
41135         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
41136         
41137         
41138         this.el.dom.style.border = '0 none';
41139         this.el.dom.setAttribute('tabIndex', -1);
41140         this.el.addClass('x-hidden hide');
41141         
41142         
41143         
41144         if(Roo.isIE){ // fix IE 1px bogus margin
41145             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
41146         }
41147        
41148         
41149         this.frameId = Roo.id();
41150         
41151          
41152         
41153         var iframe = this.owner.wrap.createChild({
41154             tag: 'iframe',
41155             cls: 'form-control', // bootstrap..
41156             id: this.frameId,
41157             name: this.frameId,
41158             frameBorder : 'no',
41159             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
41160         }, this.el
41161         );
41162         
41163         
41164         this.iframe = iframe.dom;
41165
41166          this.assignDocWin();
41167         
41168         this.doc.designMode = 'on';
41169        
41170         this.doc.open();
41171         this.doc.write(this.getDocMarkup());
41172         this.doc.close();
41173
41174         
41175         var task = { // must defer to wait for browser to be ready
41176             run : function(){
41177                 //console.log("run task?" + this.doc.readyState);
41178                 this.assignDocWin();
41179                 if(this.doc.body || this.doc.readyState == 'complete'){
41180                     try {
41181                         this.doc.designMode="on";
41182                     } catch (e) {
41183                         return;
41184                     }
41185                     Roo.TaskMgr.stop(task);
41186                     this.initEditor.defer(10, this);
41187                 }
41188             },
41189             interval : 10,
41190             duration: 10000,
41191             scope: this
41192         };
41193         Roo.TaskMgr.start(task);
41194
41195         
41196          
41197     },
41198
41199     // private
41200     onResize : function(w, h)
41201     {
41202          Roo.log('resize: ' +w + ',' + h );
41203         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
41204         if(!this.iframe){
41205             return;
41206         }
41207         if(typeof w == 'number'){
41208             
41209             this.iframe.style.width = w + 'px';
41210         }
41211         if(typeof h == 'number'){
41212             
41213             this.iframe.style.height = h + 'px';
41214             if(this.doc){
41215                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
41216             }
41217         }
41218         
41219     },
41220
41221     /**
41222      * Toggles the editor between standard and source edit mode.
41223      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
41224      */
41225     toggleSourceEdit : function(sourceEditMode){
41226         
41227         this.sourceEditMode = sourceEditMode === true;
41228         
41229         if(this.sourceEditMode){
41230  
41231             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
41232             
41233         }else{
41234             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
41235             //this.iframe.className = '';
41236             this.deferFocus();
41237         }
41238         //this.setSize(this.owner.wrap.getSize());
41239         //this.fireEvent('editmodechange', this, this.sourceEditMode);
41240     },
41241
41242     
41243   
41244
41245     /**
41246      * Protected method that will not generally be called directly. If you need/want
41247      * custom HTML cleanup, this is the method you should override.
41248      * @param {String} html The HTML to be cleaned
41249      * return {String} The cleaned HTML
41250      */
41251     cleanHtml : function(html){
41252         html = String(html);
41253         if(html.length > 5){
41254             if(Roo.isSafari){ // strip safari nonsense
41255                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
41256             }
41257         }
41258         if(html == '&nbsp;'){
41259             html = '';
41260         }
41261         return html;
41262     },
41263
41264     /**
41265      * HTML Editor -> Textarea
41266      * Protected method that will not generally be called directly. Syncs the contents
41267      * of the editor iframe with the textarea.
41268      */
41269     syncValue : function(){
41270         if(this.initialized){
41271             var bd = (this.doc.body || this.doc.documentElement);
41272             //this.cleanUpPaste(); -- this is done else where and causes havoc..
41273             var html = bd.innerHTML;
41274             if(Roo.isSafari){
41275                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
41276                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
41277                 if(m && m[1]){
41278                     html = '<div style="'+m[0]+'">' + html + '</div>';
41279                 }
41280             }
41281             html = this.cleanHtml(html);
41282             // fix up the special chars.. normaly like back quotes in word...
41283             // however we do not want to do this with chinese..
41284             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
41285                 var cc = b.charCodeAt();
41286                 if (
41287                     (cc >= 0x4E00 && cc < 0xA000 ) ||
41288                     (cc >= 0x3400 && cc < 0x4E00 ) ||
41289                     (cc >= 0xf900 && cc < 0xfb00 )
41290                 ) {
41291                         return b;
41292                 }
41293                 return "&#"+cc+";" 
41294             });
41295             if(this.owner.fireEvent('beforesync', this, html) !== false){
41296                 this.el.dom.value = html;
41297                 this.owner.fireEvent('sync', this, html);
41298             }
41299         }
41300     },
41301
41302     /**
41303      * Protected method that will not generally be called directly. Pushes the value of the textarea
41304      * into the iframe editor.
41305      */
41306     pushValue : function(){
41307         if(this.initialized){
41308             var v = this.el.dom.value.trim();
41309             
41310 //            if(v.length < 1){
41311 //                v = '&#160;';
41312 //            }
41313             
41314             if(this.owner.fireEvent('beforepush', this, v) !== false){
41315                 var d = (this.doc.body || this.doc.documentElement);
41316                 d.innerHTML = v;
41317                 this.cleanUpPaste();
41318                 this.el.dom.value = d.innerHTML;
41319                 this.owner.fireEvent('push', this, v);
41320             }
41321         }
41322     },
41323
41324     // private
41325     deferFocus : function(){
41326         this.focus.defer(10, this);
41327     },
41328
41329     // doc'ed in Field
41330     focus : function(){
41331         if(this.win && !this.sourceEditMode){
41332             this.win.focus();
41333         }else{
41334             this.el.focus();
41335         }
41336     },
41337     
41338     assignDocWin: function()
41339     {
41340         var iframe = this.iframe;
41341         
41342          if(Roo.isIE){
41343             this.doc = iframe.contentWindow.document;
41344             this.win = iframe.contentWindow;
41345         } else {
41346             if (!Roo.get(this.frameId)) {
41347                 return;
41348             }
41349             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
41350             this.win = Roo.get(this.frameId).dom.contentWindow;
41351         }
41352     },
41353     
41354     // private
41355     initEditor : function(){
41356         //console.log("INIT EDITOR");
41357         this.assignDocWin();
41358         
41359         
41360         
41361         this.doc.designMode="on";
41362         this.doc.open();
41363         this.doc.write(this.getDocMarkup());
41364         this.doc.close();
41365         
41366         var dbody = (this.doc.body || this.doc.documentElement);
41367         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
41368         // this copies styles from the containing element into thsi one..
41369         // not sure why we need all of this..
41370         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
41371         
41372         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
41373         //ss['background-attachment'] = 'fixed'; // w3c
41374         dbody.bgProperties = 'fixed'; // ie
41375         //Roo.DomHelper.applyStyles(dbody, ss);
41376         Roo.EventManager.on(this.doc, {
41377             //'mousedown': this.onEditorEvent,
41378             'mouseup': this.onEditorEvent,
41379             'dblclick': this.onEditorEvent,
41380             'click': this.onEditorEvent,
41381             'keyup': this.onEditorEvent,
41382             buffer:100,
41383             scope: this
41384         });
41385         if(Roo.isGecko){
41386             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
41387         }
41388         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
41389             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
41390         }
41391         this.initialized = true;
41392
41393         this.owner.fireEvent('initialize', this);
41394         this.pushValue();
41395     },
41396
41397     // private
41398     onDestroy : function(){
41399         
41400         
41401         
41402         if(this.rendered){
41403             
41404             //for (var i =0; i < this.toolbars.length;i++) {
41405             //    // fixme - ask toolbars for heights?
41406             //    this.toolbars[i].onDestroy();
41407            // }
41408             
41409             //this.wrap.dom.innerHTML = '';
41410             //this.wrap.remove();
41411         }
41412     },
41413
41414     // private
41415     onFirstFocus : function(){
41416         
41417         this.assignDocWin();
41418         
41419         
41420         this.activated = true;
41421          
41422     
41423         if(Roo.isGecko){ // prevent silly gecko errors
41424             this.win.focus();
41425             var s = this.win.getSelection();
41426             if(!s.focusNode || s.focusNode.nodeType != 3){
41427                 var r = s.getRangeAt(0);
41428                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
41429                 r.collapse(true);
41430                 this.deferFocus();
41431             }
41432             try{
41433                 this.execCmd('useCSS', true);
41434                 this.execCmd('styleWithCSS', false);
41435             }catch(e){}
41436         }
41437         this.owner.fireEvent('activate', this);
41438     },
41439
41440     // private
41441     adjustFont: function(btn){
41442         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
41443         //if(Roo.isSafari){ // safari
41444         //    adjust *= 2;
41445        // }
41446         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
41447         if(Roo.isSafari){ // safari
41448             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
41449             v =  (v < 10) ? 10 : v;
41450             v =  (v > 48) ? 48 : v;
41451             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
41452             
41453         }
41454         
41455         
41456         v = Math.max(1, v+adjust);
41457         
41458         this.execCmd('FontSize', v  );
41459     },
41460
41461     onEditorEvent : function(e){
41462         this.owner.fireEvent('editorevent', this, e);
41463       //  this.updateToolbar();
41464         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
41465     },
41466
41467     insertTag : function(tg)
41468     {
41469         // could be a bit smarter... -> wrap the current selected tRoo..
41470         if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
41471             
41472             range = this.createRange(this.getSelection());
41473             var wrappingNode = this.doc.createElement(tg.toLowerCase());
41474             wrappingNode.appendChild(range.extractContents());
41475             range.insertNode(wrappingNode);
41476
41477             return;
41478             
41479             
41480             
41481         }
41482         this.execCmd("formatblock",   tg);
41483         
41484     },
41485     
41486     insertText : function(txt)
41487     {
41488         
41489         
41490         var range = this.createRange();
41491         range.deleteContents();
41492                //alert(Sender.getAttribute('label'));
41493                
41494         range.insertNode(this.doc.createTextNode(txt));
41495     } ,
41496     
41497      
41498
41499     /**
41500      * Executes a Midas editor command on the editor document and performs necessary focus and
41501      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
41502      * @param {String} cmd The Midas command
41503      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
41504      */
41505     relayCmd : function(cmd, value){
41506         this.win.focus();
41507         this.execCmd(cmd, value);
41508         this.owner.fireEvent('editorevent', this);
41509         //this.updateToolbar();
41510         this.owner.deferFocus();
41511     },
41512
41513     /**
41514      * Executes a Midas editor command directly on the editor document.
41515      * For visual commands, you should use {@link #relayCmd} instead.
41516      * <b>This should only be called after the editor is initialized.</b>
41517      * @param {String} cmd The Midas command
41518      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
41519      */
41520     execCmd : function(cmd, value){
41521         this.doc.execCommand(cmd, false, value === undefined ? null : value);
41522         this.syncValue();
41523     },
41524  
41525  
41526    
41527     /**
41528      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
41529      * to insert tRoo.
41530      * @param {String} text | dom node.. 
41531      */
41532     insertAtCursor : function(text)
41533     {
41534         
41535         
41536         
41537         if(!this.activated){
41538             return;
41539         }
41540         /*
41541         if(Roo.isIE){
41542             this.win.focus();
41543             var r = this.doc.selection.createRange();
41544             if(r){
41545                 r.collapse(true);
41546                 r.pasteHTML(text);
41547                 this.syncValue();
41548                 this.deferFocus();
41549             
41550             }
41551             return;
41552         }
41553         */
41554         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
41555             this.win.focus();
41556             
41557             
41558             // from jquery ui (MIT licenced)
41559             var range, node;
41560             var win = this.win;
41561             
41562             if (win.getSelection && win.getSelection().getRangeAt) {
41563                 range = win.getSelection().getRangeAt(0);
41564                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
41565                 range.insertNode(node);
41566             } else if (win.document.selection && win.document.selection.createRange) {
41567                 // no firefox support
41568                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
41569                 win.document.selection.createRange().pasteHTML(txt);
41570             } else {
41571                 // no firefox support
41572                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
41573                 this.execCmd('InsertHTML', txt);
41574             } 
41575             
41576             this.syncValue();
41577             
41578             this.deferFocus();
41579         }
41580     },
41581  // private
41582     mozKeyPress : function(e){
41583         if(e.ctrlKey){
41584             var c = e.getCharCode(), cmd;
41585           
41586             if(c > 0){
41587                 c = String.fromCharCode(c).toLowerCase();
41588                 switch(c){
41589                     case 'b':
41590                         cmd = 'bold';
41591                         break;
41592                     case 'i':
41593                         cmd = 'italic';
41594                         break;
41595                     
41596                     case 'u':
41597                         cmd = 'underline';
41598                         break;
41599                     
41600                     case 'v':
41601                         this.cleanUpPaste.defer(100, this);
41602                         return;
41603                         
41604                 }
41605                 if(cmd){
41606                     this.win.focus();
41607                     this.execCmd(cmd);
41608                     this.deferFocus();
41609                     e.preventDefault();
41610                 }
41611                 
41612             }
41613         }
41614     },
41615
41616     // private
41617     fixKeys : function(){ // load time branching for fastest keydown performance
41618         if(Roo.isIE){
41619             return function(e){
41620                 var k = e.getKey(), r;
41621                 if(k == e.TAB){
41622                     e.stopEvent();
41623                     r = this.doc.selection.createRange();
41624                     if(r){
41625                         r.collapse(true);
41626                         r.pasteHTML('&#160;&#160;&#160;&#160;');
41627                         this.deferFocus();
41628                     }
41629                     return;
41630                 }
41631                 
41632                 if(k == e.ENTER){
41633                     r = this.doc.selection.createRange();
41634                     if(r){
41635                         var target = r.parentElement();
41636                         if(!target || target.tagName.toLowerCase() != 'li'){
41637                             e.stopEvent();
41638                             r.pasteHTML('<br />');
41639                             r.collapse(false);
41640                             r.select();
41641                         }
41642                     }
41643                 }
41644                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
41645                     this.cleanUpPaste.defer(100, this);
41646                     return;
41647                 }
41648                 
41649                 
41650             };
41651         }else if(Roo.isOpera){
41652             return function(e){
41653                 var k = e.getKey();
41654                 if(k == e.TAB){
41655                     e.stopEvent();
41656                     this.win.focus();
41657                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
41658                     this.deferFocus();
41659                 }
41660                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
41661                     this.cleanUpPaste.defer(100, this);
41662                     return;
41663                 }
41664                 
41665             };
41666         }else if(Roo.isSafari){
41667             return function(e){
41668                 var k = e.getKey();
41669                 
41670                 if(k == e.TAB){
41671                     e.stopEvent();
41672                     this.execCmd('InsertText','\t');
41673                     this.deferFocus();
41674                     return;
41675                 }
41676                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
41677                     this.cleanUpPaste.defer(100, this);
41678                     return;
41679                 }
41680                 
41681              };
41682         }
41683     }(),
41684     
41685     getAllAncestors: function()
41686     {
41687         var p = this.getSelectedNode();
41688         var a = [];
41689         if (!p) {
41690             a.push(p); // push blank onto stack..
41691             p = this.getParentElement();
41692         }
41693         
41694         
41695         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
41696             a.push(p);
41697             p = p.parentNode;
41698         }
41699         a.push(this.doc.body);
41700         return a;
41701     },
41702     lastSel : false,
41703     lastSelNode : false,
41704     
41705     
41706     getSelection : function() 
41707     {
41708         this.assignDocWin();
41709         return Roo.isIE ? this.doc.selection : this.win.getSelection();
41710     },
41711     
41712     getSelectedNode: function() 
41713     {
41714         // this may only work on Gecko!!!
41715         
41716         // should we cache this!!!!
41717         
41718         
41719         
41720          
41721         var range = this.createRange(this.getSelection()).cloneRange();
41722         
41723         if (Roo.isIE) {
41724             var parent = range.parentElement();
41725             while (true) {
41726                 var testRange = range.duplicate();
41727                 testRange.moveToElementText(parent);
41728                 if (testRange.inRange(range)) {
41729                     break;
41730                 }
41731                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
41732                     break;
41733                 }
41734                 parent = parent.parentElement;
41735             }
41736             return parent;
41737         }
41738         
41739         // is ancestor a text element.
41740         var ac =  range.commonAncestorContainer;
41741         if (ac.nodeType == 3) {
41742             ac = ac.parentNode;
41743         }
41744         
41745         var ar = ac.childNodes;
41746          
41747         var nodes = [];
41748         var other_nodes = [];
41749         var has_other_nodes = false;
41750         for (var i=0;i<ar.length;i++) {
41751             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
41752                 continue;
41753             }
41754             // fullly contained node.
41755             
41756             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
41757                 nodes.push(ar[i]);
41758                 continue;
41759             }
41760             
41761             // probably selected..
41762             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
41763                 other_nodes.push(ar[i]);
41764                 continue;
41765             }
41766             // outer..
41767             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
41768                 continue;
41769             }
41770             
41771             
41772             has_other_nodes = true;
41773         }
41774         if (!nodes.length && other_nodes.length) {
41775             nodes= other_nodes;
41776         }
41777         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
41778             return false;
41779         }
41780         
41781         return nodes[0];
41782     },
41783     createRange: function(sel)
41784     {
41785         // this has strange effects when using with 
41786         // top toolbar - not sure if it's a great idea.
41787         //this.editor.contentWindow.focus();
41788         if (typeof sel != "undefined") {
41789             try {
41790                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
41791             } catch(e) {
41792                 return this.doc.createRange();
41793             }
41794         } else {
41795             return this.doc.createRange();
41796         }
41797     },
41798     getParentElement: function()
41799     {
41800         
41801         this.assignDocWin();
41802         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
41803         
41804         var range = this.createRange(sel);
41805          
41806         try {
41807             var p = range.commonAncestorContainer;
41808             while (p.nodeType == 3) { // text node
41809                 p = p.parentNode;
41810             }
41811             return p;
41812         } catch (e) {
41813             return null;
41814         }
41815     
41816     },
41817     /***
41818      *
41819      * Range intersection.. the hard stuff...
41820      *  '-1' = before
41821      *  '0' = hits..
41822      *  '1' = after.
41823      *         [ -- selected range --- ]
41824      *   [fail]                        [fail]
41825      *
41826      *    basically..
41827      *      if end is before start or  hits it. fail.
41828      *      if start is after end or hits it fail.
41829      *
41830      *   if either hits (but other is outside. - then it's not 
41831      *   
41832      *    
41833      **/
41834     
41835     
41836     // @see http://www.thismuchiknow.co.uk/?p=64.
41837     rangeIntersectsNode : function(range, node)
41838     {
41839         var nodeRange = node.ownerDocument.createRange();
41840         try {
41841             nodeRange.selectNode(node);
41842         } catch (e) {
41843             nodeRange.selectNodeContents(node);
41844         }
41845     
41846         var rangeStartRange = range.cloneRange();
41847         rangeStartRange.collapse(true);
41848     
41849         var rangeEndRange = range.cloneRange();
41850         rangeEndRange.collapse(false);
41851     
41852         var nodeStartRange = nodeRange.cloneRange();
41853         nodeStartRange.collapse(true);
41854     
41855         var nodeEndRange = nodeRange.cloneRange();
41856         nodeEndRange.collapse(false);
41857     
41858         return rangeStartRange.compareBoundaryPoints(
41859                  Range.START_TO_START, nodeEndRange) == -1 &&
41860                rangeEndRange.compareBoundaryPoints(
41861                  Range.START_TO_START, nodeStartRange) == 1;
41862         
41863          
41864     },
41865     rangeCompareNode : function(range, node)
41866     {
41867         var nodeRange = node.ownerDocument.createRange();
41868         try {
41869             nodeRange.selectNode(node);
41870         } catch (e) {
41871             nodeRange.selectNodeContents(node);
41872         }
41873         
41874         
41875         range.collapse(true);
41876     
41877         nodeRange.collapse(true);
41878      
41879         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
41880         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
41881          
41882         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
41883         
41884         var nodeIsBefore   =  ss == 1;
41885         var nodeIsAfter    = ee == -1;
41886         
41887         if (nodeIsBefore && nodeIsAfter)
41888             return 0; // outer
41889         if (!nodeIsBefore && nodeIsAfter)
41890             return 1; //right trailed.
41891         
41892         if (nodeIsBefore && !nodeIsAfter)
41893             return 2;  // left trailed.
41894         // fully contined.
41895         return 3;
41896     },
41897
41898     // private? - in a new class?
41899     cleanUpPaste :  function()
41900     {
41901         // cleans up the whole document..
41902         Roo.log('cleanuppaste');
41903         
41904         this.cleanUpChildren(this.doc.body);
41905         var clean = this.cleanWordChars(this.doc.body.innerHTML);
41906         if (clean != this.doc.body.innerHTML) {
41907             this.doc.body.innerHTML = clean;
41908         }
41909         
41910     },
41911     
41912     cleanWordChars : function(input) {// change the chars to hex code
41913         var he = Roo.HtmlEditorCore;
41914         
41915         var output = input;
41916         Roo.each(he.swapCodes, function(sw) { 
41917             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
41918             
41919             output = output.replace(swapper, sw[1]);
41920         });
41921         
41922         return output;
41923     },
41924     
41925     
41926     cleanUpChildren : function (n)
41927     {
41928         if (!n.childNodes.length) {
41929             return;
41930         }
41931         for (var i = n.childNodes.length-1; i > -1 ; i--) {
41932            this.cleanUpChild(n.childNodes[i]);
41933         }
41934     },
41935     
41936     
41937         
41938     
41939     cleanUpChild : function (node)
41940     {
41941         var ed = this;
41942         //console.log(node);
41943         if (node.nodeName == "#text") {
41944             // clean up silly Windows -- stuff?
41945             return; 
41946         }
41947         if (node.nodeName == "#comment") {
41948             node.parentNode.removeChild(node);
41949             // clean up silly Windows -- stuff?
41950             return; 
41951         }
41952         
41953         if (Roo.HtmlEditorCore.black.indexOf(node.tagName.toLowerCase()) > -1 && this.clearUp) {
41954             // remove node.
41955             node.parentNode.removeChild(node);
41956             return;
41957             
41958         }
41959         
41960         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
41961         
41962         // remove <a name=....> as rendering on yahoo mailer is borked with this.
41963         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
41964         
41965         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
41966         //    remove_keep_children = true;
41967         //}
41968         
41969         if (remove_keep_children) {
41970             this.cleanUpChildren(node);
41971             // inserts everything just before this node...
41972             while (node.childNodes.length) {
41973                 var cn = node.childNodes[0];
41974                 node.removeChild(cn);
41975                 node.parentNode.insertBefore(cn, node);
41976             }
41977             node.parentNode.removeChild(node);
41978             return;
41979         }
41980         
41981         if (!node.attributes || !node.attributes.length) {
41982             this.cleanUpChildren(node);
41983             return;
41984         }
41985         
41986         function cleanAttr(n,v)
41987         {
41988             
41989             if (v.match(/^\./) || v.match(/^\//)) {
41990                 return;
41991             }
41992             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
41993                 return;
41994             }
41995             if (v.match(/^#/)) {
41996                 return;
41997             }
41998 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
41999             node.removeAttribute(n);
42000             
42001         }
42002         
42003         function cleanStyle(n,v)
42004         {
42005             if (v.match(/expression/)) { //XSS?? should we even bother..
42006                 node.removeAttribute(n);
42007                 return;
42008             }
42009             var cwhite = typeof(ed.cwhite) == 'undefined' ? Roo.HtmlEditorCore.cwhite : ed.cwhite;
42010             var cblack = typeof(ed.cblack) == 'undefined' ? Roo.HtmlEditorCore.cblack : ed.cblack;
42011             
42012             
42013             var parts = v.split(/;/);
42014             var clean = [];
42015             
42016             Roo.each(parts, function(p) {
42017                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
42018                 if (!p.length) {
42019                     return true;
42020                 }
42021                 var l = p.split(':').shift().replace(/\s+/g,'');
42022                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
42023                 
42024                 if ( cblack.indexOf(l) > -1) {
42025 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
42026                     //node.removeAttribute(n);
42027                     return true;
42028                 }
42029                 //Roo.log()
42030                 // only allow 'c whitelisted system attributes'
42031                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
42032 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
42033                     //node.removeAttribute(n);
42034                     return true;
42035                 }
42036                 
42037                 
42038                  
42039                 
42040                 clean.push(p);
42041                 return true;
42042             });
42043             if (clean.length) { 
42044                 node.setAttribute(n, clean.join(';'));
42045             } else {
42046                 node.removeAttribute(n);
42047             }
42048             
42049         }
42050         
42051         
42052         for (var i = node.attributes.length-1; i > -1 ; i--) {
42053             var a = node.attributes[i];
42054             //console.log(a);
42055             
42056             if (a.name.toLowerCase().substr(0,2)=='on')  {
42057                 node.removeAttribute(a.name);
42058                 continue;
42059             }
42060             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
42061                 node.removeAttribute(a.name);
42062                 continue;
42063             }
42064             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
42065                 cleanAttr(a.name,a.value); // fixme..
42066                 continue;
42067             }
42068             if (a.name == 'style') {
42069                 cleanStyle(a.name,a.value);
42070                 continue;
42071             }
42072             /// clean up MS crap..
42073             // tecnically this should be a list of valid class'es..
42074             
42075             
42076             if (a.name == 'class') {
42077                 if (a.value.match(/^Mso/)) {
42078                     node.className = '';
42079                 }
42080                 
42081                 if (a.value.match(/body/)) {
42082                     node.className = '';
42083                 }
42084                 continue;
42085             }
42086             
42087             // style cleanup!?
42088             // class cleanup?
42089             
42090         }
42091         
42092         
42093         this.cleanUpChildren(node);
42094         
42095         
42096     },
42097     /**
42098      * Clean up MS wordisms...
42099      */
42100     cleanWord : function(node)
42101     {
42102         var _t = this;
42103         var cleanWordChildren = function()
42104         {
42105             if (!node.childNodes.length) {
42106                 return;
42107             }
42108             for (var i = node.childNodes.length-1; i > -1 ; i--) {
42109                _t.cleanWord(node.childNodes[i]);
42110             }
42111         }
42112         
42113         
42114         if (!node) {
42115             this.cleanWord(this.doc.body);
42116             return;
42117         }
42118         if (node.nodeName == "#text") {
42119             // clean up silly Windows -- stuff?
42120             return; 
42121         }
42122         if (node.nodeName == "#comment") {
42123             node.parentNode.removeChild(node);
42124             // clean up silly Windows -- stuff?
42125             return; 
42126         }
42127         
42128         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
42129             node.parentNode.removeChild(node);
42130             return;
42131         }
42132         
42133         // remove - but keep children..
42134         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
42135             while (node.childNodes.length) {
42136                 var cn = node.childNodes[0];
42137                 node.removeChild(cn);
42138                 node.parentNode.insertBefore(cn, node);
42139             }
42140             node.parentNode.removeChild(node);
42141             cleanWordChildren();
42142             return;
42143         }
42144         // clean styles
42145         if (node.className.length) {
42146             
42147             var cn = node.className.split(/\W+/);
42148             var cna = [];
42149             Roo.each(cn, function(cls) {
42150                 if (cls.match(/Mso[a-zA-Z]+/)) {
42151                     return;
42152                 }
42153                 cna.push(cls);
42154             });
42155             node.className = cna.length ? cna.join(' ') : '';
42156             if (!cna.length) {
42157                 node.removeAttribute("class");
42158             }
42159         }
42160         
42161         if (node.hasAttribute("lang")) {
42162             node.removeAttribute("lang");
42163         }
42164         
42165         if (node.hasAttribute("style")) {
42166             
42167             var styles = node.getAttribute("style").split(";");
42168             var nstyle = [];
42169             Roo.each(styles, function(s) {
42170                 if (!s.match(/:/)) {
42171                     return;
42172                 }
42173                 var kv = s.split(":");
42174                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
42175                     return;
42176                 }
42177                 // what ever is left... we allow.
42178                 nstyle.push(s);
42179             });
42180             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
42181             if (!nstyle.length) {
42182                 node.removeAttribute('style');
42183             }
42184         }
42185         
42186         cleanWordChildren();
42187         
42188         
42189     },
42190     domToHTML : function(currentElement, depth, nopadtext) {
42191         
42192             depth = depth || 0;
42193             nopadtext = nopadtext || false;
42194         
42195             if (!currentElement) {
42196                 return this.domToHTML(this.doc.body);
42197             }
42198             
42199             //Roo.log(currentElement);
42200             var j;
42201             var allText = false;
42202             var nodeName = currentElement.nodeName;
42203             var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
42204             
42205             if  (nodeName == '#text') {
42206                 return currentElement.nodeValue;
42207             }
42208             
42209             
42210             var ret = '';
42211             if (nodeName != 'BODY') {
42212                  
42213                 var i = 0;
42214                 // Prints the node tagName, such as <A>, <IMG>, etc
42215                 if (tagName) {
42216                     var attr = [];
42217                     for(i = 0; i < currentElement.attributes.length;i++) {
42218                         // quoting?
42219                         var aname = currentElement.attributes.item(i).name;
42220                         if (!currentElement.attributes.item(i).value.length) {
42221                             continue;
42222                         }
42223                         attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
42224                     }
42225                     
42226                     ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
42227                 } 
42228                 else {
42229                     
42230                     // eack
42231                 }
42232             } else {
42233                 tagName = false;
42234             }
42235             if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
42236                 return ret;
42237             }
42238             if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
42239                 nopadtext = true;
42240             }
42241             
42242             
42243             // Traverse the tree
42244             i = 0;
42245             var currentElementChild = currentElement.childNodes.item(i);
42246             var allText = true;
42247             var innerHTML  = '';
42248             lastnode = '';
42249             while (currentElementChild) {
42250                 // Formatting code (indent the tree so it looks nice on the screen)
42251                 var nopad = nopadtext;
42252                 if (lastnode == 'SPAN') {
42253                     nopad  = true;
42254                 }
42255                 // text
42256                 if  (currentElementChild.nodeName == '#text') {
42257                     var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
42258                     if (!nopad && toadd.length > 80) {
42259                         innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
42260                     }
42261                     innerHTML  += toadd;
42262                     
42263                     i++;
42264                     currentElementChild = currentElement.childNodes.item(i);
42265                     lastNode = '';
42266                     continue;
42267                 }
42268                 allText = false;
42269                 
42270                 innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
42271                     
42272                 // Recursively traverse the tree structure of the child node
42273                 innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
42274                 lastnode = currentElementChild.nodeName;
42275                 i++;
42276                 currentElementChild=currentElement.childNodes.item(i);
42277             }
42278             
42279             ret += innerHTML;
42280             
42281             if (!allText) {
42282                     // The remaining code is mostly for formatting the tree
42283                 ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
42284             }
42285             
42286             
42287             if (tagName) {
42288                 ret+= "</"+tagName+">";
42289             }
42290             return ret;
42291             
42292         }
42293     
42294     // hide stuff that is not compatible
42295     /**
42296      * @event blur
42297      * @hide
42298      */
42299     /**
42300      * @event change
42301      * @hide
42302      */
42303     /**
42304      * @event focus
42305      * @hide
42306      */
42307     /**
42308      * @event specialkey
42309      * @hide
42310      */
42311     /**
42312      * @cfg {String} fieldClass @hide
42313      */
42314     /**
42315      * @cfg {String} focusClass @hide
42316      */
42317     /**
42318      * @cfg {String} autoCreate @hide
42319      */
42320     /**
42321      * @cfg {String} inputType @hide
42322      */
42323     /**
42324      * @cfg {String} invalidClass @hide
42325      */
42326     /**
42327      * @cfg {String} invalidText @hide
42328      */
42329     /**
42330      * @cfg {String} msgFx @hide
42331      */
42332     /**
42333      * @cfg {String} validateOnBlur @hide
42334      */
42335 });
42336
42337 Roo.HtmlEditorCore.white = [
42338         'area', 'br', 'img', 'input', 'hr', 'wbr',
42339         
42340        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
42341        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
42342        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
42343        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
42344        'table',   'ul',         'xmp', 
42345        
42346        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
42347       'thead',   'tr', 
42348      
42349       'dir', 'menu', 'ol', 'ul', 'dl',
42350        
42351       'embed',  'object'
42352 ];
42353
42354
42355 Roo.HtmlEditorCore.black = [
42356     //    'embed',  'object', // enable - backend responsiblity to clean thiese
42357         'applet', // 
42358         'base',   'basefont', 'bgsound', 'blink',  'body', 
42359         'frame',  'frameset', 'head',    'html',   'ilayer', 
42360         'iframe', 'layer',  'link',     'meta',    'object',   
42361         'script', 'style' ,'title',  'xml' // clean later..
42362 ];
42363 Roo.HtmlEditorCore.clean = [
42364     'script', 'style', 'title', 'xml'
42365 ];
42366 Roo.HtmlEditorCore.remove = [
42367     'font'
42368 ];
42369 // attributes..
42370
42371 Roo.HtmlEditorCore.ablack = [
42372     'on'
42373 ];
42374     
42375 Roo.HtmlEditorCore.aclean = [ 
42376     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
42377 ];
42378
42379 // protocols..
42380 Roo.HtmlEditorCore.pwhite= [
42381         'http',  'https',  'mailto'
42382 ];
42383
42384 // white listed style attributes.
42385 Roo.HtmlEditorCore.cwhite= [
42386       //  'text-align', /// default is to allow most things..
42387       
42388          
42389 //        'font-size'//??
42390 ];
42391
42392 // black listed style attributes.
42393 Roo.HtmlEditorCore.cblack= [
42394       //  'font-size' -- this can be set by the project 
42395 ];
42396
42397
42398 Roo.HtmlEditorCore.swapCodes   =[ 
42399     [    8211, "--" ], 
42400     [    8212, "--" ], 
42401     [    8216,  "'" ],  
42402     [    8217, "'" ],  
42403     [    8220, '"' ],  
42404     [    8221, '"' ],  
42405     [    8226, "*" ],  
42406     [    8230, "..." ]
42407 ]; 
42408
42409     //<script type="text/javascript">
42410
42411 /*
42412  * Ext JS Library 1.1.1
42413  * Copyright(c) 2006-2007, Ext JS, LLC.
42414  * Licence LGPL
42415  * 
42416  */
42417  
42418  
42419 Roo.form.HtmlEditor = function(config){
42420     
42421     
42422     
42423     Roo.form.HtmlEditor.superclass.constructor.call(this, config);
42424     
42425     if (!this.toolbars) {
42426         this.toolbars = [];
42427     }
42428     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
42429     
42430     
42431 };
42432
42433 /**
42434  * @class Roo.form.HtmlEditor
42435  * @extends Roo.form.Field
42436  * Provides a lightweight HTML Editor component.
42437  *
42438  * This has been tested on Fireforx / Chrome.. IE may not be so great..
42439  * 
42440  * <br><br><b>Note: The focus/blur and validation marking functionality inherited from Ext.form.Field is NOT
42441  * supported by this editor.</b><br/><br/>
42442  * An Editor is a sensitive component that can't be used in all spots standard fields can be used. Putting an Editor within
42443  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
42444  */
42445 Roo.extend(Roo.form.HtmlEditor, Roo.form.Field, {
42446     /**
42447      * @cfg {Boolean} clearUp
42448      */
42449     clearUp : true,
42450       /**
42451      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
42452      */
42453     toolbars : false,
42454    
42455      /**
42456      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
42457      *                        Roo.resizable.
42458      */
42459     resizable : false,
42460      /**
42461      * @cfg {Number} height (in pixels)
42462      */   
42463     height: 300,
42464    /**
42465      * @cfg {Number} width (in pixels)
42466      */   
42467     width: 500,
42468     
42469     /**
42470      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
42471      * 
42472      */
42473     stylesheets: false,
42474     
42475     // id of frame..
42476     frameId: false,
42477     
42478     // private properties
42479     validationEvent : false,
42480     deferHeight: true,
42481     initialized : false,
42482     activated : false,
42483     
42484     onFocus : Roo.emptyFn,
42485     iframePad:3,
42486     hideMode:'offsets',
42487     
42488     defaultAutoCreate : { // modified by initCompnoent..
42489         tag: "textarea",
42490         style:"width:500px;height:300px;",
42491         autocomplete: "off"
42492     },
42493
42494     // private
42495     initComponent : function(){
42496         this.addEvents({
42497             /**
42498              * @event initialize
42499              * Fires when the editor is fully initialized (including the iframe)
42500              * @param {HtmlEditor} this
42501              */
42502             initialize: true,
42503             /**
42504              * @event activate
42505              * Fires when the editor is first receives the focus. Any insertion must wait
42506              * until after this event.
42507              * @param {HtmlEditor} this
42508              */
42509             activate: true,
42510              /**
42511              * @event beforesync
42512              * Fires before the textarea is updated with content from the editor iframe. Return false
42513              * to cancel the sync.
42514              * @param {HtmlEditor} this
42515              * @param {String} html
42516              */
42517             beforesync: true,
42518              /**
42519              * @event beforepush
42520              * Fires before the iframe editor is updated with content from the textarea. Return false
42521              * to cancel the push.
42522              * @param {HtmlEditor} this
42523              * @param {String} html
42524              */
42525             beforepush: true,
42526              /**
42527              * @event sync
42528              * Fires when the textarea is updated with content from the editor iframe.
42529              * @param {HtmlEditor} this
42530              * @param {String} html
42531              */
42532             sync: true,
42533              /**
42534              * @event push
42535              * Fires when the iframe editor is updated with content from the textarea.
42536              * @param {HtmlEditor} this
42537              * @param {String} html
42538              */
42539             push: true,
42540              /**
42541              * @event editmodechange
42542              * Fires when the editor switches edit modes
42543              * @param {HtmlEditor} this
42544              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
42545              */
42546             editmodechange: true,
42547             /**
42548              * @event editorevent
42549              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
42550              * @param {HtmlEditor} this
42551              */
42552             editorevent: true,
42553             /**
42554              * @event firstfocus
42555              * Fires when on first focus - needed by toolbars..
42556              * @param {HtmlEditor} this
42557              */
42558             firstfocus: true,
42559             /**
42560              * @event autosave
42561              * Auto save the htmlEditor value as a file into Events
42562              * @param {HtmlEditor} this
42563              */
42564             autosave: true,
42565             /**
42566              * @event savedpreview
42567              * preview the saved version of htmlEditor
42568              * @param {HtmlEditor} this
42569              */
42570             savedpreview: true
42571         });
42572         this.defaultAutoCreate =  {
42573             tag: "textarea",
42574             style:'width: ' + this.width + 'px;height: ' + this.height + 'px;',
42575             autocomplete: "off"
42576         };
42577     },
42578
42579     /**
42580      * Protected method that will not generally be called directly. It
42581      * is called when the editor creates its toolbar. Override this method if you need to
42582      * add custom toolbar buttons.
42583      * @param {HtmlEditor} editor
42584      */
42585     createToolbar : function(editor){
42586         Roo.log("create toolbars");
42587         if (!editor.toolbars || !editor.toolbars.length) {
42588             editor.toolbars = [ new Roo.form.HtmlEditor.ToolbarStandard() ]; // can be empty?
42589         }
42590         
42591         for (var i =0 ; i < editor.toolbars.length;i++) {
42592             editor.toolbars[i] = Roo.factory(
42593                     typeof(editor.toolbars[i]) == 'string' ?
42594                         { xtype: editor.toolbars[i]} : editor.toolbars[i],
42595                 Roo.form.HtmlEditor);
42596             editor.toolbars[i].init(editor);
42597         }
42598          
42599         
42600     },
42601
42602      
42603     // private
42604     onRender : function(ct, position)
42605     {
42606         var _t = this;
42607         Roo.form.HtmlEditor.superclass.onRender.call(this, ct, position);
42608         
42609         this.wrap = this.el.wrap({
42610             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
42611         });
42612         
42613         this.editorcore.onRender(ct, position);
42614          
42615         if (this.resizable) {
42616             this.resizeEl = new Roo.Resizable(this.wrap, {
42617                 pinned : true,
42618                 wrap: true,
42619                 dynamic : true,
42620                 minHeight : this.height,
42621                 height: this.height,
42622                 handles : this.resizable,
42623                 width: this.width,
42624                 listeners : {
42625                     resize : function(r, w, h) {
42626                         _t.onResize(w,h); // -something
42627                     }
42628                 }
42629             });
42630             
42631         }
42632         this.createToolbar(this);
42633        
42634         
42635         if(!this.width){
42636             this.setSize(this.wrap.getSize());
42637         }
42638         if (this.resizeEl) {
42639             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
42640             // should trigger onReize..
42641         }
42642         
42643 //        if(this.autosave && this.w){
42644 //            this.autoSaveFn = setInterval(this.autosave, 1000);
42645 //        }
42646     },
42647
42648     // private
42649     onResize : function(w, h)
42650     {
42651         //Roo.log('resize: ' +w + ',' + h );
42652         Roo.form.HtmlEditor.superclass.onResize.apply(this, arguments);
42653         var ew = false;
42654         var eh = false;
42655         
42656         if(this.el ){
42657             if(typeof w == 'number'){
42658                 var aw = w - this.wrap.getFrameWidth('lr');
42659                 this.el.setWidth(this.adjustWidth('textarea', aw));
42660                 ew = aw;
42661             }
42662             if(typeof h == 'number'){
42663                 var tbh = 0;
42664                 for (var i =0; i < this.toolbars.length;i++) {
42665                     // fixme - ask toolbars for heights?
42666                     tbh += this.toolbars[i].tb.el.getHeight();
42667                     if (this.toolbars[i].footer) {
42668                         tbh += this.toolbars[i].footer.el.getHeight();
42669                     }
42670                 }
42671                 
42672                 
42673                 
42674                 
42675                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
42676                 ah -= 5; // knock a few pixes off for look..
42677                 this.el.setHeight(this.adjustWidth('textarea', ah));
42678                 var eh = ah;
42679             }
42680         }
42681         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
42682         this.editorcore.onResize(ew,eh);
42683         
42684     },
42685
42686     /**
42687      * Toggles the editor between standard and source edit mode.
42688      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
42689      */
42690     toggleSourceEdit : function(sourceEditMode)
42691     {
42692         this.editorcore.toggleSourceEdit(sourceEditMode);
42693         
42694         if(this.editorcore.sourceEditMode){
42695             Roo.log('editor - showing textarea');
42696             
42697 //            Roo.log('in');
42698 //            Roo.log(this.syncValue());
42699             this.editorcore.syncValue();
42700             this.el.removeClass('x-hidden');
42701             this.el.dom.removeAttribute('tabIndex');
42702             this.el.focus();
42703         }else{
42704             Roo.log('editor - hiding textarea');
42705 //            Roo.log('out')
42706 //            Roo.log(this.pushValue()); 
42707             this.editorcore.pushValue();
42708             
42709             this.el.addClass('x-hidden');
42710             this.el.dom.setAttribute('tabIndex', -1);
42711             //this.deferFocus();
42712         }
42713          
42714         this.setSize(this.wrap.getSize());
42715         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
42716     },
42717  
42718     // private (for BoxComponent)
42719     adjustSize : Roo.BoxComponent.prototype.adjustSize,
42720
42721     // private (for BoxComponent)
42722     getResizeEl : function(){
42723         return this.wrap;
42724     },
42725
42726     // private (for BoxComponent)
42727     getPositionEl : function(){
42728         return this.wrap;
42729     },
42730
42731     // private
42732     initEvents : function(){
42733         this.originalValue = this.getValue();
42734     },
42735
42736     /**
42737      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
42738      * @method
42739      */
42740     markInvalid : Roo.emptyFn,
42741     /**
42742      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
42743      * @method
42744      */
42745     clearInvalid : Roo.emptyFn,
42746
42747     setValue : function(v){
42748         Roo.form.HtmlEditor.superclass.setValue.call(this, v);
42749         this.editorcore.pushValue();
42750     },
42751
42752      
42753     // private
42754     deferFocus : function(){
42755         this.focus.defer(10, this);
42756     },
42757
42758     // doc'ed in Field
42759     focus : function(){
42760         this.editorcore.focus();
42761         
42762     },
42763       
42764
42765     // private
42766     onDestroy : function(){
42767         
42768         
42769         
42770         if(this.rendered){
42771             
42772             for (var i =0; i < this.toolbars.length;i++) {
42773                 // fixme - ask toolbars for heights?
42774                 this.toolbars[i].onDestroy();
42775             }
42776             
42777             this.wrap.dom.innerHTML = '';
42778             this.wrap.remove();
42779         }
42780     },
42781
42782     // private
42783     onFirstFocus : function(){
42784         //Roo.log("onFirstFocus");
42785         this.editorcore.onFirstFocus();
42786          for (var i =0; i < this.toolbars.length;i++) {
42787             this.toolbars[i].onFirstFocus();
42788         }
42789         
42790     },
42791     
42792     // private
42793     syncValue : function()
42794     {
42795         this.editorcore.syncValue();
42796     },
42797     
42798     pushValue : function()
42799     {
42800         this.editorcore.pushValue();
42801     }
42802      
42803     
42804     // hide stuff that is not compatible
42805     /**
42806      * @event blur
42807      * @hide
42808      */
42809     /**
42810      * @event change
42811      * @hide
42812      */
42813     /**
42814      * @event focus
42815      * @hide
42816      */
42817     /**
42818      * @event specialkey
42819      * @hide
42820      */
42821     /**
42822      * @cfg {String} fieldClass @hide
42823      */
42824     /**
42825      * @cfg {String} focusClass @hide
42826      */
42827     /**
42828      * @cfg {String} autoCreate @hide
42829      */
42830     /**
42831      * @cfg {String} inputType @hide
42832      */
42833     /**
42834      * @cfg {String} invalidClass @hide
42835      */
42836     /**
42837      * @cfg {String} invalidText @hide
42838      */
42839     /**
42840      * @cfg {String} msgFx @hide
42841      */
42842     /**
42843      * @cfg {String} validateOnBlur @hide
42844      */
42845 });
42846  
42847     // <script type="text/javascript">
42848 /*
42849  * Based on
42850  * Ext JS Library 1.1.1
42851  * Copyright(c) 2006-2007, Ext JS, LLC.
42852  *  
42853  
42854  */
42855
42856 /**
42857  * @class Roo.form.HtmlEditorToolbar1
42858  * Basic Toolbar
42859  * 
42860  * Usage:
42861  *
42862  new Roo.form.HtmlEditor({
42863     ....
42864     toolbars : [
42865         new Roo.form.HtmlEditorToolbar1({
42866             disable : { fonts: 1 , format: 1, ..., ... , ...],
42867             btns : [ .... ]
42868         })
42869     }
42870      
42871  * 
42872  * @cfg {Object} disable List of elements to disable..
42873  * @cfg {Array} btns List of additional buttons.
42874  * 
42875  * 
42876  * NEEDS Extra CSS? 
42877  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
42878  */
42879  
42880 Roo.form.HtmlEditor.ToolbarStandard = function(config)
42881 {
42882     
42883     Roo.apply(this, config);
42884     
42885     // default disabled, based on 'good practice'..
42886     this.disable = this.disable || {};
42887     Roo.applyIf(this.disable, {
42888         fontSize : true,
42889         colors : true,
42890         specialElements : true
42891     });
42892     
42893     
42894     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
42895     // dont call parent... till later.
42896 }
42897
42898 Roo.apply(Roo.form.HtmlEditor.ToolbarStandard.prototype,  {
42899     
42900     tb: false,
42901     
42902     rendered: false,
42903     
42904     editor : false,
42905     editorcore : false,
42906     /**
42907      * @cfg {Object} disable  List of toolbar elements to disable
42908          
42909      */
42910     disable : false,
42911     
42912     
42913      /**
42914      * @cfg {String} createLinkText The default text for the create link prompt
42915      */
42916     createLinkText : 'Please enter the URL for the link:',
42917     /**
42918      * @cfg {String} defaultLinkValue The default value for the create link prompt (defaults to http:/ /)
42919      */
42920     defaultLinkValue : 'http:/'+'/',
42921    
42922     
42923       /**
42924      * @cfg {Array} fontFamilies An array of available font families
42925      */
42926     fontFamilies : [
42927         'Arial',
42928         'Courier New',
42929         'Tahoma',
42930         'Times New Roman',
42931         'Verdana'
42932     ],
42933     
42934     specialChars : [
42935            "&#169;",
42936           "&#174;",     
42937           "&#8482;",    
42938           "&#163;" ,    
42939          // "&#8212;",    
42940           "&#8230;",    
42941           "&#247;" ,    
42942         //  "&#225;" ,     ?? a acute?
42943            "&#8364;"    , //Euro
42944        //   "&#8220;"    ,
42945         //  "&#8221;"    ,
42946         //  "&#8226;"    ,
42947           "&#176;"  //   , // degrees
42948
42949          // "&#233;"     , // e ecute
42950          // "&#250;"     , // u ecute?
42951     ],
42952     
42953     specialElements : [
42954         {
42955             text: "Insert Table",
42956             xtype: 'MenuItem',
42957             xns : Roo.Menu,
42958             ihtml :  '<table><tr><td>Cell</td></tr></table>' 
42959                 
42960         },
42961         {    
42962             text: "Insert Image",
42963             xtype: 'MenuItem',
42964             xns : Roo.Menu,
42965             ihtml : '<img src="about:blank"/>'
42966             
42967         }
42968         
42969          
42970     ],
42971     
42972     
42973     inputElements : [ 
42974             "form", "input:text", "input:hidden", "input:checkbox", "input:radio", "input:password", 
42975             "input:submit", "input:button", "select", "textarea", "label" ],
42976     formats : [
42977         ["p"] ,  
42978         ["h1"],["h2"],["h3"],["h4"],["h5"],["h6"], 
42979         ["pre"],[ "code"], 
42980         ["abbr"],[ "acronym"],[ "address"],[ "cite"],[ "samp"],[ "var"],
42981         ['div'],['span']
42982     ],
42983     
42984     cleanStyles : [
42985         "font-size"
42986     ],
42987      /**
42988      * @cfg {String} defaultFont default font to use.
42989      */
42990     defaultFont: 'tahoma',
42991    
42992     fontSelect : false,
42993     
42994     
42995     formatCombo : false,
42996     
42997     init : function(editor)
42998     {
42999         this.editor = editor;
43000         this.editorcore = editor.editorcore ? editor.editorcore : editor;
43001         var editorcore = this.editorcore;
43002         
43003         var _t = this;
43004         
43005         var fid = editorcore.frameId;
43006         var etb = this;
43007         function btn(id, toggle, handler){
43008             var xid = fid + '-'+ id ;
43009             return {
43010                 id : xid,
43011                 cmd : id,
43012                 cls : 'x-btn-icon x-edit-'+id,
43013                 enableToggle:toggle !== false,
43014                 scope: _t, // was editor...
43015                 handler:handler||_t.relayBtnCmd,
43016                 clickEvent:'mousedown',
43017                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
43018                 tabIndex:-1
43019             };
43020         }
43021         
43022         
43023         
43024         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
43025         this.tb = tb;
43026          // stop form submits
43027         tb.el.on('click', function(e){
43028             e.preventDefault(); // what does this do?
43029         });
43030
43031         if(!this.disable.font) { // && !Roo.isSafari){
43032             /* why no safari for fonts 
43033             editor.fontSelect = tb.el.createChild({
43034                 tag:'select',
43035                 tabIndex: -1,
43036                 cls:'x-font-select',
43037                 html: this.createFontOptions()
43038             });
43039             
43040             editor.fontSelect.on('change', function(){
43041                 var font = editor.fontSelect.dom.value;
43042                 editor.relayCmd('fontname', font);
43043                 editor.deferFocus();
43044             }, editor);
43045             
43046             tb.add(
43047                 editor.fontSelect.dom,
43048                 '-'
43049             );
43050             */
43051             
43052         };
43053         if(!this.disable.formats){
43054             this.formatCombo = new Roo.form.ComboBox({
43055                 store: new Roo.data.SimpleStore({
43056                     id : 'tag',
43057                     fields: ['tag'],
43058                     data : this.formats // from states.js
43059                 }),
43060                 blockFocus : true,
43061                 name : '',
43062                 //autoCreate : {tag: "div",  size: "20"},
43063                 displayField:'tag',
43064                 typeAhead: false,
43065                 mode: 'local',
43066                 editable : false,
43067                 triggerAction: 'all',
43068                 emptyText:'Add tag',
43069                 selectOnFocus:true,
43070                 width:135,
43071                 listeners : {
43072                     'select': function(c, r, i) {
43073                         editorcore.insertTag(r.get('tag'));
43074                         editor.focus();
43075                     }
43076                 }
43077
43078             });
43079             tb.addField(this.formatCombo);
43080             
43081         }
43082         
43083         if(!this.disable.format){
43084             tb.add(
43085                 btn('bold'),
43086                 btn('italic'),
43087                 btn('underline')
43088             );
43089         };
43090         if(!this.disable.fontSize){
43091             tb.add(
43092                 '-',
43093                 
43094                 
43095                 btn('increasefontsize', false, editorcore.adjustFont),
43096                 btn('decreasefontsize', false, editorcore.adjustFont)
43097             );
43098         };
43099         
43100         
43101         if(!this.disable.colors){
43102             tb.add(
43103                 '-', {
43104                     id:editorcore.frameId +'-forecolor',
43105                     cls:'x-btn-icon x-edit-forecolor',
43106                     clickEvent:'mousedown',
43107                     tooltip: this.buttonTips['forecolor'] || undefined,
43108                     tabIndex:-1,
43109                     menu : new Roo.menu.ColorMenu({
43110                         allowReselect: true,
43111                         focus: Roo.emptyFn,
43112                         value:'000000',
43113                         plain:true,
43114                         selectHandler: function(cp, color){
43115                             editorcore.execCmd('forecolor', Roo.isSafari || Roo.isIE ? '#'+color : color);
43116                             editor.deferFocus();
43117                         },
43118                         scope: editorcore,
43119                         clickEvent:'mousedown'
43120                     })
43121                 }, {
43122                     id:editorcore.frameId +'backcolor',
43123                     cls:'x-btn-icon x-edit-backcolor',
43124                     clickEvent:'mousedown',
43125                     tooltip: this.buttonTips['backcolor'] || undefined,
43126                     tabIndex:-1,
43127                     menu : new Roo.menu.ColorMenu({
43128                         focus: Roo.emptyFn,
43129                         value:'FFFFFF',
43130                         plain:true,
43131                         allowReselect: true,
43132                         selectHandler: function(cp, color){
43133                             if(Roo.isGecko){
43134                                 editorcore.execCmd('useCSS', false);
43135                                 editorcore.execCmd('hilitecolor', color);
43136                                 editorcore.execCmd('useCSS', true);
43137                                 editor.deferFocus();
43138                             }else{
43139                                 editorcore.execCmd(Roo.isOpera ? 'hilitecolor' : 'backcolor', 
43140                                     Roo.isSafari || Roo.isIE ? '#'+color : color);
43141                                 editor.deferFocus();
43142                             }
43143                         },
43144                         scope:editorcore,
43145                         clickEvent:'mousedown'
43146                     })
43147                 }
43148             );
43149         };
43150         // now add all the items...
43151         
43152
43153         if(!this.disable.alignments){
43154             tb.add(
43155                 '-',
43156                 btn('justifyleft'),
43157                 btn('justifycenter'),
43158                 btn('justifyright')
43159             );
43160         };
43161
43162         //if(!Roo.isSafari){
43163             if(!this.disable.links){
43164                 tb.add(
43165                     '-',
43166                     btn('createlink', false, this.createLink)    /// MOVE TO HERE?!!?!?!?!
43167                 );
43168             };
43169
43170             if(!this.disable.lists){
43171                 tb.add(
43172                     '-',
43173                     btn('insertorderedlist'),
43174                     btn('insertunorderedlist')
43175                 );
43176             }
43177             if(!this.disable.sourceEdit){
43178                 tb.add(
43179                     '-',
43180                     btn('sourceedit', true, function(btn){
43181                         Roo.log(this);
43182                         this.toggleSourceEdit(btn.pressed);
43183                     })
43184                 );
43185             }
43186         //}
43187         
43188         var smenu = { };
43189         // special menu.. - needs to be tidied up..
43190         if (!this.disable.special) {
43191             smenu = {
43192                 text: "&#169;",
43193                 cls: 'x-edit-none',
43194                 
43195                 menu : {
43196                     items : []
43197                 }
43198             };
43199             for (var i =0; i < this.specialChars.length; i++) {
43200                 smenu.menu.items.push({
43201                     
43202                     html: this.specialChars[i],
43203                     handler: function(a,b) {
43204                         editorcore.insertAtCursor(String.fromCharCode(a.html.replace('&#','').replace(';', '')));
43205                         //editor.insertAtCursor(a.html);
43206                         
43207                     },
43208                     tabIndex:-1
43209                 });
43210             }
43211             
43212             
43213             tb.add(smenu);
43214             
43215             
43216         }
43217         
43218         var cmenu = { };
43219         if (!this.disable.cleanStyles) {
43220             cmenu = {
43221                 cls: 'x-btn-icon x-btn-clear',
43222                 
43223                 menu : {
43224                     items : []
43225                 }
43226             };
43227             for (var i =0; i < this.cleanStyles.length; i++) {
43228                 cmenu.menu.items.push({
43229                     actiontype : this.cleanStyles[i],
43230                     html: 'Remove ' + this.cleanStyles[i],
43231                     handler: function(a,b) {
43232                         Roo.log(a);
43233                         Roo.log(b);
43234                         var c = Roo.get(editorcore.doc.body);
43235                         c.select('[style]').each(function(s) {
43236                             s.dom.style.removeProperty(a.actiontype);
43237                         });
43238                         editorcore.syncValue();
43239                     },
43240                     tabIndex:-1
43241                 });
43242             }
43243             cmenu.menu.items.push({
43244                 actiontype : 'word',
43245                 html: 'Remove MS Word Formating',
43246                 handler: function(a,b) {
43247                     editorcore.cleanWord();
43248                     editorcore.syncValue();
43249                 },
43250                 tabIndex:-1
43251             });
43252             
43253             cmenu.menu.items.push({
43254                 actiontype : 'all',
43255                 html: 'Remove All Styles',
43256                 handler: function(a,b) {
43257                     
43258                     var c = Roo.get(editorcore.doc.body);
43259                     c.select('[style]').each(function(s) {
43260                         s.dom.removeAttribute('style');
43261                     });
43262                     editorcore.syncValue();
43263                 },
43264                 tabIndex:-1
43265             });
43266              cmenu.menu.items.push({
43267                 actiontype : 'word',
43268                 html: 'Tidy HTML Source',
43269                 handler: function(a,b) {
43270                     editorcore.doc.body.innerHTML = editorcore.domToHTML();
43271                     editorcore.syncValue();
43272                 },
43273                 tabIndex:-1
43274             });
43275             
43276             
43277             tb.add(cmenu);
43278         }
43279          
43280         if (!this.disable.specialElements) {
43281             var semenu = {
43282                 text: "Other;",
43283                 cls: 'x-edit-none',
43284                 menu : {
43285                     items : []
43286                 }
43287             };
43288             for (var i =0; i < this.specialElements.length; i++) {
43289                 semenu.menu.items.push(
43290                     Roo.apply({ 
43291                         handler: function(a,b) {
43292                             editor.insertAtCursor(this.ihtml);
43293                         }
43294                     }, this.specialElements[i])
43295                 );
43296                     
43297             }
43298             
43299             tb.add(semenu);
43300             
43301             
43302         }
43303          
43304         
43305         if (this.btns) {
43306             for(var i =0; i< this.btns.length;i++) {
43307                 var b = Roo.factory(this.btns[i],Roo.form);
43308                 b.cls =  'x-edit-none';
43309                 b.scope = editorcore;
43310                 tb.add(b);
43311             }
43312         
43313         }
43314         
43315         
43316         
43317         // disable everything...
43318         
43319         this.tb.items.each(function(item){
43320            if(item.id != editorcore.frameId+ '-sourceedit'){
43321                 item.disable();
43322             }
43323         });
43324         this.rendered = true;
43325         
43326         // the all the btns;
43327         editor.on('editorevent', this.updateToolbar, this);
43328         // other toolbars need to implement this..
43329         //editor.on('editmodechange', this.updateToolbar, this);
43330     },
43331     
43332     
43333     relayBtnCmd : function(btn) {
43334         this.editorcore.relayCmd(btn.cmd);
43335     },
43336     // private used internally
43337     createLink : function(){
43338         Roo.log("create link?");
43339         var url = prompt(this.createLinkText, this.defaultLinkValue);
43340         if(url && url != 'http:/'+'/'){
43341             this.editorcore.relayCmd('createlink', url);
43342         }
43343     },
43344
43345     
43346     /**
43347      * Protected method that will not generally be called directly. It triggers
43348      * a toolbar update by reading the markup state of the current selection in the editor.
43349      */
43350     updateToolbar: function(){
43351
43352         if(!this.editorcore.activated){
43353             this.editor.onFirstFocus();
43354             return;
43355         }
43356
43357         var btns = this.tb.items.map, 
43358             doc = this.editorcore.doc,
43359             frameId = this.editorcore.frameId;
43360
43361         if(!this.disable.font && !Roo.isSafari){
43362             /*
43363             var name = (doc.queryCommandValue('FontName')||this.editor.defaultFont).toLowerCase();
43364             if(name != this.fontSelect.dom.value){
43365                 this.fontSelect.dom.value = name;
43366             }
43367             */
43368         }
43369         if(!this.disable.format){
43370             btns[frameId + '-bold'].toggle(doc.queryCommandState('bold'));
43371             btns[frameId + '-italic'].toggle(doc.queryCommandState('italic'));
43372             btns[frameId + '-underline'].toggle(doc.queryCommandState('underline'));
43373         }
43374         if(!this.disable.alignments){
43375             btns[frameId + '-justifyleft'].toggle(doc.queryCommandState('justifyleft'));
43376             btns[frameId + '-justifycenter'].toggle(doc.queryCommandState('justifycenter'));
43377             btns[frameId + '-justifyright'].toggle(doc.queryCommandState('justifyright'));
43378         }
43379         if(!Roo.isSafari && !this.disable.lists){
43380             btns[frameId + '-insertorderedlist'].toggle(doc.queryCommandState('insertorderedlist'));
43381             btns[frameId + '-insertunorderedlist'].toggle(doc.queryCommandState('insertunorderedlist'));
43382         }
43383         
43384         var ans = this.editorcore.getAllAncestors();
43385         if (this.formatCombo) {
43386             
43387             
43388             var store = this.formatCombo.store;
43389             this.formatCombo.setValue("");
43390             for (var i =0; i < ans.length;i++) {
43391                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
43392                     // select it..
43393                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
43394                     break;
43395                 }
43396             }
43397         }
43398         
43399         
43400         
43401         // hides menus... - so this cant be on a menu...
43402         Roo.menu.MenuMgr.hideAll();
43403
43404         //this.editorsyncValue();
43405     },
43406    
43407     
43408     createFontOptions : function(){
43409         var buf = [], fs = this.fontFamilies, ff, lc;
43410         
43411         
43412         
43413         for(var i = 0, len = fs.length; i< len; i++){
43414             ff = fs[i];
43415             lc = ff.toLowerCase();
43416             buf.push(
43417                 '<option value="',lc,'" style="font-family:',ff,';"',
43418                     (this.defaultFont == lc ? ' selected="true">' : '>'),
43419                     ff,
43420                 '</option>'
43421             );
43422         }
43423         return buf.join('');
43424     },
43425     
43426     toggleSourceEdit : function(sourceEditMode){
43427         
43428         Roo.log("toolbar toogle");
43429         if(sourceEditMode === undefined){
43430             sourceEditMode = !this.sourceEditMode;
43431         }
43432         this.sourceEditMode = sourceEditMode === true;
43433         var btn = this.tb.items.get(this.editorcore.frameId +'-sourceedit');
43434         // just toggle the button?
43435         if(btn.pressed !== this.sourceEditMode){
43436             btn.toggle(this.sourceEditMode);
43437             return;
43438         }
43439         
43440         if(sourceEditMode){
43441             Roo.log("disabling buttons");
43442             this.tb.items.each(function(item){
43443                 if(item.cmd != 'sourceedit'){
43444                     item.disable();
43445                 }
43446             });
43447           
43448         }else{
43449             Roo.log("enabling buttons");
43450             if(this.editorcore.initialized){
43451                 this.tb.items.each(function(item){
43452                     item.enable();
43453                 });
43454             }
43455             
43456         }
43457         Roo.log("calling toggole on editor");
43458         // tell the editor that it's been pressed..
43459         this.editor.toggleSourceEdit(sourceEditMode);
43460        
43461     },
43462      /**
43463      * Object collection of toolbar tooltips for the buttons in the editor. The key
43464      * is the command id associated with that button and the value is a valid QuickTips object.
43465      * For example:
43466 <pre><code>
43467 {
43468     bold : {
43469         title: 'Bold (Ctrl+B)',
43470         text: 'Make the selected text bold.',
43471         cls: 'x-html-editor-tip'
43472     },
43473     italic : {
43474         title: 'Italic (Ctrl+I)',
43475         text: 'Make the selected text italic.',
43476         cls: 'x-html-editor-tip'
43477     },
43478     ...
43479 </code></pre>
43480     * @type Object
43481      */
43482     buttonTips : {
43483         bold : {
43484             title: 'Bold (Ctrl+B)',
43485             text: 'Make the selected text bold.',
43486             cls: 'x-html-editor-tip'
43487         },
43488         italic : {
43489             title: 'Italic (Ctrl+I)',
43490             text: 'Make the selected text italic.',
43491             cls: 'x-html-editor-tip'
43492         },
43493         underline : {
43494             title: 'Underline (Ctrl+U)',
43495             text: 'Underline the selected text.',
43496             cls: 'x-html-editor-tip'
43497         },
43498         increasefontsize : {
43499             title: 'Grow Text',
43500             text: 'Increase the font size.',
43501             cls: 'x-html-editor-tip'
43502         },
43503         decreasefontsize : {
43504             title: 'Shrink Text',
43505             text: 'Decrease the font size.',
43506             cls: 'x-html-editor-tip'
43507         },
43508         backcolor : {
43509             title: 'Text Highlight Color',
43510             text: 'Change the background color of the selected text.',
43511             cls: 'x-html-editor-tip'
43512         },
43513         forecolor : {
43514             title: 'Font Color',
43515             text: 'Change the color of the selected text.',
43516             cls: 'x-html-editor-tip'
43517         },
43518         justifyleft : {
43519             title: 'Align Text Left',
43520             text: 'Align text to the left.',
43521             cls: 'x-html-editor-tip'
43522         },
43523         justifycenter : {
43524             title: 'Center Text',
43525             text: 'Center text in the editor.',
43526             cls: 'x-html-editor-tip'
43527         },
43528         justifyright : {
43529             title: 'Align Text Right',
43530             text: 'Align text to the right.',
43531             cls: 'x-html-editor-tip'
43532         },
43533         insertunorderedlist : {
43534             title: 'Bullet List',
43535             text: 'Start a bulleted list.',
43536             cls: 'x-html-editor-tip'
43537         },
43538         insertorderedlist : {
43539             title: 'Numbered List',
43540             text: 'Start a numbered list.',
43541             cls: 'x-html-editor-tip'
43542         },
43543         createlink : {
43544             title: 'Hyperlink',
43545             text: 'Make the selected text a hyperlink.',
43546             cls: 'x-html-editor-tip'
43547         },
43548         sourceedit : {
43549             title: 'Source Edit',
43550             text: 'Switch to source editing mode.',
43551             cls: 'x-html-editor-tip'
43552         }
43553     },
43554     // private
43555     onDestroy : function(){
43556         if(this.rendered){
43557             
43558             this.tb.items.each(function(item){
43559                 if(item.menu){
43560                     item.menu.removeAll();
43561                     if(item.menu.el){
43562                         item.menu.el.destroy();
43563                     }
43564                 }
43565                 item.destroy();
43566             });
43567              
43568         }
43569     },
43570     onFirstFocus: function() {
43571         this.tb.items.each(function(item){
43572            item.enable();
43573         });
43574     }
43575 });
43576
43577
43578
43579
43580 // <script type="text/javascript">
43581 /*
43582  * Based on
43583  * Ext JS Library 1.1.1
43584  * Copyright(c) 2006-2007, Ext JS, LLC.
43585  *  
43586  
43587  */
43588
43589  
43590 /**
43591  * @class Roo.form.HtmlEditor.ToolbarContext
43592  * Context Toolbar
43593  * 
43594  * Usage:
43595  *
43596  new Roo.form.HtmlEditor({
43597     ....
43598     toolbars : [
43599         { xtype: 'ToolbarStandard', styles : {} }
43600         { xtype: 'ToolbarContext', disable : {} }
43601     ]
43602 })
43603
43604      
43605  * 
43606  * @config : {Object} disable List of elements to disable.. (not done yet.)
43607  * @config : {Object} styles  Map of styles available.
43608  * 
43609  */
43610
43611 Roo.form.HtmlEditor.ToolbarContext = function(config)
43612 {
43613     
43614     Roo.apply(this, config);
43615     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
43616     // dont call parent... till later.
43617     this.styles = this.styles || {};
43618 }
43619
43620  
43621
43622 Roo.form.HtmlEditor.ToolbarContext.types = {
43623     'IMG' : {
43624         width : {
43625             title: "Width",
43626             width: 40
43627         },
43628         height:  {
43629             title: "Height",
43630             width: 40
43631         },
43632         align: {
43633             title: "Align",
43634             opts : [ [""],[ "left"],[ "right"],[ "center"],[ "top"]],
43635             width : 80
43636             
43637         },
43638         border: {
43639             title: "Border",
43640             width: 40
43641         },
43642         alt: {
43643             title: "Alt",
43644             width: 120
43645         },
43646         src : {
43647             title: "Src",
43648             width: 220
43649         }
43650         
43651     },
43652     'A' : {
43653         name : {
43654             title: "Name",
43655             width: 50
43656         },
43657         target:  {
43658             title: "Target",
43659             width: 120
43660         },
43661         href:  {
43662             title: "Href",
43663             width: 220
43664         } // border?
43665         
43666     },
43667     'TABLE' : {
43668         rows : {
43669             title: "Rows",
43670             width: 20
43671         },
43672         cols : {
43673             title: "Cols",
43674             width: 20
43675         },
43676         width : {
43677             title: "Width",
43678             width: 40
43679         },
43680         height : {
43681             title: "Height",
43682             width: 40
43683         },
43684         border : {
43685             title: "Border",
43686             width: 20
43687         }
43688     },
43689     'TD' : {
43690         width : {
43691             title: "Width",
43692             width: 40
43693         },
43694         height : {
43695             title: "Height",
43696             width: 40
43697         },   
43698         align: {
43699             title: "Align",
43700             opts : [[""],[ "left"],[ "center"],[ "right"],[ "justify"],[ "char"]],
43701             width: 80
43702         },
43703         valign: {
43704             title: "Valign",
43705             opts : [[""],[ "top"],[ "middle"],[ "bottom"],[ "baseline"]],
43706             width: 80
43707         },
43708         colspan: {
43709             title: "Colspan",
43710             width: 20
43711             
43712         },
43713          'font-family'  : {
43714             title : "Font",
43715             style : 'fontFamily',
43716             displayField: 'display',
43717             optname : 'font-family',
43718             width: 140
43719         }
43720     },
43721     'INPUT' : {
43722         name : {
43723             title: "name",
43724             width: 120
43725         },
43726         value : {
43727             title: "Value",
43728             width: 120
43729         },
43730         width : {
43731             title: "Width",
43732             width: 40
43733         }
43734     },
43735     'LABEL' : {
43736         'for' : {
43737             title: "For",
43738             width: 120
43739         }
43740     },
43741     'TEXTAREA' : {
43742           name : {
43743             title: "name",
43744             width: 120
43745         },
43746         rows : {
43747             title: "Rows",
43748             width: 20
43749         },
43750         cols : {
43751             title: "Cols",
43752             width: 20
43753         }
43754     },
43755     'SELECT' : {
43756         name : {
43757             title: "name",
43758             width: 120
43759         },
43760         selectoptions : {
43761             title: "Options",
43762             width: 200
43763         }
43764     },
43765     
43766     // should we really allow this??
43767     // should this just be 
43768     'BODY' : {
43769         title : {
43770             title: "Title",
43771             width: 200,
43772             disabled : true
43773         }
43774     },
43775     'SPAN' : {
43776         'font-family'  : {
43777             title : "Font",
43778             style : 'fontFamily',
43779             displayField: 'display',
43780             optname : 'font-family',
43781             width: 140
43782         }
43783     },
43784     'DIV' : {
43785         'font-family'  : {
43786             title : "Font",
43787             style : 'fontFamily',
43788             displayField: 'display',
43789             optname : 'font-family',
43790             width: 140
43791         }
43792     },
43793      'P' : {
43794         'font-family'  : {
43795             title : "Font",
43796             style : 'fontFamily',
43797             displayField: 'display',
43798             optname : 'font-family',
43799             width: 140
43800         }
43801     },
43802     
43803     '*' : {
43804         // empty..
43805     }
43806
43807 };
43808
43809 // this should be configurable.. - you can either set it up using stores, or modify options somehwere..
43810 Roo.form.HtmlEditor.ToolbarContext.stores = false;
43811
43812 Roo.form.HtmlEditor.ToolbarContext.options = {
43813         'font-family'  : [ 
43814                 [ 'Helvetica,Arial,sans-serif', 'Helvetica'],
43815                 [ 'Courier New', 'Courier New'],
43816                 [ 'Tahoma', 'Tahoma'],
43817                 [ 'Times New Roman,serif', 'Times'],
43818                 [ 'Verdana','Verdana' ]
43819         ]
43820 };
43821
43822 // fixme - these need to be configurable..
43823  
43824
43825 Roo.form.HtmlEditor.ToolbarContext.types
43826
43827
43828 Roo.apply(Roo.form.HtmlEditor.ToolbarContext.prototype,  {
43829     
43830     tb: false,
43831     
43832     rendered: false,
43833     
43834     editor : false,
43835     editorcore : false,
43836     /**
43837      * @cfg {Object} disable  List of toolbar elements to disable
43838          
43839      */
43840     disable : false,
43841     /**
43842      * @cfg {Object} styles List of styles 
43843      *    eg. { '*' : [ 'headline' ] , 'TD' : [ 'underline', 'double-underline' ] } 
43844      *
43845      * These must be defined in the page, so they get rendered correctly..
43846      * .headline { }
43847      * TD.underline { }
43848      * 
43849      */
43850     styles : false,
43851     
43852     options: false,
43853     
43854     toolbars : false,
43855     
43856     init : function(editor)
43857     {
43858         this.editor = editor;
43859         this.editorcore = editor.editorcore ? editor.editorcore : editor;
43860         var editorcore = this.editorcore;
43861         
43862         var fid = editorcore.frameId;
43863         var etb = this;
43864         function btn(id, toggle, handler){
43865             var xid = fid + '-'+ id ;
43866             return {
43867                 id : xid,
43868                 cmd : id,
43869                 cls : 'x-btn-icon x-edit-'+id,
43870                 enableToggle:toggle !== false,
43871                 scope: editorcore, // was editor...
43872                 handler:handler||editorcore.relayBtnCmd,
43873                 clickEvent:'mousedown',
43874                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
43875                 tabIndex:-1
43876             };
43877         }
43878         // create a new element.
43879         var wdiv = editor.wrap.createChild({
43880                 tag: 'div'
43881             }, editor.wrap.dom.firstChild.nextSibling, true);
43882         
43883         // can we do this more than once??
43884         
43885          // stop form submits
43886       
43887  
43888         // disable everything...
43889         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
43890         this.toolbars = {};
43891            
43892         for (var i in  ty) {
43893           
43894             this.toolbars[i] = this.buildToolbar(ty[i],i);
43895         }
43896         this.tb = this.toolbars.BODY;
43897         this.tb.el.show();
43898         this.buildFooter();
43899         this.footer.show();
43900         editor.on('hide', function( ) { this.footer.hide() }, this);
43901         editor.on('show', function( ) { this.footer.show() }, this);
43902         
43903          
43904         this.rendered = true;
43905         
43906         // the all the btns;
43907         editor.on('editorevent', this.updateToolbar, this);
43908         // other toolbars need to implement this..
43909         //editor.on('editmodechange', this.updateToolbar, this);
43910     },
43911     
43912     
43913     
43914     /**
43915      * Protected method that will not generally be called directly. It triggers
43916      * a toolbar update by reading the markup state of the current selection in the editor.
43917      */
43918     updateToolbar: function(editor,ev,sel){
43919
43920         //Roo.log(ev);
43921         // capture mouse up - this is handy for selecting images..
43922         // perhaps should go somewhere else...
43923         if(!this.editorcore.activated){
43924              this.editor.onFirstFocus();
43925             return;
43926         }
43927         
43928         // http://developer.yahoo.com/yui/docs/simple-editor.js.html
43929         // selectNode - might want to handle IE?
43930         if (ev &&
43931             (ev.type == 'mouseup' || ev.type == 'click' ) &&
43932             ev.target && ev.target.tagName == 'IMG') {
43933             // they have click on an image...
43934             // let's see if we can change the selection...
43935             sel = ev.target;
43936          
43937               var nodeRange = sel.ownerDocument.createRange();
43938             try {
43939                 nodeRange.selectNode(sel);
43940             } catch (e) {
43941                 nodeRange.selectNodeContents(sel);
43942             }
43943             //nodeRange.collapse(true);
43944             var s = this.editorcore.win.getSelection();
43945             s.removeAllRanges();
43946             s.addRange(nodeRange);
43947         }  
43948         
43949       
43950         var updateFooter = sel ? false : true;
43951         
43952         
43953         var ans = this.editorcore.getAllAncestors();
43954         
43955         // pick
43956         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
43957         
43958         if (!sel) { 
43959             sel = ans.length ? (ans[0] ?  ans[0]  : ans[1]) : this.editorcore.doc.body;
43960             sel = sel ? sel : this.editorcore.doc.body;
43961             sel = sel.tagName.length ? sel : this.editorcore.doc.body;
43962             
43963         }
43964         // pick a menu that exists..
43965         var tn = sel.tagName.toUpperCase();
43966         //sel = typeof(ty[tn]) != 'undefined' ? sel : this.editor.doc.body;
43967         
43968         tn = sel.tagName.toUpperCase();
43969         
43970         var lastSel = this.tb.selectedNode
43971         
43972         this.tb.selectedNode = sel;
43973         
43974         // if current menu does not match..
43975         if ((this.tb.name != tn) || (lastSel != this.tb.selectedNode)) {
43976                 
43977             this.tb.el.hide();
43978             ///console.log("show: " + tn);
43979             this.tb =  typeof(ty[tn]) != 'undefined' ? this.toolbars[tn] : this.toolbars['*'];
43980             this.tb.el.show();
43981             // update name
43982             this.tb.items.first().el.innerHTML = tn + ':&nbsp;';
43983             
43984             
43985             // update attributes
43986             if (this.tb.fields) {
43987                 this.tb.fields.each(function(e) {
43988                     if (e.stylename) {
43989                         e.setValue(sel.style[e.stylename]);
43990                         return;
43991                     } 
43992                    e.setValue(sel.getAttribute(e.attrname));
43993                 });
43994             }
43995             
43996             var hasStyles = false;
43997             for(var i in this.styles) {
43998                 hasStyles = true;
43999                 break;
44000             }
44001             
44002             // update styles
44003             if (hasStyles) { 
44004                 var st = this.tb.fields.item(0);
44005                 
44006                 st.store.removeAll();
44007                
44008                 
44009                 var cn = sel.className.split(/\s+/);
44010                 
44011                 var avs = [];
44012                 if (this.styles['*']) {
44013                     
44014                     Roo.each(this.styles['*'], function(v) {
44015                         avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
44016                     });
44017                 }
44018                 if (this.styles[tn]) { 
44019                     Roo.each(this.styles[tn], function(v) {
44020                         avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
44021                     });
44022                 }
44023                 
44024                 st.store.loadData(avs);
44025                 st.collapse();
44026                 st.setValue(cn);
44027             }
44028             // flag our selected Node.
44029             this.tb.selectedNode = sel;
44030            
44031            
44032             Roo.menu.MenuMgr.hideAll();
44033
44034         }
44035         
44036         if (!updateFooter) {
44037             //this.footDisp.dom.innerHTML = ''; 
44038             return;
44039         }
44040         // update the footer
44041         //
44042         var html = '';
44043         
44044         this.footerEls = ans.reverse();
44045         Roo.each(this.footerEls, function(a,i) {
44046             if (!a) { return; }
44047             html += html.length ? ' &gt; '  :  '';
44048             
44049             html += '<span class="x-ed-loc-' + i + '">' + a.tagName + '</span>';
44050             
44051         });
44052        
44053         // 
44054         var sz = this.footDisp.up('td').getSize();
44055         this.footDisp.dom.style.width = (sz.width -10) + 'px';
44056         this.footDisp.dom.style.marginLeft = '5px';
44057         
44058         this.footDisp.dom.style.overflow = 'hidden';
44059         
44060         this.footDisp.dom.innerHTML = html;
44061             
44062         //this.editorsyncValue();
44063     },
44064      
44065     
44066    
44067        
44068     // private
44069     onDestroy : function(){
44070         if(this.rendered){
44071             
44072             this.tb.items.each(function(item){
44073                 if(item.menu){
44074                     item.menu.removeAll();
44075                     if(item.menu.el){
44076                         item.menu.el.destroy();
44077                     }
44078                 }
44079                 item.destroy();
44080             });
44081              
44082         }
44083     },
44084     onFirstFocus: function() {
44085         // need to do this for all the toolbars..
44086         this.tb.items.each(function(item){
44087            item.enable();
44088         });
44089     },
44090     buildToolbar: function(tlist, nm)
44091     {
44092         var editor = this.editor;
44093         var editorcore = this.editorcore;
44094          // create a new element.
44095         var wdiv = editor.wrap.createChild({
44096                 tag: 'div'
44097             }, editor.wrap.dom.firstChild.nextSibling, true);
44098         
44099        
44100         var tb = new Roo.Toolbar(wdiv);
44101         // add the name..
44102         
44103         tb.add(nm+ ":&nbsp;");
44104         
44105         var styles = [];
44106         for(var i in this.styles) {
44107             styles.push(i);
44108         }
44109         
44110         // styles...
44111         if (styles && styles.length) {
44112             
44113             // this needs a multi-select checkbox...
44114             tb.addField( new Roo.form.ComboBox({
44115                 store: new Roo.data.SimpleStore({
44116                     id : 'val',
44117                     fields: ['val', 'selected'],
44118                     data : [] 
44119                 }),
44120                 name : '-roo-edit-className',
44121                 attrname : 'className',
44122                 displayField: 'val',
44123                 typeAhead: false,
44124                 mode: 'local',
44125                 editable : false,
44126                 triggerAction: 'all',
44127                 emptyText:'Select Style',
44128                 selectOnFocus:true,
44129                 width: 130,
44130                 listeners : {
44131                     'select': function(c, r, i) {
44132                         // initial support only for on class per el..
44133                         tb.selectedNode.className =  r ? r.get('val') : '';
44134                         editorcore.syncValue();
44135                     }
44136                 }
44137     
44138             }));
44139         }
44140         
44141         var tbc = Roo.form.HtmlEditor.ToolbarContext;
44142         var tbops = tbc.options;
44143         
44144         for (var i in tlist) {
44145             
44146             var item = tlist[i];
44147             tb.add(item.title + ":&nbsp;");
44148             
44149             
44150             //optname == used so you can configure the options available..
44151             var opts = item.opts ? item.opts : false;
44152             if (item.optname) {
44153                 opts = tbops[item.optname];
44154            
44155             }
44156             
44157             if (opts) {
44158                 // opts == pulldown..
44159                 tb.addField( new Roo.form.ComboBox({
44160                     store:   typeof(tbc.stores[i]) != 'undefined' ?  Roo.factory(tbc.stores[i],Roo.data) : new Roo.data.SimpleStore({
44161                         id : 'val',
44162                         fields: ['val', 'display'],
44163                         data : opts  
44164                     }),
44165                     name : '-roo-edit-' + i,
44166                     attrname : i,
44167                     stylename : item.style ? item.style : false,
44168                     displayField: item.displayField ? item.displayField : 'val',
44169                     valueField :  'val',
44170                     typeAhead: false,
44171                     mode: typeof(tbc.stores[i]) != 'undefined'  ? 'remote' : 'local',
44172                     editable : false,
44173                     triggerAction: 'all',
44174                     emptyText:'Select',
44175                     selectOnFocus:true,
44176                     width: item.width ? item.width  : 130,
44177                     listeners : {
44178                         'select': function(c, r, i) {
44179                             if (c.stylename) {
44180                                 tb.selectedNode.style[c.stylename] =  r.get('val');
44181                                 return;
44182                             }
44183                             tb.selectedNode.setAttribute(c.attrname, r.get('val'));
44184                         }
44185                     }
44186
44187                 }));
44188                 continue;
44189                     
44190                  
44191                 
44192                 tb.addField( new Roo.form.TextField({
44193                     name: i,
44194                     width: 100,
44195                     //allowBlank:false,
44196                     value: ''
44197                 }));
44198                 continue;
44199             }
44200             tb.addField( new Roo.form.TextField({
44201                 name: '-roo-edit-' + i,
44202                 attrname : i,
44203                 
44204                 width: item.width,
44205                 //allowBlank:true,
44206                 value: '',
44207                 listeners: {
44208                     'change' : function(f, nv, ov) {
44209                         tb.selectedNode.setAttribute(f.attrname, nv);
44210                     }
44211                 }
44212             }));
44213              
44214         }
44215         tb.addFill();
44216         var _this = this;
44217         tb.addButton( {
44218             text: 'Remove Tag',
44219     
44220             listeners : {
44221                 click : function ()
44222                 {
44223                     // remove
44224                     // undo does not work.
44225                      
44226                     var sn = tb.selectedNode;
44227                     
44228                     var pn = sn.parentNode;
44229                     
44230                     var stn =  sn.childNodes[0];
44231                     var en = sn.childNodes[sn.childNodes.length - 1 ];
44232                     while (sn.childNodes.length) {
44233                         var node = sn.childNodes[0];
44234                         sn.removeChild(node);
44235                         //Roo.log(node);
44236                         pn.insertBefore(node, sn);
44237                         
44238                     }
44239                     pn.removeChild(sn);
44240                     var range = editorcore.createRange();
44241         
44242                     range.setStart(stn,0);
44243                     range.setEnd(en,0); //????
44244                     //range.selectNode(sel);
44245                     
44246                     
44247                     var selection = editorcore.getSelection();
44248                     selection.removeAllRanges();
44249                     selection.addRange(range);
44250                     
44251                     
44252                     
44253                     //_this.updateToolbar(null, null, pn);
44254                     _this.updateToolbar(null, null, null);
44255                     _this.footDisp.dom.innerHTML = ''; 
44256                 }
44257             }
44258             
44259                     
44260                 
44261             
44262         });
44263         
44264         
44265         tb.el.on('click', function(e){
44266             e.preventDefault(); // what does this do?
44267         });
44268         tb.el.setVisibilityMode( Roo.Element.DISPLAY);
44269         tb.el.hide();
44270         tb.name = nm;
44271         // dont need to disable them... as they will get hidden
44272         return tb;
44273          
44274         
44275     },
44276     buildFooter : function()
44277     {
44278         
44279         var fel = this.editor.wrap.createChild();
44280         this.footer = new Roo.Toolbar(fel);
44281         // toolbar has scrolly on left / right?
44282         var footDisp= new Roo.Toolbar.Fill();
44283         var _t = this;
44284         this.footer.add(
44285             {
44286                 text : '&lt;',
44287                 xtype: 'Button',
44288                 handler : function() {
44289                     _t.footDisp.scrollTo('left',0,true)
44290                 }
44291             }
44292         );
44293         this.footer.add( footDisp );
44294         this.footer.add( 
44295             {
44296                 text : '&gt;',
44297                 xtype: 'Button',
44298                 handler : function() {
44299                     // no animation..
44300                     _t.footDisp.select('span').last().scrollIntoView(_t.footDisp,true);
44301                 }
44302             }
44303         );
44304         var fel = Roo.get(footDisp.el);
44305         fel.addClass('x-editor-context');
44306         this.footDispWrap = fel; 
44307         this.footDispWrap.overflow  = 'hidden';
44308         
44309         this.footDisp = fel.createChild();
44310         this.footDispWrap.on('click', this.onContextClick, this)
44311         
44312         
44313     },
44314     onContextClick : function (ev,dom)
44315     {
44316         ev.preventDefault();
44317         var  cn = dom.className;
44318         //Roo.log(cn);
44319         if (!cn.match(/x-ed-loc-/)) {
44320             return;
44321         }
44322         var n = cn.split('-').pop();
44323         var ans = this.footerEls;
44324         var sel = ans[n];
44325         
44326          // pick
44327         var range = this.editorcore.createRange();
44328         
44329         range.selectNodeContents(sel);
44330         //range.selectNode(sel);
44331         
44332         
44333         var selection = this.editorcore.getSelection();
44334         selection.removeAllRanges();
44335         selection.addRange(range);
44336         
44337         
44338         
44339         this.updateToolbar(null, null, sel);
44340         
44341         
44342     }
44343     
44344     
44345     
44346     
44347     
44348 });
44349
44350
44351
44352
44353
44354 /*
44355  * Based on:
44356  * Ext JS Library 1.1.1
44357  * Copyright(c) 2006-2007, Ext JS, LLC.
44358  *
44359  * Originally Released Under LGPL - original licence link has changed is not relivant.
44360  *
44361  * Fork - LGPL
44362  * <script type="text/javascript">
44363  */
44364  
44365 /**
44366  * @class Roo.form.BasicForm
44367  * @extends Roo.util.Observable
44368  * Supplies the functionality to do "actions" on forms and initialize Roo.form.Field types on existing markup.
44369  * @constructor
44370  * @param {String/HTMLElement/Roo.Element} el The form element or its id
44371  * @param {Object} config Configuration options
44372  */
44373 Roo.form.BasicForm = function(el, config){
44374     this.allItems = [];
44375     this.childForms = [];
44376     Roo.apply(this, config);
44377     /*
44378      * The Roo.form.Field items in this form.
44379      * @type MixedCollection
44380      */
44381      
44382      
44383     this.items = new Roo.util.MixedCollection(false, function(o){
44384         return o.id || (o.id = Roo.id());
44385     });
44386     this.addEvents({
44387         /**
44388          * @event beforeaction
44389          * Fires before any action is performed. Return false to cancel the action.
44390          * @param {Form} this
44391          * @param {Action} action The action to be performed
44392          */
44393         beforeaction: true,
44394         /**
44395          * @event actionfailed
44396          * Fires when an action fails.
44397          * @param {Form} this
44398          * @param {Action} action The action that failed
44399          */
44400         actionfailed : true,
44401         /**
44402          * @event actioncomplete
44403          * Fires when an action is completed.
44404          * @param {Form} this
44405          * @param {Action} action The action that completed
44406          */
44407         actioncomplete : true
44408     });
44409     if(el){
44410         this.initEl(el);
44411     }
44412     Roo.form.BasicForm.superclass.constructor.call(this);
44413 };
44414
44415 Roo.extend(Roo.form.BasicForm, Roo.util.Observable, {
44416     /**
44417      * @cfg {String} method
44418      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
44419      */
44420     /**
44421      * @cfg {DataReader} reader
44422      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when executing "load" actions.
44423      * This is optional as there is built-in support for processing JSON.
44424      */
44425     /**
44426      * @cfg {DataReader} errorReader
44427      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when reading validation errors on "submit" actions.
44428      * This is completely optional as there is built-in support for processing JSON.
44429      */
44430     /**
44431      * @cfg {String} url
44432      * The URL to use for form actions if one isn't supplied in the action options.
44433      */
44434     /**
44435      * @cfg {Boolean} fileUpload
44436      * Set to true if this form is a file upload.
44437      */
44438      
44439     /**
44440      * @cfg {Object} baseParams
44441      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
44442      */
44443      /**
44444      
44445     /**
44446      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
44447      */
44448     timeout: 30,
44449
44450     // private
44451     activeAction : null,
44452
44453     /**
44454      * @cfg {Boolean} trackResetOnLoad If set to true, form.reset() resets to the last loaded
44455      * or setValues() data instead of when the form was first created.
44456      */
44457     trackResetOnLoad : false,
44458     
44459     
44460     /**
44461      * childForms - used for multi-tab forms
44462      * @type {Array}
44463      */
44464     childForms : false,
44465     
44466     /**
44467      * allItems - full list of fields.
44468      * @type {Array}
44469      */
44470     allItems : false,
44471     
44472     /**
44473      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
44474      * element by passing it or its id or mask the form itself by passing in true.
44475      * @type Mixed
44476      */
44477     waitMsgTarget : false,
44478
44479     // private
44480     initEl : function(el){
44481         this.el = Roo.get(el);
44482         this.id = this.el.id || Roo.id();
44483         this.el.on('submit', this.onSubmit, this);
44484         this.el.addClass('x-form');
44485     },
44486
44487     // private
44488     onSubmit : function(e){
44489         e.stopEvent();
44490     },
44491
44492     /**
44493      * Returns true if client-side validation on the form is successful.
44494      * @return Boolean
44495      */
44496     isValid : function(){
44497         var valid = true;
44498         this.items.each(function(f){
44499            if(!f.validate()){
44500                valid = false;
44501            }
44502         });
44503         return valid;
44504     },
44505
44506     /**
44507      * Returns true if any fields in this form have changed since their original load.
44508      * @return Boolean
44509      */
44510     isDirty : function(){
44511         var dirty = false;
44512         this.items.each(function(f){
44513            if(f.isDirty()){
44514                dirty = true;
44515                return false;
44516            }
44517         });
44518         return dirty;
44519     },
44520
44521     /**
44522      * Performs a predefined action (submit or load) or custom actions you define on this form.
44523      * @param {String} actionName The name of the action type
44524      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
44525      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
44526      * accept other config options):
44527      * <pre>
44528 Property          Type             Description
44529 ----------------  ---------------  ----------------------------------------------------------------------------------
44530 url               String           The url for the action (defaults to the form's url)
44531 method            String           The form method to use (defaults to the form's method, or POST if not defined)
44532 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
44533 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
44534                                    validate the form on the client (defaults to false)
44535      * </pre>
44536      * @return {BasicForm} this
44537      */
44538     doAction : function(action, options){
44539         if(typeof action == 'string'){
44540             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
44541         }
44542         if(this.fireEvent('beforeaction', this, action) !== false){
44543             this.beforeAction(action);
44544             action.run.defer(100, action);
44545         }
44546         return this;
44547     },
44548
44549     /**
44550      * Shortcut to do a submit action.
44551      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
44552      * @return {BasicForm} this
44553      */
44554     submit : function(options){
44555         this.doAction('submit', options);
44556         return this;
44557     },
44558
44559     /**
44560      * Shortcut to do a load action.
44561      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
44562      * @return {BasicForm} this
44563      */
44564     load : function(options){
44565         this.doAction('load', options);
44566         return this;
44567     },
44568
44569     /**
44570      * Persists the values in this form into the passed Roo.data.Record object in a beginEdit/endEdit block.
44571      * @param {Record} record The record to edit
44572      * @return {BasicForm} this
44573      */
44574     updateRecord : function(record){
44575         record.beginEdit();
44576         var fs = record.fields;
44577         fs.each(function(f){
44578             var field = this.findField(f.name);
44579             if(field){
44580                 record.set(f.name, field.getValue());
44581             }
44582         }, this);
44583         record.endEdit();
44584         return this;
44585     },
44586
44587     /**
44588      * Loads an Roo.data.Record into this form.
44589      * @param {Record} record The record to load
44590      * @return {BasicForm} this
44591      */
44592     loadRecord : function(record){
44593         this.setValues(record.data);
44594         return this;
44595     },
44596
44597     // private
44598     beforeAction : function(action){
44599         var o = action.options;
44600         
44601        
44602         if(this.waitMsgTarget === true){
44603             this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
44604         }else if(this.waitMsgTarget){
44605             this.waitMsgTarget = Roo.get(this.waitMsgTarget);
44606             this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
44607         }else {
44608             Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
44609         }
44610          
44611     },
44612
44613     // private
44614     afterAction : function(action, success){
44615         this.activeAction = null;
44616         var o = action.options;
44617         
44618         if(this.waitMsgTarget === true){
44619             this.el.unmask();
44620         }else if(this.waitMsgTarget){
44621             this.waitMsgTarget.unmask();
44622         }else{
44623             Roo.MessageBox.updateProgress(1);
44624             Roo.MessageBox.hide();
44625         }
44626          
44627         if(success){
44628             if(o.reset){
44629                 this.reset();
44630             }
44631             Roo.callback(o.success, o.scope, [this, action]);
44632             this.fireEvent('actioncomplete', this, action);
44633             
44634         }else{
44635             
44636             // failure condition..
44637             // we have a scenario where updates need confirming.
44638             // eg. if a locking scenario exists..
44639             // we look for { errors : { needs_confirm : true }} in the response.
44640             if (
44641                 (typeof(action.result) != 'undefined')  &&
44642                 (typeof(action.result.errors) != 'undefined')  &&
44643                 (typeof(action.result.errors.needs_confirm) != 'undefined')
44644            ){
44645                 var _t = this;
44646                 Roo.MessageBox.confirm(
44647                     "Change requires confirmation",
44648                     action.result.errorMsg,
44649                     function(r) {
44650                         if (r != 'yes') {
44651                             return;
44652                         }
44653                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
44654                     }
44655                     
44656                 );
44657                 
44658                 
44659                 
44660                 return;
44661             }
44662             
44663             Roo.callback(o.failure, o.scope, [this, action]);
44664             // show an error message if no failed handler is set..
44665             if (!this.hasListener('actionfailed')) {
44666                 Roo.MessageBox.alert("Error",
44667                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
44668                         action.result.errorMsg :
44669                         "Saving Failed, please check your entries or try again"
44670                 );
44671             }
44672             
44673             this.fireEvent('actionfailed', this, action);
44674         }
44675         
44676     },
44677
44678     /**
44679      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
44680      * @param {String} id The value to search for
44681      * @return Field
44682      */
44683     findField : function(id){
44684         var field = this.items.get(id);
44685         if(!field){
44686             this.items.each(function(f){
44687                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
44688                     field = f;
44689                     return false;
44690                 }
44691             });
44692         }
44693         return field || null;
44694     },
44695
44696     /**
44697      * Add a secondary form to this one, 
44698      * Used to provide tabbed forms. One form is primary, with hidden values 
44699      * which mirror the elements from the other forms.
44700      * 
44701      * @param {Roo.form.Form} form to add.
44702      * 
44703      */
44704     addForm : function(form)
44705     {
44706        
44707         if (this.childForms.indexOf(form) > -1) {
44708             // already added..
44709             return;
44710         }
44711         this.childForms.push(form);
44712         var n = '';
44713         Roo.each(form.allItems, function (fe) {
44714             
44715             n = typeof(fe.getName) == 'undefined' ? fe.name : fe.getName();
44716             if (this.findField(n)) { // already added..
44717                 return;
44718             }
44719             var add = new Roo.form.Hidden({
44720                 name : n
44721             });
44722             add.render(this.el);
44723             
44724             this.add( add );
44725         }, this);
44726         
44727     },
44728     /**
44729      * Mark fields in this form invalid in bulk.
44730      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
44731      * @return {BasicForm} this
44732      */
44733     markInvalid : function(errors){
44734         if(errors instanceof Array){
44735             for(var i = 0, len = errors.length; i < len; i++){
44736                 var fieldError = errors[i];
44737                 var f = this.findField(fieldError.id);
44738                 if(f){
44739                     f.markInvalid(fieldError.msg);
44740                 }
44741             }
44742         }else{
44743             var field, id;
44744             for(id in errors){
44745                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
44746                     field.markInvalid(errors[id]);
44747                 }
44748             }
44749         }
44750         Roo.each(this.childForms || [], function (f) {
44751             f.markInvalid(errors);
44752         });
44753         
44754         return this;
44755     },
44756
44757     /**
44758      * Set values for fields in this form in bulk.
44759      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
44760      * @return {BasicForm} this
44761      */
44762     setValues : function(values){
44763         if(values instanceof Array){ // array of objects
44764             for(var i = 0, len = values.length; i < len; i++){
44765                 var v = values[i];
44766                 var f = this.findField(v.id);
44767                 if(f){
44768                     f.setValue(v.value);
44769                     if(this.trackResetOnLoad){
44770                         f.originalValue = f.getValue();
44771                     }
44772                 }
44773             }
44774         }else{ // object hash
44775             var field, id;
44776             for(id in values){
44777                 if(typeof values[id] != 'function' && (field = this.findField(id))){
44778                     
44779                     if (field.setFromData && 
44780                         field.valueField && 
44781                         field.displayField &&
44782                         // combos' with local stores can 
44783                         // be queried via setValue()
44784                         // to set their value..
44785                         (field.store && !field.store.isLocal)
44786                         ) {
44787                         // it's a combo
44788                         var sd = { };
44789                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
44790                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
44791                         field.setFromData(sd);
44792                         
44793                     } else {
44794                         field.setValue(values[id]);
44795                     }
44796                     
44797                     
44798                     if(this.trackResetOnLoad){
44799                         field.originalValue = field.getValue();
44800                     }
44801                 }
44802             }
44803         }
44804          
44805         Roo.each(this.childForms || [], function (f) {
44806             f.setValues(values);
44807         });
44808                 
44809         return this;
44810     },
44811
44812     /**
44813      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
44814      * they are returned as an array.
44815      * @param {Boolean} asString
44816      * @return {Object}
44817      */
44818     getValues : function(asString){
44819         if (this.childForms) {
44820             // copy values from the child forms
44821             Roo.each(this.childForms, function (f) {
44822                 this.setValues(f.getValues());
44823             }, this);
44824         }
44825         
44826         
44827         
44828         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
44829         if(asString === true){
44830             return fs;
44831         }
44832         return Roo.urlDecode(fs);
44833     },
44834     
44835     /**
44836      * Returns the fields in this form as an object with key/value pairs. 
44837      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
44838      * @return {Object}
44839      */
44840     getFieldValues : function(with_hidden)
44841     {
44842         if (this.childForms) {
44843             // copy values from the child forms
44844             // should this call getFieldValues - probably not as we do not currently copy
44845             // hidden fields when we generate..
44846             Roo.each(this.childForms, function (f) {
44847                 this.setValues(f.getValues());
44848             }, this);
44849         }
44850         
44851         var ret = {};
44852         this.items.each(function(f){
44853             if (!f.getName()) {
44854                 return;
44855             }
44856             var v = f.getValue();
44857             if (f.inputType =='radio') {
44858                 if (typeof(ret[f.getName()]) == 'undefined') {
44859                     ret[f.getName()] = ''; // empty..
44860                 }
44861                 
44862                 if (!f.el.dom.checked) {
44863                     return;
44864                     
44865                 }
44866                 v = f.el.dom.value;
44867                 
44868             }
44869             
44870             // not sure if this supported any more..
44871             if ((typeof(v) == 'object') && f.getRawValue) {
44872                 v = f.getRawValue() ; // dates..
44873             }
44874             // combo boxes where name != hiddenName...
44875             if (f.name != f.getName()) {
44876                 ret[f.name] = f.getRawValue();
44877             }
44878             ret[f.getName()] = v;
44879         });
44880         
44881         return ret;
44882     },
44883
44884     /**
44885      * Clears all invalid messages in this form.
44886      * @return {BasicForm} this
44887      */
44888     clearInvalid : function(){
44889         this.items.each(function(f){
44890            f.clearInvalid();
44891         });
44892         
44893         Roo.each(this.childForms || [], function (f) {
44894             f.clearInvalid();
44895         });
44896         
44897         
44898         return this;
44899     },
44900
44901     /**
44902      * Resets this form.
44903      * @return {BasicForm} this
44904      */
44905     reset : function(){
44906         this.items.each(function(f){
44907             f.reset();
44908         });
44909         
44910         Roo.each(this.childForms || [], function (f) {
44911             f.reset();
44912         });
44913        
44914         
44915         return this;
44916     },
44917
44918     /**
44919      * Add Roo.form components to this form.
44920      * @param {Field} field1
44921      * @param {Field} field2 (optional)
44922      * @param {Field} etc (optional)
44923      * @return {BasicForm} this
44924      */
44925     add : function(){
44926         this.items.addAll(Array.prototype.slice.call(arguments, 0));
44927         return this;
44928     },
44929
44930
44931     /**
44932      * Removes a field from the items collection (does NOT remove its markup).
44933      * @param {Field} field
44934      * @return {BasicForm} this
44935      */
44936     remove : function(field){
44937         this.items.remove(field);
44938         return this;
44939     },
44940
44941     /**
44942      * Looks at the fields in this form, checks them for an id attribute,
44943      * and calls applyTo on the existing dom element with that id.
44944      * @return {BasicForm} this
44945      */
44946     render : function(){
44947         this.items.each(function(f){
44948             if(f.isFormField && !f.rendered && document.getElementById(f.id)){ // if the element exists
44949                 f.applyTo(f.id);
44950             }
44951         });
44952         return this;
44953     },
44954
44955     /**
44956      * Calls {@link Ext#apply} for all fields in this form with the passed object.
44957      * @param {Object} values
44958      * @return {BasicForm} this
44959      */
44960     applyToFields : function(o){
44961         this.items.each(function(f){
44962            Roo.apply(f, o);
44963         });
44964         return this;
44965     },
44966
44967     /**
44968      * Calls {@link Ext#applyIf} for all field in this form with the passed object.
44969      * @param {Object} values
44970      * @return {BasicForm} this
44971      */
44972     applyIfToFields : function(o){
44973         this.items.each(function(f){
44974            Roo.applyIf(f, o);
44975         });
44976         return this;
44977     }
44978 });
44979
44980 // back compat
44981 Roo.BasicForm = Roo.form.BasicForm;/*
44982  * Based on:
44983  * Ext JS Library 1.1.1
44984  * Copyright(c) 2006-2007, Ext JS, LLC.
44985  *
44986  * Originally Released Under LGPL - original licence link has changed is not relivant.
44987  *
44988  * Fork - LGPL
44989  * <script type="text/javascript">
44990  */
44991
44992 /**
44993  * @class Roo.form.Form
44994  * @extends Roo.form.BasicForm
44995  * Adds the ability to dynamically render forms with JavaScript to {@link Roo.form.BasicForm}.
44996  * @constructor
44997  * @param {Object} config Configuration options
44998  */
44999 Roo.form.Form = function(config){
45000     var xitems =  [];
45001     if (config.items) {
45002         xitems = config.items;
45003         delete config.items;
45004     }
45005    
45006     
45007     Roo.form.Form.superclass.constructor.call(this, null, config);
45008     this.url = this.url || this.action;
45009     if(!this.root){
45010         this.root = new Roo.form.Layout(Roo.applyIf({
45011             id: Roo.id()
45012         }, config));
45013     }
45014     this.active = this.root;
45015     /**
45016      * Array of all the buttons that have been added to this form via {@link addButton}
45017      * @type Array
45018      */
45019     this.buttons = [];
45020     this.allItems = [];
45021     this.addEvents({
45022         /**
45023          * @event clientvalidation
45024          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
45025          * @param {Form} this
45026          * @param {Boolean} valid true if the form has passed client-side validation
45027          */
45028         clientvalidation: true,
45029         /**
45030          * @event rendered
45031          * Fires when the form is rendered
45032          * @param {Roo.form.Form} form
45033          */
45034         rendered : true
45035     });
45036     
45037     if (this.progressUrl) {
45038             // push a hidden field onto the list of fields..
45039             this.addxtype( {
45040                     xns: Roo.form, 
45041                     xtype : 'Hidden', 
45042                     name : 'UPLOAD_IDENTIFIER' 
45043             });
45044         }
45045         
45046     
45047     Roo.each(xitems, this.addxtype, this);
45048     
45049     
45050     
45051 };
45052
45053 Roo.extend(Roo.form.Form, Roo.form.BasicForm, {
45054     /**
45055      * @cfg {Number} labelWidth The width of labels. This property cascades to child containers.
45056      */
45057     /**
45058      * @cfg {String} itemCls A css class to apply to the x-form-item of fields. This property cascades to child containers.
45059      */
45060     /**
45061      * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "center")
45062      */
45063     buttonAlign:'center',
45064
45065     /**
45066      * @cfg {Number} minButtonWidth Minimum width of all buttons in pixels (defaults to 75)
45067      */
45068     minButtonWidth:75,
45069
45070     /**
45071      * @cfg {String} labelAlign Valid values are "left," "top" and "right" (defaults to "left").
45072      * This property cascades to child containers if not set.
45073      */
45074     labelAlign:'left',
45075
45076     /**
45077      * @cfg {Boolean} monitorValid If true the form monitors its valid state <b>client-side</b> and
45078      * fires a looping event with that state. This is required to bind buttons to the valid
45079      * state using the config value formBind:true on the button.
45080      */
45081     monitorValid : false,
45082
45083     /**
45084      * @cfg {Number} monitorPoll The milliseconds to poll valid state, ignored if monitorValid is not true (defaults to 200)
45085      */
45086     monitorPoll : 200,
45087     
45088     /**
45089      * @cfg {String} progressUrl - Url to return progress data 
45090      */
45091     
45092     progressUrl : false,
45093   
45094     /**
45095      * Opens a new {@link Roo.form.Column} container in the layout stack. If fields are passed after the config, the
45096      * fields are added and the column is closed. If no fields are passed the column remains open
45097      * until end() is called.
45098      * @param {Object} config The config to pass to the column
45099      * @param {Field} field1 (optional)
45100      * @param {Field} field2 (optional)
45101      * @param {Field} etc (optional)
45102      * @return Column The column container object
45103      */
45104     column : function(c){
45105         var col = new Roo.form.Column(c);
45106         this.start(col);
45107         if(arguments.length > 1){ // duplicate code required because of Opera
45108             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
45109             this.end();
45110         }
45111         return col;
45112     },
45113
45114     /**
45115      * Opens a new {@link Roo.form.FieldSet} container in the layout stack. If fields are passed after the config, the
45116      * fields are added and the fieldset is closed. If no fields are passed the fieldset remains open
45117      * until end() is called.
45118      * @param {Object} config The config to pass to the fieldset
45119      * @param {Field} field1 (optional)
45120      * @param {Field} field2 (optional)
45121      * @param {Field} etc (optional)
45122      * @return FieldSet The fieldset container object
45123      */
45124     fieldset : function(c){
45125         var fs = new Roo.form.FieldSet(c);
45126         this.start(fs);
45127         if(arguments.length > 1){ // duplicate code required because of Opera
45128             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
45129             this.end();
45130         }
45131         return fs;
45132     },
45133
45134     /**
45135      * Opens a new {@link Roo.form.Layout} container in the layout stack. If fields are passed after the config, the
45136      * fields are added and the container is closed. If no fields are passed the container remains open
45137      * until end() is called.
45138      * @param {Object} config The config to pass to the Layout
45139      * @param {Field} field1 (optional)
45140      * @param {Field} field2 (optional)
45141      * @param {Field} etc (optional)
45142      * @return Layout The container object
45143      */
45144     container : function(c){
45145         var l = new Roo.form.Layout(c);
45146         this.start(l);
45147         if(arguments.length > 1){ // duplicate code required because of Opera
45148             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
45149             this.end();
45150         }
45151         return l;
45152     },
45153
45154     /**
45155      * Opens the passed container in the layout stack. The container can be any {@link Roo.form.Layout} or subclass.
45156      * @param {Object} container A Roo.form.Layout or subclass of Layout
45157      * @return {Form} this
45158      */
45159     start : function(c){
45160         // cascade label info
45161         Roo.applyIf(c, {'labelAlign': this.active.labelAlign, 'labelWidth': this.active.labelWidth, 'itemCls': this.active.itemCls});
45162         this.active.stack.push(c);
45163         c.ownerCt = this.active;
45164         this.active = c;
45165         return this;
45166     },
45167
45168     /**
45169      * Closes the current open container
45170      * @return {Form} this
45171      */
45172     end : function(){
45173         if(this.active == this.root){
45174             return this;
45175         }
45176         this.active = this.active.ownerCt;
45177         return this;
45178     },
45179
45180     /**
45181      * Add Roo.form components to the current open container (e.g. column, fieldset, etc.).  Fields added via this method
45182      * can also be passed with an additional property of fieldLabel, which if supplied, will provide the text to display
45183      * as the label of the field.
45184      * @param {Field} field1
45185      * @param {Field} field2 (optional)
45186      * @param {Field} etc. (optional)
45187      * @return {Form} this
45188      */
45189     add : function(){
45190         this.active.stack.push.apply(this.active.stack, arguments);
45191         this.allItems.push.apply(this.allItems,arguments);
45192         var r = [];
45193         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
45194             if(a[i].isFormField){
45195                 r.push(a[i]);
45196             }
45197         }
45198         if(r.length > 0){
45199             Roo.form.Form.superclass.add.apply(this, r);
45200         }
45201         return this;
45202     },
45203     
45204
45205     
45206     
45207     
45208      /**
45209      * Find any element that has been added to a form, using it's ID or name
45210      * This can include framesets, columns etc. along with regular fields..
45211      * @param {String} id - id or name to find.
45212      
45213      * @return {Element} e - or false if nothing found.
45214      */
45215     findbyId : function(id)
45216     {
45217         var ret = false;
45218         if (!id) {
45219             return ret;
45220         }
45221         Roo.each(this.allItems, function(f){
45222             if (f.id == id || f.name == id ){
45223                 ret = f;
45224                 return false;
45225             }
45226         });
45227         return ret;
45228     },
45229
45230     
45231     
45232     /**
45233      * Render this form into the passed container. This should only be called once!
45234      * @param {String/HTMLElement/Element} container The element this component should be rendered into
45235      * @return {Form} this
45236      */
45237     render : function(ct)
45238     {
45239         
45240         
45241         
45242         ct = Roo.get(ct);
45243         var o = this.autoCreate || {
45244             tag: 'form',
45245             method : this.method || 'POST',
45246             id : this.id || Roo.id()
45247         };
45248         this.initEl(ct.createChild(o));
45249
45250         this.root.render(this.el);
45251         
45252        
45253              
45254         this.items.each(function(f){
45255             f.render('x-form-el-'+f.id);
45256         });
45257
45258         if(this.buttons.length > 0){
45259             // tables are required to maintain order and for correct IE layout
45260             var tb = this.el.createChild({cls:'x-form-btns-ct', cn: {
45261                 cls:"x-form-btns x-form-btns-"+this.buttonAlign,
45262                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
45263             }}, null, true);
45264             var tr = tb.getElementsByTagName('tr')[0];
45265             for(var i = 0, len = this.buttons.length; i < len; i++) {
45266                 var b = this.buttons[i];
45267                 var td = document.createElement('td');
45268                 td.className = 'x-form-btn-td';
45269                 b.render(tr.appendChild(td));
45270             }
45271         }
45272         if(this.monitorValid){ // initialize after render
45273             this.startMonitoring();
45274         }
45275         this.fireEvent('rendered', this);
45276         return this;
45277     },
45278
45279     /**
45280      * Adds a button to the footer of the form - this <b>must</b> be called before the form is rendered.
45281      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
45282      * object or a valid Roo.DomHelper element config
45283      * @param {Function} handler The function called when the button is clicked
45284      * @param {Object} scope (optional) The scope of the handler function
45285      * @return {Roo.Button}
45286      */
45287     addButton : function(config, handler, scope){
45288         var bc = {
45289             handler: handler,
45290             scope: scope,
45291             minWidth: this.minButtonWidth,
45292             hideParent:true
45293         };
45294         if(typeof config == "string"){
45295             bc.text = config;
45296         }else{
45297             Roo.apply(bc, config);
45298         }
45299         var btn = new Roo.Button(null, bc);
45300         this.buttons.push(btn);
45301         return btn;
45302     },
45303
45304      /**
45305      * Adds a series of form elements (using the xtype property as the factory method.
45306      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column, (and 'end' to close a block)
45307      * @param {Object} config 
45308      */
45309     
45310     addxtype : function()
45311     {
45312         var ar = Array.prototype.slice.call(arguments, 0);
45313         var ret = false;
45314         for(var i = 0; i < ar.length; i++) {
45315             if (!ar[i]) {
45316                 continue; // skip -- if this happends something invalid got sent, we 
45317                 // should ignore it, as basically that interface element will not show up
45318                 // and that should be pretty obvious!!
45319             }
45320             
45321             if (Roo.form[ar[i].xtype]) {
45322                 ar[i].form = this;
45323                 var fe = Roo.factory(ar[i], Roo.form);
45324                 if (!ret) {
45325                     ret = fe;
45326                 }
45327                 fe.form = this;
45328                 if (fe.store) {
45329                     fe.store.form = this;
45330                 }
45331                 if (fe.isLayout) {  
45332                          
45333                     this.start(fe);
45334                     this.allItems.push(fe);
45335                     if (fe.items && fe.addxtype) {
45336                         fe.addxtype.apply(fe, fe.items);
45337                         delete fe.items;
45338                     }
45339                      this.end();
45340                     continue;
45341                 }
45342                 
45343                 
45344                  
45345                 this.add(fe);
45346               //  console.log('adding ' + ar[i].xtype);
45347             }
45348             if (ar[i].xtype == 'Button') {  
45349                 //console.log('adding button');
45350                 //console.log(ar[i]);
45351                 this.addButton(ar[i]);
45352                 this.allItems.push(fe);
45353                 continue;
45354             }
45355             
45356             if (ar[i].xtype == 'end') { // so we can add fieldsets... / layout etc.
45357                 alert('end is not supported on xtype any more, use items');
45358             //    this.end();
45359             //    //console.log('adding end');
45360             }
45361             
45362         }
45363         return ret;
45364     },
45365     
45366     /**
45367      * Starts monitoring of the valid state of this form. Usually this is done by passing the config
45368      * option "monitorValid"
45369      */
45370     startMonitoring : function(){
45371         if(!this.bound){
45372             this.bound = true;
45373             Roo.TaskMgr.start({
45374                 run : this.bindHandler,
45375                 interval : this.monitorPoll || 200,
45376                 scope: this
45377             });
45378         }
45379     },
45380
45381     /**
45382      * Stops monitoring of the valid state of this form
45383      */
45384     stopMonitoring : function(){
45385         this.bound = false;
45386     },
45387
45388     // private
45389     bindHandler : function(){
45390         if(!this.bound){
45391             return false; // stops binding
45392         }
45393         var valid = true;
45394         this.items.each(function(f){
45395             if(!f.isValid(true)){
45396                 valid = false;
45397                 return false;
45398             }
45399         });
45400         for(var i = 0, len = this.buttons.length; i < len; i++){
45401             var btn = this.buttons[i];
45402             if(btn.formBind === true && btn.disabled === valid){
45403                 btn.setDisabled(!valid);
45404             }
45405         }
45406         this.fireEvent('clientvalidation', this, valid);
45407     }
45408     
45409     
45410     
45411     
45412     
45413     
45414     
45415     
45416 });
45417
45418
45419 // back compat
45420 Roo.Form = Roo.form.Form;
45421 /*
45422  * Based on:
45423  * Ext JS Library 1.1.1
45424  * Copyright(c) 2006-2007, Ext JS, LLC.
45425  *
45426  * Originally Released Under LGPL - original licence link has changed is not relivant.
45427  *
45428  * Fork - LGPL
45429  * <script type="text/javascript">
45430  */
45431
45432 // as we use this in bootstrap.
45433 Roo.namespace('Roo.form');
45434  /**
45435  * @class Roo.form.Action
45436  * Internal Class used to handle form actions
45437  * @constructor
45438  * @param {Roo.form.BasicForm} el The form element or its id
45439  * @param {Object} config Configuration options
45440  */
45441
45442  
45443  
45444 // define the action interface
45445 Roo.form.Action = function(form, options){
45446     this.form = form;
45447     this.options = options || {};
45448 };
45449 /**
45450  * Client Validation Failed
45451  * @const 
45452  */
45453 Roo.form.Action.CLIENT_INVALID = 'client';
45454 /**
45455  * Server Validation Failed
45456  * @const 
45457  */
45458 Roo.form.Action.SERVER_INVALID = 'server';
45459  /**
45460  * Connect to Server Failed
45461  * @const 
45462  */
45463 Roo.form.Action.CONNECT_FAILURE = 'connect';
45464 /**
45465  * Reading Data from Server Failed
45466  * @const 
45467  */
45468 Roo.form.Action.LOAD_FAILURE = 'load';
45469
45470 Roo.form.Action.prototype = {
45471     type : 'default',
45472     failureType : undefined,
45473     response : undefined,
45474     result : undefined,
45475
45476     // interface method
45477     run : function(options){
45478
45479     },
45480
45481     // interface method
45482     success : function(response){
45483
45484     },
45485
45486     // interface method
45487     handleResponse : function(response){
45488
45489     },
45490
45491     // default connection failure
45492     failure : function(response){
45493         
45494         this.response = response;
45495         this.failureType = Roo.form.Action.CONNECT_FAILURE;
45496         this.form.afterAction(this, false);
45497     },
45498
45499     processResponse : function(response){
45500         this.response = response;
45501         if(!response.responseText){
45502             return true;
45503         }
45504         this.result = this.handleResponse(response);
45505         return this.result;
45506     },
45507
45508     // utility functions used internally
45509     getUrl : function(appendParams){
45510         var url = this.options.url || this.form.url || this.form.el.dom.action;
45511         if(appendParams){
45512             var p = this.getParams();
45513             if(p){
45514                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
45515             }
45516         }
45517         return url;
45518     },
45519
45520     getMethod : function(){
45521         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
45522     },
45523
45524     getParams : function(){
45525         var bp = this.form.baseParams;
45526         var p = this.options.params;
45527         if(p){
45528             if(typeof p == "object"){
45529                 p = Roo.urlEncode(Roo.applyIf(p, bp));
45530             }else if(typeof p == 'string' && bp){
45531                 p += '&' + Roo.urlEncode(bp);
45532             }
45533         }else if(bp){
45534             p = Roo.urlEncode(bp);
45535         }
45536         return p;
45537     },
45538
45539     createCallback : function(){
45540         return {
45541             success: this.success,
45542             failure: this.failure,
45543             scope: this,
45544             timeout: (this.form.timeout*1000),
45545             upload: this.form.fileUpload ? this.success : undefined
45546         };
45547     }
45548 };
45549
45550 Roo.form.Action.Submit = function(form, options){
45551     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
45552 };
45553
45554 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
45555     type : 'submit',
45556
45557     haveProgress : false,
45558     uploadComplete : false,
45559     
45560     // uploadProgress indicator.
45561     uploadProgress : function()
45562     {
45563         if (!this.form.progressUrl) {
45564             return;
45565         }
45566         
45567         if (!this.haveProgress) {
45568             Roo.MessageBox.progress("Uploading", "Uploading");
45569         }
45570         if (this.uploadComplete) {
45571            Roo.MessageBox.hide();
45572            return;
45573         }
45574         
45575         this.haveProgress = true;
45576    
45577         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
45578         
45579         var c = new Roo.data.Connection();
45580         c.request({
45581             url : this.form.progressUrl,
45582             params: {
45583                 id : uid
45584             },
45585             method: 'GET',
45586             success : function(req){
45587                //console.log(data);
45588                 var rdata = false;
45589                 var edata;
45590                 try  {
45591                    rdata = Roo.decode(req.responseText)
45592                 } catch (e) {
45593                     Roo.log("Invalid data from server..");
45594                     Roo.log(edata);
45595                     return;
45596                 }
45597                 if (!rdata || !rdata.success) {
45598                     Roo.log(rdata);
45599                     Roo.MessageBox.alert(Roo.encode(rdata));
45600                     return;
45601                 }
45602                 var data = rdata.data;
45603                 
45604                 if (this.uploadComplete) {
45605                    Roo.MessageBox.hide();
45606                    return;
45607                 }
45608                    
45609                 if (data){
45610                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
45611                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
45612                     );
45613                 }
45614                 this.uploadProgress.defer(2000,this);
45615             },
45616        
45617             failure: function(data) {
45618                 Roo.log('progress url failed ');
45619                 Roo.log(data);
45620             },
45621             scope : this
45622         });
45623            
45624     },
45625     
45626     
45627     run : function()
45628     {
45629         // run get Values on the form, so it syncs any secondary forms.
45630         this.form.getValues();
45631         
45632         var o = this.options;
45633         var method = this.getMethod();
45634         var isPost = method == 'POST';
45635         if(o.clientValidation === false || this.form.isValid()){
45636             
45637             if (this.form.progressUrl) {
45638                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
45639                     (new Date() * 1) + '' + Math.random());
45640                     
45641             } 
45642             
45643             
45644             Roo.Ajax.request(Roo.apply(this.createCallback(), {
45645                 form:this.form.el.dom,
45646                 url:this.getUrl(!isPost),
45647                 method: method,
45648                 params:isPost ? this.getParams() : null,
45649                 isUpload: this.form.fileUpload
45650             }));
45651             
45652             this.uploadProgress();
45653
45654         }else if (o.clientValidation !== false){ // client validation failed
45655             this.failureType = Roo.form.Action.CLIENT_INVALID;
45656             this.form.afterAction(this, false);
45657         }
45658     },
45659
45660     success : function(response)
45661     {
45662         this.uploadComplete= true;
45663         if (this.haveProgress) {
45664             Roo.MessageBox.hide();
45665         }
45666         
45667         
45668         var result = this.processResponse(response);
45669         if(result === true || result.success){
45670             this.form.afterAction(this, true);
45671             return;
45672         }
45673         if(result.errors){
45674             this.form.markInvalid(result.errors);
45675             this.failureType = Roo.form.Action.SERVER_INVALID;
45676         }
45677         this.form.afterAction(this, false);
45678     },
45679     failure : function(response)
45680     {
45681         this.uploadComplete= true;
45682         if (this.haveProgress) {
45683             Roo.MessageBox.hide();
45684         }
45685         
45686         this.response = response;
45687         this.failureType = Roo.form.Action.CONNECT_FAILURE;
45688         this.form.afterAction(this, false);
45689     },
45690     
45691     handleResponse : function(response){
45692         if(this.form.errorReader){
45693             var rs = this.form.errorReader.read(response);
45694             var errors = [];
45695             if(rs.records){
45696                 for(var i = 0, len = rs.records.length; i < len; i++) {
45697                     var r = rs.records[i];
45698                     errors[i] = r.data;
45699                 }
45700             }
45701             if(errors.length < 1){
45702                 errors = null;
45703             }
45704             return {
45705                 success : rs.success,
45706                 errors : errors
45707             };
45708         }
45709         var ret = false;
45710         try {
45711             ret = Roo.decode(response.responseText);
45712         } catch (e) {
45713             ret = {
45714                 success: false,
45715                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
45716                 errors : []
45717             };
45718         }
45719         return ret;
45720         
45721     }
45722 });
45723
45724
45725 Roo.form.Action.Load = function(form, options){
45726     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
45727     this.reader = this.form.reader;
45728 };
45729
45730 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
45731     type : 'load',
45732
45733     run : function(){
45734         
45735         Roo.Ajax.request(Roo.apply(
45736                 this.createCallback(), {
45737                     method:this.getMethod(),
45738                     url:this.getUrl(false),
45739                     params:this.getParams()
45740         }));
45741     },
45742
45743     success : function(response){
45744         
45745         var result = this.processResponse(response);
45746         if(result === true || !result.success || !result.data){
45747             this.failureType = Roo.form.Action.LOAD_FAILURE;
45748             this.form.afterAction(this, false);
45749             return;
45750         }
45751         this.form.clearInvalid();
45752         this.form.setValues(result.data);
45753         this.form.afterAction(this, true);
45754     },
45755
45756     handleResponse : function(response){
45757         if(this.form.reader){
45758             var rs = this.form.reader.read(response);
45759             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
45760             return {
45761                 success : rs.success,
45762                 data : data
45763             };
45764         }
45765         return Roo.decode(response.responseText);
45766     }
45767 });
45768
45769 Roo.form.Action.ACTION_TYPES = {
45770     'load' : Roo.form.Action.Load,
45771     'submit' : Roo.form.Action.Submit
45772 };/*
45773  * Based on:
45774  * Ext JS Library 1.1.1
45775  * Copyright(c) 2006-2007, Ext JS, LLC.
45776  *
45777  * Originally Released Under LGPL - original licence link has changed is not relivant.
45778  *
45779  * Fork - LGPL
45780  * <script type="text/javascript">
45781  */
45782  
45783 /**
45784  * @class Roo.form.Layout
45785  * @extends Roo.Component
45786  * Creates a container for layout and rendering of fields in an {@link Roo.form.Form}.
45787  * @constructor
45788  * @param {Object} config Configuration options
45789  */
45790 Roo.form.Layout = function(config){
45791     var xitems = [];
45792     if (config.items) {
45793         xitems = config.items;
45794         delete config.items;
45795     }
45796     Roo.form.Layout.superclass.constructor.call(this, config);
45797     this.stack = [];
45798     Roo.each(xitems, this.addxtype, this);
45799      
45800 };
45801
45802 Roo.extend(Roo.form.Layout, Roo.Component, {
45803     /**
45804      * @cfg {String/Object} autoCreate
45805      * A DomHelper element spec used to autocreate the layout (defaults to {tag: 'div', cls: 'x-form-ct'})
45806      */
45807     /**
45808      * @cfg {String/Object/Function} style
45809      * A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
45810      * a function which returns such a specification.
45811      */
45812     /**
45813      * @cfg {String} labelAlign
45814      * Valid values are "left," "top" and "right" (defaults to "left")
45815      */
45816     /**
45817      * @cfg {Number} labelWidth
45818      * Fixed width in pixels of all field labels (defaults to undefined)
45819      */
45820     /**
45821      * @cfg {Boolean} clear
45822      * True to add a clearing element at the end of this layout, equivalent to CSS clear: both (defaults to true)
45823      */
45824     clear : true,
45825     /**
45826      * @cfg {String} labelSeparator
45827      * The separator to use after field labels (defaults to ':')
45828      */
45829     labelSeparator : ':',
45830     /**
45831      * @cfg {Boolean} hideLabels
45832      * True to suppress the display of field labels in this layout (defaults to false)
45833      */
45834     hideLabels : false,
45835
45836     // private
45837     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct'},
45838     
45839     isLayout : true,
45840     
45841     // private
45842     onRender : function(ct, position){
45843         if(this.el){ // from markup
45844             this.el = Roo.get(this.el);
45845         }else {  // generate
45846             var cfg = this.getAutoCreate();
45847             this.el = ct.createChild(cfg, position);
45848         }
45849         if(this.style){
45850             this.el.applyStyles(this.style);
45851         }
45852         if(this.labelAlign){
45853             this.el.addClass('x-form-label-'+this.labelAlign);
45854         }
45855         if(this.hideLabels){
45856             this.labelStyle = "display:none";
45857             this.elementStyle = "padding-left:0;";
45858         }else{
45859             if(typeof this.labelWidth == 'number'){
45860                 this.labelStyle = "width:"+this.labelWidth+"px;";
45861                 this.elementStyle = "padding-left:"+((this.labelWidth+(typeof this.labelPad == 'number' ? this.labelPad : 5))+'px')+";";
45862             }
45863             if(this.labelAlign == 'top'){
45864                 this.labelStyle = "width:auto;";
45865                 this.elementStyle = "padding-left:0;";
45866             }
45867         }
45868         var stack = this.stack;
45869         var slen = stack.length;
45870         if(slen > 0){
45871             if(!this.fieldTpl){
45872                 var t = new Roo.Template(
45873                     '<div class="x-form-item {5}">',
45874                         '<label for="{0}" style="{2}">{1}{4}</label>',
45875                         '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
45876                         '</div>',
45877                     '</div><div class="x-form-clear-left"></div>'
45878                 );
45879                 t.disableFormats = true;
45880                 t.compile();
45881                 Roo.form.Layout.prototype.fieldTpl = t;
45882             }
45883             for(var i = 0; i < slen; i++) {
45884                 if(stack[i].isFormField){
45885                     this.renderField(stack[i]);
45886                 }else{
45887                     this.renderComponent(stack[i]);
45888                 }
45889             }
45890         }
45891         if(this.clear){
45892             this.el.createChild({cls:'x-form-clear'});
45893         }
45894     },
45895
45896     // private
45897     renderField : function(f){
45898         f.fieldEl = Roo.get(this.fieldTpl.append(this.el, [
45899                f.id, //0
45900                f.fieldLabel, //1
45901                f.labelStyle||this.labelStyle||'', //2
45902                this.elementStyle||'', //3
45903                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator, //4
45904                f.itemCls||this.itemCls||''  //5
45905        ], true).getPrevSibling());
45906     },
45907
45908     // private
45909     renderComponent : function(c){
45910         c.render(c.isLayout ? this.el : this.el.createChild());    
45911     },
45912     /**
45913      * Adds a object form elements (using the xtype property as the factory method.)
45914      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column
45915      * @param {Object} config 
45916      */
45917     addxtype : function(o)
45918     {
45919         // create the lement.
45920         o.form = this.form;
45921         var fe = Roo.factory(o, Roo.form);
45922         this.form.allItems.push(fe);
45923         this.stack.push(fe);
45924         
45925         if (fe.isFormField) {
45926             this.form.items.add(fe);
45927         }
45928          
45929         return fe;
45930     }
45931 });
45932
45933 /**
45934  * @class Roo.form.Column
45935  * @extends Roo.form.Layout
45936  * Creates a column container for layout and rendering of fields in an {@link Roo.form.Form}.
45937  * @constructor
45938  * @param {Object} config Configuration options
45939  */
45940 Roo.form.Column = function(config){
45941     Roo.form.Column.superclass.constructor.call(this, config);
45942 };
45943
45944 Roo.extend(Roo.form.Column, Roo.form.Layout, {
45945     /**
45946      * @cfg {Number/String} width
45947      * The fixed width of the column in pixels or CSS value (defaults to "auto")
45948      */
45949     /**
45950      * @cfg {String/Object} autoCreate
45951      * A DomHelper element spec used to autocreate the column (defaults to {tag: 'div', cls: 'x-form-ct x-form-column'})
45952      */
45953
45954     // private
45955     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-column'},
45956
45957     // private
45958     onRender : function(ct, position){
45959         Roo.form.Column.superclass.onRender.call(this, ct, position);
45960         if(this.width){
45961             this.el.setWidth(this.width);
45962         }
45963     }
45964 });
45965
45966
45967 /**
45968  * @class Roo.form.Row
45969  * @extends Roo.form.Layout
45970  * Creates a row container for layout and rendering of fields in an {@link Roo.form.Form}.
45971  * @constructor
45972  * @param {Object} config Configuration options
45973  */
45974
45975  
45976 Roo.form.Row = function(config){
45977     Roo.form.Row.superclass.constructor.call(this, config);
45978 };
45979  
45980 Roo.extend(Roo.form.Row, Roo.form.Layout, {
45981       /**
45982      * @cfg {Number/String} width
45983      * The fixed width of the column in pixels or CSS value (defaults to "auto")
45984      */
45985     /**
45986      * @cfg {Number/String} height
45987      * The fixed height of the column in pixels or CSS value (defaults to "auto")
45988      */
45989     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-row'},
45990     
45991     padWidth : 20,
45992     // private
45993     onRender : function(ct, position){
45994         //console.log('row render');
45995         if(!this.rowTpl){
45996             var t = new Roo.Template(
45997                 '<div class="x-form-item {5}" style="float:left;width:{6}px">',
45998                     '<label for="{0}" style="{2}">{1}{4}</label>',
45999                     '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
46000                     '</div>',
46001                 '</div>'
46002             );
46003             t.disableFormats = true;
46004             t.compile();
46005             Roo.form.Layout.prototype.rowTpl = t;
46006         }
46007         this.fieldTpl = this.rowTpl;
46008         
46009         //console.log('lw' + this.labelWidth +', la:' + this.labelAlign);
46010         var labelWidth = 100;
46011         
46012         if ((this.labelAlign != 'top')) {
46013             if (typeof this.labelWidth == 'number') {
46014                 labelWidth = this.labelWidth
46015             }
46016             this.padWidth =  20 + labelWidth;
46017             
46018         }
46019         
46020         Roo.form.Column.superclass.onRender.call(this, ct, position);
46021         if(this.width){
46022             this.el.setWidth(this.width);
46023         }
46024         if(this.height){
46025             this.el.setHeight(this.height);
46026         }
46027     },
46028     
46029     // private
46030     renderField : function(f){
46031         f.fieldEl = this.fieldTpl.append(this.el, [
46032                f.id, f.fieldLabel,
46033                f.labelStyle||this.labelStyle||'',
46034                this.elementStyle||'',
46035                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator,
46036                f.itemCls||this.itemCls||'',
46037                f.width ? f.width + this.padWidth : 160 + this.padWidth
46038        ],true);
46039     }
46040 });
46041  
46042
46043 /**
46044  * @class Roo.form.FieldSet
46045  * @extends Roo.form.Layout
46046  * Creates a fieldset container for layout and rendering of fields in an {@link Roo.form.Form}.
46047  * @constructor
46048  * @param {Object} config Configuration options
46049  */
46050 Roo.form.FieldSet = function(config){
46051     Roo.form.FieldSet.superclass.constructor.call(this, config);
46052 };
46053
46054 Roo.extend(Roo.form.FieldSet, Roo.form.Layout, {
46055     /**
46056      * @cfg {String} legend
46057      * The text to display as the legend for the FieldSet (defaults to '')
46058      */
46059     /**
46060      * @cfg {String/Object} autoCreate
46061      * A DomHelper element spec used to autocreate the fieldset (defaults to {tag: 'fieldset', cn: {tag:'legend'}})
46062      */
46063
46064     // private
46065     defaultAutoCreate : {tag: 'fieldset', cn: {tag:'legend'}},
46066
46067     // private
46068     onRender : function(ct, position){
46069         Roo.form.FieldSet.superclass.onRender.call(this, ct, position);
46070         if(this.legend){
46071             this.setLegend(this.legend);
46072         }
46073     },
46074
46075     // private
46076     setLegend : function(text){
46077         if(this.rendered){
46078             this.el.child('legend').update(text);
46079         }
46080     }
46081 });/*
46082  * Based on:
46083  * Ext JS Library 1.1.1
46084  * Copyright(c) 2006-2007, Ext JS, LLC.
46085  *
46086  * Originally Released Under LGPL - original licence link has changed is not relivant.
46087  *
46088  * Fork - LGPL
46089  * <script type="text/javascript">
46090  */
46091 /**
46092  * @class Roo.form.VTypes
46093  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
46094  * @singleton
46095  */
46096 Roo.form.VTypes = function(){
46097     // closure these in so they are only created once.
46098     var alpha = /^[a-zA-Z_]+$/;
46099     var alphanum = /^[a-zA-Z0-9_]+$/;
46100     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,4}$/;
46101     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
46102
46103     // All these messages and functions are configurable
46104     return {
46105         /**
46106          * The function used to validate email addresses
46107          * @param {String} value The email address
46108          */
46109         'email' : function(v){
46110             return email.test(v);
46111         },
46112         /**
46113          * The error text to display when the email validation function returns false
46114          * @type String
46115          */
46116         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
46117         /**
46118          * The keystroke filter mask to be applied on email input
46119          * @type RegExp
46120          */
46121         'emailMask' : /[a-z0-9_\.\-@]/i,
46122
46123         /**
46124          * The function used to validate URLs
46125          * @param {String} value The URL
46126          */
46127         'url' : function(v){
46128             return url.test(v);
46129         },
46130         /**
46131          * The error text to display when the url validation function returns false
46132          * @type String
46133          */
46134         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
46135         
46136         /**
46137          * The function used to validate alpha values
46138          * @param {String} value The value
46139          */
46140         'alpha' : function(v){
46141             return alpha.test(v);
46142         },
46143         /**
46144          * The error text to display when the alpha validation function returns false
46145          * @type String
46146          */
46147         'alphaText' : 'This field should only contain letters and _',
46148         /**
46149          * The keystroke filter mask to be applied on alpha input
46150          * @type RegExp
46151          */
46152         'alphaMask' : /[a-z_]/i,
46153
46154         /**
46155          * The function used to validate alphanumeric values
46156          * @param {String} value The value
46157          */
46158         'alphanum' : function(v){
46159             return alphanum.test(v);
46160         },
46161         /**
46162          * The error text to display when the alphanumeric validation function returns false
46163          * @type String
46164          */
46165         'alphanumText' : 'This field should only contain letters, numbers and _',
46166         /**
46167          * The keystroke filter mask to be applied on alphanumeric input
46168          * @type RegExp
46169          */
46170         'alphanumMask' : /[a-z0-9_]/i
46171     };
46172 }();//<script type="text/javascript">
46173
46174 /**
46175  * @class Roo.form.FCKeditor
46176  * @extends Roo.form.TextArea
46177  * Wrapper around the FCKEditor http://www.fckeditor.net
46178  * @constructor
46179  * Creates a new FCKeditor
46180  * @param {Object} config Configuration options
46181  */
46182 Roo.form.FCKeditor = function(config){
46183     Roo.form.FCKeditor.superclass.constructor.call(this, config);
46184     this.addEvents({
46185          /**
46186          * @event editorinit
46187          * Fired when the editor is initialized - you can add extra handlers here..
46188          * @param {FCKeditor} this
46189          * @param {Object} the FCK object.
46190          */
46191         editorinit : true
46192     });
46193     
46194     
46195 };
46196 Roo.form.FCKeditor.editors = { };
46197 Roo.extend(Roo.form.FCKeditor, Roo.form.TextArea,
46198 {
46199     //defaultAutoCreate : {
46200     //    tag : "textarea",style   : "width:100px;height:60px;" ,autocomplete    : "off"
46201     //},
46202     // private
46203     /**
46204      * @cfg {Object} fck options - see fck manual for details.
46205      */
46206     fckconfig : false,
46207     
46208     /**
46209      * @cfg {Object} fck toolbar set (Basic or Default)
46210      */
46211     toolbarSet : 'Basic',
46212     /**
46213      * @cfg {Object} fck BasePath
46214      */ 
46215     basePath : '/fckeditor/',
46216     
46217     
46218     frame : false,
46219     
46220     value : '',
46221     
46222    
46223     onRender : function(ct, position)
46224     {
46225         if(!this.el){
46226             this.defaultAutoCreate = {
46227                 tag: "textarea",
46228                 style:"width:300px;height:60px;",
46229                 autocomplete: "off"
46230             };
46231         }
46232         Roo.form.FCKeditor.superclass.onRender.call(this, ct, position);
46233         /*
46234         if(this.grow){
46235             this.textSizeEl = Roo.DomHelper.append(document.body, {tag: "pre", cls: "x-form-grow-sizer"});
46236             if(this.preventScrollbars){
46237                 this.el.setStyle("overflow", "hidden");
46238             }
46239             this.el.setHeight(this.growMin);
46240         }
46241         */
46242         //console.log('onrender' + this.getId() );
46243         Roo.form.FCKeditor.editors[this.getId()] = this;
46244          
46245
46246         this.replaceTextarea() ;
46247         
46248     },
46249     
46250     getEditor : function() {
46251         return this.fckEditor;
46252     },
46253     /**
46254      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
46255      * @param {Mixed} value The value to set
46256      */
46257     
46258     
46259     setValue : function(value)
46260     {
46261         //console.log('setValue: ' + value);
46262         
46263         if(typeof(value) == 'undefined') { // not sure why this is happending...
46264             return;
46265         }
46266         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
46267         
46268         //if(!this.el || !this.getEditor()) {
46269         //    this.value = value;
46270             //this.setValue.defer(100,this,[value]);    
46271         //    return;
46272         //} 
46273         
46274         if(!this.getEditor()) {
46275             return;
46276         }
46277         
46278         this.getEditor().SetData(value);
46279         
46280         //
46281
46282     },
46283
46284     /**
46285      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
46286      * @return {Mixed} value The field value
46287      */
46288     getValue : function()
46289     {
46290         
46291         if (this.frame && this.frame.dom.style.display == 'none') {
46292             return Roo.form.FCKeditor.superclass.getValue.call(this);
46293         }
46294         
46295         if(!this.el || !this.getEditor()) {
46296            
46297            // this.getValue.defer(100,this); 
46298             return this.value;
46299         }
46300        
46301         
46302         var value=this.getEditor().GetData();
46303         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
46304         return Roo.form.FCKeditor.superclass.getValue.call(this);
46305         
46306
46307     },
46308
46309     /**
46310      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
46311      * @return {Mixed} value The field value
46312      */
46313     getRawValue : function()
46314     {
46315         if (this.frame && this.frame.dom.style.display == 'none') {
46316             return Roo.form.FCKeditor.superclass.getRawValue.call(this);
46317         }
46318         
46319         if(!this.el || !this.getEditor()) {
46320             //this.getRawValue.defer(100,this); 
46321             return this.value;
46322             return;
46323         }
46324         
46325         
46326         
46327         var value=this.getEditor().GetData();
46328         Roo.form.FCKeditor.superclass.setRawValue.apply(this,[value]);
46329         return Roo.form.FCKeditor.superclass.getRawValue.call(this);
46330          
46331     },
46332     
46333     setSize : function(w,h) {
46334         
46335         
46336         
46337         //if (this.frame && this.frame.dom.style.display == 'none') {
46338         //    Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
46339         //    return;
46340         //}
46341         //if(!this.el || !this.getEditor()) {
46342         //    this.setSize.defer(100,this, [w,h]); 
46343         //    return;
46344         //}
46345         
46346         
46347         
46348         Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
46349         
46350         this.frame.dom.setAttribute('width', w);
46351         this.frame.dom.setAttribute('height', h);
46352         this.frame.setSize(w,h);
46353         
46354     },
46355     
46356     toggleSourceEdit : function(value) {
46357         
46358       
46359          
46360         this.el.dom.style.display = value ? '' : 'none';
46361         this.frame.dom.style.display = value ?  'none' : '';
46362         
46363     },
46364     
46365     
46366     focus: function(tag)
46367     {
46368         if (this.frame.dom.style.display == 'none') {
46369             return Roo.form.FCKeditor.superclass.focus.call(this);
46370         }
46371         if(!this.el || !this.getEditor()) {
46372             this.focus.defer(100,this, [tag]); 
46373             return;
46374         }
46375         
46376         
46377         
46378         
46379         var tgs = this.getEditor().EditorDocument.getElementsByTagName(tag);
46380         this.getEditor().Focus();
46381         if (tgs.length) {
46382             if (!this.getEditor().Selection.GetSelection()) {
46383                 this.focus.defer(100,this, [tag]); 
46384                 return;
46385             }
46386             
46387             
46388             var r = this.getEditor().EditorDocument.createRange();
46389             r.setStart(tgs[0],0);
46390             r.setEnd(tgs[0],0);
46391             this.getEditor().Selection.GetSelection().removeAllRanges();
46392             this.getEditor().Selection.GetSelection().addRange(r);
46393             this.getEditor().Focus();
46394         }
46395         
46396     },
46397     
46398     
46399     
46400     replaceTextarea : function()
46401     {
46402         if ( document.getElementById( this.getId() + '___Frame' ) )
46403             return ;
46404         //if ( !this.checkBrowser || this._isCompatibleBrowser() )
46405         //{
46406             // We must check the elements firstly using the Id and then the name.
46407         var oTextarea = document.getElementById( this.getId() );
46408         
46409         var colElementsByName = document.getElementsByName( this.getId() ) ;
46410          
46411         oTextarea.style.display = 'none' ;
46412
46413         if ( oTextarea.tabIndex ) {            
46414             this.TabIndex = oTextarea.tabIndex ;
46415         }
46416         
46417         this._insertHtmlBefore( this._getConfigHtml(), oTextarea ) ;
46418         this._insertHtmlBefore( this._getIFrameHtml(), oTextarea ) ;
46419         this.frame = Roo.get(this.getId() + '___Frame')
46420     },
46421     
46422     _getConfigHtml : function()
46423     {
46424         var sConfig = '' ;
46425
46426         for ( var o in this.fckconfig ) {
46427             sConfig += sConfig.length > 0  ? '&amp;' : '';
46428             sConfig += encodeURIComponent( o ) + '=' + encodeURIComponent( this.fckconfig[o] ) ;
46429         }
46430
46431         return '<input type="hidden" id="' + this.getId() + '___Config" value="' + sConfig + '" style="display:none" />' ;
46432     },
46433     
46434     
46435     _getIFrameHtml : function()
46436     {
46437         var sFile = 'fckeditor.html' ;
46438         /* no idea what this is about..
46439         try
46440         {
46441             if ( (/fcksource=true/i).test( window.top.location.search ) )
46442                 sFile = 'fckeditor.original.html' ;
46443         }
46444         catch (e) { 
46445         */
46446
46447         var sLink = this.basePath + 'editor/' + sFile + '?InstanceName=' + encodeURIComponent( this.getId() ) ;
46448         sLink += this.toolbarSet ? ( '&amp;Toolbar=' + this.toolbarSet)  : '';
46449         
46450         
46451         var html = '<iframe id="' + this.getId() +
46452             '___Frame" src="' + sLink +
46453             '" width="' + this.width +
46454             '" height="' + this.height + '"' +
46455             (this.tabIndex ?  ' tabindex="' + this.tabIndex + '"' :'' ) +
46456             ' frameborder="0" scrolling="no"></iframe>' ;
46457
46458         return html ;
46459     },
46460     
46461     _insertHtmlBefore : function( html, element )
46462     {
46463         if ( element.insertAdjacentHTML )       {
46464             // IE
46465             element.insertAdjacentHTML( 'beforeBegin', html ) ;
46466         } else { // Gecko
46467             var oRange = document.createRange() ;
46468             oRange.setStartBefore( element ) ;
46469             var oFragment = oRange.createContextualFragment( html );
46470             element.parentNode.insertBefore( oFragment, element ) ;
46471         }
46472     }
46473     
46474     
46475   
46476     
46477     
46478     
46479     
46480
46481 });
46482
46483 //Roo.reg('fckeditor', Roo.form.FCKeditor);
46484
46485 function FCKeditor_OnComplete(editorInstance){
46486     var f = Roo.form.FCKeditor.editors[editorInstance.Name];
46487     f.fckEditor = editorInstance;
46488     //console.log("loaded");
46489     f.fireEvent('editorinit', f, editorInstance);
46490
46491   
46492
46493  
46494
46495
46496
46497
46498
46499
46500
46501
46502
46503
46504
46505
46506
46507
46508
46509 //<script type="text/javascript">
46510 /**
46511  * @class Roo.form.GridField
46512  * @extends Roo.form.Field
46513  * Embed a grid (or editable grid into a form)
46514  * STATUS ALPHA
46515  * 
46516  * This embeds a grid in a form, the value of the field should be the json encoded array of rows
46517  * it needs 
46518  * xgrid.store = Roo.data.Store
46519  * xgrid.store.proxy = Roo.data.MemoryProxy (data = [] )
46520  * xgrid.store.reader = Roo.data.JsonReader 
46521  * 
46522  * 
46523  * @constructor
46524  * Creates a new GridField
46525  * @param {Object} config Configuration options
46526  */
46527 Roo.form.GridField = function(config){
46528     Roo.form.GridField.superclass.constructor.call(this, config);
46529      
46530 };
46531
46532 Roo.extend(Roo.form.GridField, Roo.form.Field,  {
46533     /**
46534      * @cfg {Number} width  - used to restrict width of grid..
46535      */
46536     width : 100,
46537     /**
46538      * @cfg {Number} height - used to restrict height of grid..
46539      */
46540     height : 50,
46541      /**
46542      * @cfg {Object} xgrid (xtype'd description of grid) { xtype : 'Grid', dataSource: .... }
46543          * 
46544          *}
46545      */
46546     xgrid : false, 
46547     /**
46548      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
46549      * {tag: "input", type: "checkbox", autocomplete: "off"})
46550      */
46551    // defaultAutoCreate : { tag: 'div' },
46552     defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
46553     /**
46554      * @cfg {String} addTitle Text to include for adding a title.
46555      */
46556     addTitle : false,
46557     //
46558     onResize : function(){
46559         Roo.form.Field.superclass.onResize.apply(this, arguments);
46560     },
46561
46562     initEvents : function(){
46563         // Roo.form.Checkbox.superclass.initEvents.call(this);
46564         // has no events...
46565        
46566     },
46567
46568
46569     getResizeEl : function(){
46570         return this.wrap;
46571     },
46572
46573     getPositionEl : function(){
46574         return this.wrap;
46575     },
46576
46577     // private
46578     onRender : function(ct, position){
46579         
46580         this.style = this.style || 'overflow: hidden; border:1px solid #c3daf9;';
46581         var style = this.style;
46582         delete this.style;
46583         
46584         Roo.form.GridField.superclass.onRender.call(this, ct, position);
46585         this.wrap = this.el.wrap({cls: ''}); // not sure why ive done thsi...
46586         this.viewEl = this.wrap.createChild({ tag: 'div' });
46587         if (style) {
46588             this.viewEl.applyStyles(style);
46589         }
46590         if (this.width) {
46591             this.viewEl.setWidth(this.width);
46592         }
46593         if (this.height) {
46594             this.viewEl.setHeight(this.height);
46595         }
46596         //if(this.inputValue !== undefined){
46597         //this.setValue(this.value);
46598         
46599         
46600         this.grid = new Roo.grid[this.xgrid.xtype](this.viewEl, this.xgrid);
46601         
46602         
46603         this.grid.render();
46604         this.grid.getDataSource().on('remove', this.refreshValue, this);
46605         this.grid.getDataSource().on('update', this.refreshValue, this);
46606         this.grid.on('afteredit', this.refreshValue, this);
46607  
46608     },
46609      
46610     
46611     /**
46612      * Sets the value of the item. 
46613      * @param {String} either an object  or a string..
46614      */
46615     setValue : function(v){
46616         //this.value = v;
46617         v = v || []; // empty set..
46618         // this does not seem smart - it really only affects memoryproxy grids..
46619         if (this.grid && this.grid.getDataSource() && typeof(v) != 'undefined') {
46620             var ds = this.grid.getDataSource();
46621             // assumes a json reader..
46622             var data = {}
46623             data[ds.reader.meta.root ] =  typeof(v) == 'string' ? Roo.decode(v) : v;
46624             ds.loadData( data);
46625         }
46626         // clear selection so it does not get stale.
46627         if (this.grid.sm) { 
46628             this.grid.sm.clearSelections();
46629         }
46630         
46631         Roo.form.GridField.superclass.setValue.call(this, v);
46632         this.refreshValue();
46633         // should load data in the grid really....
46634     },
46635     
46636     // private
46637     refreshValue: function() {
46638          var val = [];
46639         this.grid.getDataSource().each(function(r) {
46640             val.push(r.data);
46641         });
46642         this.el.dom.value = Roo.encode(val);
46643     }
46644     
46645      
46646     
46647     
46648 });/*
46649  * Based on:
46650  * Ext JS Library 1.1.1
46651  * Copyright(c) 2006-2007, Ext JS, LLC.
46652  *
46653  * Originally Released Under LGPL - original licence link has changed is not relivant.
46654  *
46655  * Fork - LGPL
46656  * <script type="text/javascript">
46657  */
46658 /**
46659  * @class Roo.form.DisplayField
46660  * @extends Roo.form.Field
46661  * A generic Field to display non-editable data.
46662  * @constructor
46663  * Creates a new Display Field item.
46664  * @param {Object} config Configuration options
46665  */
46666 Roo.form.DisplayField = function(config){
46667     Roo.form.DisplayField.superclass.constructor.call(this, config);
46668     
46669 };
46670
46671 Roo.extend(Roo.form.DisplayField, Roo.form.TextField,  {
46672     inputType:      'hidden',
46673     allowBlank:     true,
46674     readOnly:         true,
46675     
46676  
46677     /**
46678      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
46679      */
46680     focusClass : undefined,
46681     /**
46682      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
46683      */
46684     fieldClass: 'x-form-field',
46685     
46686      /**
46687      * @cfg {Function} valueRenderer The renderer for the field (so you can reformat output). should return raw HTML
46688      */
46689     valueRenderer: undefined,
46690     
46691     width: 100,
46692     /**
46693      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
46694      * {tag: "input", type: "checkbox", autocomplete: "off"})
46695      */
46696      
46697  //   defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
46698
46699     onResize : function(){
46700         Roo.form.DisplayField.superclass.onResize.apply(this, arguments);
46701         
46702     },
46703
46704     initEvents : function(){
46705         // Roo.form.Checkbox.superclass.initEvents.call(this);
46706         // has no events...
46707        
46708     },
46709
46710
46711     getResizeEl : function(){
46712         return this.wrap;
46713     },
46714
46715     getPositionEl : function(){
46716         return this.wrap;
46717     },
46718
46719     // private
46720     onRender : function(ct, position){
46721         
46722         Roo.form.DisplayField.superclass.onRender.call(this, ct, position);
46723         //if(this.inputValue !== undefined){
46724         this.wrap = this.el.wrap();
46725         
46726         this.viewEl = this.wrap.createChild({ tag: 'div', cls: 'x-form-displayfield'});
46727         
46728         if (this.bodyStyle) {
46729             this.viewEl.applyStyles(this.bodyStyle);
46730         }
46731         //this.viewEl.setStyle('padding', '2px');
46732         
46733         this.setValue(this.value);
46734         
46735     },
46736 /*
46737     // private
46738     initValue : Roo.emptyFn,
46739
46740   */
46741
46742         // private
46743     onClick : function(){
46744         
46745     },
46746
46747     /**
46748      * Sets the checked state of the checkbox.
46749      * @param {Boolean/String} checked True, 'true', '1', or 'on' to check the checkbox, any other value will uncheck it.
46750      */
46751     setValue : function(v){
46752         this.value = v;
46753         var html = this.valueRenderer ?  this.valueRenderer(v) : String.format('{0}', v);
46754         // this might be called before we have a dom element..
46755         if (!this.viewEl) {
46756             return;
46757         }
46758         this.viewEl.dom.innerHTML = html;
46759         Roo.form.DisplayField.superclass.setValue.call(this, v);
46760
46761     }
46762 });/*
46763  * 
46764  * Licence- LGPL
46765  * 
46766  */
46767
46768 /**
46769  * @class Roo.form.DayPicker
46770  * @extends Roo.form.Field
46771  * A Day picker show [M] [T] [W] ....
46772  * @constructor
46773  * Creates a new Day Picker
46774  * @param {Object} config Configuration options
46775  */
46776 Roo.form.DayPicker= function(config){
46777     Roo.form.DayPicker.superclass.constructor.call(this, config);
46778      
46779 };
46780
46781 Roo.extend(Roo.form.DayPicker, Roo.form.Field,  {
46782     /**
46783      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
46784      */
46785     focusClass : undefined,
46786     /**
46787      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
46788      */
46789     fieldClass: "x-form-field",
46790    
46791     /**
46792      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
46793      * {tag: "input", type: "checkbox", autocomplete: "off"})
46794      */
46795     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "off"},
46796     
46797    
46798     actionMode : 'viewEl', 
46799     //
46800     // private
46801  
46802     inputType : 'hidden',
46803     
46804      
46805     inputElement: false, // real input element?
46806     basedOn: false, // ????
46807     
46808     isFormField: true, // not sure where this is needed!!!!
46809
46810     onResize : function(){
46811         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
46812         if(!this.boxLabel){
46813             this.el.alignTo(this.wrap, 'c-c');
46814         }
46815     },
46816
46817     initEvents : function(){
46818         Roo.form.Checkbox.superclass.initEvents.call(this);
46819         this.el.on("click", this.onClick,  this);
46820         this.el.on("change", this.onClick,  this);
46821     },
46822
46823
46824     getResizeEl : function(){
46825         return this.wrap;
46826     },
46827
46828     getPositionEl : function(){
46829         return this.wrap;
46830     },
46831
46832     
46833     // private
46834     onRender : function(ct, position){
46835         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
46836        
46837         this.wrap = this.el.wrap({cls: 'x-form-daypick-item '});
46838         
46839         var r1 = '<table><tr>';
46840         var r2 = '<tr class="x-form-daypick-icons">';
46841         for (var i=0; i < 7; i++) {
46842             r1+= '<td><div>' + Date.dayNames[i].substring(0,3) + '</div></td>';
46843             r2+= '<td><img class="x-menu-item-icon" src="' + Roo.BLANK_IMAGE_URL  +'"></td>';
46844         }
46845         
46846         var viewEl = this.wrap.createChild( r1 + '</tr>' + r2 + '</tr></table>');
46847         viewEl.select('img').on('click', this.onClick, this);
46848         this.viewEl = viewEl;   
46849         
46850         
46851         // this will not work on Chrome!!!
46852         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
46853         this.el.on('propertychange', this.setFromHidden,  this);  //ie
46854         
46855         
46856           
46857
46858     },
46859
46860     // private
46861     initValue : Roo.emptyFn,
46862
46863     /**
46864      * Returns the checked state of the checkbox.
46865      * @return {Boolean} True if checked, else false
46866      */
46867     getValue : function(){
46868         return this.el.dom.value;
46869         
46870     },
46871
46872         // private
46873     onClick : function(e){ 
46874         //this.setChecked(!this.checked);
46875         Roo.get(e.target).toggleClass('x-menu-item-checked');
46876         this.refreshValue();
46877         //if(this.el.dom.checked != this.checked){
46878         //    this.setValue(this.el.dom.checked);
46879        // }
46880     },
46881     
46882     // private
46883     refreshValue : function()
46884     {
46885         var val = '';
46886         this.viewEl.select('img',true).each(function(e,i,n)  {
46887             val += e.is(".x-menu-item-checked") ? String(n) : '';
46888         });
46889         this.setValue(val, true);
46890     },
46891
46892     /**
46893      * Sets the checked state of the checkbox.
46894      * On is always based on a string comparison between inputValue and the param.
46895      * @param {Boolean/String} value - the value to set 
46896      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
46897      */
46898     setValue : function(v,suppressEvent){
46899         if (!this.el.dom) {
46900             return;
46901         }
46902         var old = this.el.dom.value ;
46903         this.el.dom.value = v;
46904         if (suppressEvent) {
46905             return ;
46906         }
46907          
46908         // update display..
46909         this.viewEl.select('img',true).each(function(e,i,n)  {
46910             
46911             var on = e.is(".x-menu-item-checked");
46912             var newv = v.indexOf(String(n)) > -1;
46913             if (on != newv) {
46914                 e.toggleClass('x-menu-item-checked');
46915             }
46916             
46917         });
46918         
46919         
46920         this.fireEvent('change', this, v, old);
46921         
46922         
46923     },
46924    
46925     // handle setting of hidden value by some other method!!?!?
46926     setFromHidden: function()
46927     {
46928         if(!this.el){
46929             return;
46930         }
46931         //console.log("SET FROM HIDDEN");
46932         //alert('setFrom hidden');
46933         this.setValue(this.el.dom.value);
46934     },
46935     
46936     onDestroy : function()
46937     {
46938         if(this.viewEl){
46939             Roo.get(this.viewEl).remove();
46940         }
46941          
46942         Roo.form.DayPicker.superclass.onDestroy.call(this);
46943     }
46944
46945 });/*
46946  * RooJS Library 1.1.1
46947  * Copyright(c) 2008-2011  Alan Knowles
46948  *
46949  * License - LGPL
46950  */
46951  
46952
46953 /**
46954  * @class Roo.form.ComboCheck
46955  * @extends Roo.form.ComboBox
46956  * A combobox for multiple select items.
46957  *
46958  * FIXME - could do with a reset button..
46959  * 
46960  * @constructor
46961  * Create a new ComboCheck
46962  * @param {Object} config Configuration options
46963  */
46964 Roo.form.ComboCheck = function(config){
46965     Roo.form.ComboCheck.superclass.constructor.call(this, config);
46966     // should verify some data...
46967     // like
46968     // hiddenName = required..
46969     // displayField = required
46970     // valudField == required
46971     var req= [ 'hiddenName', 'displayField', 'valueField' ];
46972     var _t = this;
46973     Roo.each(req, function(e) {
46974         if ((typeof(_t[e]) == 'undefined' ) || !_t[e].length) {
46975             throw "Roo.form.ComboCheck : missing value for: " + e;
46976         }
46977     });
46978     
46979     
46980 };
46981
46982 Roo.extend(Roo.form.ComboCheck, Roo.form.ComboBox, {
46983      
46984      
46985     editable : false,
46986      
46987     selectedClass: 'x-menu-item-checked', 
46988     
46989     // private
46990     onRender : function(ct, position){
46991         var _t = this;
46992         
46993         
46994         
46995         if(!this.tpl){
46996             var cls = 'x-combo-list';
46997
46998             
46999             this.tpl =  new Roo.Template({
47000                 html :  '<div class="'+cls+'-item x-menu-check-item">' +
47001                    '<img class="x-menu-item-icon" style="margin: 0px;" src="' + Roo.BLANK_IMAGE_URL + '">' + 
47002                    '<span>{' + this.displayField + '}</span>' +
47003                     '</div>' 
47004                 
47005             });
47006         }
47007  
47008         
47009         Roo.form.ComboCheck.superclass.onRender.call(this, ct, position);
47010         this.view.singleSelect = false;
47011         this.view.multiSelect = true;
47012         this.view.toggleSelect = true;
47013         this.pageTb.add(new Roo.Toolbar.Fill(), {
47014             
47015             text: 'Done',
47016             handler: function()
47017             {
47018                 _t.collapse();
47019             }
47020         });
47021     },
47022     
47023     onViewOver : function(e, t){
47024         // do nothing...
47025         return;
47026         
47027     },
47028     
47029     onViewClick : function(doFocus,index){
47030         return;
47031         
47032     },
47033     select: function () {
47034         //Roo.log("SELECT CALLED");
47035     },
47036      
47037     selectByValue : function(xv, scrollIntoView){
47038         var ar = this.getValueArray();
47039         var sels = [];
47040         
47041         Roo.each(ar, function(v) {
47042             if(v === undefined || v === null){
47043                 return;
47044             }
47045             var r = this.findRecord(this.valueField, v);
47046             if(r){
47047                 sels.push(this.store.indexOf(r))
47048                 
47049             }
47050         },this);
47051         this.view.select(sels);
47052         return false;
47053     },
47054     
47055     
47056     
47057     onSelect : function(record, index){
47058        // Roo.log("onselect Called");
47059        // this is only called by the clear button now..
47060         this.view.clearSelections();
47061         this.setValue('[]');
47062         if (this.value != this.valueBefore) {
47063             this.fireEvent('change', this, this.value, this.valueBefore);
47064             this.valueBefore = this.value;
47065         }
47066     },
47067     getValueArray : function()
47068     {
47069         var ar = [] ;
47070         
47071         try {
47072             //Roo.log(this.value);
47073             if (typeof(this.value) == 'undefined') {
47074                 return [];
47075             }
47076             var ar = Roo.decode(this.value);
47077             return  ar instanceof Array ? ar : []; //?? valid?
47078             
47079         } catch(e) {
47080             Roo.log(e + "\nRoo.form.ComboCheck:getValueArray  invalid data:" + this.getValue());
47081             return [];
47082         }
47083          
47084     },
47085     expand : function ()
47086     {
47087         
47088         Roo.form.ComboCheck.superclass.expand.call(this);
47089         this.valueBefore = typeof(this.value) == 'undefined' ? '' : this.value;
47090         //this.valueBefore = typeof(this.valueBefore) == 'undefined' ? '' : this.valueBefore;
47091         
47092
47093     },
47094     
47095     collapse : function(){
47096         Roo.form.ComboCheck.superclass.collapse.call(this);
47097         var sl = this.view.getSelectedIndexes();
47098         var st = this.store;
47099         var nv = [];
47100         var tv = [];
47101         var r;
47102         Roo.each(sl, function(i) {
47103             r = st.getAt(i);
47104             nv.push(r.get(this.valueField));
47105         },this);
47106         this.setValue(Roo.encode(nv));
47107         if (this.value != this.valueBefore) {
47108
47109             this.fireEvent('change', this, this.value, this.valueBefore);
47110             this.valueBefore = this.value;
47111         }
47112         
47113     },
47114     
47115     setValue : function(v){
47116         // Roo.log(v);
47117         this.value = v;
47118         
47119         var vals = this.getValueArray();
47120         var tv = [];
47121         Roo.each(vals, function(k) {
47122             var r = this.findRecord(this.valueField, k);
47123             if(r){
47124                 tv.push(r.data[this.displayField]);
47125             }else if(this.valueNotFoundText !== undefined){
47126                 tv.push( this.valueNotFoundText );
47127             }
47128         },this);
47129        // Roo.log(tv);
47130         
47131         Roo.form.ComboBox.superclass.setValue.call(this, tv.join(', '));
47132         this.hiddenField.value = v;
47133         this.value = v;
47134     }
47135     
47136 });/*
47137  * Based on:
47138  * Ext JS Library 1.1.1
47139  * Copyright(c) 2006-2007, Ext JS, LLC.
47140  *
47141  * Originally Released Under LGPL - original licence link has changed is not relivant.
47142  *
47143  * Fork - LGPL
47144  * <script type="text/javascript">
47145  */
47146  
47147 /**
47148  * @class Roo.form.Signature
47149  * @extends Roo.form.Field
47150  * Signature field.  
47151  * @constructor
47152  * 
47153  * @param {Object} config Configuration options
47154  */
47155
47156 Roo.form.Signature = function(config){
47157     Roo.form.Signature.superclass.constructor.call(this, config);
47158     
47159     this.addEvents({// not in used??
47160          /**
47161          * @event confirm
47162          * Fires when the 'confirm' icon is pressed (add a listener to enable add button)
47163              * @param {Roo.form.Signature} combo This combo box
47164              */
47165         'confirm' : true,
47166         /**
47167          * @event reset
47168          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
47169              * @param {Roo.form.ComboBox} combo This combo box
47170              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
47171              */
47172         'reset' : true
47173     });
47174 };
47175
47176 Roo.extend(Roo.form.Signature, Roo.form.Field,  {
47177     /**
47178      * @cfg {Object} labels Label to use when rendering a form.
47179      * defaults to 
47180      * labels : { 
47181      *      clear : "Clear",
47182      *      confirm : "Confirm"
47183      *  }
47184      */
47185     labels : { 
47186         clear : "Clear",
47187         confirm : "Confirm"
47188     },
47189     /**
47190      * @cfg {Number} width The signature panel width (defaults to 300)
47191      */
47192     width: 300,
47193     /**
47194      * @cfg {Number} height The signature panel height (defaults to 100)
47195      */
47196     height : 100,
47197     /**
47198      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to false)
47199      */
47200     allowBlank : false,
47201     
47202     //private
47203     // {Object} signPanel The signature SVG panel element (defaults to {})
47204     signPanel : {},
47205     // {Boolean} isMouseDown False to validate that the mouse down event (defaults to false)
47206     isMouseDown : false,
47207     // {Boolean} isConfirmed validate the signature is confirmed or not for submitting form (defaults to false)
47208     isConfirmed : false,
47209     // {String} signatureTmp SVG mapping string (defaults to empty string)
47210     signatureTmp : '',
47211     
47212     
47213     defaultAutoCreate : { // modified by initCompnoent..
47214         tag: "input",
47215         type:"hidden"
47216     },
47217
47218     // private
47219     onRender : function(ct, position){
47220         
47221         Roo.form.Signature.superclass.onRender.call(this, ct, position);
47222         
47223         this.wrap = this.el.wrap({
47224             cls:'x-form-signature-wrap', style : 'width: ' + this.width + 'px', cn:{cls:'x-form-signature'}
47225         });
47226         
47227         this.createToolbar(this);
47228         this.signPanel = this.wrap.createChild({
47229                 tag: 'div',
47230                 style: 'width: ' + this.width + 'px; height: ' + this.height + 'px; border: 0;'
47231             }, this.el
47232         );
47233             
47234         this.svgID = Roo.id();
47235         this.svgEl = this.signPanel.createChild({
47236               xmlns : 'http://www.w3.org/2000/svg',
47237               tag : 'svg',
47238               id : this.svgID + "-svg",
47239               width: this.width,
47240               height: this.height,
47241               viewBox: '0 0 '+this.width+' '+this.height,
47242               cn : [
47243                 {
47244                     tag: "rect",
47245                     id: this.svgID + "-svg-r",
47246                     width: this.width,
47247                     height: this.height,
47248                     fill: "#ffa"
47249                 },
47250                 {
47251                     tag: "line",
47252                     id: this.svgID + "-svg-l",
47253                     x1: "0", // start
47254                     y1: (this.height*0.8), // start set the line in 80% of height
47255                     x2: this.width, // end
47256                     y2: (this.height*0.8), // end set the line in 80% of height
47257                     'stroke': "#666",
47258                     'stroke-width': "1",
47259                     'stroke-dasharray': "3",
47260                     'shape-rendering': "crispEdges",
47261                     'pointer-events': "none"
47262                 },
47263                 {
47264                     tag: "path",
47265                     id: this.svgID + "-svg-p",
47266                     'stroke': "navy",
47267                     'stroke-width': "3",
47268                     'fill': "none",
47269                     'pointer-events': 'none'
47270                 }
47271               ]
47272         });
47273         this.createSVG();
47274         this.svgBox = this.svgEl.dom.getScreenCTM();
47275     },
47276     createSVG : function(){ 
47277         var svg = this.signPanel;
47278         var r = svg.select('#'+ this.svgID + '-svg-r', true).first().dom;
47279         var t = this;
47280
47281         r.addEventListener('mousedown', function(e) { return t.down(e); }, false);
47282         r.addEventListener('mousemove', function(e) { return t.move(e); }, false);
47283         r.addEventListener('mouseup', function(e) { return t.up(e); }, false);
47284         r.addEventListener('mouseout', function(e) { return t.up(e); }, false);
47285         r.addEventListener('touchstart', function(e) { return t.down(e); }, false);
47286         r.addEventListener('touchmove', function(e) { return t.move(e); }, false);
47287         r.addEventListener('touchend', function(e) { return t.up(e); }, false);
47288         
47289     },
47290     isTouchEvent : function(e){
47291         return e.type.match(/^touch/);
47292     },
47293     getCoords : function (e) {
47294         var pt    = this.svgEl.dom.createSVGPoint();
47295         pt.x = e.clientX; 
47296         pt.y = e.clientY;
47297         if (this.isTouchEvent(e)) {
47298             pt.x =  e.targetTouches[0].clientX 
47299             pt.y = e.targetTouches[0].clientY;
47300         }
47301         var a = this.svgEl.dom.getScreenCTM();
47302         var b = a.inverse();
47303         var mx = pt.matrixTransform(b);
47304         return mx.x + ',' + mx.y;
47305     },
47306     //mouse event headler 
47307     down : function (e) {
47308         this.signatureTmp += 'M' + this.getCoords(e) + ' ';
47309         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr('d', this.signatureTmp);
47310         
47311         this.isMouseDown = true;
47312         
47313         e.preventDefault();
47314     },
47315     move : function (e) {
47316         if (this.isMouseDown) {
47317             this.signatureTmp += 'L' + this.getCoords(e) + ' ';
47318             this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', this.signatureTmp);
47319         }
47320         
47321         e.preventDefault();
47322     },
47323     up : function (e) {
47324         this.isMouseDown = false;
47325         var sp = this.signatureTmp.split(' ');
47326         
47327         if(sp.length > 1){
47328             if(!sp[sp.length-2].match(/^L/)){
47329                 sp.pop();
47330                 sp.pop();
47331                 sp.push("");
47332                 this.signatureTmp = sp.join(" ");
47333             }
47334         }
47335         if(this.getValue() != this.signatureTmp){
47336             this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
47337             this.isConfirmed = false;
47338         }
47339         e.preventDefault();
47340     },
47341     
47342     /**
47343      * Protected method that will not generally be called directly. It
47344      * is called when the editor creates its toolbar. Override this method if you need to
47345      * add custom toolbar buttons.
47346      * @param {HtmlEditor} editor
47347      */
47348     createToolbar : function(editor){
47349          function btn(id, toggle, handler){
47350             var xid = fid + '-'+ id ;
47351             return {
47352                 id : xid,
47353                 cmd : id,
47354                 cls : 'x-btn-icon x-edit-'+id,
47355                 enableToggle:toggle !== false,
47356                 scope: editor, // was editor...
47357                 handler:handler||editor.relayBtnCmd,
47358                 clickEvent:'mousedown',
47359                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
47360                 tabIndex:-1
47361             };
47362         }
47363         
47364         
47365         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
47366         this.tb = tb;
47367         this.tb.add(
47368            {
47369                 cls : ' x-signature-btn x-signature-'+id,
47370                 scope: editor, // was editor...
47371                 handler: this.reset,
47372                 clickEvent:'mousedown',
47373                 text: this.labels.clear
47374             },
47375             {
47376                  xtype : 'Fill',
47377                  xns: Roo.Toolbar
47378             }, 
47379             {
47380                 cls : '  x-signature-btn x-signature-'+id,
47381                 scope: editor, // was editor...
47382                 handler: this.confirmHandler,
47383                 clickEvent:'mousedown',
47384                 text: this.labels.confirm
47385             }
47386         );
47387     
47388     },
47389     //public
47390     /**
47391      * when user is clicked confirm then show this image.....
47392      * 
47393      * @return {String} Image Data URI
47394      */
47395     getImageDataURI : function(){
47396         var svg = this.svgEl.dom.parentNode.innerHTML;
47397         var src = 'data:image/svg+xml;base64,'+window.btoa(svg);
47398         return src; 
47399     },
47400     /**
47401      * 
47402      * @return {Boolean} this.isConfirmed
47403      */
47404     getConfirmed : function(){
47405         return this.isConfirmed;
47406     },
47407     /**
47408      * 
47409      * @return {Number} this.width
47410      */
47411     getWidth : function(){
47412         return this.width;
47413     },
47414     /**
47415      * 
47416      * @return {Number} this.height
47417      */
47418     getHeight : function(){
47419         return this.height;
47420     },
47421     // private
47422     getSignature : function(){
47423         return this.signatureTmp;
47424     },
47425     // private
47426     reset : function(){
47427         this.signatureTmp = '';
47428         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
47429         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', '');
47430         this.isConfirmed = false;
47431         Roo.form.Signature.superclass.reset.call(this);
47432     },
47433     setSignature : function(s){
47434         this.signatureTmp = s;
47435         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
47436         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', s);
47437         this.setValue(s);
47438         this.isConfirmed = false;
47439         Roo.form.Signature.superclass.reset.call(this);
47440     }, 
47441     test : function(){
47442 //        Roo.log(this.signPanel.dom.contentWindow.up())
47443     },
47444     //private
47445     setConfirmed : function(){
47446         
47447         
47448         
47449 //        Roo.log(Roo.get(this.signPanel.dom.contentWindow.r).attr('fill', '#cfc'));
47450     },
47451     // private
47452     confirmHandler : function(){
47453         if(!this.getSignature()){
47454             return;
47455         }
47456         
47457         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#cfc');
47458         this.setValue(this.getSignature());
47459         this.isConfirmed = true;
47460         
47461         this.fireEvent('confirm', this);
47462     },
47463     // private
47464     // Subclasses should provide the validation implementation by overriding this
47465     validateValue : function(value){
47466         if(this.allowBlank){
47467             return true;
47468         }
47469         
47470         if(this.isConfirmed){
47471             return true;
47472         }
47473         return false;
47474     }
47475 });/*
47476  * Based on:
47477  * Ext JS Library 1.1.1
47478  * Copyright(c) 2006-2007, Ext JS, LLC.
47479  *
47480  * Originally Released Under LGPL - original licence link has changed is not relivant.
47481  *
47482  * Fork - LGPL
47483  * <script type="text/javascript">
47484  */
47485  
47486
47487 /**
47488  * @class Roo.form.ComboBox
47489  * @extends Roo.form.TriggerField
47490  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
47491  * @constructor
47492  * Create a new ComboBox.
47493  * @param {Object} config Configuration options
47494  */
47495 Roo.form.Select = function(config){
47496     Roo.form.Select.superclass.constructor.call(this, config);
47497      
47498 };
47499
47500 Roo.extend(Roo.form.Select , Roo.form.ComboBox, {
47501     /**
47502      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
47503      */
47504     /**
47505      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
47506      * rendering into an Roo.Editor, defaults to false)
47507      */
47508     /**
47509      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
47510      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
47511      */
47512     /**
47513      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
47514      */
47515     /**
47516      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
47517      * the dropdown list (defaults to undefined, with no header element)
47518      */
47519
47520      /**
47521      * @cfg {String/Roo.Template} tpl The template to use to render the output
47522      */
47523      
47524     // private
47525     defaultAutoCreate : {tag: "select"  },
47526     /**
47527      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
47528      */
47529     listWidth: undefined,
47530     /**
47531      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
47532      * mode = 'remote' or 'text' if mode = 'local')
47533      */
47534     displayField: undefined,
47535     /**
47536      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
47537      * mode = 'remote' or 'value' if mode = 'local'). 
47538      * Note: use of a valueField requires the user make a selection
47539      * in order for a value to be mapped.
47540      */
47541     valueField: undefined,
47542     
47543     
47544     /**
47545      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
47546      * field's data value (defaults to the underlying DOM element's name)
47547      */
47548     hiddenName: undefined,
47549     /**
47550      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
47551      */
47552     listClass: '',
47553     /**
47554      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
47555      */
47556     selectedClass: 'x-combo-selected',
47557     /**
47558      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
47559      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
47560      * which displays a downward arrow icon).
47561      */
47562     triggerClass : 'x-form-arrow-trigger',
47563     /**
47564      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
47565      */
47566     shadow:'sides',
47567     /**
47568      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
47569      * anchor positions (defaults to 'tl-bl')
47570      */
47571     listAlign: 'tl-bl?',
47572     /**
47573      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
47574      */
47575     maxHeight: 300,
47576     /**
47577      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
47578      * query specified by the allQuery config option (defaults to 'query')
47579      */
47580     triggerAction: 'query',
47581     /**
47582      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
47583      * (defaults to 4, does not apply if editable = false)
47584      */
47585     minChars : 4,
47586     /**
47587      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
47588      * delay (typeAheadDelay) if it matches a known value (defaults to false)
47589      */
47590     typeAhead: false,
47591     /**
47592      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
47593      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
47594      */
47595     queryDelay: 500,
47596     /**
47597      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
47598      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
47599      */
47600     pageSize: 0,
47601     /**
47602      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
47603      * when editable = true (defaults to false)
47604      */
47605     selectOnFocus:false,
47606     /**
47607      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
47608      */
47609     queryParam: 'query',
47610     /**
47611      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
47612      * when mode = 'remote' (defaults to 'Loading...')
47613      */
47614     loadingText: 'Loading...',
47615     /**
47616      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
47617      */
47618     resizable: false,
47619     /**
47620      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
47621      */
47622     handleHeight : 8,
47623     /**
47624      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
47625      * traditional select (defaults to true)
47626      */
47627     editable: true,
47628     /**
47629      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
47630      */
47631     allQuery: '',
47632     /**
47633      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
47634      */
47635     mode: 'remote',
47636     /**
47637      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
47638      * listWidth has a higher value)
47639      */
47640     minListWidth : 70,
47641     /**
47642      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
47643      * allow the user to set arbitrary text into the field (defaults to false)
47644      */
47645     forceSelection:false,
47646     /**
47647      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
47648      * if typeAhead = true (defaults to 250)
47649      */
47650     typeAheadDelay : 250,
47651     /**
47652      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
47653      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
47654      */
47655     valueNotFoundText : undefined,
47656     
47657     /**
47658      * @cfg {String} defaultValue The value displayed after loading the store.
47659      */
47660     defaultValue: '',
47661     
47662     /**
47663      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
47664      */
47665     blockFocus : false,
47666     
47667     /**
47668      * @cfg {Boolean} disableClear Disable showing of clear button.
47669      */
47670     disableClear : false,
47671     /**
47672      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
47673      */
47674     alwaysQuery : false,
47675     
47676     //private
47677     addicon : false,
47678     editicon: false,
47679     
47680     // element that contains real text value.. (when hidden is used..)
47681      
47682     // private
47683     onRender : function(ct, position){
47684         Roo.form.Field.prototype.onRender.call(this, ct, position);
47685         
47686         if(this.store){
47687             this.store.on('beforeload', this.onBeforeLoad, this);
47688             this.store.on('load', this.onLoad, this);
47689             this.store.on('loadexception', this.onLoadException, this);
47690             this.store.load({});
47691         }
47692         
47693         
47694         
47695     },
47696
47697     // private
47698     initEvents : function(){
47699         //Roo.form.ComboBox.superclass.initEvents.call(this);
47700  
47701     },
47702
47703     onDestroy : function(){
47704        
47705         if(this.store){
47706             this.store.un('beforeload', this.onBeforeLoad, this);
47707             this.store.un('load', this.onLoad, this);
47708             this.store.un('loadexception', this.onLoadException, this);
47709         }
47710         //Roo.form.ComboBox.superclass.onDestroy.call(this);
47711     },
47712
47713     // private
47714     fireKey : function(e){
47715         if(e.isNavKeyPress() && !this.list.isVisible()){
47716             this.fireEvent("specialkey", this, e);
47717         }
47718     },
47719
47720     // private
47721     onResize: function(w, h){
47722         
47723         return; 
47724     
47725         
47726     },
47727
47728     /**
47729      * Allow or prevent the user from directly editing the field text.  If false is passed,
47730      * the user will only be able to select from the items defined in the dropdown list.  This method
47731      * is the runtime equivalent of setting the 'editable' config option at config time.
47732      * @param {Boolean} value True to allow the user to directly edit the field text
47733      */
47734     setEditable : function(value){
47735          
47736     },
47737
47738     // private
47739     onBeforeLoad : function(){
47740         
47741         Roo.log("Select before load");
47742         return;
47743     
47744         this.innerList.update(this.loadingText ?
47745                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
47746         //this.restrictHeight();
47747         this.selectedIndex = -1;
47748     },
47749
47750     // private
47751     onLoad : function(){
47752
47753     
47754         var dom = this.el.dom;
47755         dom.innerHTML = '';
47756          var od = dom.ownerDocument;
47757          
47758         if (this.emptyText) {
47759             var op = od.createElement('option');
47760             op.setAttribute('value', '');
47761             op.innerHTML = String.format('{0}', this.emptyText);
47762             dom.appendChild(op);
47763         }
47764         if(this.store.getCount() > 0){
47765            
47766             var vf = this.valueField;
47767             var df = this.displayField;
47768             this.store.data.each(function(r) {
47769                 // which colmsn to use... testing - cdoe / title..
47770                 var op = od.createElement('option');
47771                 op.setAttribute('value', r.data[vf]);
47772                 op.innerHTML = String.format('{0}', r.data[df]);
47773                 dom.appendChild(op);
47774             });
47775             if (typeof(this.defaultValue != 'undefined')) {
47776                 this.setValue(this.defaultValue);
47777             }
47778             
47779              
47780         }else{
47781             //this.onEmptyResults();
47782         }
47783         //this.el.focus();
47784     },
47785     // private
47786     onLoadException : function()
47787     {
47788         dom.innerHTML = '';
47789             
47790         Roo.log("Select on load exception");
47791         return;
47792     
47793         this.collapse();
47794         Roo.log(this.store.reader.jsonData);
47795         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
47796             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
47797         }
47798         
47799         
47800     },
47801     // private
47802     onTypeAhead : function(){
47803          
47804     },
47805
47806     // private
47807     onSelect : function(record, index){
47808         Roo.log('on select?');
47809         return;
47810         if(this.fireEvent('beforeselect', this, record, index) !== false){
47811             this.setFromData(index > -1 ? record.data : false);
47812             this.collapse();
47813             this.fireEvent('select', this, record, index);
47814         }
47815     },
47816
47817     /**
47818      * Returns the currently selected field value or empty string if no value is set.
47819      * @return {String} value The selected value
47820      */
47821     getValue : function(){
47822         var dom = this.el.dom;
47823         this.value = dom.options[dom.selectedIndex].value;
47824         return this.value;
47825         
47826     },
47827
47828     /**
47829      * Clears any text/value currently set in the field
47830      */
47831     clearValue : function(){
47832         this.value = '';
47833         this.el.dom.selectedIndex = this.emptyText ? 0 : -1;
47834         
47835     },
47836
47837     /**
47838      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
47839      * will be displayed in the field.  If the value does not match the data value of an existing item,
47840      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
47841      * Otherwise the field will be blank (although the value will still be set).
47842      * @param {String} value The value to match
47843      */
47844     setValue : function(v){
47845         var d = this.el.dom;
47846         for (var i =0; i < d.options.length;i++) {
47847             if (v == d.options[i].value) {
47848                 d.selectedIndex = i;
47849                 this.value = v;
47850                 return;
47851             }
47852         }
47853         this.clearValue();
47854     },
47855     /**
47856      * @property {Object} the last set data for the element
47857      */
47858     
47859     lastData : false,
47860     /**
47861      * Sets the value of the field based on a object which is related to the record format for the store.
47862      * @param {Object} value the value to set as. or false on reset?
47863      */
47864     setFromData : function(o){
47865         Roo.log('setfrom data?');
47866          
47867         
47868         
47869     },
47870     // private
47871     reset : function(){
47872         this.clearValue();
47873     },
47874     // private
47875     findRecord : function(prop, value){
47876         
47877         return false;
47878     
47879         var record;
47880         if(this.store.getCount() > 0){
47881             this.store.each(function(r){
47882                 if(r.data[prop] == value){
47883                     record = r;
47884                     return false;
47885                 }
47886                 return true;
47887             });
47888         }
47889         return record;
47890     },
47891     
47892     getName: function()
47893     {
47894         // returns hidden if it's set..
47895         if (!this.rendered) {return ''};
47896         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
47897         
47898     },
47899      
47900
47901     
47902
47903     // private
47904     onEmptyResults : function(){
47905         Roo.log('empty results');
47906         //this.collapse();
47907     },
47908
47909     /**
47910      * Returns true if the dropdown list is expanded, else false.
47911      */
47912     isExpanded : function(){
47913         return false;
47914     },
47915
47916     /**
47917      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
47918      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
47919      * @param {String} value The data value of the item to select
47920      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
47921      * selected item if it is not currently in view (defaults to true)
47922      * @return {Boolean} True if the value matched an item in the list, else false
47923      */
47924     selectByValue : function(v, scrollIntoView){
47925         Roo.log('select By Value');
47926         return false;
47927     
47928         if(v !== undefined && v !== null){
47929             var r = this.findRecord(this.valueField || this.displayField, v);
47930             if(r){
47931                 this.select(this.store.indexOf(r), scrollIntoView);
47932                 return true;
47933             }
47934         }
47935         return false;
47936     },
47937
47938     /**
47939      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
47940      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
47941      * @param {Number} index The zero-based index of the list item to select
47942      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
47943      * selected item if it is not currently in view (defaults to true)
47944      */
47945     select : function(index, scrollIntoView){
47946         Roo.log('select ');
47947         return  ;
47948         
47949         this.selectedIndex = index;
47950         this.view.select(index);
47951         if(scrollIntoView !== false){
47952             var el = this.view.getNode(index);
47953             if(el){
47954                 this.innerList.scrollChildIntoView(el, false);
47955             }
47956         }
47957     },
47958
47959       
47960
47961     // private
47962     validateBlur : function(){
47963         
47964         return;
47965         
47966     },
47967
47968     // private
47969     initQuery : function(){
47970         this.doQuery(this.getRawValue());
47971     },
47972
47973     // private
47974     doForce : function(){
47975         if(this.el.dom.value.length > 0){
47976             this.el.dom.value =
47977                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
47978              
47979         }
47980     },
47981
47982     /**
47983      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
47984      * query allowing the query action to be canceled if needed.
47985      * @param {String} query The SQL query to execute
47986      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
47987      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
47988      * saved in the current store (defaults to false)
47989      */
47990     doQuery : function(q, forceAll){
47991         
47992         Roo.log('doQuery?');
47993         if(q === undefined || q === null){
47994             q = '';
47995         }
47996         var qe = {
47997             query: q,
47998             forceAll: forceAll,
47999             combo: this,
48000             cancel:false
48001         };
48002         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
48003             return false;
48004         }
48005         q = qe.query;
48006         forceAll = qe.forceAll;
48007         if(forceAll === true || (q.length >= this.minChars)){
48008             if(this.lastQuery != q || this.alwaysQuery){
48009                 this.lastQuery = q;
48010                 if(this.mode == 'local'){
48011                     this.selectedIndex = -1;
48012                     if(forceAll){
48013                         this.store.clearFilter();
48014                     }else{
48015                         this.store.filter(this.displayField, q);
48016                     }
48017                     this.onLoad();
48018                 }else{
48019                     this.store.baseParams[this.queryParam] = q;
48020                     this.store.load({
48021                         params: this.getParams(q)
48022                     });
48023                     this.expand();
48024                 }
48025             }else{
48026                 this.selectedIndex = -1;
48027                 this.onLoad();   
48028             }
48029         }
48030     },
48031
48032     // private
48033     getParams : function(q){
48034         var p = {};
48035         //p[this.queryParam] = q;
48036         if(this.pageSize){
48037             p.start = 0;
48038             p.limit = this.pageSize;
48039         }
48040         return p;
48041     },
48042
48043     /**
48044      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
48045      */
48046     collapse : function(){
48047         
48048     },
48049
48050     // private
48051     collapseIf : function(e){
48052         
48053     },
48054
48055     /**
48056      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
48057      */
48058     expand : function(){
48059         
48060     } ,
48061
48062     // private
48063      
48064
48065     /** 
48066     * @cfg {Boolean} grow 
48067     * @hide 
48068     */
48069     /** 
48070     * @cfg {Number} growMin 
48071     * @hide 
48072     */
48073     /** 
48074     * @cfg {Number} growMax 
48075     * @hide 
48076     */
48077     /**
48078      * @hide
48079      * @method autoSize
48080      */
48081     
48082     setWidth : function()
48083     {
48084         
48085     },
48086     getResizeEl : function(){
48087         return this.el;
48088     }
48089 });//<script type="text/javasscript">
48090  
48091
48092 /**
48093  * @class Roo.DDView
48094  * A DnD enabled version of Roo.View.
48095  * @param {Element/String} container The Element in which to create the View.
48096  * @param {String} tpl The template string used to create the markup for each element of the View
48097  * @param {Object} config The configuration properties. These include all the config options of
48098  * {@link Roo.View} plus some specific to this class.<br>
48099  * <p>
48100  * Drag/drop is implemented by adding {@link Roo.data.Record}s to the target DDView. If copying is
48101  * not being performed, the original {@link Roo.data.Record} is removed from the source DDView.<br>
48102  * <p>
48103  * The following extra CSS rules are needed to provide insertion point highlighting:<pre><code>
48104 .x-view-drag-insert-above {
48105         border-top:1px dotted #3366cc;
48106 }
48107 .x-view-drag-insert-below {
48108         border-bottom:1px dotted #3366cc;
48109 }
48110 </code></pre>
48111  * 
48112  */
48113  
48114 Roo.DDView = function(container, tpl, config) {
48115     Roo.DDView.superclass.constructor.apply(this, arguments);
48116     this.getEl().setStyle("outline", "0px none");
48117     this.getEl().unselectable();
48118     if (this.dragGroup) {
48119                 this.setDraggable(this.dragGroup.split(","));
48120     }
48121     if (this.dropGroup) {
48122                 this.setDroppable(this.dropGroup.split(","));
48123     }
48124     if (this.deletable) {
48125         this.setDeletable();
48126     }
48127     this.isDirtyFlag = false;
48128         this.addEvents({
48129                 "drop" : true
48130         });
48131 };
48132
48133 Roo.extend(Roo.DDView, Roo.View, {
48134 /**     @cfg {String/Array} dragGroup The ddgroup name(s) for the View's DragZone. */
48135 /**     @cfg {String/Array} dropGroup The ddgroup name(s) for the View's DropZone. */
48136 /**     @cfg {Boolean} copy Causes drag operations to copy nodes rather than move. */
48137 /**     @cfg {Boolean} allowCopy Causes ctrl/drag operations to copy nodes rather than move. */
48138
48139         isFormField: true,
48140
48141         reset: Roo.emptyFn,
48142         
48143         clearInvalid: Roo.form.Field.prototype.clearInvalid,
48144
48145         validate: function() {
48146                 return true;
48147         },
48148         
48149         destroy: function() {
48150                 this.purgeListeners();
48151                 this.getEl.removeAllListeners();
48152                 this.getEl().remove();
48153                 if (this.dragZone) {
48154                         if (this.dragZone.destroy) {
48155                                 this.dragZone.destroy();
48156                         }
48157                 }
48158                 if (this.dropZone) {
48159                         if (this.dropZone.destroy) {
48160                                 this.dropZone.destroy();
48161                         }
48162                 }
48163         },
48164
48165 /**     Allows this class to be an Roo.form.Field so it can be found using {@link Roo.form.BasicForm#findField}. */
48166         getName: function() {
48167                 return this.name;
48168         },
48169
48170 /**     Loads the View from a JSON string representing the Records to put into the Store. */
48171         setValue: function(v) {
48172                 if (!this.store) {
48173                         throw "DDView.setValue(). DDView must be constructed with a valid Store";
48174                 }
48175                 var data = {};
48176                 data[this.store.reader.meta.root] = v ? [].concat(v) : [];
48177                 this.store.proxy = new Roo.data.MemoryProxy(data);
48178                 this.store.load();
48179         },
48180
48181 /**     @return {String} a parenthesised list of the ids of the Records in the View. */
48182         getValue: function() {
48183                 var result = '(';
48184                 this.store.each(function(rec) {
48185                         result += rec.id + ',';
48186                 });
48187                 return result.substr(0, result.length - 1) + ')';
48188         },
48189         
48190         getIds: function() {
48191                 var i = 0, result = new Array(this.store.getCount());
48192                 this.store.each(function(rec) {
48193                         result[i++] = rec.id;
48194                 });
48195                 return result;
48196         },
48197         
48198         isDirty: function() {
48199                 return this.isDirtyFlag;
48200         },
48201
48202 /**
48203  *      Part of the Roo.dd.DropZone interface. If no target node is found, the
48204  *      whole Element becomes the target, and this causes the drop gesture to append.
48205  */
48206     getTargetFromEvent : function(e) {
48207                 var target = e.getTarget();
48208                 while ((target !== null) && (target.parentNode != this.el.dom)) {
48209                 target = target.parentNode;
48210                 }
48211                 if (!target) {
48212                         target = this.el.dom.lastChild || this.el.dom;
48213                 }
48214                 return target;
48215     },
48216
48217 /**
48218  *      Create the drag data which consists of an object which has the property "ddel" as
48219  *      the drag proxy element. 
48220  */
48221     getDragData : function(e) {
48222         var target = this.findItemFromChild(e.getTarget());
48223                 if(target) {
48224                         this.handleSelection(e);
48225                         var selNodes = this.getSelectedNodes();
48226             var dragData = {
48227                 source: this,
48228                 copy: this.copy || (this.allowCopy && e.ctrlKey),
48229                 nodes: selNodes,
48230                 records: []
48231                         };
48232                         var selectedIndices = this.getSelectedIndexes();
48233                         for (var i = 0; i < selectedIndices.length; i++) {
48234                                 dragData.records.push(this.store.getAt(selectedIndices[i]));
48235                         }
48236                         if (selNodes.length == 1) {
48237                                 dragData.ddel = target.cloneNode(true); // the div element
48238                         } else {
48239                                 var div = document.createElement('div'); // create the multi element drag "ghost"
48240                                 div.className = 'multi-proxy';
48241                                 for (var i = 0, len = selNodes.length; i < len; i++) {
48242                                         div.appendChild(selNodes[i].cloneNode(true));
48243                                 }
48244                                 dragData.ddel = div;
48245                         }
48246             //console.log(dragData)
48247             //console.log(dragData.ddel.innerHTML)
48248                         return dragData;
48249                 }
48250         //console.log('nodragData')
48251                 return false;
48252     },
48253     
48254 /**     Specify to which ddGroup items in this DDView may be dragged. */
48255     setDraggable: function(ddGroup) {
48256         if (ddGroup instanceof Array) {
48257                 Roo.each(ddGroup, this.setDraggable, this);
48258                 return;
48259         }
48260         if (this.dragZone) {
48261                 this.dragZone.addToGroup(ddGroup);
48262         } else {
48263                         this.dragZone = new Roo.dd.DragZone(this.getEl(), {
48264                                 containerScroll: true,
48265                                 ddGroup: ddGroup 
48266
48267                         });
48268 //                      Draggability implies selection. DragZone's mousedown selects the element.
48269                         if (!this.multiSelect) { this.singleSelect = true; }
48270
48271 //                      Wire the DragZone's handlers up to methods in *this*
48272                         this.dragZone.getDragData = this.getDragData.createDelegate(this);
48273                 }
48274     },
48275
48276 /**     Specify from which ddGroup this DDView accepts drops. */
48277     setDroppable: function(ddGroup) {
48278         if (ddGroup instanceof Array) {
48279                 Roo.each(ddGroup, this.setDroppable, this);
48280                 return;
48281         }
48282         if (this.dropZone) {
48283                 this.dropZone.addToGroup(ddGroup);
48284         } else {
48285                         this.dropZone = new Roo.dd.DropZone(this.getEl(), {
48286                                 containerScroll: true,
48287                                 ddGroup: ddGroup
48288                         });
48289
48290 //                      Wire the DropZone's handlers up to methods in *this*
48291                         this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
48292                         this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
48293                         this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
48294                         this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
48295                         this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
48296                 }
48297     },
48298
48299 /**     Decide whether to drop above or below a View node. */
48300     getDropPoint : function(e, n, dd){
48301         if (n == this.el.dom) { return "above"; }
48302                 var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
48303                 var c = t + (b - t) / 2;
48304                 var y = Roo.lib.Event.getPageY(e);
48305                 if(y <= c) {
48306                         return "above";
48307                 }else{
48308                         return "below";
48309                 }
48310     },
48311
48312     onNodeEnter : function(n, dd, e, data){
48313                 return false;
48314     },
48315     
48316     onNodeOver : function(n, dd, e, data){
48317                 var pt = this.getDropPoint(e, n, dd);
48318                 // set the insert point style on the target node
48319                 var dragElClass = this.dropNotAllowed;
48320                 if (pt) {
48321                         var targetElClass;
48322                         if (pt == "above"){
48323                                 dragElClass = n.previousSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-above";
48324                                 targetElClass = "x-view-drag-insert-above";
48325                         } else {
48326                                 dragElClass = n.nextSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-below";
48327                                 targetElClass = "x-view-drag-insert-below";
48328                         }
48329                         if (this.lastInsertClass != targetElClass){
48330                                 Roo.fly(n).replaceClass(this.lastInsertClass, targetElClass);
48331                                 this.lastInsertClass = targetElClass;
48332                         }
48333                 }
48334                 return dragElClass;
48335         },
48336
48337     onNodeOut : function(n, dd, e, data){
48338                 this.removeDropIndicators(n);
48339     },
48340
48341     onNodeDrop : function(n, dd, e, data){
48342         if (this.fireEvent("drop", this, n, dd, e, data) === false) {
48343                 return false;
48344         }
48345         var pt = this.getDropPoint(e, n, dd);
48346                 var insertAt = (n == this.el.dom) ? this.nodes.length : n.nodeIndex;
48347                 if (pt == "below") { insertAt++; }
48348                 for (var i = 0; i < data.records.length; i++) {
48349                         var r = data.records[i];
48350                         var dup = this.store.getById(r.id);
48351                         if (dup && (dd != this.dragZone)) {
48352                                 Roo.fly(this.getNode(this.store.indexOf(dup))).frame("red", 1);
48353                         } else {
48354                                 if (data.copy) {
48355                                         this.store.insert(insertAt++, r.copy());
48356                                 } else {
48357                                         data.source.isDirtyFlag = true;
48358                                         r.store.remove(r);
48359                                         this.store.insert(insertAt++, r);
48360                                 }
48361                                 this.isDirtyFlag = true;
48362                         }
48363                 }
48364                 this.dragZone.cachedTarget = null;
48365                 return true;
48366     },
48367
48368     removeDropIndicators : function(n){
48369                 if(n){
48370                         Roo.fly(n).removeClass([
48371                                 "x-view-drag-insert-above",
48372                                 "x-view-drag-insert-below"]);
48373                         this.lastInsertClass = "_noclass";
48374                 }
48375     },
48376
48377 /**
48378  *      Utility method. Add a delete option to the DDView's context menu.
48379  *      @param {String} imageUrl The URL of the "delete" icon image.
48380  */
48381         setDeletable: function(imageUrl) {
48382                 if (!this.singleSelect && !this.multiSelect) {
48383                         this.singleSelect = true;
48384                 }
48385                 var c = this.getContextMenu();
48386                 this.contextMenu.on("itemclick", function(item) {
48387                         switch (item.id) {
48388                                 case "delete":
48389                                         this.remove(this.getSelectedIndexes());
48390                                         break;
48391                         }
48392                 }, this);
48393                 this.contextMenu.add({
48394                         icon: imageUrl,
48395                         id: "delete",
48396                         text: 'Delete'
48397                 });
48398         },
48399         
48400 /**     Return the context menu for this DDView. */
48401         getContextMenu: function() {
48402                 if (!this.contextMenu) {
48403 //                      Create the View's context menu
48404                         this.contextMenu = new Roo.menu.Menu({
48405                                 id: this.id + "-contextmenu"
48406                         });
48407                         this.el.on("contextmenu", this.showContextMenu, this);
48408                 }
48409                 return this.contextMenu;
48410         },
48411         
48412         disableContextMenu: function() {
48413                 if (this.contextMenu) {
48414                         this.el.un("contextmenu", this.showContextMenu, this);
48415                 }
48416         },
48417
48418         showContextMenu: function(e, item) {
48419         item = this.findItemFromChild(e.getTarget());
48420                 if (item) {
48421                         e.stopEvent();
48422                         this.select(this.getNode(item), this.multiSelect && e.ctrlKey, true);
48423                         this.contextMenu.showAt(e.getXY());
48424             }
48425     },
48426
48427 /**
48428  *      Remove {@link Roo.data.Record}s at the specified indices.
48429  *      @param {Array/Number} selectedIndices The index (or Array of indices) of Records to remove.
48430  */
48431     remove: function(selectedIndices) {
48432                 selectedIndices = [].concat(selectedIndices);
48433                 for (var i = 0; i < selectedIndices.length; i++) {
48434                         var rec = this.store.getAt(selectedIndices[i]);
48435                         this.store.remove(rec);
48436                 }
48437     },
48438
48439 /**
48440  *      Double click fires the event, but also, if this is draggable, and there is only one other
48441  *      related DropZone, it transfers the selected node.
48442  */
48443     onDblClick : function(e){
48444         var item = this.findItemFromChild(e.getTarget());
48445         if(item){
48446             if (this.fireEvent("dblclick", this, this.indexOf(item), item, e) === false) {
48447                 return false;
48448             }
48449             if (this.dragGroup) {
48450                     var targets = Roo.dd.DragDropMgr.getRelated(this.dragZone, true);
48451                     while (targets.indexOf(this.dropZone) > -1) {
48452                             targets.remove(this.dropZone);
48453                                 }
48454                     if (targets.length == 1) {
48455                                         this.dragZone.cachedTarget = null;
48456                         var el = Roo.get(targets[0].getEl());
48457                         var box = el.getBox(true);
48458                         targets[0].onNodeDrop(el.dom, {
48459                                 target: el.dom,
48460                                 xy: [box.x, box.y + box.height - 1]
48461                         }, null, this.getDragData(e));
48462                     }
48463                 }
48464         }
48465     },
48466     
48467     handleSelection: function(e) {
48468                 this.dragZone.cachedTarget = null;
48469         var item = this.findItemFromChild(e.getTarget());
48470         if (!item) {
48471                 this.clearSelections(true);
48472                 return;
48473         }
48474                 if (item && (this.multiSelect || this.singleSelect)){
48475                         if(this.multiSelect && e.shiftKey && (!e.ctrlKey) && this.lastSelection){
48476                                 this.select(this.getNodes(this.indexOf(this.lastSelection), item.nodeIndex), false);
48477                         }else if (this.isSelected(this.getNode(item)) && e.ctrlKey){
48478                                 this.unselect(item);
48479                         } else {
48480                                 this.select(item, this.multiSelect && e.ctrlKey);
48481                                 this.lastSelection = item;
48482                         }
48483                 }
48484     },
48485
48486     onItemClick : function(item, index, e){
48487                 if(this.fireEvent("beforeclick", this, index, item, e) === false){
48488                         return false;
48489                 }
48490                 return true;
48491     },
48492
48493     unselect : function(nodeInfo, suppressEvent){
48494                 var node = this.getNode(nodeInfo);
48495                 if(node && this.isSelected(node)){
48496                         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
48497                                 Roo.fly(node).removeClass(this.selectedClass);
48498                                 this.selections.remove(node);
48499                                 if(!suppressEvent){
48500                                         this.fireEvent("selectionchange", this, this.selections);
48501                                 }
48502                         }
48503                 }
48504     }
48505 });
48506 /*
48507  * Based on:
48508  * Ext JS Library 1.1.1
48509  * Copyright(c) 2006-2007, Ext JS, LLC.
48510  *
48511  * Originally Released Under LGPL - original licence link has changed is not relivant.
48512  *
48513  * Fork - LGPL
48514  * <script type="text/javascript">
48515  */
48516  
48517 /**
48518  * @class Roo.LayoutManager
48519  * @extends Roo.util.Observable
48520  * Base class for layout managers.
48521  */
48522 Roo.LayoutManager = function(container, config){
48523     Roo.LayoutManager.superclass.constructor.call(this);
48524     this.el = Roo.get(container);
48525     // ie scrollbar fix
48526     if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
48527         document.body.scroll = "no";
48528     }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
48529         this.el.position('relative');
48530     }
48531     this.id = this.el.id;
48532     this.el.addClass("x-layout-container");
48533     /** false to disable window resize monitoring @type Boolean */
48534     this.monitorWindowResize = true;
48535     this.regions = {};
48536     this.addEvents({
48537         /**
48538          * @event layout
48539          * Fires when a layout is performed. 
48540          * @param {Roo.LayoutManager} this
48541          */
48542         "layout" : true,
48543         /**
48544          * @event regionresized
48545          * Fires when the user resizes a region. 
48546          * @param {Roo.LayoutRegion} region The resized region
48547          * @param {Number} newSize The new size (width for east/west, height for north/south)
48548          */
48549         "regionresized" : true,
48550         /**
48551          * @event regioncollapsed
48552          * Fires when a region is collapsed. 
48553          * @param {Roo.LayoutRegion} region The collapsed region
48554          */
48555         "regioncollapsed" : true,
48556         /**
48557          * @event regionexpanded
48558          * Fires when a region is expanded.  
48559          * @param {Roo.LayoutRegion} region The expanded region
48560          */
48561         "regionexpanded" : true
48562     });
48563     this.updating = false;
48564     Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
48565 };
48566
48567 Roo.extend(Roo.LayoutManager, Roo.util.Observable, {
48568     /**
48569      * Returns true if this layout is currently being updated
48570      * @return {Boolean}
48571      */
48572     isUpdating : function(){
48573         return this.updating; 
48574     },
48575     
48576     /**
48577      * Suspend the LayoutManager from doing auto-layouts while
48578      * making multiple add or remove calls
48579      */
48580     beginUpdate : function(){
48581         this.updating = true;    
48582     },
48583     
48584     /**
48585      * Restore auto-layouts and optionally disable the manager from performing a layout
48586      * @param {Boolean} noLayout true to disable a layout update 
48587      */
48588     endUpdate : function(noLayout){
48589         this.updating = false;
48590         if(!noLayout){
48591             this.layout();
48592         }    
48593     },
48594     
48595     layout: function(){
48596         
48597     },
48598     
48599     onRegionResized : function(region, newSize){
48600         this.fireEvent("regionresized", region, newSize);
48601         this.layout();
48602     },
48603     
48604     onRegionCollapsed : function(region){
48605         this.fireEvent("regioncollapsed", region);
48606     },
48607     
48608     onRegionExpanded : function(region){
48609         this.fireEvent("regionexpanded", region);
48610     },
48611         
48612     /**
48613      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
48614      * performs box-model adjustments.
48615      * @return {Object} The size as an object {width: (the width), height: (the height)}
48616      */
48617     getViewSize : function(){
48618         var size;
48619         if(this.el.dom != document.body){
48620             size = this.el.getSize();
48621         }else{
48622             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
48623         }
48624         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
48625         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
48626         return size;
48627     },
48628     
48629     /**
48630      * Returns the Element this layout is bound to.
48631      * @return {Roo.Element}
48632      */
48633     getEl : function(){
48634         return this.el;
48635     },
48636     
48637     /**
48638      * Returns the specified region.
48639      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
48640      * @return {Roo.LayoutRegion}
48641      */
48642     getRegion : function(target){
48643         return this.regions[target.toLowerCase()];
48644     },
48645     
48646     onWindowResize : function(){
48647         if(this.monitorWindowResize){
48648             this.layout();
48649         }
48650     }
48651 });/*
48652  * Based on:
48653  * Ext JS Library 1.1.1
48654  * Copyright(c) 2006-2007, Ext JS, LLC.
48655  *
48656  * Originally Released Under LGPL - original licence link has changed is not relivant.
48657  *
48658  * Fork - LGPL
48659  * <script type="text/javascript">
48660  */
48661 /**
48662  * @class Roo.BorderLayout
48663  * @extends Roo.LayoutManager
48664  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
48665  * please see: <br><br>
48666  * <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>
48667  * <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>
48668  * Example:
48669  <pre><code>
48670  var layout = new Roo.BorderLayout(document.body, {
48671     north: {
48672         initialSize: 25,
48673         titlebar: false
48674     },
48675     west: {
48676         split:true,
48677         initialSize: 200,
48678         minSize: 175,
48679         maxSize: 400,
48680         titlebar: true,
48681         collapsible: true
48682     },
48683     east: {
48684         split:true,
48685         initialSize: 202,
48686         minSize: 175,
48687         maxSize: 400,
48688         titlebar: true,
48689         collapsible: true
48690     },
48691     south: {
48692         split:true,
48693         initialSize: 100,
48694         minSize: 100,
48695         maxSize: 200,
48696         titlebar: true,
48697         collapsible: true
48698     },
48699     center: {
48700         titlebar: true,
48701         autoScroll:true,
48702         resizeTabs: true,
48703         minTabWidth: 50,
48704         preferredTabWidth: 150
48705     }
48706 });
48707
48708 // shorthand
48709 var CP = Roo.ContentPanel;
48710
48711 layout.beginUpdate();
48712 layout.add("north", new CP("north", "North"));
48713 layout.add("south", new CP("south", {title: "South", closable: true}));
48714 layout.add("west", new CP("west", {title: "West"}));
48715 layout.add("east", new CP("autoTabs", {title: "Auto Tabs", closable: true}));
48716 layout.add("center", new CP("center1", {title: "Close Me", closable: true}));
48717 layout.add("center", new CP("center2", {title: "Center Panel", closable: false}));
48718 layout.getRegion("center").showPanel("center1");
48719 layout.endUpdate();
48720 </code></pre>
48721
48722 <b>The container the layout is rendered into can be either the body element or any other element.
48723 If it is not the body element, the container needs to either be an absolute positioned element,
48724 or you will need to add "position:relative" to the css of the container.  You will also need to specify
48725 the container size if it is not the body element.</b>
48726
48727 * @constructor
48728 * Create a new BorderLayout
48729 * @param {String/HTMLElement/Element} container The container this layout is bound to
48730 * @param {Object} config Configuration options
48731  */
48732 Roo.BorderLayout = function(container, config){
48733     config = config || {};
48734     Roo.BorderLayout.superclass.constructor.call(this, container, config);
48735     this.factory = config.factory || Roo.BorderLayout.RegionFactory;
48736     for(var i = 0, len = this.factory.validRegions.length; i < len; i++) {
48737         var target = this.factory.validRegions[i];
48738         if(config[target]){
48739             this.addRegion(target, config[target]);
48740         }
48741     }
48742 };
48743
48744 Roo.extend(Roo.BorderLayout, Roo.LayoutManager, {
48745     /**
48746      * Creates and adds a new region if it doesn't already exist.
48747      * @param {String} target The target region key (north, south, east, west or center).
48748      * @param {Object} config The regions config object
48749      * @return {BorderLayoutRegion} The new region
48750      */
48751     addRegion : function(target, config){
48752         if(!this.regions[target]){
48753             var r = this.factory.create(target, this, config);
48754             this.bindRegion(target, r);
48755         }
48756         return this.regions[target];
48757     },
48758
48759     // private (kinda)
48760     bindRegion : function(name, r){
48761         this.regions[name] = r;
48762         r.on("visibilitychange", this.layout, this);
48763         r.on("paneladded", this.layout, this);
48764         r.on("panelremoved", this.layout, this);
48765         r.on("invalidated", this.layout, this);
48766         r.on("resized", this.onRegionResized, this);
48767         r.on("collapsed", this.onRegionCollapsed, this);
48768         r.on("expanded", this.onRegionExpanded, this);
48769     },
48770
48771     /**
48772      * Performs a layout update.
48773      */
48774     layout : function(){
48775         if(this.updating) return;
48776         var size = this.getViewSize();
48777         var w = size.width;
48778         var h = size.height;
48779         var centerW = w;
48780         var centerH = h;
48781         var centerY = 0;
48782         var centerX = 0;
48783         //var x = 0, y = 0;
48784
48785         var rs = this.regions;
48786         var north = rs["north"];
48787         var south = rs["south"]; 
48788         var west = rs["west"];
48789         var east = rs["east"];
48790         var center = rs["center"];
48791         //if(this.hideOnLayout){ // not supported anymore
48792             //c.el.setStyle("display", "none");
48793         //}
48794         if(north && north.isVisible()){
48795             var b = north.getBox();
48796             var m = north.getMargins();
48797             b.width = w - (m.left+m.right);
48798             b.x = m.left;
48799             b.y = m.top;
48800             centerY = b.height + b.y + m.bottom;
48801             centerH -= centerY;
48802             north.updateBox(this.safeBox(b));
48803         }
48804         if(south && south.isVisible()){
48805             var b = south.getBox();
48806             var m = south.getMargins();
48807             b.width = w - (m.left+m.right);
48808             b.x = m.left;
48809             var totalHeight = (b.height + m.top + m.bottom);
48810             b.y = h - totalHeight + m.top;
48811             centerH -= totalHeight;
48812             south.updateBox(this.safeBox(b));
48813         }
48814         if(west && west.isVisible()){
48815             var b = west.getBox();
48816             var m = west.getMargins();
48817             b.height = centerH - (m.top+m.bottom);
48818             b.x = m.left;
48819             b.y = centerY + m.top;
48820             var totalWidth = (b.width + m.left + m.right);
48821             centerX += totalWidth;
48822             centerW -= totalWidth;
48823             west.updateBox(this.safeBox(b));
48824         }
48825         if(east && east.isVisible()){
48826             var b = east.getBox();
48827             var m = east.getMargins();
48828             b.height = centerH - (m.top+m.bottom);
48829             var totalWidth = (b.width + m.left + m.right);
48830             b.x = w - totalWidth + m.left;
48831             b.y = centerY + m.top;
48832             centerW -= totalWidth;
48833             east.updateBox(this.safeBox(b));
48834         }
48835         if(center){
48836             var m = center.getMargins();
48837             var centerBox = {
48838                 x: centerX + m.left,
48839                 y: centerY + m.top,
48840                 width: centerW - (m.left+m.right),
48841                 height: centerH - (m.top+m.bottom)
48842             };
48843             //if(this.hideOnLayout){
48844                 //center.el.setStyle("display", "block");
48845             //}
48846             center.updateBox(this.safeBox(centerBox));
48847         }
48848         this.el.repaint();
48849         this.fireEvent("layout", this);
48850     },
48851
48852     // private
48853     safeBox : function(box){
48854         box.width = Math.max(0, box.width);
48855         box.height = Math.max(0, box.height);
48856         return box;
48857     },
48858
48859     /**
48860      * Adds a ContentPanel (or subclass) to this layout.
48861      * @param {String} target The target region key (north, south, east, west or center).
48862      * @param {Roo.ContentPanel} panel The panel to add
48863      * @return {Roo.ContentPanel} The added panel
48864      */
48865     add : function(target, panel){
48866          
48867         target = target.toLowerCase();
48868         return this.regions[target].add(panel);
48869     },
48870
48871     /**
48872      * Remove a ContentPanel (or subclass) to this layout.
48873      * @param {String} target The target region key (north, south, east, west or center).
48874      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
48875      * @return {Roo.ContentPanel} The removed panel
48876      */
48877     remove : function(target, panel){
48878         target = target.toLowerCase();
48879         return this.regions[target].remove(panel);
48880     },
48881
48882     /**
48883      * Searches all regions for a panel with the specified id
48884      * @param {String} panelId
48885      * @return {Roo.ContentPanel} The panel or null if it wasn't found
48886      */
48887     findPanel : function(panelId){
48888         var rs = this.regions;
48889         for(var target in rs){
48890             if(typeof rs[target] != "function"){
48891                 var p = rs[target].getPanel(panelId);
48892                 if(p){
48893                     return p;
48894                 }
48895             }
48896         }
48897         return null;
48898     },
48899
48900     /**
48901      * Searches all regions for a panel with the specified id and activates (shows) it.
48902      * @param {String/ContentPanel} panelId The panels id or the panel itself
48903      * @return {Roo.ContentPanel} The shown panel or null
48904      */
48905     showPanel : function(panelId) {
48906       var rs = this.regions;
48907       for(var target in rs){
48908          var r = rs[target];
48909          if(typeof r != "function"){
48910             if(r.hasPanel(panelId)){
48911                return r.showPanel(panelId);
48912             }
48913          }
48914       }
48915       return null;
48916    },
48917
48918    /**
48919      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
48920      * @param {Roo.state.Provider} provider (optional) An alternate state provider
48921      */
48922     restoreState : function(provider){
48923         if(!provider){
48924             provider = Roo.state.Manager;
48925         }
48926         var sm = new Roo.LayoutStateManager();
48927         sm.init(this, provider);
48928     },
48929
48930     /**
48931      * Adds a batch of multiple ContentPanels dynamically by passing a special regions config object.  This config
48932      * object should contain properties for each region to add ContentPanels to, and each property's value should be
48933      * a valid ContentPanel config object.  Example:
48934      * <pre><code>
48935 // Create the main layout
48936 var layout = new Roo.BorderLayout('main-ct', {
48937     west: {
48938         split:true,
48939         minSize: 175,
48940         titlebar: true
48941     },
48942     center: {
48943         title:'Components'
48944     }
48945 }, 'main-ct');
48946
48947 // Create and add multiple ContentPanels at once via configs
48948 layout.batchAdd({
48949    west: {
48950        id: 'source-files',
48951        autoCreate:true,
48952        title:'Ext Source Files',
48953        autoScroll:true,
48954        fitToFrame:true
48955    },
48956    center : {
48957        el: cview,
48958        autoScroll:true,
48959        fitToFrame:true,
48960        toolbar: tb,
48961        resizeEl:'cbody'
48962    }
48963 });
48964 </code></pre>
48965      * @param {Object} regions An object containing ContentPanel configs by region name
48966      */
48967     batchAdd : function(regions){
48968         this.beginUpdate();
48969         for(var rname in regions){
48970             var lr = this.regions[rname];
48971             if(lr){
48972                 this.addTypedPanels(lr, regions[rname]);
48973             }
48974         }
48975         this.endUpdate();
48976     },
48977
48978     // private
48979     addTypedPanels : function(lr, ps){
48980         if(typeof ps == 'string'){
48981             lr.add(new Roo.ContentPanel(ps));
48982         }
48983         else if(ps instanceof Array){
48984             for(var i =0, len = ps.length; i < len; i++){
48985                 this.addTypedPanels(lr, ps[i]);
48986             }
48987         }
48988         else if(!ps.events){ // raw config?
48989             var el = ps.el;
48990             delete ps.el; // prevent conflict
48991             lr.add(new Roo.ContentPanel(el || Roo.id(), ps));
48992         }
48993         else {  // panel object assumed!
48994             lr.add(ps);
48995         }
48996     },
48997     /**
48998      * Adds a xtype elements to the layout.
48999      * <pre><code>
49000
49001 layout.addxtype({
49002        xtype : 'ContentPanel',
49003        region: 'west',
49004        items: [ .... ]
49005    }
49006 );
49007
49008 layout.addxtype({
49009         xtype : 'NestedLayoutPanel',
49010         region: 'west',
49011         layout: {
49012            center: { },
49013            west: { }   
49014         },
49015         items : [ ... list of content panels or nested layout panels.. ]
49016    }
49017 );
49018 </code></pre>
49019      * @param {Object} cfg Xtype definition of item to add.
49020      */
49021     addxtype : function(cfg)
49022     {
49023         // basically accepts a pannel...
49024         // can accept a layout region..!?!?
49025         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
49026         
49027         if (!cfg.xtype.match(/Panel$/)) {
49028             return false;
49029         }
49030         var ret = false;
49031         
49032         if (typeof(cfg.region) == 'undefined') {
49033             Roo.log("Failed to add Panel, region was not set");
49034             Roo.log(cfg);
49035             return false;
49036         }
49037         var region = cfg.region;
49038         delete cfg.region;
49039         
49040           
49041         var xitems = [];
49042         if (cfg.items) {
49043             xitems = cfg.items;
49044             delete cfg.items;
49045         }
49046         var nb = false;
49047         
49048         switch(cfg.xtype) 
49049         {
49050             case 'ContentPanel':  // ContentPanel (el, cfg)
49051             case 'ScrollPanel':  // ContentPanel (el, cfg)
49052             case 'ViewPanel': 
49053                 if(cfg.autoCreate) {
49054                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
49055                 } else {
49056                     var el = this.el.createChild();
49057                     ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
49058                 }
49059                 
49060                 this.add(region, ret);
49061                 break;
49062             
49063             
49064             case 'TreePanel': // our new panel!
49065                 cfg.el = this.el.createChild();
49066                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
49067                 this.add(region, ret);
49068                 break;
49069             
49070             case 'NestedLayoutPanel': 
49071                 // create a new Layout (which is  a Border Layout...
49072                 var el = this.el.createChild();
49073                 var clayout = cfg.layout;
49074                 delete cfg.layout;
49075                 clayout.items   = clayout.items  || [];
49076                 // replace this exitems with the clayout ones..
49077                 xitems = clayout.items;
49078                  
49079                 
49080                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
49081                     cfg.background = false;
49082                 }
49083                 var layout = new Roo.BorderLayout(el, clayout);
49084                 
49085                 ret = new Roo[cfg.xtype](layout, cfg); // new panel!!!!!
49086                 //console.log('adding nested layout panel '  + cfg.toSource());
49087                 this.add(region, ret);
49088                 nb = {}; /// find first...
49089                 break;
49090                 
49091             case 'GridPanel': 
49092             
49093                 // needs grid and region
49094                 
49095                 //var el = this.getRegion(region).el.createChild();
49096                 var el = this.el.createChild();
49097                 // create the grid first...
49098                 
49099                 var grid = new Roo.grid[cfg.grid.xtype](el, cfg.grid);
49100                 delete cfg.grid;
49101                 if (region == 'center' && this.active ) {
49102                     cfg.background = false;
49103                 }
49104                 ret = new Roo[cfg.xtype](grid, cfg); // new panel!!!!!
49105                 
49106                 this.add(region, ret);
49107                 if (cfg.background) {
49108                     ret.on('activate', function(gp) {
49109                         if (!gp.grid.rendered) {
49110                             gp.grid.render();
49111                         }
49112                     });
49113                 } else {
49114                     grid.render();
49115                 }
49116                 break;
49117            
49118            
49119            
49120                 
49121                 
49122                 
49123             default:
49124                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
49125                     
49126                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
49127                     this.add(region, ret);
49128                 } else {
49129                 
49130                     alert("Can not add '" + cfg.xtype + "' to BorderLayout");
49131                     return null;
49132                 }
49133                 
49134              // GridPanel (grid, cfg)
49135             
49136         }
49137         this.beginUpdate();
49138         // add children..
49139         var region = '';
49140         var abn = {};
49141         Roo.each(xitems, function(i)  {
49142             region = nb && i.region ? i.region : false;
49143             
49144             var add = ret.addxtype(i);
49145            
49146             if (region) {
49147                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
49148                 if (!i.background) {
49149                     abn[region] = nb[region] ;
49150                 }
49151             }
49152             
49153         });
49154         this.endUpdate();
49155
49156         // make the last non-background panel active..
49157         //if (nb) { Roo.log(abn); }
49158         if (nb) {
49159             
49160             for(var r in abn) {
49161                 region = this.getRegion(r);
49162                 if (region) {
49163                     // tried using nb[r], but it does not work..
49164                      
49165                     region.showPanel(abn[r]);
49166                    
49167                 }
49168             }
49169         }
49170         return ret;
49171         
49172     }
49173 });
49174
49175 /**
49176  * Shortcut for creating a new BorderLayout object and adding one or more ContentPanels to it in a single step, handling
49177  * the beginUpdate and endUpdate calls internally.  The key to this method is the <b>panels</b> property that can be
49178  * provided with each region config, which allows you to add ContentPanel configs in addition to the region configs
49179  * during creation.  The following code is equivalent to the constructor-based example at the beginning of this class:
49180  * <pre><code>
49181 // shorthand
49182 var CP = Roo.ContentPanel;
49183
49184 var layout = Roo.BorderLayout.create({
49185     north: {
49186         initialSize: 25,
49187         titlebar: false,
49188         panels: [new CP("north", "North")]
49189     },
49190     west: {
49191         split:true,
49192         initialSize: 200,
49193         minSize: 175,
49194         maxSize: 400,
49195         titlebar: true,
49196         collapsible: true,
49197         panels: [new CP("west", {title: "West"})]
49198     },
49199     east: {
49200         split:true,
49201         initialSize: 202,
49202         minSize: 175,
49203         maxSize: 400,
49204         titlebar: true,
49205         collapsible: true,
49206         panels: [new CP("autoTabs", {title: "Auto Tabs", closable: true})]
49207     },
49208     south: {
49209         split:true,
49210         initialSize: 100,
49211         minSize: 100,
49212         maxSize: 200,
49213         titlebar: true,
49214         collapsible: true,
49215         panels: [new CP("south", {title: "South", closable: true})]
49216     },
49217     center: {
49218         titlebar: true,
49219         autoScroll:true,
49220         resizeTabs: true,
49221         minTabWidth: 50,
49222         preferredTabWidth: 150,
49223         panels: [
49224             new CP("center1", {title: "Close Me", closable: true}),
49225             new CP("center2", {title: "Center Panel", closable: false})
49226         ]
49227     }
49228 }, document.body);
49229
49230 layout.getRegion("center").showPanel("center1");
49231 </code></pre>
49232  * @param config
49233  * @param targetEl
49234  */
49235 Roo.BorderLayout.create = function(config, targetEl){
49236     var layout = new Roo.BorderLayout(targetEl || document.body, config);
49237     layout.beginUpdate();
49238     var regions = Roo.BorderLayout.RegionFactory.validRegions;
49239     for(var j = 0, jlen = regions.length; j < jlen; j++){
49240         var lr = regions[j];
49241         if(layout.regions[lr] && config[lr].panels){
49242             var r = layout.regions[lr];
49243             var ps = config[lr].panels;
49244             layout.addTypedPanels(r, ps);
49245         }
49246     }
49247     layout.endUpdate();
49248     return layout;
49249 };
49250
49251 // private
49252 Roo.BorderLayout.RegionFactory = {
49253     // private
49254     validRegions : ["north","south","east","west","center"],
49255
49256     // private
49257     create : function(target, mgr, config){
49258         target = target.toLowerCase();
49259         if(config.lightweight || config.basic){
49260             return new Roo.BasicLayoutRegion(mgr, config, target);
49261         }
49262         switch(target){
49263             case "north":
49264                 return new Roo.NorthLayoutRegion(mgr, config);
49265             case "south":
49266                 return new Roo.SouthLayoutRegion(mgr, config);
49267             case "east":
49268                 return new Roo.EastLayoutRegion(mgr, config);
49269             case "west":
49270                 return new Roo.WestLayoutRegion(mgr, config);
49271             case "center":
49272                 return new Roo.CenterLayoutRegion(mgr, config);
49273         }
49274         throw 'Layout region "'+target+'" not supported.';
49275     }
49276 };/*
49277  * Based on:
49278  * Ext JS Library 1.1.1
49279  * Copyright(c) 2006-2007, Ext JS, LLC.
49280  *
49281  * Originally Released Under LGPL - original licence link has changed is not relivant.
49282  *
49283  * Fork - LGPL
49284  * <script type="text/javascript">
49285  */
49286  
49287 /**
49288  * @class Roo.BasicLayoutRegion
49289  * @extends Roo.util.Observable
49290  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
49291  * and does not have a titlebar, tabs or any other features. All it does is size and position 
49292  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
49293  */
49294 Roo.BasicLayoutRegion = function(mgr, config, pos, skipConfig){
49295     this.mgr = mgr;
49296     this.position  = pos;
49297     this.events = {
49298         /**
49299          * @scope Roo.BasicLayoutRegion
49300          */
49301         
49302         /**
49303          * @event beforeremove
49304          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
49305          * @param {Roo.LayoutRegion} this
49306          * @param {Roo.ContentPanel} panel The panel
49307          * @param {Object} e The cancel event object
49308          */
49309         "beforeremove" : true,
49310         /**
49311          * @event invalidated
49312          * Fires when the layout for this region is changed.
49313          * @param {Roo.LayoutRegion} this
49314          */
49315         "invalidated" : true,
49316         /**
49317          * @event visibilitychange
49318          * Fires when this region is shown or hidden 
49319          * @param {Roo.LayoutRegion} this
49320          * @param {Boolean} visibility true or false
49321          */
49322         "visibilitychange" : true,
49323         /**
49324          * @event paneladded
49325          * Fires when a panel is added. 
49326          * @param {Roo.LayoutRegion} this
49327          * @param {Roo.ContentPanel} panel The panel
49328          */
49329         "paneladded" : true,
49330         /**
49331          * @event panelremoved
49332          * Fires when a panel is removed. 
49333          * @param {Roo.LayoutRegion} this
49334          * @param {Roo.ContentPanel} panel The panel
49335          */
49336         "panelremoved" : true,
49337         /**
49338          * @event collapsed
49339          * Fires when this region is collapsed.
49340          * @param {Roo.LayoutRegion} this
49341          */
49342         "collapsed" : true,
49343         /**
49344          * @event expanded
49345          * Fires when this region is expanded.
49346          * @param {Roo.LayoutRegion} this
49347          */
49348         "expanded" : true,
49349         /**
49350          * @event slideshow
49351          * Fires when this region is slid into view.
49352          * @param {Roo.LayoutRegion} this
49353          */
49354         "slideshow" : true,
49355         /**
49356          * @event slidehide
49357          * Fires when this region slides out of view. 
49358          * @param {Roo.LayoutRegion} this
49359          */
49360         "slidehide" : true,
49361         /**
49362          * @event panelactivated
49363          * Fires when a panel is activated. 
49364          * @param {Roo.LayoutRegion} this
49365          * @param {Roo.ContentPanel} panel The activated panel
49366          */
49367         "panelactivated" : true,
49368         /**
49369          * @event resized
49370          * Fires when the user resizes this region. 
49371          * @param {Roo.LayoutRegion} this
49372          * @param {Number} newSize The new size (width for east/west, height for north/south)
49373          */
49374         "resized" : true
49375     };
49376     /** A collection of panels in this region. @type Roo.util.MixedCollection */
49377     this.panels = new Roo.util.MixedCollection();
49378     this.panels.getKey = this.getPanelId.createDelegate(this);
49379     this.box = null;
49380     this.activePanel = null;
49381     // ensure listeners are added...
49382     
49383     if (config.listeners || config.events) {
49384         Roo.BasicLayoutRegion.superclass.constructor.call(this, {
49385             listeners : config.listeners || {},
49386             events : config.events || {}
49387         });
49388     }
49389     
49390     if(skipConfig !== true){
49391         this.applyConfig(config);
49392     }
49393 };
49394
49395 Roo.extend(Roo.BasicLayoutRegion, Roo.util.Observable, {
49396     getPanelId : function(p){
49397         return p.getId();
49398     },
49399     
49400     applyConfig : function(config){
49401         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
49402         this.config = config;
49403         
49404     },
49405     
49406     /**
49407      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
49408      * the width, for horizontal (north, south) the height.
49409      * @param {Number} newSize The new width or height
49410      */
49411     resizeTo : function(newSize){
49412         var el = this.el ? this.el :
49413                  (this.activePanel ? this.activePanel.getEl() : null);
49414         if(el){
49415             switch(this.position){
49416                 case "east":
49417                 case "west":
49418                     el.setWidth(newSize);
49419                     this.fireEvent("resized", this, newSize);
49420                 break;
49421                 case "north":
49422                 case "south":
49423                     el.setHeight(newSize);
49424                     this.fireEvent("resized", this, newSize);
49425                 break;                
49426             }
49427         }
49428     },
49429     
49430     getBox : function(){
49431         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
49432     },
49433     
49434     getMargins : function(){
49435         return this.margins;
49436     },
49437     
49438     updateBox : function(box){
49439         this.box = box;
49440         var el = this.activePanel.getEl();
49441         el.dom.style.left = box.x + "px";
49442         el.dom.style.top = box.y + "px";
49443         this.activePanel.setSize(box.width, box.height);
49444     },
49445     
49446     /**
49447      * Returns the container element for this region.
49448      * @return {Roo.Element}
49449      */
49450     getEl : function(){
49451         return this.activePanel;
49452     },
49453     
49454     /**
49455      * Returns true if this region is currently visible.
49456      * @return {Boolean}
49457      */
49458     isVisible : function(){
49459         return this.activePanel ? true : false;
49460     },
49461     
49462     setActivePanel : function(panel){
49463         panel = this.getPanel(panel);
49464         if(this.activePanel && this.activePanel != panel){
49465             this.activePanel.setActiveState(false);
49466             this.activePanel.getEl().setLeftTop(-10000,-10000);
49467         }
49468         this.activePanel = panel;
49469         panel.setActiveState(true);
49470         if(this.box){
49471             panel.setSize(this.box.width, this.box.height);
49472         }
49473         this.fireEvent("panelactivated", this, panel);
49474         this.fireEvent("invalidated");
49475     },
49476     
49477     /**
49478      * Show the specified panel.
49479      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
49480      * @return {Roo.ContentPanel} The shown panel or null
49481      */
49482     showPanel : function(panel){
49483         if(panel = this.getPanel(panel)){
49484             this.setActivePanel(panel);
49485         }
49486         return panel;
49487     },
49488     
49489     /**
49490      * Get the active panel for this region.
49491      * @return {Roo.ContentPanel} The active panel or null
49492      */
49493     getActivePanel : function(){
49494         return this.activePanel;
49495     },
49496     
49497     /**
49498      * Add the passed ContentPanel(s)
49499      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
49500      * @return {Roo.ContentPanel} The panel added (if only one was added)
49501      */
49502     add : function(panel){
49503         if(arguments.length > 1){
49504             for(var i = 0, len = arguments.length; i < len; i++) {
49505                 this.add(arguments[i]);
49506             }
49507             return null;
49508         }
49509         if(this.hasPanel(panel)){
49510             this.showPanel(panel);
49511             return panel;
49512         }
49513         var el = panel.getEl();
49514         if(el.dom.parentNode != this.mgr.el.dom){
49515             this.mgr.el.dom.appendChild(el.dom);
49516         }
49517         if(panel.setRegion){
49518             panel.setRegion(this);
49519         }
49520         this.panels.add(panel);
49521         el.setStyle("position", "absolute");
49522         if(!panel.background){
49523             this.setActivePanel(panel);
49524             if(this.config.initialSize && this.panels.getCount()==1){
49525                 this.resizeTo(this.config.initialSize);
49526             }
49527         }
49528         this.fireEvent("paneladded", this, panel);
49529         return panel;
49530     },
49531     
49532     /**
49533      * Returns true if the panel is in this region.
49534      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
49535      * @return {Boolean}
49536      */
49537     hasPanel : function(panel){
49538         if(typeof panel == "object"){ // must be panel obj
49539             panel = panel.getId();
49540         }
49541         return this.getPanel(panel) ? true : false;
49542     },
49543     
49544     /**
49545      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
49546      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
49547      * @param {Boolean} preservePanel Overrides the config preservePanel option
49548      * @return {Roo.ContentPanel} The panel that was removed
49549      */
49550     remove : function(panel, preservePanel){
49551         panel = this.getPanel(panel);
49552         if(!panel){
49553             return null;
49554         }
49555         var e = {};
49556         this.fireEvent("beforeremove", this, panel, e);
49557         if(e.cancel === true){
49558             return null;
49559         }
49560         var panelId = panel.getId();
49561         this.panels.removeKey(panelId);
49562         return panel;
49563     },
49564     
49565     /**
49566      * Returns the panel specified or null if it's not in this region.
49567      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
49568      * @return {Roo.ContentPanel}
49569      */
49570     getPanel : function(id){
49571         if(typeof id == "object"){ // must be panel obj
49572             return id;
49573         }
49574         return this.panels.get(id);
49575     },
49576     
49577     /**
49578      * Returns this regions position (north/south/east/west/center).
49579      * @return {String} 
49580      */
49581     getPosition: function(){
49582         return this.position;    
49583     }
49584 });/*
49585  * Based on:
49586  * Ext JS Library 1.1.1
49587  * Copyright(c) 2006-2007, Ext JS, LLC.
49588  *
49589  * Originally Released Under LGPL - original licence link has changed is not relivant.
49590  *
49591  * Fork - LGPL
49592  * <script type="text/javascript">
49593  */
49594  
49595 /**
49596  * @class Roo.LayoutRegion
49597  * @extends Roo.BasicLayoutRegion
49598  * This class represents a region in a layout manager.
49599  * @cfg {Boolean}   collapsible     False to disable collapsing (defaults to true)
49600  * @cfg {Boolean}   collapsed       True to set the initial display to collapsed (defaults to false)
49601  * @cfg {Boolean}   floatable       False to disable floating (defaults to true)
49602  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
49603  * @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})
49604  * @cfg {String}    tabPosition     "top" or "bottom" (defaults to "bottom")
49605  * @cfg {String}    collapsedTitle  Optional string message to display in the collapsed block of a north or south region
49606  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
49607  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
49608  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
49609  * @cfg {String}    title           The title for the region (overrides panel titles)
49610  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
49611  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
49612  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
49613  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
49614  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
49615  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
49616  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
49617  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
49618  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
49619  * @cfg {Boolean}   showPin         True to show a pin button
49620  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
49621  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
49622  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
49623  * @cfg {Number}    width           For East/West panels
49624  * @cfg {Number}    height          For North/South panels
49625  * @cfg {Boolean}   split           To show the splitter
49626  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
49627  */
49628 Roo.LayoutRegion = function(mgr, config, pos){
49629     Roo.LayoutRegion.superclass.constructor.call(this, mgr, config, pos, true);
49630     var dh = Roo.DomHelper;
49631     /** This region's container element 
49632     * @type Roo.Element */
49633     this.el = dh.append(mgr.el.dom, {tag: "div", cls: "x-layout-panel x-layout-panel-" + this.position}, true);
49634     /** This region's title element 
49635     * @type Roo.Element */
49636
49637     this.titleEl = dh.append(this.el.dom, {tag: "div", unselectable: "on", cls: "x-unselectable x-layout-panel-hd x-layout-title-"+this.position, children:[
49638         {tag: "span", cls: "x-unselectable x-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
49639         {tag: "div", cls: "x-unselectable x-layout-panel-hd-tools", unselectable: "on"}
49640     ]}, true);
49641     this.titleEl.enableDisplayMode();
49642     /** This region's title text element 
49643     * @type HTMLElement */
49644     this.titleTextEl = this.titleEl.dom.firstChild;
49645     this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
49646     this.closeBtn = this.createTool(this.tools.dom, "x-layout-close");
49647     this.closeBtn.enableDisplayMode();
49648     this.closeBtn.on("click", this.closeClicked, this);
49649     this.closeBtn.hide();
49650
49651     this.createBody(config);
49652     this.visible = true;
49653     this.collapsed = false;
49654
49655     if(config.hideWhenEmpty){
49656         this.hide();
49657         this.on("paneladded", this.validateVisibility, this);
49658         this.on("panelremoved", this.validateVisibility, this);
49659     }
49660     this.applyConfig(config);
49661 };
49662
49663 Roo.extend(Roo.LayoutRegion, Roo.BasicLayoutRegion, {
49664
49665     createBody : function(){
49666         /** This region's body element 
49667         * @type Roo.Element */
49668         this.bodyEl = this.el.createChild({tag: "div", cls: "x-layout-panel-body"});
49669     },
49670
49671     applyConfig : function(c){
49672         if(c.collapsible && this.position != "center" && !this.collapsedEl){
49673             var dh = Roo.DomHelper;
49674             if(c.titlebar !== false){
49675                 this.collapseBtn = this.createTool(this.tools.dom, "x-layout-collapse-"+this.position);
49676                 this.collapseBtn.on("click", this.collapse, this);
49677                 this.collapseBtn.enableDisplayMode();
49678
49679                 if(c.showPin === true || this.showPin){
49680                     this.stickBtn = this.createTool(this.tools.dom, "x-layout-stick");
49681                     this.stickBtn.enableDisplayMode();
49682                     this.stickBtn.on("click", this.expand, this);
49683                     this.stickBtn.hide();
49684                 }
49685             }
49686             /** This region's collapsed element
49687             * @type Roo.Element */
49688             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
49689                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
49690             ]}, true);
49691             if(c.floatable !== false){
49692                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
49693                this.collapsedEl.on("click", this.collapseClick, this);
49694             }
49695
49696             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
49697                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
49698                    id: "message", unselectable: "on", style:{"float":"left"}});
49699                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
49700              }
49701             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
49702             this.expandBtn.on("click", this.expand, this);
49703         }
49704         if(this.collapseBtn){
49705             this.collapseBtn.setVisible(c.collapsible == true);
49706         }
49707         this.cmargins = c.cmargins || this.cmargins ||
49708                          (this.position == "west" || this.position == "east" ?
49709                              {top: 0, left: 2, right:2, bottom: 0} :
49710                              {top: 2, left: 0, right:0, bottom: 2});
49711         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
49712         this.bottomTabs = c.tabPosition != "top";
49713         this.autoScroll = c.autoScroll || false;
49714         if(this.autoScroll){
49715             this.bodyEl.setStyle("overflow", "auto");
49716         }else{
49717             this.bodyEl.setStyle("overflow", "hidden");
49718         }
49719         //if(c.titlebar !== false){
49720             if((!c.titlebar && !c.title) || c.titlebar === false){
49721                 this.titleEl.hide();
49722             }else{
49723                 this.titleEl.show();
49724                 if(c.title){
49725                     this.titleTextEl.innerHTML = c.title;
49726                 }
49727             }
49728         //}
49729         this.duration = c.duration || .30;
49730         this.slideDuration = c.slideDuration || .45;
49731         this.config = c;
49732         if(c.collapsed){
49733             this.collapse(true);
49734         }
49735         if(c.hidden){
49736             this.hide();
49737         }
49738     },
49739     /**
49740      * Returns true if this region is currently visible.
49741      * @return {Boolean}
49742      */
49743     isVisible : function(){
49744         return this.visible;
49745     },
49746
49747     /**
49748      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
49749      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
49750      */
49751     setCollapsedTitle : function(title){
49752         title = title || "&#160;";
49753         if(this.collapsedTitleTextEl){
49754             this.collapsedTitleTextEl.innerHTML = title;
49755         }
49756     },
49757
49758     getBox : function(){
49759         var b;
49760         if(!this.collapsed){
49761             b = this.el.getBox(false, true);
49762         }else{
49763             b = this.collapsedEl.getBox(false, true);
49764         }
49765         return b;
49766     },
49767
49768     getMargins : function(){
49769         return this.collapsed ? this.cmargins : this.margins;
49770     },
49771
49772     highlight : function(){
49773         this.el.addClass("x-layout-panel-dragover");
49774     },
49775
49776     unhighlight : function(){
49777         this.el.removeClass("x-layout-panel-dragover");
49778     },
49779
49780     updateBox : function(box){
49781         this.box = box;
49782         if(!this.collapsed){
49783             this.el.dom.style.left = box.x + "px";
49784             this.el.dom.style.top = box.y + "px";
49785             this.updateBody(box.width, box.height);
49786         }else{
49787             this.collapsedEl.dom.style.left = box.x + "px";
49788             this.collapsedEl.dom.style.top = box.y + "px";
49789             this.collapsedEl.setSize(box.width, box.height);
49790         }
49791         if(this.tabs){
49792             this.tabs.autoSizeTabs();
49793         }
49794     },
49795
49796     updateBody : function(w, h){
49797         if(w !== null){
49798             this.el.setWidth(w);
49799             w -= this.el.getBorderWidth("rl");
49800             if(this.config.adjustments){
49801                 w += this.config.adjustments[0];
49802             }
49803         }
49804         if(h !== null){
49805             this.el.setHeight(h);
49806             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
49807             h -= this.el.getBorderWidth("tb");
49808             if(this.config.adjustments){
49809                 h += this.config.adjustments[1];
49810             }
49811             this.bodyEl.setHeight(h);
49812             if(this.tabs){
49813                 h = this.tabs.syncHeight(h);
49814             }
49815         }
49816         if(this.panelSize){
49817             w = w !== null ? w : this.panelSize.width;
49818             h = h !== null ? h : this.panelSize.height;
49819         }
49820         if(this.activePanel){
49821             var el = this.activePanel.getEl();
49822             w = w !== null ? w : el.getWidth();
49823             h = h !== null ? h : el.getHeight();
49824             this.panelSize = {width: w, height: h};
49825             this.activePanel.setSize(w, h);
49826         }
49827         if(Roo.isIE && this.tabs){
49828             this.tabs.el.repaint();
49829         }
49830     },
49831
49832     /**
49833      * Returns the container element for this region.
49834      * @return {Roo.Element}
49835      */
49836     getEl : function(){
49837         return this.el;
49838     },
49839
49840     /**
49841      * Hides this region.
49842      */
49843     hide : function(){
49844         if(!this.collapsed){
49845             this.el.dom.style.left = "-2000px";
49846             this.el.hide();
49847         }else{
49848             this.collapsedEl.dom.style.left = "-2000px";
49849             this.collapsedEl.hide();
49850         }
49851         this.visible = false;
49852         this.fireEvent("visibilitychange", this, false);
49853     },
49854
49855     /**
49856      * Shows this region if it was previously hidden.
49857      */
49858     show : function(){
49859         if(!this.collapsed){
49860             this.el.show();
49861         }else{
49862             this.collapsedEl.show();
49863         }
49864         this.visible = true;
49865         this.fireEvent("visibilitychange", this, true);
49866     },
49867
49868     closeClicked : function(){
49869         if(this.activePanel){
49870             this.remove(this.activePanel);
49871         }
49872     },
49873
49874     collapseClick : function(e){
49875         if(this.isSlid){
49876            e.stopPropagation();
49877            this.slideIn();
49878         }else{
49879            e.stopPropagation();
49880            this.slideOut();
49881         }
49882     },
49883
49884     /**
49885      * Collapses this region.
49886      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
49887      */
49888     collapse : function(skipAnim){
49889         if(this.collapsed) return;
49890         this.collapsed = true;
49891         if(this.split){
49892             this.split.el.hide();
49893         }
49894         if(this.config.animate && skipAnim !== true){
49895             this.fireEvent("invalidated", this);
49896             this.animateCollapse();
49897         }else{
49898             this.el.setLocation(-20000,-20000);
49899             this.el.hide();
49900             this.collapsedEl.show();
49901             this.fireEvent("collapsed", this);
49902             this.fireEvent("invalidated", this);
49903         }
49904     },
49905
49906     animateCollapse : function(){
49907         // overridden
49908     },
49909
49910     /**
49911      * Expands this region if it was previously collapsed.
49912      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
49913      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
49914      */
49915     expand : function(e, skipAnim){
49916         if(e) e.stopPropagation();
49917         if(!this.collapsed || this.el.hasActiveFx()) return;
49918         if(this.isSlid){
49919             this.afterSlideIn();
49920             skipAnim = true;
49921         }
49922         this.collapsed = false;
49923         if(this.config.animate && skipAnim !== true){
49924             this.animateExpand();
49925         }else{
49926             this.el.show();
49927             if(this.split){
49928                 this.split.el.show();
49929             }
49930             this.collapsedEl.setLocation(-2000,-2000);
49931             this.collapsedEl.hide();
49932             this.fireEvent("invalidated", this);
49933             this.fireEvent("expanded", this);
49934         }
49935     },
49936
49937     animateExpand : function(){
49938         // overridden
49939     },
49940
49941     initTabs : function()
49942     {
49943         this.bodyEl.setStyle("overflow", "hidden");
49944         var ts = new Roo.TabPanel(
49945                 this.bodyEl.dom,
49946                 {
49947                     tabPosition: this.bottomTabs ? 'bottom' : 'top',
49948                     disableTooltips: this.config.disableTabTips,
49949                     toolbar : this.config.toolbar
49950                 }
49951         );
49952         if(this.config.hideTabs){
49953             ts.stripWrap.setDisplayed(false);
49954         }
49955         this.tabs = ts;
49956         ts.resizeTabs = this.config.resizeTabs === true;
49957         ts.minTabWidth = this.config.minTabWidth || 40;
49958         ts.maxTabWidth = this.config.maxTabWidth || 250;
49959         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
49960         ts.monitorResize = false;
49961         ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
49962         ts.bodyEl.addClass('x-layout-tabs-body');
49963         this.panels.each(this.initPanelAsTab, this);
49964     },
49965
49966     initPanelAsTab : function(panel){
49967         var ti = this.tabs.addTab(panel.getEl().id, panel.getTitle(), null,
49968                     this.config.closeOnTab && panel.isClosable());
49969         if(panel.tabTip !== undefined){
49970             ti.setTooltip(panel.tabTip);
49971         }
49972         ti.on("activate", function(){
49973               this.setActivePanel(panel);
49974         }, this);
49975         if(this.config.closeOnTab){
49976             ti.on("beforeclose", function(t, e){
49977                 e.cancel = true;
49978                 this.remove(panel);
49979             }, this);
49980         }
49981         return ti;
49982     },
49983
49984     updatePanelTitle : function(panel, title){
49985         if(this.activePanel == panel){
49986             this.updateTitle(title);
49987         }
49988         if(this.tabs){
49989             var ti = this.tabs.getTab(panel.getEl().id);
49990             ti.setText(title);
49991             if(panel.tabTip !== undefined){
49992                 ti.setTooltip(panel.tabTip);
49993             }
49994         }
49995     },
49996
49997     updateTitle : function(title){
49998         if(this.titleTextEl && !this.config.title){
49999             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
50000         }
50001     },
50002
50003     setActivePanel : function(panel){
50004         panel = this.getPanel(panel);
50005         if(this.activePanel && this.activePanel != panel){
50006             this.activePanel.setActiveState(false);
50007         }
50008         this.activePanel = panel;
50009         panel.setActiveState(true);
50010         if(this.panelSize){
50011             panel.setSize(this.panelSize.width, this.panelSize.height);
50012         }
50013         if(this.closeBtn){
50014             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
50015         }
50016         this.updateTitle(panel.getTitle());
50017         if(this.tabs){
50018             this.fireEvent("invalidated", this);
50019         }
50020         this.fireEvent("panelactivated", this, panel);
50021     },
50022
50023     /**
50024      * Shows the specified panel.
50025      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
50026      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
50027      */
50028     showPanel : function(panel){
50029         if(panel = this.getPanel(panel)){
50030             if(this.tabs){
50031                 var tab = this.tabs.getTab(panel.getEl().id);
50032                 if(tab.isHidden()){
50033                     this.tabs.unhideTab(tab.id);
50034                 }
50035                 tab.activate();
50036             }else{
50037                 this.setActivePanel(panel);
50038             }
50039         }
50040         return panel;
50041     },
50042
50043     /**
50044      * Get the active panel for this region.
50045      * @return {Roo.ContentPanel} The active panel or null
50046      */
50047     getActivePanel : function(){
50048         return this.activePanel;
50049     },
50050
50051     validateVisibility : function(){
50052         if(this.panels.getCount() < 1){
50053             this.updateTitle("&#160;");
50054             this.closeBtn.hide();
50055             this.hide();
50056         }else{
50057             if(!this.isVisible()){
50058                 this.show();
50059             }
50060         }
50061     },
50062
50063     /**
50064      * Adds the passed ContentPanel(s) to this region.
50065      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
50066      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
50067      */
50068     add : function(panel){
50069         if(arguments.length > 1){
50070             for(var i = 0, len = arguments.length; i < len; i++) {
50071                 this.add(arguments[i]);
50072             }
50073             return null;
50074         }
50075         if(this.hasPanel(panel)){
50076             this.showPanel(panel);
50077             return panel;
50078         }
50079         panel.setRegion(this);
50080         this.panels.add(panel);
50081         if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
50082             this.bodyEl.dom.appendChild(panel.getEl().dom);
50083             if(panel.background !== true){
50084                 this.setActivePanel(panel);
50085             }
50086             this.fireEvent("paneladded", this, panel);
50087             return panel;
50088         }
50089         if(!this.tabs){
50090             this.initTabs();
50091         }else{
50092             this.initPanelAsTab(panel);
50093         }
50094         if(panel.background !== true){
50095             this.tabs.activate(panel.getEl().id);
50096         }
50097         this.fireEvent("paneladded", this, panel);
50098         return panel;
50099     },
50100
50101     /**
50102      * Hides the tab for the specified panel.
50103      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
50104      */
50105     hidePanel : function(panel){
50106         if(this.tabs && (panel = this.getPanel(panel))){
50107             this.tabs.hideTab(panel.getEl().id);
50108         }
50109     },
50110
50111     /**
50112      * Unhides the tab for a previously hidden panel.
50113      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
50114      */
50115     unhidePanel : function(panel){
50116         if(this.tabs && (panel = this.getPanel(panel))){
50117             this.tabs.unhideTab(panel.getEl().id);
50118         }
50119     },
50120
50121     clearPanels : function(){
50122         while(this.panels.getCount() > 0){
50123              this.remove(this.panels.first());
50124         }
50125     },
50126
50127     /**
50128      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
50129      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
50130      * @param {Boolean} preservePanel Overrides the config preservePanel option
50131      * @return {Roo.ContentPanel} The panel that was removed
50132      */
50133     remove : function(panel, preservePanel){
50134         panel = this.getPanel(panel);
50135         if(!panel){
50136             return null;
50137         }
50138         var e = {};
50139         this.fireEvent("beforeremove", this, panel, e);
50140         if(e.cancel === true){
50141             return null;
50142         }
50143         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
50144         var panelId = panel.getId();
50145         this.panels.removeKey(panelId);
50146         if(preservePanel){
50147             document.body.appendChild(panel.getEl().dom);
50148         }
50149         if(this.tabs){
50150             this.tabs.removeTab(panel.getEl().id);
50151         }else if (!preservePanel){
50152             this.bodyEl.dom.removeChild(panel.getEl().dom);
50153         }
50154         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
50155             var p = this.panels.first();
50156             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
50157             tempEl.appendChild(p.getEl().dom);
50158             this.bodyEl.update("");
50159             this.bodyEl.dom.appendChild(p.getEl().dom);
50160             tempEl = null;
50161             this.updateTitle(p.getTitle());
50162             this.tabs = null;
50163             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
50164             this.setActivePanel(p);
50165         }
50166         panel.setRegion(null);
50167         if(this.activePanel == panel){
50168             this.activePanel = null;
50169         }
50170         if(this.config.autoDestroy !== false && preservePanel !== true){
50171             try{panel.destroy();}catch(e){}
50172         }
50173         this.fireEvent("panelremoved", this, panel);
50174         return panel;
50175     },
50176
50177     /**
50178      * Returns the TabPanel component used by this region
50179      * @return {Roo.TabPanel}
50180      */
50181     getTabs : function(){
50182         return this.tabs;
50183     },
50184
50185     createTool : function(parentEl, className){
50186         var btn = Roo.DomHelper.append(parentEl, {tag: "div", cls: "x-layout-tools-button",
50187             children: [{tag: "div", cls: "x-layout-tools-button-inner " + className, html: "&#160;"}]}, true);
50188         btn.addClassOnOver("x-layout-tools-button-over");
50189         return btn;
50190     }
50191 });/*
50192  * Based on:
50193  * Ext JS Library 1.1.1
50194  * Copyright(c) 2006-2007, Ext JS, LLC.
50195  *
50196  * Originally Released Under LGPL - original licence link has changed is not relivant.
50197  *
50198  * Fork - LGPL
50199  * <script type="text/javascript">
50200  */
50201  
50202
50203
50204 /**
50205  * @class Roo.SplitLayoutRegion
50206  * @extends Roo.LayoutRegion
50207  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
50208  */
50209 Roo.SplitLayoutRegion = function(mgr, config, pos, cursor){
50210     this.cursor = cursor;
50211     Roo.SplitLayoutRegion.superclass.constructor.call(this, mgr, config, pos);
50212 };
50213
50214 Roo.extend(Roo.SplitLayoutRegion, Roo.LayoutRegion, {
50215     splitTip : "Drag to resize.",
50216     collapsibleSplitTip : "Drag to resize. Double click to hide.",
50217     useSplitTips : false,
50218
50219     applyConfig : function(config){
50220         Roo.SplitLayoutRegion.superclass.applyConfig.call(this, config);
50221         if(config.split){
50222             if(!this.split){
50223                 var splitEl = Roo.DomHelper.append(this.mgr.el.dom, 
50224                         {tag: "div", id: this.el.id + "-split", cls: "x-layout-split x-layout-split-"+this.position, html: "&#160;"});
50225                 /** The SplitBar for this region 
50226                 * @type Roo.SplitBar */
50227                 this.split = new Roo.SplitBar(splitEl, this.el, this.orientation);
50228                 this.split.on("moved", this.onSplitMove, this);
50229                 this.split.useShim = config.useShim === true;
50230                 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
50231                 if(this.useSplitTips){
50232                     this.split.el.dom.title = config.collapsible ? this.collapsibleSplitTip : this.splitTip;
50233                 }
50234                 if(config.collapsible){
50235                     this.split.el.on("dblclick", this.collapse,  this);
50236                 }
50237             }
50238             if(typeof config.minSize != "undefined"){
50239                 this.split.minSize = config.minSize;
50240             }
50241             if(typeof config.maxSize != "undefined"){
50242                 this.split.maxSize = config.maxSize;
50243             }
50244             if(config.hideWhenEmpty || config.hidden || config.collapsed){
50245                 this.hideSplitter();
50246             }
50247         }
50248     },
50249
50250     getHMaxSize : function(){
50251          var cmax = this.config.maxSize || 10000;
50252          var center = this.mgr.getRegion("center");
50253          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
50254     },
50255
50256     getVMaxSize : function(){
50257          var cmax = this.config.maxSize || 10000;
50258          var center = this.mgr.getRegion("center");
50259          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
50260     },
50261
50262     onSplitMove : function(split, newSize){
50263         this.fireEvent("resized", this, newSize);
50264     },
50265     
50266     /** 
50267      * Returns the {@link Roo.SplitBar} for this region.
50268      * @return {Roo.SplitBar}
50269      */
50270     getSplitBar : function(){
50271         return this.split;
50272     },
50273     
50274     hide : function(){
50275         this.hideSplitter();
50276         Roo.SplitLayoutRegion.superclass.hide.call(this);
50277     },
50278
50279     hideSplitter : function(){
50280         if(this.split){
50281             this.split.el.setLocation(-2000,-2000);
50282             this.split.el.hide();
50283         }
50284     },
50285
50286     show : function(){
50287         if(this.split){
50288             this.split.el.show();
50289         }
50290         Roo.SplitLayoutRegion.superclass.show.call(this);
50291     },
50292     
50293     beforeSlide: function(){
50294         if(Roo.isGecko){// firefox overflow auto bug workaround
50295             this.bodyEl.clip();
50296             if(this.tabs) this.tabs.bodyEl.clip();
50297             if(this.activePanel){
50298                 this.activePanel.getEl().clip();
50299                 
50300                 if(this.activePanel.beforeSlide){
50301                     this.activePanel.beforeSlide();
50302                 }
50303             }
50304         }
50305     },
50306     
50307     afterSlide : function(){
50308         if(Roo.isGecko){// firefox overflow auto bug workaround
50309             this.bodyEl.unclip();
50310             if(this.tabs) this.tabs.bodyEl.unclip();
50311             if(this.activePanel){
50312                 this.activePanel.getEl().unclip();
50313                 if(this.activePanel.afterSlide){
50314                     this.activePanel.afterSlide();
50315                 }
50316             }
50317         }
50318     },
50319
50320     initAutoHide : function(){
50321         if(this.autoHide !== false){
50322             if(!this.autoHideHd){
50323                 var st = new Roo.util.DelayedTask(this.slideIn, this);
50324                 this.autoHideHd = {
50325                     "mouseout": function(e){
50326                         if(!e.within(this.el, true)){
50327                             st.delay(500);
50328                         }
50329                     },
50330                     "mouseover" : function(e){
50331                         st.cancel();
50332                     },
50333                     scope : this
50334                 };
50335             }
50336             this.el.on(this.autoHideHd);
50337         }
50338     },
50339
50340     clearAutoHide : function(){
50341         if(this.autoHide !== false){
50342             this.el.un("mouseout", this.autoHideHd.mouseout);
50343             this.el.un("mouseover", this.autoHideHd.mouseover);
50344         }
50345     },
50346
50347     clearMonitor : function(){
50348         Roo.get(document).un("click", this.slideInIf, this);
50349     },
50350
50351     // these names are backwards but not changed for compat
50352     slideOut : function(){
50353         if(this.isSlid || this.el.hasActiveFx()){
50354             return;
50355         }
50356         this.isSlid = true;
50357         if(this.collapseBtn){
50358             this.collapseBtn.hide();
50359         }
50360         this.closeBtnState = this.closeBtn.getStyle('display');
50361         this.closeBtn.hide();
50362         if(this.stickBtn){
50363             this.stickBtn.show();
50364         }
50365         this.el.show();
50366         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
50367         this.beforeSlide();
50368         this.el.setStyle("z-index", 10001);
50369         this.el.slideIn(this.getSlideAnchor(), {
50370             callback: function(){
50371                 this.afterSlide();
50372                 this.initAutoHide();
50373                 Roo.get(document).on("click", this.slideInIf, this);
50374                 this.fireEvent("slideshow", this);
50375             },
50376             scope: this,
50377             block: true
50378         });
50379     },
50380
50381     afterSlideIn : function(){
50382         this.clearAutoHide();
50383         this.isSlid = false;
50384         this.clearMonitor();
50385         this.el.setStyle("z-index", "");
50386         if(this.collapseBtn){
50387             this.collapseBtn.show();
50388         }
50389         this.closeBtn.setStyle('display', this.closeBtnState);
50390         if(this.stickBtn){
50391             this.stickBtn.hide();
50392         }
50393         this.fireEvent("slidehide", this);
50394     },
50395
50396     slideIn : function(cb){
50397         if(!this.isSlid || this.el.hasActiveFx()){
50398             Roo.callback(cb);
50399             return;
50400         }
50401         this.isSlid = false;
50402         this.beforeSlide();
50403         this.el.slideOut(this.getSlideAnchor(), {
50404             callback: function(){
50405                 this.el.setLeftTop(-10000, -10000);
50406                 this.afterSlide();
50407                 this.afterSlideIn();
50408                 Roo.callback(cb);
50409             },
50410             scope: this,
50411             block: true
50412         });
50413     },
50414     
50415     slideInIf : function(e){
50416         if(!e.within(this.el)){
50417             this.slideIn();
50418         }
50419     },
50420
50421     animateCollapse : function(){
50422         this.beforeSlide();
50423         this.el.setStyle("z-index", 20000);
50424         var anchor = this.getSlideAnchor();
50425         this.el.slideOut(anchor, {
50426             callback : function(){
50427                 this.el.setStyle("z-index", "");
50428                 this.collapsedEl.slideIn(anchor, {duration:.3});
50429                 this.afterSlide();
50430                 this.el.setLocation(-10000,-10000);
50431                 this.el.hide();
50432                 this.fireEvent("collapsed", this);
50433             },
50434             scope: this,
50435             block: true
50436         });
50437     },
50438
50439     animateExpand : function(){
50440         this.beforeSlide();
50441         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
50442         this.el.setStyle("z-index", 20000);
50443         this.collapsedEl.hide({
50444             duration:.1
50445         });
50446         this.el.slideIn(this.getSlideAnchor(), {
50447             callback : function(){
50448                 this.el.setStyle("z-index", "");
50449                 this.afterSlide();
50450                 if(this.split){
50451                     this.split.el.show();
50452                 }
50453                 this.fireEvent("invalidated", this);
50454                 this.fireEvent("expanded", this);
50455             },
50456             scope: this,
50457             block: true
50458         });
50459     },
50460
50461     anchors : {
50462         "west" : "left",
50463         "east" : "right",
50464         "north" : "top",
50465         "south" : "bottom"
50466     },
50467
50468     sanchors : {
50469         "west" : "l",
50470         "east" : "r",
50471         "north" : "t",
50472         "south" : "b"
50473     },
50474
50475     canchors : {
50476         "west" : "tl-tr",
50477         "east" : "tr-tl",
50478         "north" : "tl-bl",
50479         "south" : "bl-tl"
50480     },
50481
50482     getAnchor : function(){
50483         return this.anchors[this.position];
50484     },
50485
50486     getCollapseAnchor : function(){
50487         return this.canchors[this.position];
50488     },
50489
50490     getSlideAnchor : function(){
50491         return this.sanchors[this.position];
50492     },
50493
50494     getAlignAdj : function(){
50495         var cm = this.cmargins;
50496         switch(this.position){
50497             case "west":
50498                 return [0, 0];
50499             break;
50500             case "east":
50501                 return [0, 0];
50502             break;
50503             case "north":
50504                 return [0, 0];
50505             break;
50506             case "south":
50507                 return [0, 0];
50508             break;
50509         }
50510     },
50511
50512     getExpandAdj : function(){
50513         var c = this.collapsedEl, cm = this.cmargins;
50514         switch(this.position){
50515             case "west":
50516                 return [-(cm.right+c.getWidth()+cm.left), 0];
50517             break;
50518             case "east":
50519                 return [cm.right+c.getWidth()+cm.left, 0];
50520             break;
50521             case "north":
50522                 return [0, -(cm.top+cm.bottom+c.getHeight())];
50523             break;
50524             case "south":
50525                 return [0, cm.top+cm.bottom+c.getHeight()];
50526             break;
50527         }
50528     }
50529 });/*
50530  * Based on:
50531  * Ext JS Library 1.1.1
50532  * Copyright(c) 2006-2007, Ext JS, LLC.
50533  *
50534  * Originally Released Under LGPL - original licence link has changed is not relivant.
50535  *
50536  * Fork - LGPL
50537  * <script type="text/javascript">
50538  */
50539 /*
50540  * These classes are private internal classes
50541  */
50542 Roo.CenterLayoutRegion = function(mgr, config){
50543     Roo.LayoutRegion.call(this, mgr, config, "center");
50544     this.visible = true;
50545     this.minWidth = config.minWidth || 20;
50546     this.minHeight = config.minHeight || 20;
50547 };
50548
50549 Roo.extend(Roo.CenterLayoutRegion, Roo.LayoutRegion, {
50550     hide : function(){
50551         // center panel can't be hidden
50552     },
50553     
50554     show : function(){
50555         // center panel can't be hidden
50556     },
50557     
50558     getMinWidth: function(){
50559         return this.minWidth;
50560     },
50561     
50562     getMinHeight: function(){
50563         return this.minHeight;
50564     }
50565 });
50566
50567
50568 Roo.NorthLayoutRegion = function(mgr, config){
50569     Roo.LayoutRegion.call(this, mgr, config, "north", "n-resize");
50570     if(this.split){
50571         this.split.placement = Roo.SplitBar.TOP;
50572         this.split.orientation = Roo.SplitBar.VERTICAL;
50573         this.split.el.addClass("x-layout-split-v");
50574     }
50575     var size = config.initialSize || config.height;
50576     if(typeof size != "undefined"){
50577         this.el.setHeight(size);
50578     }
50579 };
50580 Roo.extend(Roo.NorthLayoutRegion, Roo.SplitLayoutRegion, {
50581     orientation: Roo.SplitBar.VERTICAL,
50582     getBox : function(){
50583         if(this.collapsed){
50584             return this.collapsedEl.getBox();
50585         }
50586         var box = this.el.getBox();
50587         if(this.split){
50588             box.height += this.split.el.getHeight();
50589         }
50590         return box;
50591     },
50592     
50593     updateBox : function(box){
50594         if(this.split && !this.collapsed){
50595             box.height -= this.split.el.getHeight();
50596             this.split.el.setLeft(box.x);
50597             this.split.el.setTop(box.y+box.height);
50598             this.split.el.setWidth(box.width);
50599         }
50600         if(this.collapsed){
50601             this.updateBody(box.width, null);
50602         }
50603         Roo.LayoutRegion.prototype.updateBox.call(this, box);
50604     }
50605 });
50606
50607 Roo.SouthLayoutRegion = function(mgr, config){
50608     Roo.SplitLayoutRegion.call(this, mgr, config, "south", "s-resize");
50609     if(this.split){
50610         this.split.placement = Roo.SplitBar.BOTTOM;
50611         this.split.orientation = Roo.SplitBar.VERTICAL;
50612         this.split.el.addClass("x-layout-split-v");
50613     }
50614     var size = config.initialSize || config.height;
50615     if(typeof size != "undefined"){
50616         this.el.setHeight(size);
50617     }
50618 };
50619 Roo.extend(Roo.SouthLayoutRegion, Roo.SplitLayoutRegion, {
50620     orientation: Roo.SplitBar.VERTICAL,
50621     getBox : function(){
50622         if(this.collapsed){
50623             return this.collapsedEl.getBox();
50624         }
50625         var box = this.el.getBox();
50626         if(this.split){
50627             var sh = this.split.el.getHeight();
50628             box.height += sh;
50629             box.y -= sh;
50630         }
50631         return box;
50632     },
50633     
50634     updateBox : function(box){
50635         if(this.split && !this.collapsed){
50636             var sh = this.split.el.getHeight();
50637             box.height -= sh;
50638             box.y += sh;
50639             this.split.el.setLeft(box.x);
50640             this.split.el.setTop(box.y-sh);
50641             this.split.el.setWidth(box.width);
50642         }
50643         if(this.collapsed){
50644             this.updateBody(box.width, null);
50645         }
50646         Roo.LayoutRegion.prototype.updateBox.call(this, box);
50647     }
50648 });
50649
50650 Roo.EastLayoutRegion = function(mgr, config){
50651     Roo.SplitLayoutRegion.call(this, mgr, config, "east", "e-resize");
50652     if(this.split){
50653         this.split.placement = Roo.SplitBar.RIGHT;
50654         this.split.orientation = Roo.SplitBar.HORIZONTAL;
50655         this.split.el.addClass("x-layout-split-h");
50656     }
50657     var size = config.initialSize || config.width;
50658     if(typeof size != "undefined"){
50659         this.el.setWidth(size);
50660     }
50661 };
50662 Roo.extend(Roo.EastLayoutRegion, Roo.SplitLayoutRegion, {
50663     orientation: Roo.SplitBar.HORIZONTAL,
50664     getBox : function(){
50665         if(this.collapsed){
50666             return this.collapsedEl.getBox();
50667         }
50668         var box = this.el.getBox();
50669         if(this.split){
50670             var sw = this.split.el.getWidth();
50671             box.width += sw;
50672             box.x -= sw;
50673         }
50674         return box;
50675     },
50676
50677     updateBox : function(box){
50678         if(this.split && !this.collapsed){
50679             var sw = this.split.el.getWidth();
50680             box.width -= sw;
50681             this.split.el.setLeft(box.x);
50682             this.split.el.setTop(box.y);
50683             this.split.el.setHeight(box.height);
50684             box.x += sw;
50685         }
50686         if(this.collapsed){
50687             this.updateBody(null, box.height);
50688         }
50689         Roo.LayoutRegion.prototype.updateBox.call(this, box);
50690     }
50691 });
50692
50693 Roo.WestLayoutRegion = function(mgr, config){
50694     Roo.SplitLayoutRegion.call(this, mgr, config, "west", "w-resize");
50695     if(this.split){
50696         this.split.placement = Roo.SplitBar.LEFT;
50697         this.split.orientation = Roo.SplitBar.HORIZONTAL;
50698         this.split.el.addClass("x-layout-split-h");
50699     }
50700     var size = config.initialSize || config.width;
50701     if(typeof size != "undefined"){
50702         this.el.setWidth(size);
50703     }
50704 };
50705 Roo.extend(Roo.WestLayoutRegion, Roo.SplitLayoutRegion, {
50706     orientation: Roo.SplitBar.HORIZONTAL,
50707     getBox : function(){
50708         if(this.collapsed){
50709             return this.collapsedEl.getBox();
50710         }
50711         var box = this.el.getBox();
50712         if(this.split){
50713             box.width += this.split.el.getWidth();
50714         }
50715         return box;
50716     },
50717     
50718     updateBox : function(box){
50719         if(this.split && !this.collapsed){
50720             var sw = this.split.el.getWidth();
50721             box.width -= sw;
50722             this.split.el.setLeft(box.x+box.width);
50723             this.split.el.setTop(box.y);
50724             this.split.el.setHeight(box.height);
50725         }
50726         if(this.collapsed){
50727             this.updateBody(null, box.height);
50728         }
50729         Roo.LayoutRegion.prototype.updateBox.call(this, box);
50730     }
50731 });
50732 /*
50733  * Based on:
50734  * Ext JS Library 1.1.1
50735  * Copyright(c) 2006-2007, Ext JS, LLC.
50736  *
50737  * Originally Released Under LGPL - original licence link has changed is not relivant.
50738  *
50739  * Fork - LGPL
50740  * <script type="text/javascript">
50741  */
50742  
50743  
50744 /*
50745  * Private internal class for reading and applying state
50746  */
50747 Roo.LayoutStateManager = function(layout){
50748      // default empty state
50749      this.state = {
50750         north: {},
50751         south: {},
50752         east: {},
50753         west: {}       
50754     };
50755 };
50756
50757 Roo.LayoutStateManager.prototype = {
50758     init : function(layout, provider){
50759         this.provider = provider;
50760         var state = provider.get(layout.id+"-layout-state");
50761         if(state){
50762             var wasUpdating = layout.isUpdating();
50763             if(!wasUpdating){
50764                 layout.beginUpdate();
50765             }
50766             for(var key in state){
50767                 if(typeof state[key] != "function"){
50768                     var rstate = state[key];
50769                     var r = layout.getRegion(key);
50770                     if(r && rstate){
50771                         if(rstate.size){
50772                             r.resizeTo(rstate.size);
50773                         }
50774                         if(rstate.collapsed == true){
50775                             r.collapse(true);
50776                         }else{
50777                             r.expand(null, true);
50778                         }
50779                     }
50780                 }
50781             }
50782             if(!wasUpdating){
50783                 layout.endUpdate();
50784             }
50785             this.state = state; 
50786         }
50787         this.layout = layout;
50788         layout.on("regionresized", this.onRegionResized, this);
50789         layout.on("regioncollapsed", this.onRegionCollapsed, this);
50790         layout.on("regionexpanded", this.onRegionExpanded, this);
50791     },
50792     
50793     storeState : function(){
50794         this.provider.set(this.layout.id+"-layout-state", this.state);
50795     },
50796     
50797     onRegionResized : function(region, newSize){
50798         this.state[region.getPosition()].size = newSize;
50799         this.storeState();
50800     },
50801     
50802     onRegionCollapsed : function(region){
50803         this.state[region.getPosition()].collapsed = true;
50804         this.storeState();
50805     },
50806     
50807     onRegionExpanded : function(region){
50808         this.state[region.getPosition()].collapsed = false;
50809         this.storeState();
50810     }
50811 };/*
50812  * Based on:
50813  * Ext JS Library 1.1.1
50814  * Copyright(c) 2006-2007, Ext JS, LLC.
50815  *
50816  * Originally Released Under LGPL - original licence link has changed is not relivant.
50817  *
50818  * Fork - LGPL
50819  * <script type="text/javascript">
50820  */
50821 /**
50822  * @class Roo.ContentPanel
50823  * @extends Roo.util.Observable
50824  * A basic ContentPanel element.
50825  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
50826  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
50827  * @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
50828  * @cfg {Boolean}   closable      True if the panel can be closed/removed
50829  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
50830  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
50831  * @cfg {Toolbar}   toolbar       A toolbar for this panel
50832  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
50833  * @cfg {String} title          The title for this panel
50834  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
50835  * @cfg {String} url            Calls {@link #setUrl} with this value
50836  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
50837  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
50838  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
50839  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
50840
50841  * @constructor
50842  * Create a new ContentPanel.
50843  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
50844  * @param {String/Object} config A string to set only the title or a config object
50845  * @param {String} content (optional) Set the HTML content for this panel
50846  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
50847  */
50848 Roo.ContentPanel = function(el, config, content){
50849     
50850      
50851     /*
50852     if(el.autoCreate || el.xtype){ // xtype is available if this is called from factory
50853         config = el;
50854         el = Roo.id();
50855     }
50856     if (config && config.parentLayout) { 
50857         el = config.parentLayout.el.createChild(); 
50858     }
50859     */
50860     if(el.autoCreate){ // xtype is available if this is called from factory
50861         config = el;
50862         el = Roo.id();
50863     }
50864     this.el = Roo.get(el);
50865     if(!this.el && config && config.autoCreate){
50866         if(typeof config.autoCreate == "object"){
50867             if(!config.autoCreate.id){
50868                 config.autoCreate.id = config.id||el;
50869             }
50870             this.el = Roo.DomHelper.append(document.body,
50871                         config.autoCreate, true);
50872         }else{
50873             this.el = Roo.DomHelper.append(document.body,
50874                         {tag: "div", cls: "x-layout-inactive-content", id: config.id||el}, true);
50875         }
50876     }
50877     this.closable = false;
50878     this.loaded = false;
50879     this.active = false;
50880     if(typeof config == "string"){
50881         this.title = config;
50882     }else{
50883         Roo.apply(this, config);
50884     }
50885     
50886     if (this.toolbar && !this.toolbar.el && this.toolbar.xtype) {
50887         this.wrapEl = this.el.wrap();
50888         this.toolbar.container = this.el.insertSibling(false, 'before');
50889         this.toolbar = new Roo.Toolbar(this.toolbar);
50890     }
50891     
50892     // xtype created footer. - not sure if will work as we normally have to render first..
50893     if (this.footer && !this.footer.el && this.footer.xtype) {
50894         if (!this.wrapEl) {
50895             this.wrapEl = this.el.wrap();
50896         }
50897     
50898         this.footer.container = this.wrapEl.createChild();
50899          
50900         this.footer = Roo.factory(this.footer, Roo);
50901         
50902     }
50903     
50904     if(this.resizeEl){
50905         this.resizeEl = Roo.get(this.resizeEl, true);
50906     }else{
50907         this.resizeEl = this.el;
50908     }
50909     // handle view.xtype
50910     
50911  
50912     
50913     
50914     this.addEvents({
50915         /**
50916          * @event activate
50917          * Fires when this panel is activated. 
50918          * @param {Roo.ContentPanel} this
50919          */
50920         "activate" : true,
50921         /**
50922          * @event deactivate
50923          * Fires when this panel is activated. 
50924          * @param {Roo.ContentPanel} this
50925          */
50926         "deactivate" : true,
50927
50928         /**
50929          * @event resize
50930          * Fires when this panel is resized if fitToFrame is true.
50931          * @param {Roo.ContentPanel} this
50932          * @param {Number} width The width after any component adjustments
50933          * @param {Number} height The height after any component adjustments
50934          */
50935         "resize" : true,
50936         
50937          /**
50938          * @event render
50939          * Fires when this tab is created
50940          * @param {Roo.ContentPanel} this
50941          */
50942         "render" : true
50943         
50944         
50945         
50946     });
50947     
50948
50949     
50950     
50951     if(this.autoScroll){
50952         this.resizeEl.setStyle("overflow", "auto");
50953     } else {
50954         // fix randome scrolling
50955         this.el.on('scroll', function() {
50956             Roo.log('fix random scolling');
50957             this.scrollTo('top',0); 
50958         });
50959     }
50960     content = content || this.content;
50961     if(content){
50962         this.setContent(content);
50963     }
50964     if(config && config.url){
50965         this.setUrl(this.url, this.params, this.loadOnce);
50966     }
50967     
50968     
50969     
50970     Roo.ContentPanel.superclass.constructor.call(this);
50971     
50972     if (this.view && typeof(this.view.xtype) != 'undefined') {
50973         this.view.el = this.el.appendChild(document.createElement("div"));
50974         this.view = Roo.factory(this.view); 
50975         this.view.render  &&  this.view.render(false, '');  
50976     }
50977     
50978     
50979     this.fireEvent('render', this);
50980 };
50981
50982 Roo.extend(Roo.ContentPanel, Roo.util.Observable, {
50983     tabTip:'',
50984     setRegion : function(region){
50985         this.region = region;
50986         if(region){
50987            this.el.replaceClass("x-layout-inactive-content", "x-layout-active-content");
50988         }else{
50989            this.el.replaceClass("x-layout-active-content", "x-layout-inactive-content");
50990         } 
50991     },
50992     
50993     /**
50994      * Returns the toolbar for this Panel if one was configured. 
50995      * @return {Roo.Toolbar} 
50996      */
50997     getToolbar : function(){
50998         return this.toolbar;
50999     },
51000     
51001     setActiveState : function(active){
51002         this.active = active;
51003         if(!active){
51004             this.fireEvent("deactivate", this);
51005         }else{
51006             this.fireEvent("activate", this);
51007         }
51008     },
51009     /**
51010      * Updates this panel's element
51011      * @param {String} content The new content
51012      * @param {Boolean} loadScripts (optional) true to look for and process scripts
51013     */
51014     setContent : function(content, loadScripts){
51015         this.el.update(content, loadScripts);
51016     },
51017
51018     ignoreResize : function(w, h){
51019         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
51020             return true;
51021         }else{
51022             this.lastSize = {width: w, height: h};
51023             return false;
51024         }
51025     },
51026     /**
51027      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
51028      * @return {Roo.UpdateManager} The UpdateManager
51029      */
51030     getUpdateManager : function(){
51031         return this.el.getUpdateManager();
51032     },
51033      /**
51034      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
51035      * @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:
51036 <pre><code>
51037 panel.load({
51038     url: "your-url.php",
51039     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
51040     callback: yourFunction,
51041     scope: yourObject, //(optional scope)
51042     discardUrl: false,
51043     nocache: false,
51044     text: "Loading...",
51045     timeout: 30,
51046     scripts: false
51047 });
51048 </code></pre>
51049      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
51050      * 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.
51051      * @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}
51052      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
51053      * @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.
51054      * @return {Roo.ContentPanel} this
51055      */
51056     load : function(){
51057         var um = this.el.getUpdateManager();
51058         um.update.apply(um, arguments);
51059         return this;
51060     },
51061
51062
51063     /**
51064      * 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.
51065      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
51066      * @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)
51067      * @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)
51068      * @return {Roo.UpdateManager} The UpdateManager
51069      */
51070     setUrl : function(url, params, loadOnce){
51071         if(this.refreshDelegate){
51072             this.removeListener("activate", this.refreshDelegate);
51073         }
51074         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
51075         this.on("activate", this.refreshDelegate);
51076         return this.el.getUpdateManager();
51077     },
51078     
51079     _handleRefresh : function(url, params, loadOnce){
51080         if(!loadOnce || !this.loaded){
51081             var updater = this.el.getUpdateManager();
51082             updater.update(url, params, this._setLoaded.createDelegate(this));
51083         }
51084     },
51085     
51086     _setLoaded : function(){
51087         this.loaded = true;
51088     }, 
51089     
51090     /**
51091      * Returns this panel's id
51092      * @return {String} 
51093      */
51094     getId : function(){
51095         return this.el.id;
51096     },
51097     
51098     /** 
51099      * Returns this panel's element - used by regiosn to add.
51100      * @return {Roo.Element} 
51101      */
51102     getEl : function(){
51103         return this.wrapEl || this.el;
51104     },
51105     
51106     adjustForComponents : function(width, height)
51107     {
51108         //Roo.log('adjustForComponents ');
51109         if(this.resizeEl != this.el){
51110             width -= this.el.getFrameWidth('lr');
51111             height -= this.el.getFrameWidth('tb');
51112         }
51113         if(this.toolbar){
51114             var te = this.toolbar.getEl();
51115             height -= te.getHeight();
51116             te.setWidth(width);
51117         }
51118         if(this.footer){
51119             var te = this.footer.getEl();
51120             Roo.log("footer:" + te.getHeight());
51121             
51122             height -= te.getHeight();
51123             te.setWidth(width);
51124         }
51125         
51126         
51127         if(this.adjustments){
51128             width += this.adjustments[0];
51129             height += this.adjustments[1];
51130         }
51131         return {"width": width, "height": height};
51132     },
51133     
51134     setSize : function(width, height){
51135         if(this.fitToFrame && !this.ignoreResize(width, height)){
51136             if(this.fitContainer && this.resizeEl != this.el){
51137                 this.el.setSize(width, height);
51138             }
51139             var size = this.adjustForComponents(width, height);
51140             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
51141             this.fireEvent('resize', this, size.width, size.height);
51142         }
51143     },
51144     
51145     /**
51146      * Returns this panel's title
51147      * @return {String} 
51148      */
51149     getTitle : function(){
51150         return this.title;
51151     },
51152     
51153     /**
51154      * Set this panel's title
51155      * @param {String} title
51156      */
51157     setTitle : function(title){
51158         this.title = title;
51159         if(this.region){
51160             this.region.updatePanelTitle(this, title);
51161         }
51162     },
51163     
51164     /**
51165      * Returns true is this panel was configured to be closable
51166      * @return {Boolean} 
51167      */
51168     isClosable : function(){
51169         return this.closable;
51170     },
51171     
51172     beforeSlide : function(){
51173         this.el.clip();
51174         this.resizeEl.clip();
51175     },
51176     
51177     afterSlide : function(){
51178         this.el.unclip();
51179         this.resizeEl.unclip();
51180     },
51181     
51182     /**
51183      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
51184      *   Will fail silently if the {@link #setUrl} method has not been called.
51185      *   This does not activate the panel, just updates its content.
51186      */
51187     refresh : function(){
51188         if(this.refreshDelegate){
51189            this.loaded = false;
51190            this.refreshDelegate();
51191         }
51192     },
51193     
51194     /**
51195      * Destroys this panel
51196      */
51197     destroy : function(){
51198         this.el.removeAllListeners();
51199         var tempEl = document.createElement("span");
51200         tempEl.appendChild(this.el.dom);
51201         tempEl.innerHTML = "";
51202         this.el.remove();
51203         this.el = null;
51204     },
51205     
51206     /**
51207      * form - if the content panel contains a form - this is a reference to it.
51208      * @type {Roo.form.Form}
51209      */
51210     form : false,
51211     /**
51212      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
51213      *    This contains a reference to it.
51214      * @type {Roo.View}
51215      */
51216     view : false,
51217     
51218       /**
51219      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
51220      * <pre><code>
51221
51222 layout.addxtype({
51223        xtype : 'Form',
51224        items: [ .... ]
51225    }
51226 );
51227
51228 </code></pre>
51229      * @param {Object} cfg Xtype definition of item to add.
51230      */
51231     
51232     addxtype : function(cfg) {
51233         // add form..
51234         if (cfg.xtype.match(/^Form$/)) {
51235             
51236             var el;
51237             //if (this.footer) {
51238             //    el = this.footer.container.insertSibling(false, 'before');
51239             //} else {
51240                 el = this.el.createChild();
51241             //}
51242
51243             this.form = new  Roo.form.Form(cfg);
51244             
51245             
51246             if ( this.form.allItems.length) this.form.render(el.dom);
51247             return this.form;
51248         }
51249         // should only have one of theses..
51250         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
51251             // views.. should not be just added - used named prop 'view''
51252             
51253             cfg.el = this.el.appendChild(document.createElement("div"));
51254             // factory?
51255             
51256             var ret = new Roo.factory(cfg);
51257              
51258              ret.render && ret.render(false, ''); // render blank..
51259             this.view = ret;
51260             return ret;
51261         }
51262         return false;
51263     }
51264 });
51265
51266 /**
51267  * @class Roo.GridPanel
51268  * @extends Roo.ContentPanel
51269  * @constructor
51270  * Create a new GridPanel.
51271  * @param {Roo.grid.Grid} grid The grid for this panel
51272  * @param {String/Object} config A string to set only the panel's title, or a config object
51273  */
51274 Roo.GridPanel = function(grid, config){
51275     
51276   
51277     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
51278         {tag: "div", cls: "x-layout-grid-wrapper x-layout-inactive-content"}, true);
51279         
51280     this.wrapper.dom.appendChild(grid.getGridEl().dom);
51281     
51282     Roo.GridPanel.superclass.constructor.call(this, this.wrapper, config);
51283     
51284     if(this.toolbar){
51285         this.toolbar.el.insertBefore(this.wrapper.dom.firstChild);
51286     }
51287     // xtype created footer. - not sure if will work as we normally have to render first..
51288     if (this.footer && !this.footer.el && this.footer.xtype) {
51289         
51290         this.footer.container = this.grid.getView().getFooterPanel(true);
51291         this.footer.dataSource = this.grid.dataSource;
51292         this.footer = Roo.factory(this.footer, Roo);
51293         
51294     }
51295     
51296     grid.monitorWindowResize = false; // turn off autosizing
51297     grid.autoHeight = false;
51298     grid.autoWidth = false;
51299     this.grid = grid;
51300     this.grid.getGridEl().replaceClass("x-layout-inactive-content", "x-layout-component-panel");
51301 };
51302
51303 Roo.extend(Roo.GridPanel, Roo.ContentPanel, {
51304     getId : function(){
51305         return this.grid.id;
51306     },
51307     
51308     /**
51309      * Returns the grid for this panel
51310      * @return {Roo.grid.Grid} 
51311      */
51312     getGrid : function(){
51313         return this.grid;    
51314     },
51315     
51316     setSize : function(width, height){
51317         if(!this.ignoreResize(width, height)){
51318             var grid = this.grid;
51319             var size = this.adjustForComponents(width, height);
51320             grid.getGridEl().setSize(size.width, size.height);
51321             grid.autoSize();
51322         }
51323     },
51324     
51325     beforeSlide : function(){
51326         this.grid.getView().scroller.clip();
51327     },
51328     
51329     afterSlide : function(){
51330         this.grid.getView().scroller.unclip();
51331     },
51332     
51333     destroy : function(){
51334         this.grid.destroy();
51335         delete this.grid;
51336         Roo.GridPanel.superclass.destroy.call(this); 
51337     }
51338 });
51339
51340
51341 /**
51342  * @class Roo.NestedLayoutPanel
51343  * @extends Roo.ContentPanel
51344  * @constructor
51345  * Create a new NestedLayoutPanel.
51346  * 
51347  * 
51348  * @param {Roo.BorderLayout} layout The layout for this panel
51349  * @param {String/Object} config A string to set only the title or a config object
51350  */
51351 Roo.NestedLayoutPanel = function(layout, config)
51352 {
51353     // construct with only one argument..
51354     /* FIXME - implement nicer consturctors
51355     if (layout.layout) {
51356         config = layout;
51357         layout = config.layout;
51358         delete config.layout;
51359     }
51360     if (layout.xtype && !layout.getEl) {
51361         // then layout needs constructing..
51362         layout = Roo.factory(layout, Roo);
51363     }
51364     */
51365     
51366     
51367     Roo.NestedLayoutPanel.superclass.constructor.call(this, layout.getEl(), config);
51368     
51369     layout.monitorWindowResize = false; // turn off autosizing
51370     this.layout = layout;
51371     this.layout.getEl().addClass("x-layout-nested-layout");
51372     
51373     
51374     
51375     
51376 };
51377
51378 Roo.extend(Roo.NestedLayoutPanel, Roo.ContentPanel, {
51379
51380     setSize : function(width, height){
51381         if(!this.ignoreResize(width, height)){
51382             var size = this.adjustForComponents(width, height);
51383             var el = this.layout.getEl();
51384             el.setSize(size.width, size.height);
51385             var touch = el.dom.offsetWidth;
51386             this.layout.layout();
51387             // ie requires a double layout on the first pass
51388             if(Roo.isIE && !this.initialized){
51389                 this.initialized = true;
51390                 this.layout.layout();
51391             }
51392         }
51393     },
51394     
51395     // activate all subpanels if not currently active..
51396     
51397     setActiveState : function(active){
51398         this.active = active;
51399         if(!active){
51400             this.fireEvent("deactivate", this);
51401             return;
51402         }
51403         
51404         this.fireEvent("activate", this);
51405         // not sure if this should happen before or after..
51406         if (!this.layout) {
51407             return; // should not happen..
51408         }
51409         var reg = false;
51410         for (var r in this.layout.regions) {
51411             reg = this.layout.getRegion(r);
51412             if (reg.getActivePanel()) {
51413                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
51414                 reg.setActivePanel(reg.getActivePanel());
51415                 continue;
51416             }
51417             if (!reg.panels.length) {
51418                 continue;
51419             }
51420             reg.showPanel(reg.getPanel(0));
51421         }
51422         
51423         
51424         
51425         
51426     },
51427     
51428     /**
51429      * Returns the nested BorderLayout for this panel
51430      * @return {Roo.BorderLayout} 
51431      */
51432     getLayout : function(){
51433         return this.layout;
51434     },
51435     
51436      /**
51437      * Adds a xtype elements to the layout of the nested panel
51438      * <pre><code>
51439
51440 panel.addxtype({
51441        xtype : 'ContentPanel',
51442        region: 'west',
51443        items: [ .... ]
51444    }
51445 );
51446
51447 panel.addxtype({
51448         xtype : 'NestedLayoutPanel',
51449         region: 'west',
51450         layout: {
51451            center: { },
51452            west: { }   
51453         },
51454         items : [ ... list of content panels or nested layout panels.. ]
51455    }
51456 );
51457 </code></pre>
51458      * @param {Object} cfg Xtype definition of item to add.
51459      */
51460     addxtype : function(cfg) {
51461         return this.layout.addxtype(cfg);
51462     
51463     }
51464 });
51465
51466 Roo.ScrollPanel = function(el, config, content){
51467     config = config || {};
51468     config.fitToFrame = true;
51469     Roo.ScrollPanel.superclass.constructor.call(this, el, config, content);
51470     
51471     this.el.dom.style.overflow = "hidden";
51472     var wrap = this.el.wrap({cls: "x-scroller x-layout-inactive-content"});
51473     this.el.removeClass("x-layout-inactive-content");
51474     this.el.on("mousewheel", this.onWheel, this);
51475
51476     var up = wrap.createChild({cls: "x-scroller-up", html: "&#160;"}, this.el.dom);
51477     var down = wrap.createChild({cls: "x-scroller-down", html: "&#160;"});
51478     up.unselectable(); down.unselectable();
51479     up.on("click", this.scrollUp, this);
51480     down.on("click", this.scrollDown, this);
51481     up.addClassOnOver("x-scroller-btn-over");
51482     down.addClassOnOver("x-scroller-btn-over");
51483     up.addClassOnClick("x-scroller-btn-click");
51484     down.addClassOnClick("x-scroller-btn-click");
51485     this.adjustments = [0, -(up.getHeight() + down.getHeight())];
51486
51487     this.resizeEl = this.el;
51488     this.el = wrap; this.up = up; this.down = down;
51489 };
51490
51491 Roo.extend(Roo.ScrollPanel, Roo.ContentPanel, {
51492     increment : 100,
51493     wheelIncrement : 5,
51494     scrollUp : function(){
51495         this.resizeEl.scroll("up", this.increment, {callback: this.afterScroll, scope: this});
51496     },
51497
51498     scrollDown : function(){
51499         this.resizeEl.scroll("down", this.increment, {callback: this.afterScroll, scope: this});
51500     },
51501
51502     afterScroll : function(){
51503         var el = this.resizeEl;
51504         var t = el.dom.scrollTop, h = el.dom.scrollHeight, ch = el.dom.clientHeight;
51505         this.up[t == 0 ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
51506         this.down[h - t <= ch ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
51507     },
51508
51509     setSize : function(){
51510         Roo.ScrollPanel.superclass.setSize.apply(this, arguments);
51511         this.afterScroll();
51512     },
51513
51514     onWheel : function(e){
51515         var d = e.getWheelDelta();
51516         this.resizeEl.dom.scrollTop -= (d*this.wheelIncrement);
51517         this.afterScroll();
51518         e.stopEvent();
51519     },
51520
51521     setContent : function(content, loadScripts){
51522         this.resizeEl.update(content, loadScripts);
51523     }
51524
51525 });
51526
51527
51528
51529
51530
51531
51532
51533
51534
51535 /**
51536  * @class Roo.TreePanel
51537  * @extends Roo.ContentPanel
51538  * @constructor
51539  * Create a new TreePanel. - defaults to fit/scoll contents.
51540  * @param {String/Object} config A string to set only the panel's title, or a config object
51541  * @cfg {Roo.tree.TreePanel} tree The tree TreePanel, with config etc.
51542  */
51543 Roo.TreePanel = function(config){
51544     var el = config.el;
51545     var tree = config.tree;
51546     delete config.tree; 
51547     delete config.el; // hopefull!
51548     
51549     // wrapper for IE7 strict & safari scroll issue
51550     
51551     var treeEl = el.createChild();
51552     config.resizeEl = treeEl;
51553     
51554     
51555     
51556     Roo.TreePanel.superclass.constructor.call(this, el, config);
51557  
51558  
51559     this.tree = new Roo.tree.TreePanel(treeEl , tree);
51560     //console.log(tree);
51561     this.on('activate', function()
51562     {
51563         if (this.tree.rendered) {
51564             return;
51565         }
51566         //console.log('render tree');
51567         this.tree.render();
51568     });
51569     // this should not be needed.. - it's actually the 'el' that resizes?
51570     // actuall it breaks the containerScroll - dragging nodes auto scroll at top
51571     
51572     //this.on('resize',  function (cp, w, h) {
51573     //        this.tree.innerCt.setWidth(w);
51574     //        this.tree.innerCt.setHeight(h);
51575     //        //this.tree.innerCt.setStyle('overflow-y', 'auto');
51576     //});
51577
51578         
51579     
51580 };
51581
51582 Roo.extend(Roo.TreePanel, Roo.ContentPanel, {   
51583     fitToFrame : true,
51584     autoScroll : true
51585 });
51586
51587
51588
51589
51590
51591
51592
51593
51594
51595
51596
51597 /*
51598  * Based on:
51599  * Ext JS Library 1.1.1
51600  * Copyright(c) 2006-2007, Ext JS, LLC.
51601  *
51602  * Originally Released Under LGPL - original licence link has changed is not relivant.
51603  *
51604  * Fork - LGPL
51605  * <script type="text/javascript">
51606  */
51607  
51608
51609 /**
51610  * @class Roo.ReaderLayout
51611  * @extends Roo.BorderLayout
51612  * This is a pre-built layout that represents a classic, 5-pane application.  It consists of a header, a primary
51613  * center region containing two nested regions (a top one for a list view and one for item preview below),
51614  * and regions on either side that can be used for navigation, application commands, informational displays, etc.
51615  * The setup and configuration work exactly the same as it does for a {@link Roo.BorderLayout} - this class simply
51616  * expedites the setup of the overall layout and regions for this common application style.
51617  * Example:
51618  <pre><code>
51619 var reader = new Roo.ReaderLayout();
51620 var CP = Roo.ContentPanel;  // shortcut for adding
51621
51622 reader.beginUpdate();
51623 reader.add("north", new CP("north", "North"));
51624 reader.add("west", new CP("west", {title: "West"}));
51625 reader.add("east", new CP("east", {title: "East"}));
51626
51627 reader.regions.listView.add(new CP("listView", "List"));
51628 reader.regions.preview.add(new CP("preview", "Preview"));
51629 reader.endUpdate();
51630 </code></pre>
51631 * @constructor
51632 * Create a new ReaderLayout
51633 * @param {Object} config Configuration options
51634 * @param {String/HTMLElement/Element} container (optional) The container this layout is bound to (defaults to
51635 * document.body if omitted)
51636 */
51637 Roo.ReaderLayout = function(config, renderTo){
51638     var c = config || {size:{}};
51639     Roo.ReaderLayout.superclass.constructor.call(this, renderTo || document.body, {
51640         north: c.north !== false ? Roo.apply({
51641             split:false,
51642             initialSize: 32,
51643             titlebar: false
51644         }, c.north) : false,
51645         west: c.west !== false ? Roo.apply({
51646             split:true,
51647             initialSize: 200,
51648             minSize: 175,
51649             maxSize: 400,
51650             titlebar: true,
51651             collapsible: true,
51652             animate: true,
51653             margins:{left:5,right:0,bottom:5,top:5},
51654             cmargins:{left:5,right:5,bottom:5,top:5}
51655         }, c.west) : false,
51656         east: c.east !== false ? Roo.apply({
51657             split:true,
51658             initialSize: 200,
51659             minSize: 175,
51660             maxSize: 400,
51661             titlebar: true,
51662             collapsible: true,
51663             animate: true,
51664             margins:{left:0,right:5,bottom:5,top:5},
51665             cmargins:{left:5,right:5,bottom:5,top:5}
51666         }, c.east) : false,
51667         center: Roo.apply({
51668             tabPosition: 'top',
51669             autoScroll:false,
51670             closeOnTab: true,
51671             titlebar:false,
51672             margins:{left:c.west!==false ? 0 : 5,right:c.east!==false ? 0 : 5,bottom:5,top:2}
51673         }, c.center)
51674     });
51675
51676     this.el.addClass('x-reader');
51677
51678     this.beginUpdate();
51679
51680     var inner = new Roo.BorderLayout(Roo.get(document.body).createChild(), {
51681         south: c.preview !== false ? Roo.apply({
51682             split:true,
51683             initialSize: 200,
51684             minSize: 100,
51685             autoScroll:true,
51686             collapsible:true,
51687             titlebar: true,
51688             cmargins:{top:5,left:0, right:0, bottom:0}
51689         }, c.preview) : false,
51690         center: Roo.apply({
51691             autoScroll:false,
51692             titlebar:false,
51693             minHeight:200
51694         }, c.listView)
51695     });
51696     this.add('center', new Roo.NestedLayoutPanel(inner,
51697             Roo.apply({title: c.mainTitle || '',tabTip:''},c.innerPanelCfg)));
51698
51699     this.endUpdate();
51700
51701     this.regions.preview = inner.getRegion('south');
51702     this.regions.listView = inner.getRegion('center');
51703 };
51704
51705 Roo.extend(Roo.ReaderLayout, Roo.BorderLayout);/*
51706  * Based on:
51707  * Ext JS Library 1.1.1
51708  * Copyright(c) 2006-2007, Ext JS, LLC.
51709  *
51710  * Originally Released Under LGPL - original licence link has changed is not relivant.
51711  *
51712  * Fork - LGPL
51713  * <script type="text/javascript">
51714  */
51715  
51716 /**
51717  * @class Roo.grid.Grid
51718  * @extends Roo.util.Observable
51719  * This class represents the primary interface of a component based grid control.
51720  * <br><br>Usage:<pre><code>
51721  var grid = new Roo.grid.Grid("my-container-id", {
51722      ds: myDataStore,
51723      cm: myColModel,
51724      selModel: mySelectionModel,
51725      autoSizeColumns: true,
51726      monitorWindowResize: false,
51727      trackMouseOver: true
51728  });
51729  // set any options
51730  grid.render();
51731  * </code></pre>
51732  * <b>Common Problems:</b><br/>
51733  * - Grid does not resize properly when going smaller: Setting overflow hidden on the container
51734  * element will correct this<br/>
51735  * - If you get el.style[camel]= NaNpx or -2px or something related, be certain you have given your container element
51736  * dimensions. The grid adapts to your container's size, if your container has no size defined then the results
51737  * are unpredictable.<br/>
51738  * - Do not render the grid into an element with display:none. Try using visibility:hidden. Otherwise there is no way for the
51739  * grid to calculate dimensions/offsets.<br/>
51740   * @constructor
51741  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
51742  * The container MUST have some type of size defined for the grid to fill. The container will be
51743  * automatically set to position relative if it isn't already.
51744  * @param {Object} config A config object that sets properties on this grid.
51745  */
51746 Roo.grid.Grid = function(container, config){
51747         // initialize the container
51748         this.container = Roo.get(container);
51749         this.container.update("");
51750         this.container.setStyle("overflow", "hidden");
51751     this.container.addClass('x-grid-container');
51752
51753     this.id = this.container.id;
51754
51755     Roo.apply(this, config);
51756     // check and correct shorthanded configs
51757     if(this.ds){
51758         this.dataSource = this.ds;
51759         delete this.ds;
51760     }
51761     if(this.cm){
51762         this.colModel = this.cm;
51763         delete this.cm;
51764     }
51765     if(this.sm){
51766         this.selModel = this.sm;
51767         delete this.sm;
51768     }
51769
51770     if (this.selModel) {
51771         this.selModel = Roo.factory(this.selModel, Roo.grid);
51772         this.sm = this.selModel;
51773         this.sm.xmodule = this.xmodule || false;
51774     }
51775     if (typeof(this.colModel.config) == 'undefined') {
51776         this.colModel = new Roo.grid.ColumnModel(this.colModel);
51777         this.cm = this.colModel;
51778         this.cm.xmodule = this.xmodule || false;
51779     }
51780     if (this.dataSource) {
51781         this.dataSource= Roo.factory(this.dataSource, Roo.data);
51782         this.ds = this.dataSource;
51783         this.ds.xmodule = this.xmodule || false;
51784          
51785     }
51786     
51787     
51788     
51789     if(this.width){
51790         this.container.setWidth(this.width);
51791     }
51792
51793     if(this.height){
51794         this.container.setHeight(this.height);
51795     }
51796     /** @private */
51797         this.addEvents({
51798         // raw events
51799         /**
51800          * @event click
51801          * The raw click event for the entire grid.
51802          * @param {Roo.EventObject} e
51803          */
51804         "click" : true,
51805         /**
51806          * @event dblclick
51807          * The raw dblclick event for the entire grid.
51808          * @param {Roo.EventObject} e
51809          */
51810         "dblclick" : true,
51811         /**
51812          * @event contextmenu
51813          * The raw contextmenu event for the entire grid.
51814          * @param {Roo.EventObject} e
51815          */
51816         "contextmenu" : true,
51817         /**
51818          * @event mousedown
51819          * The raw mousedown event for the entire grid.
51820          * @param {Roo.EventObject} e
51821          */
51822         "mousedown" : true,
51823         /**
51824          * @event mouseup
51825          * The raw mouseup event for the entire grid.
51826          * @param {Roo.EventObject} e
51827          */
51828         "mouseup" : true,
51829         /**
51830          * @event mouseover
51831          * The raw mouseover event for the entire grid.
51832          * @param {Roo.EventObject} e
51833          */
51834         "mouseover" : true,
51835         /**
51836          * @event mouseout
51837          * The raw mouseout event for the entire grid.
51838          * @param {Roo.EventObject} e
51839          */
51840         "mouseout" : true,
51841         /**
51842          * @event keypress
51843          * The raw keypress event for the entire grid.
51844          * @param {Roo.EventObject} e
51845          */
51846         "keypress" : true,
51847         /**
51848          * @event keydown
51849          * The raw keydown event for the entire grid.
51850          * @param {Roo.EventObject} e
51851          */
51852         "keydown" : true,
51853
51854         // custom events
51855
51856         /**
51857          * @event cellclick
51858          * Fires when a cell is clicked
51859          * @param {Grid} this
51860          * @param {Number} rowIndex
51861          * @param {Number} columnIndex
51862          * @param {Roo.EventObject} e
51863          */
51864         "cellclick" : true,
51865         /**
51866          * @event celldblclick
51867          * Fires when a cell is double clicked
51868          * @param {Grid} this
51869          * @param {Number} rowIndex
51870          * @param {Number} columnIndex
51871          * @param {Roo.EventObject} e
51872          */
51873         "celldblclick" : true,
51874         /**
51875          * @event rowclick
51876          * Fires when a row is clicked
51877          * @param {Grid} this
51878          * @param {Number} rowIndex
51879          * @param {Roo.EventObject} e
51880          */
51881         "rowclick" : true,
51882         /**
51883          * @event rowdblclick
51884          * Fires when a row is double clicked
51885          * @param {Grid} this
51886          * @param {Number} rowIndex
51887          * @param {Roo.EventObject} e
51888          */
51889         "rowdblclick" : true,
51890         /**
51891          * @event headerclick
51892          * Fires when a header is clicked
51893          * @param {Grid} this
51894          * @param {Number} columnIndex
51895          * @param {Roo.EventObject} e
51896          */
51897         "headerclick" : true,
51898         /**
51899          * @event headerdblclick
51900          * Fires when a header cell is double clicked
51901          * @param {Grid} this
51902          * @param {Number} columnIndex
51903          * @param {Roo.EventObject} e
51904          */
51905         "headerdblclick" : true,
51906         /**
51907          * @event rowcontextmenu
51908          * Fires when a row is right clicked
51909          * @param {Grid} this
51910          * @param {Number} rowIndex
51911          * @param {Roo.EventObject} e
51912          */
51913         "rowcontextmenu" : true,
51914         /**
51915          * @event cellcontextmenu
51916          * Fires when a cell is right clicked
51917          * @param {Grid} this
51918          * @param {Number} rowIndex
51919          * @param {Number} cellIndex
51920          * @param {Roo.EventObject} e
51921          */
51922          "cellcontextmenu" : true,
51923         /**
51924          * @event headercontextmenu
51925          * Fires when a header is right clicked
51926          * @param {Grid} this
51927          * @param {Number} columnIndex
51928          * @param {Roo.EventObject} e
51929          */
51930         "headercontextmenu" : true,
51931         /**
51932          * @event bodyscroll
51933          * Fires when the body element is scrolled
51934          * @param {Number} scrollLeft
51935          * @param {Number} scrollTop
51936          */
51937         "bodyscroll" : true,
51938         /**
51939          * @event columnresize
51940          * Fires when the user resizes a column
51941          * @param {Number} columnIndex
51942          * @param {Number} newSize
51943          */
51944         "columnresize" : true,
51945         /**
51946          * @event columnmove
51947          * Fires when the user moves a column
51948          * @param {Number} oldIndex
51949          * @param {Number} newIndex
51950          */
51951         "columnmove" : true,
51952         /**
51953          * @event startdrag
51954          * Fires when row(s) start being dragged
51955          * @param {Grid} this
51956          * @param {Roo.GridDD} dd The drag drop object
51957          * @param {event} e The raw browser event
51958          */
51959         "startdrag" : true,
51960         /**
51961          * @event enddrag
51962          * Fires when a drag operation is complete
51963          * @param {Grid} this
51964          * @param {Roo.GridDD} dd The drag drop object
51965          * @param {event} e The raw browser event
51966          */
51967         "enddrag" : true,
51968         /**
51969          * @event dragdrop
51970          * Fires when dragged row(s) are dropped on a valid DD target
51971          * @param {Grid} this
51972          * @param {Roo.GridDD} dd The drag drop object
51973          * @param {String} targetId The target drag drop object
51974          * @param {event} e The raw browser event
51975          */
51976         "dragdrop" : true,
51977         /**
51978          * @event dragover
51979          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
51980          * @param {Grid} this
51981          * @param {Roo.GridDD} dd The drag drop object
51982          * @param {String} targetId The target drag drop object
51983          * @param {event} e The raw browser event
51984          */
51985         "dragover" : true,
51986         /**
51987          * @event dragenter
51988          *  Fires when the dragged row(s) first cross another DD target while being dragged
51989          * @param {Grid} this
51990          * @param {Roo.GridDD} dd The drag drop object
51991          * @param {String} targetId The target drag drop object
51992          * @param {event} e The raw browser event
51993          */
51994         "dragenter" : true,
51995         /**
51996          * @event dragout
51997          * Fires when the dragged row(s) leave another DD target while being dragged
51998          * @param {Grid} this
51999          * @param {Roo.GridDD} dd The drag drop object
52000          * @param {String} targetId The target drag drop object
52001          * @param {event} e The raw browser event
52002          */
52003         "dragout" : true,
52004         /**
52005          * @event rowclass
52006          * Fires when a row is rendered, so you can change add a style to it.
52007          * @param {GridView} gridview   The grid view
52008          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
52009          */
52010         'rowclass' : true,
52011
52012         /**
52013          * @event render
52014          * Fires when the grid is rendered
52015          * @param {Grid} grid
52016          */
52017         'render' : true
52018     });
52019
52020     Roo.grid.Grid.superclass.constructor.call(this);
52021 };
52022 Roo.extend(Roo.grid.Grid, Roo.util.Observable, {
52023     
52024     /**
52025      * @cfg {String} ddGroup - drag drop group.
52026      */
52027
52028     /**
52029      * @cfg {Number} minColumnWidth The minimum width a column can be resized to. Default is 25.
52030      */
52031     minColumnWidth : 25,
52032
52033     /**
52034      * @cfg {Boolean} autoSizeColumns True to automatically resize the columns to fit their content
52035      * <b>on initial render.</b> It is more efficient to explicitly size the columns
52036      * through the ColumnModel's {@link Roo.grid.ColumnModel#width} config option.  Default is false.
52037      */
52038     autoSizeColumns : false,
52039
52040     /**
52041      * @cfg {Boolean} autoSizeHeaders True to measure headers with column data when auto sizing columns. Default is true.
52042      */
52043     autoSizeHeaders : true,
52044
52045     /**
52046      * @cfg {Boolean} monitorWindowResize True to autoSize the grid when the window resizes. Default is true.
52047      */
52048     monitorWindowResize : true,
52049
52050     /**
52051      * @cfg {Boolean} maxRowsToMeasure If autoSizeColumns is on, maxRowsToMeasure can be used to limit the number of
52052      * rows measured to get a columns size. Default is 0 (all rows).
52053      */
52054     maxRowsToMeasure : 0,
52055
52056     /**
52057      * @cfg {Boolean} trackMouseOver True to highlight rows when the mouse is over. Default is true.
52058      */
52059     trackMouseOver : true,
52060
52061     /**
52062     * @cfg {Boolean} enableDrag  True to enable drag of rows. Default is false. (double check if this is needed?)
52063     */
52064     
52065     /**
52066     * @cfg {Boolean} enableDragDrop True to enable drag and drop of rows. Default is false.
52067     */
52068     enableDragDrop : false,
52069     
52070     /**
52071     * @cfg {Boolean} enableColumnMove True to enable drag and drop reorder of columns. Default is true.
52072     */
52073     enableColumnMove : true,
52074     
52075     /**
52076     * @cfg {Boolean} enableColumnHide True to enable hiding of columns with the header context menu. Default is true.
52077     */
52078     enableColumnHide : true,
52079     
52080     /**
52081     * @cfg {Boolean} enableRowHeightSync True to manually sync row heights across locked and not locked rows. Default is false.
52082     */
52083     enableRowHeightSync : false,
52084     
52085     /**
52086     * @cfg {Boolean} stripeRows True to stripe the rows.  Default is true.
52087     */
52088     stripeRows : true,
52089     
52090     /**
52091     * @cfg {Boolean} autoHeight True to fit the height of the grid container to the height of the data. Default is false.
52092     */
52093     autoHeight : false,
52094
52095     /**
52096      * @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.
52097      */
52098     autoExpandColumn : false,
52099
52100     /**
52101     * @cfg {Number} autoExpandMin The minimum width the autoExpandColumn can have (if enabled).
52102     * Default is 50.
52103     */
52104     autoExpandMin : 50,
52105
52106     /**
52107     * @cfg {Number} autoExpandMax The maximum width the autoExpandColumn can have (if enabled). Default is 1000.
52108     */
52109     autoExpandMax : 1000,
52110
52111     /**
52112     * @cfg {Object} view The {@link Roo.grid.GridView} used by the grid. This can be set before a call to render().
52113     */
52114     view : null,
52115
52116     /**
52117     * @cfg {Object} loadMask An {@link Roo.LoadMask} config or true to mask the grid while loading. Default is false.
52118     */
52119     loadMask : false,
52120     /**
52121     * @cfg {Roo.dd.DropTarget} dropTarget An {@link Roo.dd.DropTarget} config
52122     */
52123     dropTarget: false,
52124     
52125    
52126     
52127     // private
52128     rendered : false,
52129
52130     /**
52131     * @cfg {Boolean} autoWidth True to set the grid's width to the default total width of the grid's columns instead
52132     * of a fixed width. Default is false.
52133     */
52134     /**
52135     * @cfg {Number} maxHeight Sets the maximum height of the grid - ignored if autoHeight is not on.
52136     */
52137     /**
52138      * Called once after all setup has been completed and the grid is ready to be rendered.
52139      * @return {Roo.grid.Grid} this
52140      */
52141     render : function()
52142     {
52143         var c = this.container;
52144         // try to detect autoHeight/width mode
52145         if((!c.dom.offsetHeight || c.dom.offsetHeight < 20) || c.getStyle("height") == "auto"){
52146             this.autoHeight = true;
52147         }
52148         var view = this.getView();
52149         view.init(this);
52150
52151         c.on("click", this.onClick, this);
52152         c.on("dblclick", this.onDblClick, this);
52153         c.on("contextmenu", this.onContextMenu, this);
52154         c.on("keydown", this.onKeyDown, this);
52155         if (Roo.isTouch) {
52156             c.on("touchstart", this.onTouchStart, this);
52157         }
52158
52159         this.relayEvents(c, ["mousedown","mouseup","mouseover","mouseout","keypress"]);
52160
52161         this.getSelectionModel().init(this);
52162
52163         view.render();
52164
52165         if(this.loadMask){
52166             this.loadMask = new Roo.LoadMask(this.container,
52167                     Roo.apply({store:this.dataSource}, this.loadMask));
52168         }
52169         
52170         
52171         if (this.toolbar && this.toolbar.xtype) {
52172             this.toolbar.container = this.getView().getHeaderPanel(true);
52173             this.toolbar = new Roo.Toolbar(this.toolbar);
52174         }
52175         if (this.footer && this.footer.xtype) {
52176             this.footer.dataSource = this.getDataSource();
52177             this.footer.container = this.getView().getFooterPanel(true);
52178             this.footer = Roo.factory(this.footer, Roo);
52179         }
52180         if (this.dropTarget && this.dropTarget.xtype) {
52181             delete this.dropTarget.xtype;
52182             this.dropTarget =  new Roo.dd.DropTarget(this.getView().mainBody, this.dropTarget);
52183         }
52184         
52185         
52186         this.rendered = true;
52187         this.fireEvent('render', this);
52188         return this;
52189     },
52190
52191         /**
52192          * Reconfigures the grid to use a different Store and Column Model.
52193          * The View will be bound to the new objects and refreshed.
52194          * @param {Roo.data.Store} dataSource The new {@link Roo.data.Store} object
52195          * @param {Roo.grid.ColumnModel} The new {@link Roo.grid.ColumnModel} object
52196          */
52197     reconfigure : function(dataSource, colModel){
52198         if(this.loadMask){
52199             this.loadMask.destroy();
52200             this.loadMask = new Roo.LoadMask(this.container,
52201                     Roo.apply({store:dataSource}, this.loadMask));
52202         }
52203         this.view.bind(dataSource, colModel);
52204         this.dataSource = dataSource;
52205         this.colModel = colModel;
52206         this.view.refresh(true);
52207     },
52208
52209     // private
52210     onKeyDown : function(e){
52211         this.fireEvent("keydown", e);
52212     },
52213
52214     /**
52215      * Destroy this grid.
52216      * @param {Boolean} removeEl True to remove the element
52217      */
52218     destroy : function(removeEl, keepListeners){
52219         if(this.loadMask){
52220             this.loadMask.destroy();
52221         }
52222         var c = this.container;
52223         c.removeAllListeners();
52224         this.view.destroy();
52225         this.colModel.purgeListeners();
52226         if(!keepListeners){
52227             this.purgeListeners();
52228         }
52229         c.update("");
52230         if(removeEl === true){
52231             c.remove();
52232         }
52233     },
52234
52235     // private
52236     processEvent : function(name, e){
52237         // does this fire select???
52238         Roo.log('grid:processEvent '  + name);
52239         
52240         if (name != 'touchstart' ) {
52241             this.fireEvent(name, e);    
52242         }
52243         
52244         var t = e.getTarget();
52245         var v = this.view;
52246         var header = v.findHeaderIndex(t);
52247         if(header !== false){
52248             var ename = name == 'touchstart' ? 'click' : name;
52249              
52250             this.fireEvent("header" + ename, this, header, e);
52251         }else{
52252             var row = v.findRowIndex(t);
52253             var cell = v.findCellIndex(t);
52254             if (name == 'touchstart') {
52255                 // first touch is always a click.
52256                 // hopefull this happens after selection is updated.?
52257                 name = false;
52258                 
52259                 if (typeof(this.selModel.getSelectedCell) != 'undefined') {
52260                     var cs = this.selModel.getSelectedCell();
52261                     if (row == cs[0] && cell == cs[1]){
52262                         name = 'dblclick';
52263                     }
52264                 }
52265                 if (typeof(this.selModel.getSelections) != 'undefined') {
52266                     var cs = this.selModel.getSelections();
52267                     var ds = this.dataSource;
52268                     if (cs.length == 1 && ds.getAt(row) == cs[0]){
52269                         name = 'dblclick';
52270                     }
52271                 }
52272                 if (!name) {
52273                     return;
52274                 }
52275             }
52276             
52277             
52278             if(row !== false){
52279                 this.fireEvent("row" + name, this, row, e);
52280                 if(cell !== false){
52281                     this.fireEvent("cell" + name, this, row, cell, e);
52282                 }
52283             }
52284         }
52285     },
52286
52287     // private
52288     onClick : function(e){
52289         this.processEvent("click", e);
52290     },
52291    // private
52292     onTouchStart : function(e){
52293         this.processEvent("touchstart", e);
52294     },
52295
52296     // private
52297     onContextMenu : function(e, t){
52298         this.processEvent("contextmenu", e);
52299     },
52300
52301     // private
52302     onDblClick : function(e){
52303         this.processEvent("dblclick", e);
52304     },
52305
52306     // private
52307     walkCells : function(row, col, step, fn, scope){
52308         var cm = this.colModel, clen = cm.getColumnCount();
52309         var ds = this.dataSource, rlen = ds.getCount(), first = true;
52310         if(step < 0){
52311             if(col < 0){
52312                 row--;
52313                 first = false;
52314             }
52315             while(row >= 0){
52316                 if(!first){
52317                     col = clen-1;
52318                 }
52319                 first = false;
52320                 while(col >= 0){
52321                     if(fn.call(scope || this, row, col, cm) === true){
52322                         return [row, col];
52323                     }
52324                     col--;
52325                 }
52326                 row--;
52327             }
52328         } else {
52329             if(col >= clen){
52330                 row++;
52331                 first = false;
52332             }
52333             while(row < rlen){
52334                 if(!first){
52335                     col = 0;
52336                 }
52337                 first = false;
52338                 while(col < clen){
52339                     if(fn.call(scope || this, row, col, cm) === true){
52340                         return [row, col];
52341                     }
52342                     col++;
52343                 }
52344                 row++;
52345             }
52346         }
52347         return null;
52348     },
52349
52350     // private
52351     getSelections : function(){
52352         return this.selModel.getSelections();
52353     },
52354
52355     /**
52356      * Causes the grid to manually recalculate its dimensions. Generally this is done automatically,
52357      * but if manual update is required this method will initiate it.
52358      */
52359     autoSize : function(){
52360         if(this.rendered){
52361             this.view.layout();
52362             if(this.view.adjustForScroll){
52363                 this.view.adjustForScroll();
52364             }
52365         }
52366     },
52367
52368     /**
52369      * Returns the grid's underlying element.
52370      * @return {Element} The element
52371      */
52372     getGridEl : function(){
52373         return this.container;
52374     },
52375
52376     // private for compatibility, overridden by editor grid
52377     stopEditing : function(){},
52378
52379     /**
52380      * Returns the grid's SelectionModel.
52381      * @return {SelectionModel}
52382      */
52383     getSelectionModel : function(){
52384         if(!this.selModel){
52385             this.selModel = new Roo.grid.RowSelectionModel();
52386         }
52387         return this.selModel;
52388     },
52389
52390     /**
52391      * Returns the grid's DataSource.
52392      * @return {DataSource}
52393      */
52394     getDataSource : function(){
52395         return this.dataSource;
52396     },
52397
52398     /**
52399      * Returns the grid's ColumnModel.
52400      * @return {ColumnModel}
52401      */
52402     getColumnModel : function(){
52403         return this.colModel;
52404     },
52405
52406     /**
52407      * Returns the grid's GridView object.
52408      * @return {GridView}
52409      */
52410     getView : function(){
52411         if(!this.view){
52412             this.view = new Roo.grid.GridView(this.viewConfig);
52413         }
52414         return this.view;
52415     },
52416     /**
52417      * Called to get grid's drag proxy text, by default returns this.ddText.
52418      * @return {String}
52419      */
52420     getDragDropText : function(){
52421         var count = this.selModel.getCount();
52422         return String.format(this.ddText, count, count == 1 ? '' : 's');
52423     }
52424 });
52425 /**
52426  * Configures the text is the drag proxy (defaults to "%0 selected row(s)").
52427  * %0 is replaced with the number of selected rows.
52428  * @type String
52429  */
52430 Roo.grid.Grid.prototype.ddText = "{0} selected row{1}";/*
52431  * Based on:
52432  * Ext JS Library 1.1.1
52433  * Copyright(c) 2006-2007, Ext JS, LLC.
52434  *
52435  * Originally Released Under LGPL - original licence link has changed is not relivant.
52436  *
52437  * Fork - LGPL
52438  * <script type="text/javascript">
52439  */
52440  
52441 Roo.grid.AbstractGridView = function(){
52442         this.grid = null;
52443         
52444         this.events = {
52445             "beforerowremoved" : true,
52446             "beforerowsinserted" : true,
52447             "beforerefresh" : true,
52448             "rowremoved" : true,
52449             "rowsinserted" : true,
52450             "rowupdated" : true,
52451             "refresh" : true
52452         };
52453     Roo.grid.AbstractGridView.superclass.constructor.call(this);
52454 };
52455
52456 Roo.extend(Roo.grid.AbstractGridView, Roo.util.Observable, {
52457     rowClass : "x-grid-row",
52458     cellClass : "x-grid-cell",
52459     tdClass : "x-grid-td",
52460     hdClass : "x-grid-hd",
52461     splitClass : "x-grid-hd-split",
52462     
52463     init: function(grid){
52464         this.grid = grid;
52465                 var cid = this.grid.getGridEl().id;
52466         this.colSelector = "#" + cid + " ." + this.cellClass + "-";
52467         this.tdSelector = "#" + cid + " ." + this.tdClass + "-";
52468         this.hdSelector = "#" + cid + " ." + this.hdClass + "-";
52469         this.splitSelector = "#" + cid + " ." + this.splitClass + "-";
52470         },
52471         
52472     getColumnRenderers : function(){
52473         var renderers = [];
52474         var cm = this.grid.colModel;
52475         var colCount = cm.getColumnCount();
52476         for(var i = 0; i < colCount; i++){
52477             renderers[i] = cm.getRenderer(i);
52478         }
52479         return renderers;
52480     },
52481     
52482     getColumnIds : function(){
52483         var ids = [];
52484         var cm = this.grid.colModel;
52485         var colCount = cm.getColumnCount();
52486         for(var i = 0; i < colCount; i++){
52487             ids[i] = cm.getColumnId(i);
52488         }
52489         return ids;
52490     },
52491     
52492     getDataIndexes : function(){
52493         if(!this.indexMap){
52494             this.indexMap = this.buildIndexMap();
52495         }
52496         return this.indexMap.colToData;
52497     },
52498     
52499     getColumnIndexByDataIndex : function(dataIndex){
52500         if(!this.indexMap){
52501             this.indexMap = this.buildIndexMap();
52502         }
52503         return this.indexMap.dataToCol[dataIndex];
52504     },
52505     
52506     /**
52507      * Set a css style for a column dynamically. 
52508      * @param {Number} colIndex The index of the column
52509      * @param {String} name The css property name
52510      * @param {String} value The css value
52511      */
52512     setCSSStyle : function(colIndex, name, value){
52513         var selector = "#" + this.grid.id + " .x-grid-col-" + colIndex;
52514         Roo.util.CSS.updateRule(selector, name, value);
52515     },
52516     
52517     generateRules : function(cm){
52518         var ruleBuf = [], rulesId = this.grid.id + '-cssrules';
52519         Roo.util.CSS.removeStyleSheet(rulesId);
52520         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
52521             var cid = cm.getColumnId(i);
52522             ruleBuf.push(this.colSelector, cid, " {\n", cm.config[i].css, "}\n",
52523                          this.tdSelector, cid, " {\n}\n",
52524                          this.hdSelector, cid, " {\n}\n",
52525                          this.splitSelector, cid, " {\n}\n");
52526         }
52527         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
52528     }
52529 });/*
52530  * Based on:
52531  * Ext JS Library 1.1.1
52532  * Copyright(c) 2006-2007, Ext JS, LLC.
52533  *
52534  * Originally Released Under LGPL - original licence link has changed is not relivant.
52535  *
52536  * Fork - LGPL
52537  * <script type="text/javascript">
52538  */
52539
52540 // private
52541 // This is a support class used internally by the Grid components
52542 Roo.grid.HeaderDragZone = function(grid, hd, hd2){
52543     this.grid = grid;
52544     this.view = grid.getView();
52545     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
52546     Roo.grid.HeaderDragZone.superclass.constructor.call(this, hd);
52547     if(hd2){
52548         this.setHandleElId(Roo.id(hd));
52549         this.setOuterHandleElId(Roo.id(hd2));
52550     }
52551     this.scroll = false;
52552 };
52553 Roo.extend(Roo.grid.HeaderDragZone, Roo.dd.DragZone, {
52554     maxDragWidth: 120,
52555     getDragData : function(e){
52556         var t = Roo.lib.Event.getTarget(e);
52557         var h = this.view.findHeaderCell(t);
52558         if(h){
52559             return {ddel: h.firstChild, header:h};
52560         }
52561         return false;
52562     },
52563
52564     onInitDrag : function(e){
52565         this.view.headersDisabled = true;
52566         var clone = this.dragData.ddel.cloneNode(true);
52567         clone.id = Roo.id();
52568         clone.style.width = Math.min(this.dragData.header.offsetWidth,this.maxDragWidth) + "px";
52569         this.proxy.update(clone);
52570         return true;
52571     },
52572
52573     afterValidDrop : function(){
52574         var v = this.view;
52575         setTimeout(function(){
52576             v.headersDisabled = false;
52577         }, 50);
52578     },
52579
52580     afterInvalidDrop : function(){
52581         var v = this.view;
52582         setTimeout(function(){
52583             v.headersDisabled = false;
52584         }, 50);
52585     }
52586 });
52587 /*
52588  * Based on:
52589  * Ext JS Library 1.1.1
52590  * Copyright(c) 2006-2007, Ext JS, LLC.
52591  *
52592  * Originally Released Under LGPL - original licence link has changed is not relivant.
52593  *
52594  * Fork - LGPL
52595  * <script type="text/javascript">
52596  */
52597 // private
52598 // This is a support class used internally by the Grid components
52599 Roo.grid.HeaderDropZone = function(grid, hd, hd2){
52600     this.grid = grid;
52601     this.view = grid.getView();
52602     // split the proxies so they don't interfere with mouse events
52603     this.proxyTop = Roo.DomHelper.append(document.body, {
52604         cls:"col-move-top", html:"&#160;"
52605     }, true);
52606     this.proxyBottom = Roo.DomHelper.append(document.body, {
52607         cls:"col-move-bottom", html:"&#160;"
52608     }, true);
52609     this.proxyTop.hide = this.proxyBottom.hide = function(){
52610         this.setLeftTop(-100,-100);
52611         this.setStyle("visibility", "hidden");
52612     };
52613     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
52614     // temporarily disabled
52615     //Roo.dd.ScrollManager.register(this.view.scroller.dom);
52616     Roo.grid.HeaderDropZone.superclass.constructor.call(this, grid.getGridEl().dom);
52617 };
52618 Roo.extend(Roo.grid.HeaderDropZone, Roo.dd.DropZone, {
52619     proxyOffsets : [-4, -9],
52620     fly: Roo.Element.fly,
52621
52622     getTargetFromEvent : function(e){
52623         var t = Roo.lib.Event.getTarget(e);
52624         var cindex = this.view.findCellIndex(t);
52625         if(cindex !== false){
52626             return this.view.getHeaderCell(cindex);
52627         }
52628         return null;
52629     },
52630
52631     nextVisible : function(h){
52632         var v = this.view, cm = this.grid.colModel;
52633         h = h.nextSibling;
52634         while(h){
52635             if(!cm.isHidden(v.getCellIndex(h))){
52636                 return h;
52637             }
52638             h = h.nextSibling;
52639         }
52640         return null;
52641     },
52642
52643     prevVisible : function(h){
52644         var v = this.view, cm = this.grid.colModel;
52645         h = h.prevSibling;
52646         while(h){
52647             if(!cm.isHidden(v.getCellIndex(h))){
52648                 return h;
52649             }
52650             h = h.prevSibling;
52651         }
52652         return null;
52653     },
52654
52655     positionIndicator : function(h, n, e){
52656         var x = Roo.lib.Event.getPageX(e);
52657         var r = Roo.lib.Dom.getRegion(n.firstChild);
52658         var px, pt, py = r.top + this.proxyOffsets[1];
52659         if((r.right - x) <= (r.right-r.left)/2){
52660             px = r.right+this.view.borderWidth;
52661             pt = "after";
52662         }else{
52663             px = r.left;
52664             pt = "before";
52665         }
52666         var oldIndex = this.view.getCellIndex(h);
52667         var newIndex = this.view.getCellIndex(n);
52668
52669         if(this.grid.colModel.isFixed(newIndex)){
52670             return false;
52671         }
52672
52673         var locked = this.grid.colModel.isLocked(newIndex);
52674
52675         if(pt == "after"){
52676             newIndex++;
52677         }
52678         if(oldIndex < newIndex){
52679             newIndex--;
52680         }
52681         if(oldIndex == newIndex && (locked == this.grid.colModel.isLocked(oldIndex))){
52682             return false;
52683         }
52684         px +=  this.proxyOffsets[0];
52685         this.proxyTop.setLeftTop(px, py);
52686         this.proxyTop.show();
52687         if(!this.bottomOffset){
52688             this.bottomOffset = this.view.mainHd.getHeight();
52689         }
52690         this.proxyBottom.setLeftTop(px, py+this.proxyTop.dom.offsetHeight+this.bottomOffset);
52691         this.proxyBottom.show();
52692         return pt;
52693     },
52694
52695     onNodeEnter : function(n, dd, e, data){
52696         if(data.header != n){
52697             this.positionIndicator(data.header, n, e);
52698         }
52699     },
52700
52701     onNodeOver : function(n, dd, e, data){
52702         var result = false;
52703         if(data.header != n){
52704             result = this.positionIndicator(data.header, n, e);
52705         }
52706         if(!result){
52707             this.proxyTop.hide();
52708             this.proxyBottom.hide();
52709         }
52710         return result ? this.dropAllowed : this.dropNotAllowed;
52711     },
52712
52713     onNodeOut : function(n, dd, e, data){
52714         this.proxyTop.hide();
52715         this.proxyBottom.hide();
52716     },
52717
52718     onNodeDrop : function(n, dd, e, data){
52719         var h = data.header;
52720         if(h != n){
52721             var cm = this.grid.colModel;
52722             var x = Roo.lib.Event.getPageX(e);
52723             var r = Roo.lib.Dom.getRegion(n.firstChild);
52724             var pt = (r.right - x) <= ((r.right-r.left)/2) ? "after" : "before";
52725             var oldIndex = this.view.getCellIndex(h);
52726             var newIndex = this.view.getCellIndex(n);
52727             var locked = cm.isLocked(newIndex);
52728             if(pt == "after"){
52729                 newIndex++;
52730             }
52731             if(oldIndex < newIndex){
52732                 newIndex--;
52733             }
52734             if(oldIndex == newIndex && (locked == cm.isLocked(oldIndex))){
52735                 return false;
52736             }
52737             cm.setLocked(oldIndex, locked, true);
52738             cm.moveColumn(oldIndex, newIndex);
52739             this.grid.fireEvent("columnmove", oldIndex, newIndex);
52740             return true;
52741         }
52742         return false;
52743     }
52744 });
52745 /*
52746  * Based on:
52747  * Ext JS Library 1.1.1
52748  * Copyright(c) 2006-2007, Ext JS, LLC.
52749  *
52750  * Originally Released Under LGPL - original licence link has changed is not relivant.
52751  *
52752  * Fork - LGPL
52753  * <script type="text/javascript">
52754  */
52755   
52756 /**
52757  * @class Roo.grid.GridView
52758  * @extends Roo.util.Observable
52759  *
52760  * @constructor
52761  * @param {Object} config
52762  */
52763 Roo.grid.GridView = function(config){
52764     Roo.grid.GridView.superclass.constructor.call(this);
52765     this.el = null;
52766
52767     Roo.apply(this, config);
52768 };
52769
52770 Roo.extend(Roo.grid.GridView, Roo.grid.AbstractGridView, {
52771
52772     unselectable :  'unselectable="on"',
52773     unselectableCls :  'x-unselectable',
52774     
52775     
52776     rowClass : "x-grid-row",
52777
52778     cellClass : "x-grid-col",
52779
52780     tdClass : "x-grid-td",
52781
52782     hdClass : "x-grid-hd",
52783
52784     splitClass : "x-grid-split",
52785
52786     sortClasses : ["sort-asc", "sort-desc"],
52787
52788     enableMoveAnim : false,
52789
52790     hlColor: "C3DAF9",
52791
52792     dh : Roo.DomHelper,
52793
52794     fly : Roo.Element.fly,
52795
52796     css : Roo.util.CSS,
52797
52798     borderWidth: 1,
52799
52800     splitOffset: 3,
52801
52802     scrollIncrement : 22,
52803
52804     cellRE: /(?:.*?)x-grid-(?:hd|cell|csplit)-(?:[\d]+)-([\d]+)(?:.*?)/,
52805
52806     findRE: /\s?(?:x-grid-hd|x-grid-col|x-grid-csplit)\s/,
52807
52808     bind : function(ds, cm){
52809         if(this.ds){
52810             this.ds.un("load", this.onLoad, this);
52811             this.ds.un("datachanged", this.onDataChange, this);
52812             this.ds.un("add", this.onAdd, this);
52813             this.ds.un("remove", this.onRemove, this);
52814             this.ds.un("update", this.onUpdate, this);
52815             this.ds.un("clear", this.onClear, this);
52816         }
52817         if(ds){
52818             ds.on("load", this.onLoad, this);
52819             ds.on("datachanged", this.onDataChange, this);
52820             ds.on("add", this.onAdd, this);
52821             ds.on("remove", this.onRemove, this);
52822             ds.on("update", this.onUpdate, this);
52823             ds.on("clear", this.onClear, this);
52824         }
52825         this.ds = ds;
52826
52827         if(this.cm){
52828             this.cm.un("widthchange", this.onColWidthChange, this);
52829             this.cm.un("headerchange", this.onHeaderChange, this);
52830             this.cm.un("hiddenchange", this.onHiddenChange, this);
52831             this.cm.un("columnmoved", this.onColumnMove, this);
52832             this.cm.un("columnlockchange", this.onColumnLock, this);
52833         }
52834         if(cm){
52835             this.generateRules(cm);
52836             cm.on("widthchange", this.onColWidthChange, this);
52837             cm.on("headerchange", this.onHeaderChange, this);
52838             cm.on("hiddenchange", this.onHiddenChange, this);
52839             cm.on("columnmoved", this.onColumnMove, this);
52840             cm.on("columnlockchange", this.onColumnLock, this);
52841         }
52842         this.cm = cm;
52843     },
52844
52845     init: function(grid){
52846         Roo.grid.GridView.superclass.init.call(this, grid);
52847
52848         this.bind(grid.dataSource, grid.colModel);
52849
52850         grid.on("headerclick", this.handleHeaderClick, this);
52851
52852         if(grid.trackMouseOver){
52853             grid.on("mouseover", this.onRowOver, this);
52854             grid.on("mouseout", this.onRowOut, this);
52855         }
52856         grid.cancelTextSelection = function(){};
52857         this.gridId = grid.id;
52858
52859         var tpls = this.templates || {};
52860
52861         if(!tpls.master){
52862             tpls.master = new Roo.Template(
52863                '<div class="x-grid" hidefocus="true">',
52864                 '<a href="#" class="x-grid-focus" tabIndex="-1"></a>',
52865                   '<div class="x-grid-topbar"></div>',
52866                   '<div class="x-grid-scroller"><div></div></div>',
52867                   '<div class="x-grid-locked">',
52868                       '<div class="x-grid-header">{lockedHeader}</div>',
52869                       '<div class="x-grid-body">{lockedBody}</div>',
52870                   "</div>",
52871                   '<div class="x-grid-viewport">',
52872                       '<div class="x-grid-header">{header}</div>',
52873                       '<div class="x-grid-body">{body}</div>',
52874                   "</div>",
52875                   '<div class="x-grid-bottombar"></div>',
52876                  
52877                   '<div class="x-grid-resize-proxy">&#160;</div>',
52878                "</div>"
52879             );
52880             tpls.master.disableformats = true;
52881         }
52882
52883         if(!tpls.header){
52884             tpls.header = new Roo.Template(
52885                '<table border="0" cellspacing="0" cellpadding="0">',
52886                '<tbody><tr class="x-grid-hd-row">{cells}</tr></tbody>',
52887                "</table>{splits}"
52888             );
52889             tpls.header.disableformats = true;
52890         }
52891         tpls.header.compile();
52892
52893         if(!tpls.hcell){
52894             tpls.hcell = new Roo.Template(
52895                 '<td class="x-grid-hd x-grid-td-{id} {cellId}"><div title="{title}" class="x-grid-hd-inner x-grid-hd-{id}">',
52896                 '<div class="x-grid-hd-text ' + this.unselectableCls +  '" ' + this.unselectable +'>{value}<img class="x-grid-sort-icon" src="', Roo.BLANK_IMAGE_URL, '" /></div>',
52897                 "</div></td>"
52898              );
52899              tpls.hcell.disableFormats = true;
52900         }
52901         tpls.hcell.compile();
52902
52903         if(!tpls.hsplit){
52904             tpls.hsplit = new Roo.Template('<div class="x-grid-split {splitId} x-grid-split-{id}" style="{style} ' +
52905                                             this.unselectableCls +  '" ' + this.unselectable +'>&#160;</div>');
52906             tpls.hsplit.disableFormats = true;
52907         }
52908         tpls.hsplit.compile();
52909
52910         if(!tpls.body){
52911             tpls.body = new Roo.Template(
52912                '<table border="0" cellspacing="0" cellpadding="0">',
52913                "<tbody>{rows}</tbody>",
52914                "</table>"
52915             );
52916             tpls.body.disableFormats = true;
52917         }
52918         tpls.body.compile();
52919
52920         if(!tpls.row){
52921             tpls.row = new Roo.Template('<tr class="x-grid-row {alt}">{cells}</tr>');
52922             tpls.row.disableFormats = true;
52923         }
52924         tpls.row.compile();
52925
52926         if(!tpls.cell){
52927             tpls.cell = new Roo.Template(
52928                 '<td class="x-grid-col x-grid-td-{id} {cellId} {css}" tabIndex="0">',
52929                 '<div class="x-grid-col-{id} x-grid-cell-inner"><div class="x-grid-cell-text ' +
52930                     this.unselectableCls +  '" ' + this.unselectable +'" {attr}>{value}</div></div>',
52931                 "</td>"
52932             );
52933             tpls.cell.disableFormats = true;
52934         }
52935         tpls.cell.compile();
52936
52937         this.templates = tpls;
52938     },
52939
52940     // remap these for backwards compat
52941     onColWidthChange : function(){
52942         this.updateColumns.apply(this, arguments);
52943     },
52944     onHeaderChange : function(){
52945         this.updateHeaders.apply(this, arguments);
52946     }, 
52947     onHiddenChange : function(){
52948         this.handleHiddenChange.apply(this, arguments);
52949     },
52950     onColumnMove : function(){
52951         this.handleColumnMove.apply(this, arguments);
52952     },
52953     onColumnLock : function(){
52954         this.handleLockChange.apply(this, arguments);
52955     },
52956
52957     onDataChange : function(){
52958         this.refresh();
52959         this.updateHeaderSortState();
52960     },
52961
52962     onClear : function(){
52963         this.refresh();
52964     },
52965
52966     onUpdate : function(ds, record){
52967         this.refreshRow(record);
52968     },
52969
52970     refreshRow : function(record){
52971         var ds = this.ds, index;
52972         if(typeof record == 'number'){
52973             index = record;
52974             record = ds.getAt(index);
52975         }else{
52976             index = ds.indexOf(record);
52977         }
52978         this.insertRows(ds, index, index, true);
52979         this.onRemove(ds, record, index+1, true);
52980         this.syncRowHeights(index, index);
52981         this.layout();
52982         this.fireEvent("rowupdated", this, index, record);
52983     },
52984
52985     onAdd : function(ds, records, index){
52986         this.insertRows(ds, index, index + (records.length-1));
52987     },
52988
52989     onRemove : function(ds, record, index, isUpdate){
52990         if(isUpdate !== true){
52991             this.fireEvent("beforerowremoved", this, index, record);
52992         }
52993         var bt = this.getBodyTable(), lt = this.getLockedTable();
52994         if(bt.rows[index]){
52995             bt.firstChild.removeChild(bt.rows[index]);
52996         }
52997         if(lt.rows[index]){
52998             lt.firstChild.removeChild(lt.rows[index]);
52999         }
53000         if(isUpdate !== true){
53001             this.stripeRows(index);
53002             this.syncRowHeights(index, index);
53003             this.layout();
53004             this.fireEvent("rowremoved", this, index, record);
53005         }
53006     },
53007
53008     onLoad : function(){
53009         this.scrollToTop();
53010     },
53011
53012     /**
53013      * Scrolls the grid to the top
53014      */
53015     scrollToTop : function(){
53016         if(this.scroller){
53017             this.scroller.dom.scrollTop = 0;
53018             this.syncScroll();
53019         }
53020     },
53021
53022     /**
53023      * Gets a panel in the header of the grid that can be used for toolbars etc.
53024      * After modifying the contents of this panel a call to grid.autoSize() may be
53025      * required to register any changes in size.
53026      * @param {Boolean} doShow By default the header is hidden. Pass true to show the panel
53027      * @return Roo.Element
53028      */
53029     getHeaderPanel : function(doShow){
53030         if(doShow){
53031             this.headerPanel.show();
53032         }
53033         return this.headerPanel;
53034     },
53035
53036     /**
53037      * Gets a panel in the footer of the grid that can be used for toolbars etc.
53038      * After modifying the contents of this panel a call to grid.autoSize() may be
53039      * required to register any changes in size.
53040      * @param {Boolean} doShow By default the footer is hidden. Pass true to show the panel
53041      * @return Roo.Element
53042      */
53043     getFooterPanel : function(doShow){
53044         if(doShow){
53045             this.footerPanel.show();
53046         }
53047         return this.footerPanel;
53048     },
53049
53050     initElements : function(){
53051         var E = Roo.Element;
53052         var el = this.grid.getGridEl().dom.firstChild;
53053         var cs = el.childNodes;
53054
53055         this.el = new E(el);
53056         
53057          this.focusEl = new E(el.firstChild);
53058         this.focusEl.swallowEvent("click", true);
53059         
53060         this.headerPanel = new E(cs[1]);
53061         this.headerPanel.enableDisplayMode("block");
53062
53063         this.scroller = new E(cs[2]);
53064         this.scrollSizer = new E(this.scroller.dom.firstChild);
53065
53066         this.lockedWrap = new E(cs[3]);
53067         this.lockedHd = new E(this.lockedWrap.dom.firstChild);
53068         this.lockedBody = new E(this.lockedWrap.dom.childNodes[1]);
53069
53070         this.mainWrap = new E(cs[4]);
53071         this.mainHd = new E(this.mainWrap.dom.firstChild);
53072         this.mainBody = new E(this.mainWrap.dom.childNodes[1]);
53073
53074         this.footerPanel = new E(cs[5]);
53075         this.footerPanel.enableDisplayMode("block");
53076
53077         this.resizeProxy = new E(cs[6]);
53078
53079         this.headerSelector = String.format(
53080            '#{0} td.x-grid-hd, #{1} td.x-grid-hd',
53081            this.lockedHd.id, this.mainHd.id
53082         );
53083
53084         this.splitterSelector = String.format(
53085            '#{0} div.x-grid-split, #{1} div.x-grid-split',
53086            this.idToCssName(this.lockedHd.id), this.idToCssName(this.mainHd.id)
53087         );
53088     },
53089     idToCssName : function(s)
53090     {
53091         return s.replace(/[^a-z0-9]+/ig, '-');
53092     },
53093
53094     getHeaderCell : function(index){
53095         return Roo.DomQuery.select(this.headerSelector)[index];
53096     },
53097
53098     getHeaderCellMeasure : function(index){
53099         return this.getHeaderCell(index).firstChild;
53100     },
53101
53102     getHeaderCellText : function(index){
53103         return this.getHeaderCell(index).firstChild.firstChild;
53104     },
53105
53106     getLockedTable : function(){
53107         return this.lockedBody.dom.firstChild;
53108     },
53109
53110     getBodyTable : function(){
53111         return this.mainBody.dom.firstChild;
53112     },
53113
53114     getLockedRow : function(index){
53115         return this.getLockedTable().rows[index];
53116     },
53117
53118     getRow : function(index){
53119         return this.getBodyTable().rows[index];
53120     },
53121
53122     getRowComposite : function(index){
53123         if(!this.rowEl){
53124             this.rowEl = new Roo.CompositeElementLite();
53125         }
53126         var els = [], lrow, mrow;
53127         if(lrow = this.getLockedRow(index)){
53128             els.push(lrow);
53129         }
53130         if(mrow = this.getRow(index)){
53131             els.push(mrow);
53132         }
53133         this.rowEl.elements = els;
53134         return this.rowEl;
53135     },
53136     /**
53137      * Gets the 'td' of the cell
53138      * 
53139      * @param {Integer} rowIndex row to select
53140      * @param {Integer} colIndex column to select
53141      * 
53142      * @return {Object} 
53143      */
53144     getCell : function(rowIndex, colIndex){
53145         var locked = this.cm.getLockedCount();
53146         var source;
53147         if(colIndex < locked){
53148             source = this.lockedBody.dom.firstChild;
53149         }else{
53150             source = this.mainBody.dom.firstChild;
53151             colIndex -= locked;
53152         }
53153         return source.rows[rowIndex].childNodes[colIndex];
53154     },
53155
53156     getCellText : function(rowIndex, colIndex){
53157         return this.getCell(rowIndex, colIndex).firstChild.firstChild;
53158     },
53159
53160     getCellBox : function(cell){
53161         var b = this.fly(cell).getBox();
53162         if(Roo.isOpera){ // opera fails to report the Y
53163             b.y = cell.offsetTop + this.mainBody.getY();
53164         }
53165         return b;
53166     },
53167
53168     getCellIndex : function(cell){
53169         var id = String(cell.className).match(this.cellRE);
53170         if(id){
53171             return parseInt(id[1], 10);
53172         }
53173         return 0;
53174     },
53175
53176     findHeaderIndex : function(n){
53177         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
53178         return r ? this.getCellIndex(r) : false;
53179     },
53180
53181     findHeaderCell : function(n){
53182         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
53183         return r ? r : false;
53184     },
53185
53186     findRowIndex : function(n){
53187         if(!n){
53188             return false;
53189         }
53190         var r = Roo.fly(n).findParent("tr." + this.rowClass, 6);
53191         return r ? r.rowIndex : false;
53192     },
53193
53194     findCellIndex : function(node){
53195         var stop = this.el.dom;
53196         while(node && node != stop){
53197             if(this.findRE.test(node.className)){
53198                 return this.getCellIndex(node);
53199             }
53200             node = node.parentNode;
53201         }
53202         return false;
53203     },
53204
53205     getColumnId : function(index){
53206         return this.cm.getColumnId(index);
53207     },
53208
53209     getSplitters : function()
53210     {
53211         if(this.splitterSelector){
53212            return Roo.DomQuery.select(this.splitterSelector);
53213         }else{
53214             return null;
53215       }
53216     },
53217
53218     getSplitter : function(index){
53219         return this.getSplitters()[index];
53220     },
53221
53222     onRowOver : function(e, t){
53223         var row;
53224         if((row = this.findRowIndex(t)) !== false){
53225             this.getRowComposite(row).addClass("x-grid-row-over");
53226         }
53227     },
53228
53229     onRowOut : function(e, t){
53230         var row;
53231         if((row = this.findRowIndex(t)) !== false && row !== this.findRowIndex(e.getRelatedTarget())){
53232             this.getRowComposite(row).removeClass("x-grid-row-over");
53233         }
53234     },
53235
53236     renderHeaders : function(){
53237         var cm = this.cm;
53238         var ct = this.templates.hcell, ht = this.templates.header, st = this.templates.hsplit;
53239         var cb = [], lb = [], sb = [], lsb = [], p = {};
53240         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
53241             p.cellId = "x-grid-hd-0-" + i;
53242             p.splitId = "x-grid-csplit-0-" + i;
53243             p.id = cm.getColumnId(i);
53244             p.title = cm.getColumnTooltip(i) || "";
53245             p.value = cm.getColumnHeader(i) || "";
53246             p.style = (this.grid.enableColumnResize === false || !cm.isResizable(i) || cm.isFixed(i)) ? 'cursor:default' : '';
53247             if(!cm.isLocked(i)){
53248                 cb[cb.length] = ct.apply(p);
53249                 sb[sb.length] = st.apply(p);
53250             }else{
53251                 lb[lb.length] = ct.apply(p);
53252                 lsb[lsb.length] = st.apply(p);
53253             }
53254         }
53255         return [ht.apply({cells: lb.join(""), splits:lsb.join("")}),
53256                 ht.apply({cells: cb.join(""), splits:sb.join("")})];
53257     },
53258
53259     updateHeaders : function(){
53260         var html = this.renderHeaders();
53261         this.lockedHd.update(html[0]);
53262         this.mainHd.update(html[1]);
53263     },
53264
53265     /**
53266      * Focuses the specified row.
53267      * @param {Number} row The row index
53268      */
53269     focusRow : function(row)
53270     {
53271         //Roo.log('GridView.focusRow');
53272         var x = this.scroller.dom.scrollLeft;
53273         this.focusCell(row, 0, false);
53274         this.scroller.dom.scrollLeft = x;
53275     },
53276
53277     /**
53278      * Focuses the specified cell.
53279      * @param {Number} row The row index
53280      * @param {Number} col The column index
53281      * @param {Boolean} hscroll false to disable horizontal scrolling
53282      */
53283     focusCell : function(row, col, hscroll)
53284     {
53285         //Roo.log('GridView.focusCell');
53286         var el = this.ensureVisible(row, col, hscroll);
53287         this.focusEl.alignTo(el, "tl-tl");
53288         if(Roo.isGecko){
53289             this.focusEl.focus();
53290         }else{
53291             this.focusEl.focus.defer(1, this.focusEl);
53292         }
53293     },
53294
53295     /**
53296      * Scrolls the specified cell into view
53297      * @param {Number} row The row index
53298      * @param {Number} col The column index
53299      * @param {Boolean} hscroll false to disable horizontal scrolling
53300      */
53301     ensureVisible : function(row, col, hscroll)
53302     {
53303         //Roo.log('GridView.ensureVisible,' + row + ',' + col);
53304         //return null; //disable for testing.
53305         if(typeof row != "number"){
53306             row = row.rowIndex;
53307         }
53308         if(row < 0 && row >= this.ds.getCount()){
53309             return  null;
53310         }
53311         col = (col !== undefined ? col : 0);
53312         var cm = this.grid.colModel;
53313         while(cm.isHidden(col)){
53314             col++;
53315         }
53316
53317         var el = this.getCell(row, col);
53318         if(!el){
53319             return null;
53320         }
53321         var c = this.scroller.dom;
53322
53323         var ctop = parseInt(el.offsetTop, 10);
53324         var cleft = parseInt(el.offsetLeft, 10);
53325         var cbot = ctop + el.offsetHeight;
53326         var cright = cleft + el.offsetWidth;
53327         
53328         var ch = c.clientHeight - this.mainHd.dom.offsetHeight;
53329         var stop = parseInt(c.scrollTop, 10);
53330         var sleft = parseInt(c.scrollLeft, 10);
53331         var sbot = stop + ch;
53332         var sright = sleft + c.clientWidth;
53333         /*
53334         Roo.log('GridView.ensureVisible:' +
53335                 ' ctop:' + ctop +
53336                 ' c.clientHeight:' + c.clientHeight +
53337                 ' this.mainHd.dom.offsetHeight:' + this.mainHd.dom.offsetHeight +
53338                 ' stop:' + stop +
53339                 ' cbot:' + cbot +
53340                 ' sbot:' + sbot +
53341                 ' ch:' + ch  
53342                 );
53343         */
53344         if(ctop < stop){
53345              c.scrollTop = ctop;
53346             //Roo.log("set scrolltop to ctop DISABLE?");
53347         }else if(cbot > sbot){
53348             //Roo.log("set scrolltop to cbot-ch");
53349             c.scrollTop = cbot-ch;
53350         }
53351         
53352         if(hscroll !== false){
53353             if(cleft < sleft){
53354                 c.scrollLeft = cleft;
53355             }else if(cright > sright){
53356                 c.scrollLeft = cright-c.clientWidth;
53357             }
53358         }
53359          
53360         return el;
53361     },
53362
53363     updateColumns : function(){
53364         this.grid.stopEditing();
53365         var cm = this.grid.colModel, colIds = this.getColumnIds();
53366         //var totalWidth = cm.getTotalWidth();
53367         var pos = 0;
53368         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
53369             //if(cm.isHidden(i)) continue;
53370             var w = cm.getColumnWidth(i);
53371             this.css.updateRule(this.colSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
53372             this.css.updateRule(this.hdSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
53373         }
53374         this.updateSplitters();
53375     },
53376
53377     generateRules : function(cm){
53378         var ruleBuf = [], rulesId = this.idToCssName(this.grid.id)+ '-cssrules';
53379         Roo.util.CSS.removeStyleSheet(rulesId);
53380         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
53381             var cid = cm.getColumnId(i);
53382             var align = '';
53383             if(cm.config[i].align){
53384                 align = 'text-align:'+cm.config[i].align+';';
53385             }
53386             var hidden = '';
53387             if(cm.isHidden(i)){
53388                 hidden = 'display:none;';
53389             }
53390             var width = "width:" + (cm.getColumnWidth(i) - this.borderWidth) + "px;";
53391             ruleBuf.push(
53392                     this.colSelector, cid, " {\n", cm.config[i].css, align, width, "\n}\n",
53393                     this.hdSelector, cid, " {\n", align, width, "}\n",
53394                     this.tdSelector, cid, " {\n",hidden,"\n}\n",
53395                     this.splitSelector, cid, " {\n", hidden , "\n}\n");
53396         }
53397         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
53398     },
53399
53400     updateSplitters : function(){
53401         var cm = this.cm, s = this.getSplitters();
53402         if(s){ // splitters not created yet
53403             var pos = 0, locked = true;
53404             for(var i = 0, len = cm.getColumnCount(); i < len; i++){
53405                 if(cm.isHidden(i)) continue;
53406                 var w = cm.getColumnWidth(i); // make sure it's a number
53407                 if(!cm.isLocked(i) && locked){
53408                     pos = 0;
53409                     locked = false;
53410                 }
53411                 pos += w;
53412                 s[i].style.left = (pos-this.splitOffset) + "px";
53413             }
53414         }
53415     },
53416
53417     handleHiddenChange : function(colModel, colIndex, hidden){
53418         if(hidden){
53419             this.hideColumn(colIndex);
53420         }else{
53421             this.unhideColumn(colIndex);
53422         }
53423     },
53424
53425     hideColumn : function(colIndex){
53426         var cid = this.getColumnId(colIndex);
53427         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "none");
53428         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "none");
53429         if(Roo.isSafari){
53430             this.updateHeaders();
53431         }
53432         this.updateSplitters();
53433         this.layout();
53434     },
53435
53436     unhideColumn : function(colIndex){
53437         var cid = this.getColumnId(colIndex);
53438         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "");
53439         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "");
53440
53441         if(Roo.isSafari){
53442             this.updateHeaders();
53443         }
53444         this.updateSplitters();
53445         this.layout();
53446     },
53447
53448     insertRows : function(dm, firstRow, lastRow, isUpdate){
53449         if(firstRow == 0 && lastRow == dm.getCount()-1){
53450             this.refresh();
53451         }else{
53452             if(!isUpdate){
53453                 this.fireEvent("beforerowsinserted", this, firstRow, lastRow);
53454             }
53455             var s = this.getScrollState();
53456             var markup = this.renderRows(firstRow, lastRow);
53457             this.bufferRows(markup[0], this.getLockedTable(), firstRow);
53458             this.bufferRows(markup[1], this.getBodyTable(), firstRow);
53459             this.restoreScroll(s);
53460             if(!isUpdate){
53461                 this.fireEvent("rowsinserted", this, firstRow, lastRow);
53462                 this.syncRowHeights(firstRow, lastRow);
53463                 this.stripeRows(firstRow);
53464                 this.layout();
53465             }
53466         }
53467     },
53468
53469     bufferRows : function(markup, target, index){
53470         var before = null, trows = target.rows, tbody = target.tBodies[0];
53471         if(index < trows.length){
53472             before = trows[index];
53473         }
53474         var b = document.createElement("div");
53475         b.innerHTML = "<table><tbody>"+markup+"</tbody></table>";
53476         var rows = b.firstChild.rows;
53477         for(var i = 0, len = rows.length; i < len; i++){
53478             if(before){
53479                 tbody.insertBefore(rows[0], before);
53480             }else{
53481                 tbody.appendChild(rows[0]);
53482             }
53483         }
53484         b.innerHTML = "";
53485         b = null;
53486     },
53487
53488     deleteRows : function(dm, firstRow, lastRow){
53489         if(dm.getRowCount()<1){
53490             this.fireEvent("beforerefresh", this);
53491             this.mainBody.update("");
53492             this.lockedBody.update("");
53493             this.fireEvent("refresh", this);
53494         }else{
53495             this.fireEvent("beforerowsdeleted", this, firstRow, lastRow);
53496             var bt = this.getBodyTable();
53497             var tbody = bt.firstChild;
53498             var rows = bt.rows;
53499             for(var rowIndex = firstRow; rowIndex <= lastRow; rowIndex++){
53500                 tbody.removeChild(rows[firstRow]);
53501             }
53502             this.stripeRows(firstRow);
53503             this.fireEvent("rowsdeleted", this, firstRow, lastRow);
53504         }
53505     },
53506
53507     updateRows : function(dataSource, firstRow, lastRow){
53508         var s = this.getScrollState();
53509         this.refresh();
53510         this.restoreScroll(s);
53511     },
53512
53513     handleSort : function(dataSource, sortColumnIndex, sortDir, noRefresh){
53514         if(!noRefresh){
53515            this.refresh();
53516         }
53517         this.updateHeaderSortState();
53518     },
53519
53520     getScrollState : function(){
53521         
53522         var sb = this.scroller.dom;
53523         return {left: sb.scrollLeft, top: sb.scrollTop};
53524     },
53525
53526     stripeRows : function(startRow){
53527         if(!this.grid.stripeRows || this.ds.getCount() < 1){
53528             return;
53529         }
53530         startRow = startRow || 0;
53531         var rows = this.getBodyTable().rows;
53532         var lrows = this.getLockedTable().rows;
53533         var cls = ' x-grid-row-alt ';
53534         for(var i = startRow, len = rows.length; i < len; i++){
53535             var row = rows[i], lrow = lrows[i];
53536             var isAlt = ((i+1) % 2 == 0);
53537             var hasAlt = (' '+row.className + ' ').indexOf(cls) != -1;
53538             if(isAlt == hasAlt){
53539                 continue;
53540             }
53541             if(isAlt){
53542                 row.className += " x-grid-row-alt";
53543             }else{
53544                 row.className = row.className.replace("x-grid-row-alt", "");
53545             }
53546             if(lrow){
53547                 lrow.className = row.className;
53548             }
53549         }
53550     },
53551
53552     restoreScroll : function(state){
53553         //Roo.log('GridView.restoreScroll');
53554         var sb = this.scroller.dom;
53555         sb.scrollLeft = state.left;
53556         sb.scrollTop = state.top;
53557         this.syncScroll();
53558     },
53559
53560     syncScroll : function(){
53561         //Roo.log('GridView.syncScroll');
53562         var sb = this.scroller.dom;
53563         var sh = this.mainHd.dom;
53564         var bs = this.mainBody.dom;
53565         var lv = this.lockedBody.dom;
53566         sh.scrollLeft = bs.scrollLeft = sb.scrollLeft;
53567         lv.scrollTop = bs.scrollTop = sb.scrollTop;
53568     },
53569
53570     handleScroll : function(e){
53571         this.syncScroll();
53572         var sb = this.scroller.dom;
53573         this.grid.fireEvent("bodyscroll", sb.scrollLeft, sb.scrollTop);
53574         e.stopEvent();
53575     },
53576
53577     handleWheel : function(e){
53578         var d = e.getWheelDelta();
53579         this.scroller.dom.scrollTop -= d*22;
53580         // set this here to prevent jumpy scrolling on large tables
53581         this.lockedBody.dom.scrollTop = this.mainBody.dom.scrollTop = this.scroller.dom.scrollTop;
53582         e.stopEvent();
53583     },
53584
53585     renderRows : function(startRow, endRow){
53586         // pull in all the crap needed to render rows
53587         var g = this.grid, cm = g.colModel, ds = g.dataSource, stripe = g.stripeRows;
53588         var colCount = cm.getColumnCount();
53589
53590         if(ds.getCount() < 1){
53591             return ["", ""];
53592         }
53593
53594         // build a map for all the columns
53595         var cs = [];
53596         for(var i = 0; i < colCount; i++){
53597             var name = cm.getDataIndex(i);
53598             cs[i] = {
53599                 name : typeof name == 'undefined' ? ds.fields.get(i).name : name,
53600                 renderer : cm.getRenderer(i),
53601                 id : cm.getColumnId(i),
53602                 locked : cm.isLocked(i)
53603             };
53604         }
53605
53606         startRow = startRow || 0;
53607         endRow = typeof endRow == "undefined"? ds.getCount()-1 : endRow;
53608
53609         // records to render
53610         var rs = ds.getRange(startRow, endRow);
53611
53612         return this.doRender(cs, rs, ds, startRow, colCount, stripe);
53613     },
53614
53615     // As much as I hate to duplicate code, this was branched because FireFox really hates
53616     // [].join("") on strings. The performance difference was substantial enough to
53617     // branch this function
53618     doRender : Roo.isGecko ?
53619             function(cs, rs, ds, startRow, colCount, stripe){
53620                 var ts = this.templates, ct = ts.cell, rt = ts.row;
53621                 // buffers
53622                 var buf = "", lbuf = "", cb, lcb, c, p = {}, rp = {}, r, rowIndex;
53623                 
53624                 var hasListener = this.grid.hasListener('rowclass');
53625                 var rowcfg = {};
53626                 for(var j = 0, len = rs.length; j < len; j++){
53627                     r = rs[j]; cb = ""; lcb = ""; rowIndex = (j+startRow);
53628                     for(var i = 0; i < colCount; i++){
53629                         c = cs[i];
53630                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
53631                         p.id = c.id;
53632                         p.css = p.attr = "";
53633                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
53634                         if(p.value == undefined || p.value === "") p.value = "&#160;";
53635                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
53636                             p.css += p.css ? ' x-grid-dirty-cell' : 'x-grid-dirty-cell';
53637                         }
53638                         var markup = ct.apply(p);
53639                         if(!c.locked){
53640                             cb+= markup;
53641                         }else{
53642                             lcb+= markup;
53643                         }
53644                     }
53645                     var alt = [];
53646                     if(stripe && ((rowIndex+1) % 2 == 0)){
53647                         alt.push("x-grid-row-alt")
53648                     }
53649                     if(r.dirty){
53650                         alt.push(  " x-grid-dirty-row");
53651                     }
53652                     rp.cells = lcb;
53653                     if(this.getRowClass){
53654                         alt.push(this.getRowClass(r, rowIndex));
53655                     }
53656                     if (hasListener) {
53657                         rowcfg = {
53658                              
53659                             record: r,
53660                             rowIndex : rowIndex,
53661                             rowClass : ''
53662                         }
53663                         this.grid.fireEvent('rowclass', this, rowcfg);
53664                         alt.push(rowcfg.rowClass);
53665                     }
53666                     rp.alt = alt.join(" ");
53667                     lbuf+= rt.apply(rp);
53668                     rp.cells = cb;
53669                     buf+=  rt.apply(rp);
53670                 }
53671                 return [lbuf, buf];
53672             } :
53673             function(cs, rs, ds, startRow, colCount, stripe){
53674                 var ts = this.templates, ct = ts.cell, rt = ts.row;
53675                 // buffers
53676                 var buf = [], lbuf = [], cb, lcb, c, p = {}, rp = {}, r, rowIndex;
53677                 var hasListener = this.grid.hasListener('rowclass');
53678  
53679                 var rowcfg = {};
53680                 for(var j = 0, len = rs.length; j < len; j++){
53681                     r = rs[j]; cb = []; lcb = []; rowIndex = (j+startRow);
53682                     for(var i = 0; i < colCount; i++){
53683                         c = cs[i];
53684                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
53685                         p.id = c.id;
53686                         p.css = p.attr = "";
53687                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
53688                         if(p.value == undefined || p.value === "") p.value = "&#160;";
53689                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
53690                             p.css += p.css ? ' x-grid-dirty-cell' : 'x-grid-dirty-cell';
53691                         }
53692                         
53693                         var markup = ct.apply(p);
53694                         if(!c.locked){
53695                             cb[cb.length] = markup;
53696                         }else{
53697                             lcb[lcb.length] = markup;
53698                         }
53699                     }
53700                     var alt = [];
53701                     if(stripe && ((rowIndex+1) % 2 == 0)){
53702                         alt.push( "x-grid-row-alt");
53703                     }
53704                     if(r.dirty){
53705                         alt.push(" x-grid-dirty-row");
53706                     }
53707                     rp.cells = lcb;
53708                     if(this.getRowClass){
53709                         alt.push( this.getRowClass(r, rowIndex));
53710                     }
53711                     if (hasListener) {
53712                         rowcfg = {
53713                              
53714                             record: r,
53715                             rowIndex : rowIndex,
53716                             rowClass : ''
53717                         }
53718                         this.grid.fireEvent('rowclass', this, rowcfg);
53719                         alt.push(rowcfg.rowClass);
53720                     }
53721                     rp.alt = alt.join(" ");
53722                     rp.cells = lcb.join("");
53723                     lbuf[lbuf.length] = rt.apply(rp);
53724                     rp.cells = cb.join("");
53725                     buf[buf.length] =  rt.apply(rp);
53726                 }
53727                 return [lbuf.join(""), buf.join("")];
53728             },
53729
53730     renderBody : function(){
53731         var markup = this.renderRows();
53732         var bt = this.templates.body;
53733         return [bt.apply({rows: markup[0]}), bt.apply({rows: markup[1]})];
53734     },
53735
53736     /**
53737      * Refreshes the grid
53738      * @param {Boolean} headersToo
53739      */
53740     refresh : function(headersToo){
53741         this.fireEvent("beforerefresh", this);
53742         this.grid.stopEditing();
53743         var result = this.renderBody();
53744         this.lockedBody.update(result[0]);
53745         this.mainBody.update(result[1]);
53746         if(headersToo === true){
53747             this.updateHeaders();
53748             this.updateColumns();
53749             this.updateSplitters();
53750             this.updateHeaderSortState();
53751         }
53752         this.syncRowHeights();
53753         this.layout();
53754         this.fireEvent("refresh", this);
53755     },
53756
53757     handleColumnMove : function(cm, oldIndex, newIndex){
53758         this.indexMap = null;
53759         var s = this.getScrollState();
53760         this.refresh(true);
53761         this.restoreScroll(s);
53762         this.afterMove(newIndex);
53763     },
53764
53765     afterMove : function(colIndex){
53766         if(this.enableMoveAnim && Roo.enableFx){
53767             this.fly(this.getHeaderCell(colIndex).firstChild).highlight(this.hlColor);
53768         }
53769         // if multisort - fix sortOrder, and reload..
53770         if (this.grid.dataSource.multiSort) {
53771             // the we can call sort again..
53772             var dm = this.grid.dataSource;
53773             var cm = this.grid.colModel;
53774             var so = [];
53775             for(var i = 0; i < cm.config.length; i++ ) {
53776                 
53777                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined')) {
53778                     continue; // dont' bother, it's not in sort list or being set.
53779                 }
53780                 
53781                 so.push(cm.config[i].dataIndex);
53782             };
53783             dm.sortOrder = so;
53784             dm.load(dm.lastOptions);
53785             
53786             
53787         }
53788         
53789     },
53790
53791     updateCell : function(dm, rowIndex, dataIndex){
53792         var colIndex = this.getColumnIndexByDataIndex(dataIndex);
53793         if(typeof colIndex == "undefined"){ // not present in grid
53794             return;
53795         }
53796         var cm = this.grid.colModel;
53797         var cell = this.getCell(rowIndex, colIndex);
53798         var cellText = this.getCellText(rowIndex, colIndex);
53799
53800         var p = {
53801             cellId : "x-grid-cell-" + rowIndex + "-" + colIndex,
53802             id : cm.getColumnId(colIndex),
53803             css: colIndex == cm.getColumnCount()-1 ? "x-grid-col-last" : ""
53804         };
53805         var renderer = cm.getRenderer(colIndex);
53806         var val = renderer(dm.getValueAt(rowIndex, dataIndex), p, rowIndex, colIndex, dm);
53807         if(typeof val == "undefined" || val === "") val = "&#160;";
53808         cellText.innerHTML = val;
53809         cell.className = this.cellClass + " " + this.idToCssName(p.cellId) + " " + p.css;
53810         this.syncRowHeights(rowIndex, rowIndex);
53811     },
53812
53813     calcColumnWidth : function(colIndex, maxRowsToMeasure){
53814         var maxWidth = 0;
53815         if(this.grid.autoSizeHeaders){
53816             var h = this.getHeaderCellMeasure(colIndex);
53817             maxWidth = Math.max(maxWidth, h.scrollWidth);
53818         }
53819         var tb, index;
53820         if(this.cm.isLocked(colIndex)){
53821             tb = this.getLockedTable();
53822             index = colIndex;
53823         }else{
53824             tb = this.getBodyTable();
53825             index = colIndex - this.cm.getLockedCount();
53826         }
53827         if(tb && tb.rows){
53828             var rows = tb.rows;
53829             var stopIndex = Math.min(maxRowsToMeasure || rows.length, rows.length);
53830             for(var i = 0; i < stopIndex; i++){
53831                 var cell = rows[i].childNodes[index].firstChild;
53832                 maxWidth = Math.max(maxWidth, cell.scrollWidth);
53833             }
53834         }
53835         return maxWidth + /*margin for error in IE*/ 5;
53836     },
53837     /**
53838      * Autofit a column to its content.
53839      * @param {Number} colIndex
53840      * @param {Boolean} forceMinSize true to force the column to go smaller if possible
53841      */
53842      autoSizeColumn : function(colIndex, forceMinSize, suppressEvent){
53843          if(this.cm.isHidden(colIndex)){
53844              return; // can't calc a hidden column
53845          }
53846         if(forceMinSize){
53847             var cid = this.cm.getColumnId(colIndex);
53848             this.css.updateRule(this.colSelector +this.idToCssName( cid), "width", this.grid.minColumnWidth + "px");
53849            if(this.grid.autoSizeHeaders){
53850                this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", this.grid.minColumnWidth + "px");
53851            }
53852         }
53853         var newWidth = this.calcColumnWidth(colIndex);
53854         this.cm.setColumnWidth(colIndex,
53855             Math.max(this.grid.minColumnWidth, newWidth), suppressEvent);
53856         if(!suppressEvent){
53857             this.grid.fireEvent("columnresize", colIndex, newWidth);
53858         }
53859     },
53860
53861     /**
53862      * Autofits all columns to their content and then expands to fit any extra space in the grid
53863      */
53864      autoSizeColumns : function(){
53865         var cm = this.grid.colModel;
53866         var colCount = cm.getColumnCount();
53867         for(var i = 0; i < colCount; i++){
53868             this.autoSizeColumn(i, true, true);
53869         }
53870         if(cm.getTotalWidth() < this.scroller.dom.clientWidth){
53871             this.fitColumns();
53872         }else{
53873             this.updateColumns();
53874             this.layout();
53875         }
53876     },
53877
53878     /**
53879      * Autofits all columns to the grid's width proportionate with their current size
53880      * @param {Boolean} reserveScrollSpace Reserve space for a scrollbar
53881      */
53882     fitColumns : function(reserveScrollSpace){
53883         var cm = this.grid.colModel;
53884         var colCount = cm.getColumnCount();
53885         var cols = [];
53886         var width = 0;
53887         var i, w;
53888         for (i = 0; i < colCount; i++){
53889             if(!cm.isHidden(i) && !cm.isFixed(i)){
53890                 w = cm.getColumnWidth(i);
53891                 cols.push(i);
53892                 cols.push(w);
53893                 width += w;
53894             }
53895         }
53896         var avail = Math.min(this.scroller.dom.clientWidth, this.el.getWidth());
53897         if(reserveScrollSpace){
53898             avail -= 17;
53899         }
53900         var frac = (avail - cm.getTotalWidth())/width;
53901         while (cols.length){
53902             w = cols.pop();
53903             i = cols.pop();
53904             cm.setColumnWidth(i, Math.floor(w + w*frac), true);
53905         }
53906         this.updateColumns();
53907         this.layout();
53908     },
53909
53910     onRowSelect : function(rowIndex){
53911         var row = this.getRowComposite(rowIndex);
53912         row.addClass("x-grid-row-selected");
53913     },
53914
53915     onRowDeselect : function(rowIndex){
53916         var row = this.getRowComposite(rowIndex);
53917         row.removeClass("x-grid-row-selected");
53918     },
53919
53920     onCellSelect : function(row, col){
53921         var cell = this.getCell(row, col);
53922         if(cell){
53923             Roo.fly(cell).addClass("x-grid-cell-selected");
53924         }
53925     },
53926
53927     onCellDeselect : function(row, col){
53928         var cell = this.getCell(row, col);
53929         if(cell){
53930             Roo.fly(cell).removeClass("x-grid-cell-selected");
53931         }
53932     },
53933
53934     updateHeaderSortState : function(){
53935         
53936         // sort state can be single { field: xxx, direction : yyy}
53937         // or   { xxx=>ASC , yyy : DESC ..... }
53938         
53939         var mstate = {};
53940         if (!this.ds.multiSort) { 
53941             var state = this.ds.getSortState();
53942             if(!state){
53943                 return;
53944             }
53945             mstate[state.field] = state.direction;
53946             // FIXME... - this is not used here.. but might be elsewhere..
53947             this.sortState = state;
53948             
53949         } else {
53950             mstate = this.ds.sortToggle;
53951         }
53952         //remove existing sort classes..
53953         
53954         var sc = this.sortClasses;
53955         var hds = this.el.select(this.headerSelector).removeClass(sc);
53956         
53957         for(var f in mstate) {
53958         
53959             var sortColumn = this.cm.findColumnIndex(f);
53960             
53961             if(sortColumn != -1){
53962                 var sortDir = mstate[f];        
53963                 hds.item(sortColumn).addClass(sc[sortDir == "DESC" ? 1 : 0]);
53964             }
53965         }
53966         
53967          
53968         
53969     },
53970
53971
53972     handleHeaderClick : function(g, index,e){
53973         
53974         Roo.log("header click");
53975         
53976         if (Roo.isTouch) {
53977             // touch events on header are handled by context
53978             this.handleHdCtx(g,index,e);
53979             return;
53980         }
53981         
53982         
53983         if(this.headersDisabled){
53984             return;
53985         }
53986         var dm = g.dataSource, cm = g.colModel;
53987         if(!cm.isSortable(index)){
53988             return;
53989         }
53990         g.stopEditing();
53991         
53992         if (dm.multiSort) {
53993             // update the sortOrder
53994             var so = [];
53995             for(var i = 0; i < cm.config.length; i++ ) {
53996                 
53997                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined') && (index != i)) {
53998                     continue; // dont' bother, it's not in sort list or being set.
53999                 }
54000                 
54001                 so.push(cm.config[i].dataIndex);
54002             };
54003             dm.sortOrder = so;
54004         }
54005         
54006         
54007         dm.sort(cm.getDataIndex(index));
54008     },
54009
54010
54011     destroy : function(){
54012         if(this.colMenu){
54013             this.colMenu.removeAll();
54014             Roo.menu.MenuMgr.unregister(this.colMenu);
54015             this.colMenu.getEl().remove();
54016             delete this.colMenu;
54017         }
54018         if(this.hmenu){
54019             this.hmenu.removeAll();
54020             Roo.menu.MenuMgr.unregister(this.hmenu);
54021             this.hmenu.getEl().remove();
54022             delete this.hmenu;
54023         }
54024         if(this.grid.enableColumnMove){
54025             var dds = Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
54026             if(dds){
54027                 for(var dd in dds){
54028                     if(!dds[dd].config.isTarget && dds[dd].dragElId){
54029                         var elid = dds[dd].dragElId;
54030                         dds[dd].unreg();
54031                         Roo.get(elid).remove();
54032                     } else if(dds[dd].config.isTarget){
54033                         dds[dd].proxyTop.remove();
54034                         dds[dd].proxyBottom.remove();
54035                         dds[dd].unreg();
54036                     }
54037                     if(Roo.dd.DDM.locationCache[dd]){
54038                         delete Roo.dd.DDM.locationCache[dd];
54039                     }
54040                 }
54041                 delete Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
54042             }
54043         }
54044         Roo.util.CSS.removeStyleSheet(this.idToCssName(this.grid.id) + '-cssrules');
54045         this.bind(null, null);
54046         Roo.EventManager.removeResizeListener(this.onWindowResize, this);
54047     },
54048
54049     handleLockChange : function(){
54050         this.refresh(true);
54051     },
54052
54053     onDenyColumnLock : function(){
54054
54055     },
54056
54057     onDenyColumnHide : function(){
54058
54059     },
54060
54061     handleHdMenuClick : function(item){
54062         var index = this.hdCtxIndex;
54063         var cm = this.cm, ds = this.ds;
54064         switch(item.id){
54065             case "asc":
54066                 ds.sort(cm.getDataIndex(index), "ASC");
54067                 break;
54068             case "desc":
54069                 ds.sort(cm.getDataIndex(index), "DESC");
54070                 break;
54071             case "lock":
54072                 var lc = cm.getLockedCount();
54073                 if(cm.getColumnCount(true) <= lc+1){
54074                     this.onDenyColumnLock();
54075                     return;
54076                 }
54077                 if(lc != index){
54078                     cm.setLocked(index, true, true);
54079                     cm.moveColumn(index, lc);
54080                     this.grid.fireEvent("columnmove", index, lc);
54081                 }else{
54082                     cm.setLocked(index, true);
54083                 }
54084             break;
54085             case "unlock":
54086                 var lc = cm.getLockedCount();
54087                 if((lc-1) != index){
54088                     cm.setLocked(index, false, true);
54089                     cm.moveColumn(index, lc-1);
54090                     this.grid.fireEvent("columnmove", index, lc-1);
54091                 }else{
54092                     cm.setLocked(index, false);
54093                 }
54094             break;
54095             case 'wider': // used to expand cols on touch..
54096             case 'narrow':
54097                 var cw = cm.getColumnWidth(index);
54098                 cw += (item.id == 'wider' ? 1 : -1) * 50;
54099                 cw = Math.max(0, cw);
54100                 cw = Math.min(cw,4000);
54101                 cm.setColumnWidth(index, cw);
54102                 break;
54103                 
54104             default:
54105                 index = cm.getIndexById(item.id.substr(4));
54106                 if(index != -1){
54107                     if(item.checked && cm.getColumnCount(true) <= 1){
54108                         this.onDenyColumnHide();
54109                         return false;
54110                     }
54111                     cm.setHidden(index, item.checked);
54112                 }
54113         }
54114         return true;
54115     },
54116
54117     beforeColMenuShow : function(){
54118         var cm = this.cm,  colCount = cm.getColumnCount();
54119         this.colMenu.removeAll();
54120         for(var i = 0; i < colCount; i++){
54121             this.colMenu.add(new Roo.menu.CheckItem({
54122                 id: "col-"+cm.getColumnId(i),
54123                 text: cm.getColumnHeader(i),
54124                 checked: !cm.isHidden(i),
54125                 hideOnClick:false
54126             }));
54127         }
54128     },
54129
54130     handleHdCtx : function(g, index, e){
54131         e.stopEvent();
54132         var hd = this.getHeaderCell(index);
54133         this.hdCtxIndex = index;
54134         var ms = this.hmenu.items, cm = this.cm;
54135         ms.get("asc").setDisabled(!cm.isSortable(index));
54136         ms.get("desc").setDisabled(!cm.isSortable(index));
54137         if(this.grid.enableColLock !== false){
54138             ms.get("lock").setDisabled(cm.isLocked(index));
54139             ms.get("unlock").setDisabled(!cm.isLocked(index));
54140         }
54141         this.hmenu.show(hd, "tl-bl");
54142     },
54143
54144     handleHdOver : function(e){
54145         var hd = this.findHeaderCell(e.getTarget());
54146         if(hd && !this.headersDisabled){
54147             if(this.grid.colModel.isSortable(this.getCellIndex(hd))){
54148                this.fly(hd).addClass("x-grid-hd-over");
54149             }
54150         }
54151     },
54152
54153     handleHdOut : function(e){
54154         var hd = this.findHeaderCell(e.getTarget());
54155         if(hd){
54156             this.fly(hd).removeClass("x-grid-hd-over");
54157         }
54158     },
54159
54160     handleSplitDblClick : function(e, t){
54161         var i = this.getCellIndex(t);
54162         if(this.grid.enableColumnResize !== false && this.cm.isResizable(i) && !this.cm.isFixed(i)){
54163             this.autoSizeColumn(i, true);
54164             this.layout();
54165         }
54166     },
54167
54168     render : function(){
54169
54170         var cm = this.cm;
54171         var colCount = cm.getColumnCount();
54172
54173         if(this.grid.monitorWindowResize === true){
54174             Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
54175         }
54176         var header = this.renderHeaders();
54177         var body = this.templates.body.apply({rows:""});
54178         var html = this.templates.master.apply({
54179             lockedBody: body,
54180             body: body,
54181             lockedHeader: header[0],
54182             header: header[1]
54183         });
54184
54185         //this.updateColumns();
54186
54187         this.grid.getGridEl().dom.innerHTML = html;
54188
54189         this.initElements();
54190         
54191         // a kludge to fix the random scolling effect in webkit
54192         this.el.on("scroll", function() {
54193             this.el.dom.scrollTop=0; // hopefully not recursive..
54194         },this);
54195
54196         this.scroller.on("scroll", this.handleScroll, this);
54197         this.lockedBody.on("mousewheel", this.handleWheel, this);
54198         this.mainBody.on("mousewheel", this.handleWheel, this);
54199
54200         this.mainHd.on("mouseover", this.handleHdOver, this);
54201         this.mainHd.on("mouseout", this.handleHdOut, this);
54202         this.mainHd.on("dblclick", this.handleSplitDblClick, this,
54203                 {delegate: "."+this.splitClass});
54204
54205         this.lockedHd.on("mouseover", this.handleHdOver, this);
54206         this.lockedHd.on("mouseout", this.handleHdOut, this);
54207         this.lockedHd.on("dblclick", this.handleSplitDblClick, this,
54208                 {delegate: "."+this.splitClass});
54209
54210         if(this.grid.enableColumnResize !== false && Roo.grid.SplitDragZone){
54211             new Roo.grid.SplitDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
54212         }
54213
54214         this.updateSplitters();
54215
54216         if(this.grid.enableColumnMove && Roo.grid.HeaderDragZone){
54217             new Roo.grid.HeaderDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
54218             new Roo.grid.HeaderDropZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
54219         }
54220
54221         if(this.grid.enableCtxMenu !== false && Roo.menu.Menu){
54222             this.hmenu = new Roo.menu.Menu({id: this.grid.id + "-hctx"});
54223             this.hmenu.add(
54224                 {id:"asc", text: this.sortAscText, cls: "xg-hmenu-sort-asc"},
54225                 {id:"desc", text: this.sortDescText, cls: "xg-hmenu-sort-desc"}
54226             );
54227             if(this.grid.enableColLock !== false){
54228                 this.hmenu.add('-',
54229                     {id:"lock", text: this.lockText, cls: "xg-hmenu-lock"},
54230                     {id:"unlock", text: this.unlockText, cls: "xg-hmenu-unlock"}
54231                 );
54232             }
54233             if (Roo.isTouch) {
54234                  this.hmenu.add('-',
54235                     {id:"wider", text: this.columnsWiderText},
54236                     {id:"narrow", text: this.columnsNarrowText }
54237                 );
54238                 
54239                  
54240             }
54241             
54242             if(this.grid.enableColumnHide !== false){
54243
54244                 this.colMenu = new Roo.menu.Menu({id:this.grid.id + "-hcols-menu"});
54245                 this.colMenu.on("beforeshow", this.beforeColMenuShow, this);
54246                 this.colMenu.on("itemclick", this.handleHdMenuClick, this);
54247
54248                 this.hmenu.add('-',
54249                     {id:"columns", text: this.columnsText, menu: this.colMenu}
54250                 );
54251             }
54252             this.hmenu.on("itemclick", this.handleHdMenuClick, this);
54253
54254             this.grid.on("headercontextmenu", this.handleHdCtx, this);
54255         }
54256
54257         if((this.grid.enableDragDrop || this.grid.enableDrag) && Roo.grid.GridDragZone){
54258             this.dd = new Roo.grid.GridDragZone(this.grid, {
54259                 ddGroup : this.grid.ddGroup || 'GridDD'
54260             });
54261             
54262         }
54263
54264         /*
54265         for(var i = 0; i < colCount; i++){
54266             if(cm.isHidden(i)){
54267                 this.hideColumn(i);
54268             }
54269             if(cm.config[i].align){
54270                 this.css.updateRule(this.colSelector + i, "textAlign", cm.config[i].align);
54271                 this.css.updateRule(this.hdSelector + i, "textAlign", cm.config[i].align);
54272             }
54273         }*/
54274         
54275         this.updateHeaderSortState();
54276
54277         this.beforeInitialResize();
54278         this.layout(true);
54279
54280         // two part rendering gives faster view to the user
54281         this.renderPhase2.defer(1, this);
54282     },
54283
54284     renderPhase2 : function(){
54285         // render the rows now
54286         this.refresh();
54287         if(this.grid.autoSizeColumns){
54288             this.autoSizeColumns();
54289         }
54290     },
54291
54292     beforeInitialResize : function(){
54293
54294     },
54295
54296     onColumnSplitterMoved : function(i, w){
54297         this.userResized = true;
54298         var cm = this.grid.colModel;
54299         cm.setColumnWidth(i, w, true);
54300         var cid = cm.getColumnId(i);
54301         this.css.updateRule(this.colSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
54302         this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
54303         this.updateSplitters();
54304         this.layout();
54305         this.grid.fireEvent("columnresize", i, w);
54306     },
54307
54308     syncRowHeights : function(startIndex, endIndex){
54309         if(this.grid.enableRowHeightSync === true && this.cm.getLockedCount() > 0){
54310             startIndex = startIndex || 0;
54311             var mrows = this.getBodyTable().rows;
54312             var lrows = this.getLockedTable().rows;
54313             var len = mrows.length-1;
54314             endIndex = Math.min(endIndex || len, len);
54315             for(var i = startIndex; i <= endIndex; i++){
54316                 var m = mrows[i], l = lrows[i];
54317                 var h = Math.max(m.offsetHeight, l.offsetHeight);
54318                 m.style.height = l.style.height = h + "px";
54319             }
54320         }
54321     },
54322
54323     layout : function(initialRender, is2ndPass){
54324         var g = this.grid;
54325         var auto = g.autoHeight;
54326         var scrollOffset = 16;
54327         var c = g.getGridEl(), cm = this.cm,
54328                 expandCol = g.autoExpandColumn,
54329                 gv = this;
54330         //c.beginMeasure();
54331
54332         if(!c.dom.offsetWidth){ // display:none?
54333             if(initialRender){
54334                 this.lockedWrap.show();
54335                 this.mainWrap.show();
54336             }
54337             return;
54338         }
54339
54340         var hasLock = this.cm.isLocked(0);
54341
54342         var tbh = this.headerPanel.getHeight();
54343         var bbh = this.footerPanel.getHeight();
54344
54345         if(auto){
54346             var ch = this.getBodyTable().offsetHeight + tbh + bbh + this.mainHd.getHeight();
54347             var newHeight = ch + c.getBorderWidth("tb");
54348             if(g.maxHeight){
54349                 newHeight = Math.min(g.maxHeight, newHeight);
54350             }
54351             c.setHeight(newHeight);
54352         }
54353
54354         if(g.autoWidth){
54355             c.setWidth(cm.getTotalWidth()+c.getBorderWidth('lr'));
54356         }
54357
54358         var s = this.scroller;
54359
54360         var csize = c.getSize(true);
54361
54362         this.el.setSize(csize.width, csize.height);
54363
54364         this.headerPanel.setWidth(csize.width);
54365         this.footerPanel.setWidth(csize.width);
54366
54367         var hdHeight = this.mainHd.getHeight();
54368         var vw = csize.width;
54369         var vh = csize.height - (tbh + bbh);
54370
54371         s.setSize(vw, vh);
54372
54373         var bt = this.getBodyTable();
54374         var ltWidth = hasLock ?
54375                       Math.max(this.getLockedTable().offsetWidth, this.lockedHd.dom.firstChild.offsetWidth) : 0;
54376
54377         var scrollHeight = bt.offsetHeight;
54378         var scrollWidth = ltWidth + bt.offsetWidth;
54379         var vscroll = false, hscroll = false;
54380
54381         this.scrollSizer.setSize(scrollWidth, scrollHeight+hdHeight);
54382
54383         var lw = this.lockedWrap, mw = this.mainWrap;
54384         var lb = this.lockedBody, mb = this.mainBody;
54385
54386         setTimeout(function(){
54387             var t = s.dom.offsetTop;
54388             var w = s.dom.clientWidth,
54389                 h = s.dom.clientHeight;
54390
54391             lw.setTop(t);
54392             lw.setSize(ltWidth, h);
54393
54394             mw.setLeftTop(ltWidth, t);
54395             mw.setSize(w-ltWidth, h);
54396
54397             lb.setHeight(h-hdHeight);
54398             mb.setHeight(h-hdHeight);
54399
54400             if(is2ndPass !== true && !gv.userResized && expandCol){
54401                 // high speed resize without full column calculation
54402                 
54403                 var ci = cm.getIndexById(expandCol);
54404                 if (ci < 0) {
54405                     ci = cm.findColumnIndex(expandCol);
54406                 }
54407                 ci = Math.max(0, ci); // make sure it's got at least the first col.
54408                 var expandId = cm.getColumnId(ci);
54409                 var  tw = cm.getTotalWidth(false);
54410                 var currentWidth = cm.getColumnWidth(ci);
54411                 var cw = Math.min(Math.max(((w-tw)+currentWidth-2)-/*scrollbar*/(w <= s.dom.offsetWidth ? 0 : 18), g.autoExpandMin), g.autoExpandMax);
54412                 if(currentWidth != cw){
54413                     cm.setColumnWidth(ci, cw, true);
54414                     gv.css.updateRule(gv.colSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
54415                     gv.css.updateRule(gv.hdSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
54416                     gv.updateSplitters();
54417                     gv.layout(false, true);
54418                 }
54419             }
54420
54421             if(initialRender){
54422                 lw.show();
54423                 mw.show();
54424             }
54425             //c.endMeasure();
54426         }, 10);
54427     },
54428
54429     onWindowResize : function(){
54430         if(!this.grid.monitorWindowResize || this.grid.autoHeight){
54431             return;
54432         }
54433         this.layout();
54434     },
54435
54436     appendFooter : function(parentEl){
54437         return null;
54438     },
54439
54440     sortAscText : "Sort Ascending",
54441     sortDescText : "Sort Descending",
54442     lockText : "Lock Column",
54443     unlockText : "Unlock Column",
54444     columnsText : "Columns",
54445  
54446     columnsWiderText : "Wider",
54447     columnsNarrowText : "Thinner"
54448 });
54449
54450
54451 Roo.grid.GridView.ColumnDragZone = function(grid, hd){
54452     Roo.grid.GridView.ColumnDragZone.superclass.constructor.call(this, grid, hd, null);
54453     this.proxy.el.addClass('x-grid3-col-dd');
54454 };
54455
54456 Roo.extend(Roo.grid.GridView.ColumnDragZone, Roo.grid.HeaderDragZone, {
54457     handleMouseDown : function(e){
54458
54459     },
54460
54461     callHandleMouseDown : function(e){
54462         Roo.grid.GridView.ColumnDragZone.superclass.handleMouseDown.call(this, e);
54463     }
54464 });
54465 /*
54466  * Based on:
54467  * Ext JS Library 1.1.1
54468  * Copyright(c) 2006-2007, Ext JS, LLC.
54469  *
54470  * Originally Released Under LGPL - original licence link has changed is not relivant.
54471  *
54472  * Fork - LGPL
54473  * <script type="text/javascript">
54474  */
54475  
54476 // private
54477 // This is a support class used internally by the Grid components
54478 Roo.grid.SplitDragZone = function(grid, hd, hd2){
54479     this.grid = grid;
54480     this.view = grid.getView();
54481     this.proxy = this.view.resizeProxy;
54482     Roo.grid.SplitDragZone.superclass.constructor.call(this, hd,
54483         "gridSplitters" + this.grid.getGridEl().id, {
54484         dragElId : Roo.id(this.proxy.dom), resizeFrame:false
54485     });
54486     this.setHandleElId(Roo.id(hd));
54487     this.setOuterHandleElId(Roo.id(hd2));
54488     this.scroll = false;
54489 };
54490 Roo.extend(Roo.grid.SplitDragZone, Roo.dd.DDProxy, {
54491     fly: Roo.Element.fly,
54492
54493     b4StartDrag : function(x, y){
54494         this.view.headersDisabled = true;
54495         this.proxy.setHeight(this.view.mainWrap.getHeight());
54496         var w = this.cm.getColumnWidth(this.cellIndex);
54497         var minw = Math.max(w-this.grid.minColumnWidth, 0);
54498         this.resetConstraints();
54499         this.setXConstraint(minw, 1000);
54500         this.setYConstraint(0, 0);
54501         this.minX = x - minw;
54502         this.maxX = x + 1000;
54503         this.startPos = x;
54504         Roo.dd.DDProxy.prototype.b4StartDrag.call(this, x, y);
54505     },
54506
54507
54508     handleMouseDown : function(e){
54509         ev = Roo.EventObject.setEvent(e);
54510         var t = this.fly(ev.getTarget());
54511         if(t.hasClass("x-grid-split")){
54512             this.cellIndex = this.view.getCellIndex(t.dom);
54513             this.split = t.dom;
54514             this.cm = this.grid.colModel;
54515             if(this.cm.isResizable(this.cellIndex) && !this.cm.isFixed(this.cellIndex)){
54516                 Roo.grid.SplitDragZone.superclass.handleMouseDown.apply(this, arguments);
54517             }
54518         }
54519     },
54520
54521     endDrag : function(e){
54522         this.view.headersDisabled = false;
54523         var endX = Math.max(this.minX, Roo.lib.Event.getPageX(e));
54524         var diff = endX - this.startPos;
54525         this.view.onColumnSplitterMoved(this.cellIndex, this.cm.getColumnWidth(this.cellIndex)+diff);
54526     },
54527
54528     autoOffset : function(){
54529         this.setDelta(0,0);
54530     }
54531 });/*
54532  * Based on:
54533  * Ext JS Library 1.1.1
54534  * Copyright(c) 2006-2007, Ext JS, LLC.
54535  *
54536  * Originally Released Under LGPL - original licence link has changed is not relivant.
54537  *
54538  * Fork - LGPL
54539  * <script type="text/javascript">
54540  */
54541  
54542 // private
54543 // This is a support class used internally by the Grid components
54544 Roo.grid.GridDragZone = function(grid, config){
54545     this.view = grid.getView();
54546     Roo.grid.GridDragZone.superclass.constructor.call(this, this.view.mainBody.dom, config);
54547     if(this.view.lockedBody){
54548         this.setHandleElId(Roo.id(this.view.mainBody.dom));
54549         this.setOuterHandleElId(Roo.id(this.view.lockedBody.dom));
54550     }
54551     this.scroll = false;
54552     this.grid = grid;
54553     this.ddel = document.createElement('div');
54554     this.ddel.className = 'x-grid-dd-wrap';
54555 };
54556
54557 Roo.extend(Roo.grid.GridDragZone, Roo.dd.DragZone, {
54558     ddGroup : "GridDD",
54559
54560     getDragData : function(e){
54561         var t = Roo.lib.Event.getTarget(e);
54562         var rowIndex = this.view.findRowIndex(t);
54563         var sm = this.grid.selModel;
54564             
54565         //Roo.log(rowIndex);
54566         
54567         if (sm.getSelectedCell) {
54568             // cell selection..
54569             if (!sm.getSelectedCell()) {
54570                 return false;
54571             }
54572             if (rowIndex != sm.getSelectedCell()[0]) {
54573                 return false;
54574             }
54575         
54576         }
54577         
54578         if(rowIndex !== false){
54579             
54580             // if editorgrid.. 
54581             
54582             
54583             //Roo.log([ sm.getSelectedCell() ? sm.getSelectedCell()[0] : 'NO' , rowIndex ]);
54584                
54585             //if(!sm.isSelected(rowIndex) || e.hasModifier()){
54586               //  
54587             //}
54588             if (e.hasModifier()){
54589                 sm.handleMouseDown(e, t); // non modifier buttons are handled by row select.
54590             }
54591             
54592             Roo.log("getDragData");
54593             
54594             return {
54595                 grid: this.grid,
54596                 ddel: this.ddel,
54597                 rowIndex: rowIndex,
54598                 selections:sm.getSelections ? sm.getSelections() : (
54599                     sm.getSelectedCell() ? [ this.grid.ds.getAt(sm.getSelectedCell()[0]) ] : []
54600                 )
54601             };
54602         }
54603         return false;
54604     },
54605
54606     onInitDrag : function(e){
54607         var data = this.dragData;
54608         this.ddel.innerHTML = this.grid.getDragDropText();
54609         this.proxy.update(this.ddel);
54610         // fire start drag?
54611     },
54612
54613     afterRepair : function(){
54614         this.dragging = false;
54615     },
54616
54617     getRepairXY : function(e, data){
54618         return false;
54619     },
54620
54621     onEndDrag : function(data, e){
54622         // fire end drag?
54623     },
54624
54625     onValidDrop : function(dd, e, id){
54626         // fire drag drop?
54627         this.hideProxy();
54628     },
54629
54630     beforeInvalidDrop : function(e, id){
54631
54632     }
54633 });/*
54634  * Based on:
54635  * Ext JS Library 1.1.1
54636  * Copyright(c) 2006-2007, Ext JS, LLC.
54637  *
54638  * Originally Released Under LGPL - original licence link has changed is not relivant.
54639  *
54640  * Fork - LGPL
54641  * <script type="text/javascript">
54642  */
54643  
54644
54645 /**
54646  * @class Roo.grid.ColumnModel
54647  * @extends Roo.util.Observable
54648  * This is the default implementation of a ColumnModel used by the Grid. It defines
54649  * the columns in the grid.
54650  * <br>Usage:<br>
54651  <pre><code>
54652  var colModel = new Roo.grid.ColumnModel([
54653         {header: "Ticker", width: 60, sortable: true, locked: true},
54654         {header: "Company Name", width: 150, sortable: true},
54655         {header: "Market Cap.", width: 100, sortable: true},
54656         {header: "$ Sales", width: 100, sortable: true, renderer: money},
54657         {header: "Employees", width: 100, sortable: true, resizable: false}
54658  ]);
54659  </code></pre>
54660  * <p>
54661  
54662  * The config options listed for this class are options which may appear in each
54663  * individual column definition.
54664  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
54665  * @constructor
54666  * @param {Object} config An Array of column config objects. See this class's
54667  * config objects for details.
54668 */
54669 Roo.grid.ColumnModel = function(config){
54670         /**
54671      * The config passed into the constructor
54672      */
54673     this.config = config;
54674     this.lookup = {};
54675
54676     // if no id, create one
54677     // if the column does not have a dataIndex mapping,
54678     // map it to the order it is in the config
54679     for(var i = 0, len = config.length; i < len; i++){
54680         var c = config[i];
54681         if(typeof c.dataIndex == "undefined"){
54682             c.dataIndex = i;
54683         }
54684         if(typeof c.renderer == "string"){
54685             c.renderer = Roo.util.Format[c.renderer];
54686         }
54687         if(typeof c.id == "undefined"){
54688             c.id = Roo.id();
54689         }
54690         if(c.editor && c.editor.xtype){
54691             c.editor  = Roo.factory(c.editor, Roo.grid);
54692         }
54693         if(c.editor && c.editor.isFormField){
54694             c.editor = new Roo.grid.GridEditor(c.editor);
54695         }
54696         this.lookup[c.id] = c;
54697     }
54698
54699     /**
54700      * The width of columns which have no width specified (defaults to 100)
54701      * @type Number
54702      */
54703     this.defaultWidth = 100;
54704
54705     /**
54706      * Default sortable of columns which have no sortable specified (defaults to false)
54707      * @type Boolean
54708      */
54709     this.defaultSortable = false;
54710
54711     this.addEvents({
54712         /**
54713              * @event widthchange
54714              * Fires when the width of a column changes.
54715              * @param {ColumnModel} this
54716              * @param {Number} columnIndex The column index
54717              * @param {Number} newWidth The new width
54718              */
54719             "widthchange": true,
54720         /**
54721              * @event headerchange
54722              * Fires when the text of a header changes.
54723              * @param {ColumnModel} this
54724              * @param {Number} columnIndex The column index
54725              * @param {Number} newText The new header text
54726              */
54727             "headerchange": true,
54728         /**
54729              * @event hiddenchange
54730              * Fires when a column is hidden or "unhidden".
54731              * @param {ColumnModel} this
54732              * @param {Number} columnIndex The column index
54733              * @param {Boolean} hidden true if hidden, false otherwise
54734              */
54735             "hiddenchange": true,
54736             /**
54737          * @event columnmoved
54738          * Fires when a column is moved.
54739          * @param {ColumnModel} this
54740          * @param {Number} oldIndex
54741          * @param {Number} newIndex
54742          */
54743         "columnmoved" : true,
54744         /**
54745          * @event columlockchange
54746          * Fires when a column's locked state is changed
54747          * @param {ColumnModel} this
54748          * @param {Number} colIndex
54749          * @param {Boolean} locked true if locked
54750          */
54751         "columnlockchange" : true
54752     });
54753     Roo.grid.ColumnModel.superclass.constructor.call(this);
54754 };
54755 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
54756     /**
54757      * @cfg {String} header The header text to display in the Grid view.
54758      */
54759     /**
54760      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
54761      * {@link Roo.data.Record} definition from which to draw the column's value. If not
54762      * specified, the column's index is used as an index into the Record's data Array.
54763      */
54764     /**
54765      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
54766      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
54767      */
54768     /**
54769      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
54770      * Defaults to the value of the {@link #defaultSortable} property.
54771      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
54772      */
54773     /**
54774      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
54775      */
54776     /**
54777      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
54778      */
54779     /**
54780      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
54781      */
54782     /**
54783      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
54784      */
54785     /**
54786      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
54787      * given the cell's data value. See {@link #setRenderer}. If not specified, the
54788      * default renderer uses the raw data value.
54789      */
54790        /**
54791      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
54792      */
54793     /**
54794      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
54795      */
54796
54797     /**
54798      * Returns the id of the column at the specified index.
54799      * @param {Number} index The column index
54800      * @return {String} the id
54801      */
54802     getColumnId : function(index){
54803         return this.config[index].id;
54804     },
54805
54806     /**
54807      * Returns the column for a specified id.
54808      * @param {String} id The column id
54809      * @return {Object} the column
54810      */
54811     getColumnById : function(id){
54812         return this.lookup[id];
54813     },
54814
54815     
54816     /**
54817      * Returns the column for a specified dataIndex.
54818      * @param {String} dataIndex The column dataIndex
54819      * @return {Object|Boolean} the column or false if not found
54820      */
54821     getColumnByDataIndex: function(dataIndex){
54822         var index = this.findColumnIndex(dataIndex);
54823         return index > -1 ? this.config[index] : false;
54824     },
54825     
54826     /**
54827      * Returns the index for a specified column id.
54828      * @param {String} id The column id
54829      * @return {Number} the index, or -1 if not found
54830      */
54831     getIndexById : function(id){
54832         for(var i = 0, len = this.config.length; i < len; i++){
54833             if(this.config[i].id == id){
54834                 return i;
54835             }
54836         }
54837         return -1;
54838     },
54839     
54840     /**
54841      * Returns the index for a specified column dataIndex.
54842      * @param {String} dataIndex The column dataIndex
54843      * @return {Number} the index, or -1 if not found
54844      */
54845     
54846     findColumnIndex : function(dataIndex){
54847         for(var i = 0, len = this.config.length; i < len; i++){
54848             if(this.config[i].dataIndex == dataIndex){
54849                 return i;
54850             }
54851         }
54852         return -1;
54853     },
54854     
54855     
54856     moveColumn : function(oldIndex, newIndex){
54857         var c = this.config[oldIndex];
54858         this.config.splice(oldIndex, 1);
54859         this.config.splice(newIndex, 0, c);
54860         this.dataMap = null;
54861         this.fireEvent("columnmoved", this, oldIndex, newIndex);
54862     },
54863
54864     isLocked : function(colIndex){
54865         return this.config[colIndex].locked === true;
54866     },
54867
54868     setLocked : function(colIndex, value, suppressEvent){
54869         if(this.isLocked(colIndex) == value){
54870             return;
54871         }
54872         this.config[colIndex].locked = value;
54873         if(!suppressEvent){
54874             this.fireEvent("columnlockchange", this, colIndex, value);
54875         }
54876     },
54877
54878     getTotalLockedWidth : function(){
54879         var totalWidth = 0;
54880         for(var i = 0; i < this.config.length; i++){
54881             if(this.isLocked(i) && !this.isHidden(i)){
54882                 this.totalWidth += this.getColumnWidth(i);
54883             }
54884         }
54885         return totalWidth;
54886     },
54887
54888     getLockedCount : function(){
54889         for(var i = 0, len = this.config.length; i < len; i++){
54890             if(!this.isLocked(i)){
54891                 return i;
54892             }
54893         }
54894     },
54895
54896     /**
54897      * Returns the number of columns.
54898      * @return {Number}
54899      */
54900     getColumnCount : function(visibleOnly){
54901         if(visibleOnly === true){
54902             var c = 0;
54903             for(var i = 0, len = this.config.length; i < len; i++){
54904                 if(!this.isHidden(i)){
54905                     c++;
54906                 }
54907             }
54908             return c;
54909         }
54910         return this.config.length;
54911     },
54912
54913     /**
54914      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
54915      * @param {Function} fn
54916      * @param {Object} scope (optional)
54917      * @return {Array} result
54918      */
54919     getColumnsBy : function(fn, scope){
54920         var r = [];
54921         for(var i = 0, len = this.config.length; i < len; i++){
54922             var c = this.config[i];
54923             if(fn.call(scope||this, c, i) === true){
54924                 r[r.length] = c;
54925             }
54926         }
54927         return r;
54928     },
54929
54930     /**
54931      * Returns true if the specified column is sortable.
54932      * @param {Number} col The column index
54933      * @return {Boolean}
54934      */
54935     isSortable : function(col){
54936         if(typeof this.config[col].sortable == "undefined"){
54937             return this.defaultSortable;
54938         }
54939         return this.config[col].sortable;
54940     },
54941
54942     /**
54943      * Returns the rendering (formatting) function defined for the column.
54944      * @param {Number} col The column index.
54945      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
54946      */
54947     getRenderer : function(col){
54948         if(!this.config[col].renderer){
54949             return Roo.grid.ColumnModel.defaultRenderer;
54950         }
54951         return this.config[col].renderer;
54952     },
54953
54954     /**
54955      * Sets the rendering (formatting) function for a column.
54956      * @param {Number} col The column index
54957      * @param {Function} fn The function to use to process the cell's raw data
54958      * to return HTML markup for the grid view. The render function is called with
54959      * the following parameters:<ul>
54960      * <li>Data value.</li>
54961      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
54962      * <li>css A CSS style string to apply to the table cell.</li>
54963      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
54964      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
54965      * <li>Row index</li>
54966      * <li>Column index</li>
54967      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
54968      */
54969     setRenderer : function(col, fn){
54970         this.config[col].renderer = fn;
54971     },
54972
54973     /**
54974      * Returns the width for the specified column.
54975      * @param {Number} col The column index
54976      * @return {Number}
54977      */
54978     getColumnWidth : function(col){
54979         return this.config[col].width * 1 || this.defaultWidth;
54980     },
54981
54982     /**
54983      * Sets the width for a column.
54984      * @param {Number} col The column index
54985      * @param {Number} width The new width
54986      */
54987     setColumnWidth : function(col, width, suppressEvent){
54988         this.config[col].width = width;
54989         this.totalWidth = null;
54990         if(!suppressEvent){
54991              this.fireEvent("widthchange", this, col, width);
54992         }
54993     },
54994
54995     /**
54996      * Returns the total width of all columns.
54997      * @param {Boolean} includeHidden True to include hidden column widths
54998      * @return {Number}
54999      */
55000     getTotalWidth : function(includeHidden){
55001         if(!this.totalWidth){
55002             this.totalWidth = 0;
55003             for(var i = 0, len = this.config.length; i < len; i++){
55004                 if(includeHidden || !this.isHidden(i)){
55005                     this.totalWidth += this.getColumnWidth(i);
55006                 }
55007             }
55008         }
55009         return this.totalWidth;
55010     },
55011
55012     /**
55013      * Returns the header for the specified column.
55014      * @param {Number} col The column index
55015      * @return {String}
55016      */
55017     getColumnHeader : function(col){
55018         return this.config[col].header;
55019     },
55020
55021     /**
55022      * Sets the header for a column.
55023      * @param {Number} col The column index
55024      * @param {String} header The new header
55025      */
55026     setColumnHeader : function(col, header){
55027         this.config[col].header = header;
55028         this.fireEvent("headerchange", this, col, header);
55029     },
55030
55031     /**
55032      * Returns the tooltip for the specified column.
55033      * @param {Number} col The column index
55034      * @return {String}
55035      */
55036     getColumnTooltip : function(col){
55037             return this.config[col].tooltip;
55038     },
55039     /**
55040      * Sets the tooltip for a column.
55041      * @param {Number} col The column index
55042      * @param {String} tooltip The new tooltip
55043      */
55044     setColumnTooltip : function(col, tooltip){
55045             this.config[col].tooltip = tooltip;
55046     },
55047
55048     /**
55049      * Returns the dataIndex for the specified column.
55050      * @param {Number} col The column index
55051      * @return {Number}
55052      */
55053     getDataIndex : function(col){
55054         return this.config[col].dataIndex;
55055     },
55056
55057     /**
55058      * Sets the dataIndex for a column.
55059      * @param {Number} col The column index
55060      * @param {Number} dataIndex The new dataIndex
55061      */
55062     setDataIndex : function(col, dataIndex){
55063         this.config[col].dataIndex = dataIndex;
55064     },
55065
55066     
55067     
55068     /**
55069      * Returns true if the cell is editable.
55070      * @param {Number} colIndex The column index
55071      * @param {Number} rowIndex The row index
55072      * @return {Boolean}
55073      */
55074     isCellEditable : function(colIndex, rowIndex){
55075         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
55076     },
55077
55078     /**
55079      * Returns the editor defined for the cell/column.
55080      * return false or null to disable editing.
55081      * @param {Number} colIndex The column index
55082      * @param {Number} rowIndex The row index
55083      * @return {Object}
55084      */
55085     getCellEditor : function(colIndex, rowIndex){
55086         return this.config[colIndex].editor;
55087     },
55088
55089     /**
55090      * Sets if a column is editable.
55091      * @param {Number} col The column index
55092      * @param {Boolean} editable True if the column is editable
55093      */
55094     setEditable : function(col, editable){
55095         this.config[col].editable = editable;
55096     },
55097
55098
55099     /**
55100      * Returns true if the column is hidden.
55101      * @param {Number} colIndex The column index
55102      * @return {Boolean}
55103      */
55104     isHidden : function(colIndex){
55105         return this.config[colIndex].hidden;
55106     },
55107
55108
55109     /**
55110      * Returns true if the column width cannot be changed
55111      */
55112     isFixed : function(colIndex){
55113         return this.config[colIndex].fixed;
55114     },
55115
55116     /**
55117      * Returns true if the column can be resized
55118      * @return {Boolean}
55119      */
55120     isResizable : function(colIndex){
55121         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
55122     },
55123     /**
55124      * Sets if a column is hidden.
55125      * @param {Number} colIndex The column index
55126      * @param {Boolean} hidden True if the column is hidden
55127      */
55128     setHidden : function(colIndex, hidden){
55129         this.config[colIndex].hidden = hidden;
55130         this.totalWidth = null;
55131         this.fireEvent("hiddenchange", this, colIndex, hidden);
55132     },
55133
55134     /**
55135      * Sets the editor for a column.
55136      * @param {Number} col The column index
55137      * @param {Object} editor The editor object
55138      */
55139     setEditor : function(col, editor){
55140         this.config[col].editor = editor;
55141     }
55142 });
55143
55144 Roo.grid.ColumnModel.defaultRenderer = function(value){
55145         if(typeof value == "string" && value.length < 1){
55146             return "&#160;";
55147         }
55148         return value;
55149 };
55150
55151 // Alias for backwards compatibility
55152 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
55153 /*
55154  * Based on:
55155  * Ext JS Library 1.1.1
55156  * Copyright(c) 2006-2007, Ext JS, LLC.
55157  *
55158  * Originally Released Under LGPL - original licence link has changed is not relivant.
55159  *
55160  * Fork - LGPL
55161  * <script type="text/javascript">
55162  */
55163
55164 /**
55165  * @class Roo.grid.AbstractSelectionModel
55166  * @extends Roo.util.Observable
55167  * Abstract base class for grid SelectionModels.  It provides the interface that should be
55168  * implemented by descendant classes.  This class should not be directly instantiated.
55169  * @constructor
55170  */
55171 Roo.grid.AbstractSelectionModel = function(){
55172     this.locked = false;
55173     Roo.grid.AbstractSelectionModel.superclass.constructor.call(this);
55174 };
55175
55176 Roo.extend(Roo.grid.AbstractSelectionModel, Roo.util.Observable,  {
55177     /** @ignore Called by the grid automatically. Do not call directly. */
55178     init : function(grid){
55179         this.grid = grid;
55180         this.initEvents();
55181     },
55182
55183     /**
55184      * Locks the selections.
55185      */
55186     lock : function(){
55187         this.locked = true;
55188     },
55189
55190     /**
55191      * Unlocks the selections.
55192      */
55193     unlock : function(){
55194         this.locked = false;
55195     },
55196
55197     /**
55198      * Returns true if the selections are locked.
55199      * @return {Boolean}
55200      */
55201     isLocked : function(){
55202         return this.locked;
55203     }
55204 });/*
55205  * Based on:
55206  * Ext JS Library 1.1.1
55207  * Copyright(c) 2006-2007, Ext JS, LLC.
55208  *
55209  * Originally Released Under LGPL - original licence link has changed is not relivant.
55210  *
55211  * Fork - LGPL
55212  * <script type="text/javascript">
55213  */
55214 /**
55215  * @extends Roo.grid.AbstractSelectionModel
55216  * @class Roo.grid.RowSelectionModel
55217  * The default SelectionModel used by {@link Roo.grid.Grid}.
55218  * It supports multiple selections and keyboard selection/navigation. 
55219  * @constructor
55220  * @param {Object} config
55221  */
55222 Roo.grid.RowSelectionModel = function(config){
55223     Roo.apply(this, config);
55224     this.selections = new Roo.util.MixedCollection(false, function(o){
55225         return o.id;
55226     });
55227
55228     this.last = false;
55229     this.lastActive = false;
55230
55231     this.addEvents({
55232         /**
55233              * @event selectionchange
55234              * Fires when the selection changes
55235              * @param {SelectionModel} this
55236              */
55237             "selectionchange" : true,
55238         /**
55239              * @event afterselectionchange
55240              * Fires after the selection changes (eg. by key press or clicking)
55241              * @param {SelectionModel} this
55242              */
55243             "afterselectionchange" : true,
55244         /**
55245              * @event beforerowselect
55246              * Fires when a row is selected being selected, return false to cancel.
55247              * @param {SelectionModel} this
55248              * @param {Number} rowIndex The selected index
55249              * @param {Boolean} keepExisting False if other selections will be cleared
55250              */
55251             "beforerowselect" : true,
55252         /**
55253              * @event rowselect
55254              * Fires when a row is selected.
55255              * @param {SelectionModel} this
55256              * @param {Number} rowIndex The selected index
55257              * @param {Roo.data.Record} r The record
55258              */
55259             "rowselect" : true,
55260         /**
55261              * @event rowdeselect
55262              * Fires when a row is deselected.
55263              * @param {SelectionModel} this
55264              * @param {Number} rowIndex The selected index
55265              */
55266         "rowdeselect" : true
55267     });
55268     Roo.grid.RowSelectionModel.superclass.constructor.call(this);
55269     this.locked = false;
55270 };
55271
55272 Roo.extend(Roo.grid.RowSelectionModel, Roo.grid.AbstractSelectionModel,  {
55273     /**
55274      * @cfg {Boolean} singleSelect
55275      * True to allow selection of only one row at a time (defaults to false)
55276      */
55277     singleSelect : false,
55278
55279     // private
55280     initEvents : function(){
55281
55282         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
55283             this.grid.on("mousedown", this.handleMouseDown, this);
55284         }else{ // allow click to work like normal
55285             this.grid.on("rowclick", this.handleDragableRowClick, this);
55286         }
55287
55288         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
55289             "up" : function(e){
55290                 if(!e.shiftKey){
55291                     this.selectPrevious(e.shiftKey);
55292                 }else if(this.last !== false && this.lastActive !== false){
55293                     var last = this.last;
55294                     this.selectRange(this.last,  this.lastActive-1);
55295                     this.grid.getView().focusRow(this.lastActive);
55296                     if(last !== false){
55297                         this.last = last;
55298                     }
55299                 }else{
55300                     this.selectFirstRow();
55301                 }
55302                 this.fireEvent("afterselectionchange", this);
55303             },
55304             "down" : function(e){
55305                 if(!e.shiftKey){
55306                     this.selectNext(e.shiftKey);
55307                 }else if(this.last !== false && this.lastActive !== false){
55308                     var last = this.last;
55309                     this.selectRange(this.last,  this.lastActive+1);
55310                     this.grid.getView().focusRow(this.lastActive);
55311                     if(last !== false){
55312                         this.last = last;
55313                     }
55314                 }else{
55315                     this.selectFirstRow();
55316                 }
55317                 this.fireEvent("afterselectionchange", this);
55318             },
55319             scope: this
55320         });
55321
55322         var view = this.grid.view;
55323         view.on("refresh", this.onRefresh, this);
55324         view.on("rowupdated", this.onRowUpdated, this);
55325         view.on("rowremoved", this.onRemove, this);
55326     },
55327
55328     // private
55329     onRefresh : function(){
55330         var ds = this.grid.dataSource, i, v = this.grid.view;
55331         var s = this.selections;
55332         s.each(function(r){
55333             if((i = ds.indexOfId(r.id)) != -1){
55334                 v.onRowSelect(i);
55335             }else{
55336                 s.remove(r);
55337             }
55338         });
55339     },
55340
55341     // private
55342     onRemove : function(v, index, r){
55343         this.selections.remove(r);
55344     },
55345
55346     // private
55347     onRowUpdated : function(v, index, r){
55348         if(this.isSelected(r)){
55349             v.onRowSelect(index);
55350         }
55351     },
55352
55353     /**
55354      * Select records.
55355      * @param {Array} records The records to select
55356      * @param {Boolean} keepExisting (optional) True to keep existing selections
55357      */
55358     selectRecords : function(records, keepExisting){
55359         if(!keepExisting){
55360             this.clearSelections();
55361         }
55362         var ds = this.grid.dataSource;
55363         for(var i = 0, len = records.length; i < len; i++){
55364             this.selectRow(ds.indexOf(records[i]), true);
55365         }
55366     },
55367
55368     /**
55369      * Gets the number of selected rows.
55370      * @return {Number}
55371      */
55372     getCount : function(){
55373         return this.selections.length;
55374     },
55375
55376     /**
55377      * Selects the first row in the grid.
55378      */
55379     selectFirstRow : function(){
55380         this.selectRow(0);
55381     },
55382
55383     /**
55384      * Select the last row.
55385      * @param {Boolean} keepExisting (optional) True to keep existing selections
55386      */
55387     selectLastRow : function(keepExisting){
55388         this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
55389     },
55390
55391     /**
55392      * Selects the row immediately following the last selected row.
55393      * @param {Boolean} keepExisting (optional) True to keep existing selections
55394      */
55395     selectNext : function(keepExisting){
55396         if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
55397             this.selectRow(this.last+1, keepExisting);
55398             this.grid.getView().focusRow(this.last);
55399         }
55400     },
55401
55402     /**
55403      * Selects the row that precedes the last selected row.
55404      * @param {Boolean} keepExisting (optional) True to keep existing selections
55405      */
55406     selectPrevious : function(keepExisting){
55407         if(this.last){
55408             this.selectRow(this.last-1, keepExisting);
55409             this.grid.getView().focusRow(this.last);
55410         }
55411     },
55412
55413     /**
55414      * Returns the selected records
55415      * @return {Array} Array of selected records
55416      */
55417     getSelections : function(){
55418         return [].concat(this.selections.items);
55419     },
55420
55421     /**
55422      * Returns the first selected record.
55423      * @return {Record}
55424      */
55425     getSelected : function(){
55426         return this.selections.itemAt(0);
55427     },
55428
55429
55430     /**
55431      * Clears all selections.
55432      */
55433     clearSelections : function(fast){
55434         if(this.locked) return;
55435         if(fast !== true){
55436             var ds = this.grid.dataSource;
55437             var s = this.selections;
55438             s.each(function(r){
55439                 this.deselectRow(ds.indexOfId(r.id));
55440             }, this);
55441             s.clear();
55442         }else{
55443             this.selections.clear();
55444         }
55445         this.last = false;
55446     },
55447
55448
55449     /**
55450      * Selects all rows.
55451      */
55452     selectAll : function(){
55453         if(this.locked) return;
55454         this.selections.clear();
55455         for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
55456             this.selectRow(i, true);
55457         }
55458     },
55459
55460     /**
55461      * Returns True if there is a selection.
55462      * @return {Boolean}
55463      */
55464     hasSelection : function(){
55465         return this.selections.length > 0;
55466     },
55467
55468     /**
55469      * Returns True if the specified row is selected.
55470      * @param {Number/Record} record The record or index of the record to check
55471      * @return {Boolean}
55472      */
55473     isSelected : function(index){
55474         var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
55475         return (r && this.selections.key(r.id) ? true : false);
55476     },
55477
55478     /**
55479      * Returns True if the specified record id is selected.
55480      * @param {String} id The id of record to check
55481      * @return {Boolean}
55482      */
55483     isIdSelected : function(id){
55484         return (this.selections.key(id) ? true : false);
55485     },
55486
55487     // private
55488     handleMouseDown : function(e, t){
55489         var view = this.grid.getView(), rowIndex;
55490         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
55491             return;
55492         };
55493         if(e.shiftKey && this.last !== false){
55494             var last = this.last;
55495             this.selectRange(last, rowIndex, e.ctrlKey);
55496             this.last = last; // reset the last
55497             view.focusRow(rowIndex);
55498         }else{
55499             var isSelected = this.isSelected(rowIndex);
55500             if(e.button !== 0 && isSelected){
55501                 view.focusRow(rowIndex);
55502             }else if(e.ctrlKey && isSelected){
55503                 this.deselectRow(rowIndex);
55504             }else if(!isSelected){
55505                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
55506                 view.focusRow(rowIndex);
55507             }
55508         }
55509         this.fireEvent("afterselectionchange", this);
55510     },
55511     // private
55512     handleDragableRowClick :  function(grid, rowIndex, e) 
55513     {
55514         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
55515             this.selectRow(rowIndex, false);
55516             grid.view.focusRow(rowIndex);
55517              this.fireEvent("afterselectionchange", this);
55518         }
55519     },
55520     
55521     /**
55522      * Selects multiple rows.
55523      * @param {Array} rows Array of the indexes of the row to select
55524      * @param {Boolean} keepExisting (optional) True to keep existing selections
55525      */
55526     selectRows : function(rows, keepExisting){
55527         if(!keepExisting){
55528             this.clearSelections();
55529         }
55530         for(var i = 0, len = rows.length; i < len; i++){
55531             this.selectRow(rows[i], true);
55532         }
55533     },
55534
55535     /**
55536      * Selects a range of rows. All rows in between startRow and endRow are also selected.
55537      * @param {Number} startRow The index of the first row in the range
55538      * @param {Number} endRow The index of the last row in the range
55539      * @param {Boolean} keepExisting (optional) True to retain existing selections
55540      */
55541     selectRange : function(startRow, endRow, keepExisting){
55542         if(this.locked) return;
55543         if(!keepExisting){
55544             this.clearSelections();
55545         }
55546         if(startRow <= endRow){
55547             for(var i = startRow; i <= endRow; i++){
55548                 this.selectRow(i, true);
55549             }
55550         }else{
55551             for(var i = startRow; i >= endRow; i--){
55552                 this.selectRow(i, true);
55553             }
55554         }
55555     },
55556
55557     /**
55558      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
55559      * @param {Number} startRow The index of the first row in the range
55560      * @param {Number} endRow The index of the last row in the range
55561      */
55562     deselectRange : function(startRow, endRow, preventViewNotify){
55563         if(this.locked) return;
55564         for(var i = startRow; i <= endRow; i++){
55565             this.deselectRow(i, preventViewNotify);
55566         }
55567     },
55568
55569     /**
55570      * Selects a row.
55571      * @param {Number} row The index of the row to select
55572      * @param {Boolean} keepExisting (optional) True to keep existing selections
55573      */
55574     selectRow : function(index, keepExisting, preventViewNotify){
55575         if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) return;
55576         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
55577             if(!keepExisting || this.singleSelect){
55578                 this.clearSelections();
55579             }
55580             var r = this.grid.dataSource.getAt(index);
55581             this.selections.add(r);
55582             this.last = this.lastActive = index;
55583             if(!preventViewNotify){
55584                 this.grid.getView().onRowSelect(index);
55585             }
55586             this.fireEvent("rowselect", this, index, r);
55587             this.fireEvent("selectionchange", this);
55588         }
55589     },
55590
55591     /**
55592      * Deselects a row.
55593      * @param {Number} row The index of the row to deselect
55594      */
55595     deselectRow : function(index, preventViewNotify){
55596         if(this.locked) return;
55597         if(this.last == index){
55598             this.last = false;
55599         }
55600         if(this.lastActive == index){
55601             this.lastActive = false;
55602         }
55603         var r = this.grid.dataSource.getAt(index);
55604         this.selections.remove(r);
55605         if(!preventViewNotify){
55606             this.grid.getView().onRowDeselect(index);
55607         }
55608         this.fireEvent("rowdeselect", this, index);
55609         this.fireEvent("selectionchange", this);
55610     },
55611
55612     // private
55613     restoreLast : function(){
55614         if(this._last){
55615             this.last = this._last;
55616         }
55617     },
55618
55619     // private
55620     acceptsNav : function(row, col, cm){
55621         return !cm.isHidden(col) && cm.isCellEditable(col, row);
55622     },
55623
55624     // private
55625     onEditorKey : function(field, e){
55626         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
55627         if(k == e.TAB){
55628             e.stopEvent();
55629             ed.completeEdit();
55630             if(e.shiftKey){
55631                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
55632             }else{
55633                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
55634             }
55635         }else if(k == e.ENTER && !e.ctrlKey){
55636             e.stopEvent();
55637             ed.completeEdit();
55638             if(e.shiftKey){
55639                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
55640             }else{
55641                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
55642             }
55643         }else if(k == e.ESC){
55644             ed.cancelEdit();
55645         }
55646         if(newCell){
55647             g.startEditing(newCell[0], newCell[1]);
55648         }
55649     }
55650 });/*
55651  * Based on:
55652  * Ext JS Library 1.1.1
55653  * Copyright(c) 2006-2007, Ext JS, LLC.
55654  *
55655  * Originally Released Under LGPL - original licence link has changed is not relivant.
55656  *
55657  * Fork - LGPL
55658  * <script type="text/javascript">
55659  */
55660 /**
55661  * @class Roo.grid.CellSelectionModel
55662  * @extends Roo.grid.AbstractSelectionModel
55663  * This class provides the basic implementation for cell selection in a grid.
55664  * @constructor
55665  * @param {Object} config The object containing the configuration of this model.
55666  * @cfg {Boolean} enter_is_tab Enter behaves the same as tab. (eg. goes to next cell) default: false
55667  */
55668 Roo.grid.CellSelectionModel = function(config){
55669     Roo.apply(this, config);
55670
55671     this.selection = null;
55672
55673     this.addEvents({
55674         /**
55675              * @event beforerowselect
55676              * Fires before a cell is selected.
55677              * @param {SelectionModel} this
55678              * @param {Number} rowIndex The selected row index
55679              * @param {Number} colIndex The selected cell index
55680              */
55681             "beforecellselect" : true,
55682         /**
55683              * @event cellselect
55684              * Fires when a cell is selected.
55685              * @param {SelectionModel} this
55686              * @param {Number} rowIndex The selected row index
55687              * @param {Number} colIndex The selected cell index
55688              */
55689             "cellselect" : true,
55690         /**
55691              * @event selectionchange
55692              * Fires when the active selection changes.
55693              * @param {SelectionModel} this
55694              * @param {Object} selection null for no selection or an object (o) with two properties
55695                 <ul>
55696                 <li>o.record: the record object for the row the selection is in</li>
55697                 <li>o.cell: An array of [rowIndex, columnIndex]</li>
55698                 </ul>
55699              */
55700             "selectionchange" : true,
55701         /**
55702              * @event tabend
55703              * Fires when the tab (or enter) was pressed on the last editable cell
55704              * You can use this to trigger add new row.
55705              * @param {SelectionModel} this
55706              */
55707             "tabend" : true,
55708          /**
55709              * @event beforeeditnext
55710              * Fires before the next editable sell is made active
55711              * You can use this to skip to another cell or fire the tabend
55712              *    if you set cell to false
55713              * @param {Object} eventdata object : { cell : [ row, col ] } 
55714              */
55715             "beforeeditnext" : true
55716     });
55717     Roo.grid.CellSelectionModel.superclass.constructor.call(this);
55718 };
55719
55720 Roo.extend(Roo.grid.CellSelectionModel, Roo.grid.AbstractSelectionModel,  {
55721     
55722     enter_is_tab: false,
55723
55724     /** @ignore */
55725     initEvents : function(){
55726         this.grid.on("mousedown", this.handleMouseDown, this);
55727         this.grid.getGridEl().on(Roo.isIE ? "keydown" : "keypress", this.handleKeyDown, this);
55728         var view = this.grid.view;
55729         view.on("refresh", this.onViewChange, this);
55730         view.on("rowupdated", this.onRowUpdated, this);
55731         view.on("beforerowremoved", this.clearSelections, this);
55732         view.on("beforerowsinserted", this.clearSelections, this);
55733         if(this.grid.isEditor){
55734             this.grid.on("beforeedit", this.beforeEdit,  this);
55735         }
55736     },
55737
55738         //private
55739     beforeEdit : function(e){
55740         this.select(e.row, e.column, false, true, e.record);
55741     },
55742
55743         //private
55744     onRowUpdated : function(v, index, r){
55745         if(this.selection && this.selection.record == r){
55746             v.onCellSelect(index, this.selection.cell[1]);
55747         }
55748     },
55749
55750         //private
55751     onViewChange : function(){
55752         this.clearSelections(true);
55753     },
55754
55755         /**
55756          * Returns the currently selected cell,.
55757          * @return {Array} The selected cell (row, column) or null if none selected.
55758          */
55759     getSelectedCell : function(){
55760         return this.selection ? this.selection.cell : null;
55761     },
55762
55763     /**
55764      * Clears all selections.
55765      * @param {Boolean} true to prevent the gridview from being notified about the change.
55766      */
55767     clearSelections : function(preventNotify){
55768         var s = this.selection;
55769         if(s){
55770             if(preventNotify !== true){
55771                 this.grid.view.onCellDeselect(s.cell[0], s.cell[1]);
55772             }
55773             this.selection = null;
55774             this.fireEvent("selectionchange", this, null);
55775         }
55776     },
55777
55778     /**
55779      * Returns true if there is a selection.
55780      * @return {Boolean}
55781      */
55782     hasSelection : function(){
55783         return this.selection ? true : false;
55784     },
55785
55786     /** @ignore */
55787     handleMouseDown : function(e, t){
55788         var v = this.grid.getView();
55789         if(this.isLocked()){
55790             return;
55791         };
55792         var row = v.findRowIndex(t);
55793         var cell = v.findCellIndex(t);
55794         if(row !== false && cell !== false){
55795             this.select(row, cell);
55796         }
55797     },
55798
55799     /**
55800      * Selects a cell.
55801      * @param {Number} rowIndex
55802      * @param {Number} collIndex
55803      */
55804     select : function(rowIndex, colIndex, preventViewNotify, preventFocus, /*internal*/ r){
55805         if(this.fireEvent("beforecellselect", this, rowIndex, colIndex) !== false){
55806             this.clearSelections();
55807             r = r || this.grid.dataSource.getAt(rowIndex);
55808             this.selection = {
55809                 record : r,
55810                 cell : [rowIndex, colIndex]
55811             };
55812             if(!preventViewNotify){
55813                 var v = this.grid.getView();
55814                 v.onCellSelect(rowIndex, colIndex);
55815                 if(preventFocus !== true){
55816                     v.focusCell(rowIndex, colIndex);
55817                 }
55818             }
55819             this.fireEvent("cellselect", this, rowIndex, colIndex);
55820             this.fireEvent("selectionchange", this, this.selection);
55821         }
55822     },
55823
55824         //private
55825     isSelectable : function(rowIndex, colIndex, cm){
55826         return !cm.isHidden(colIndex);
55827     },
55828
55829     /** @ignore */
55830     handleKeyDown : function(e){
55831         //Roo.log('Cell Sel Model handleKeyDown');
55832         if(!e.isNavKeyPress()){
55833             return;
55834         }
55835         var g = this.grid, s = this.selection;
55836         if(!s){
55837             e.stopEvent();
55838             var cell = g.walkCells(0, 0, 1, this.isSelectable,  this);
55839             if(cell){
55840                 this.select(cell[0], cell[1]);
55841             }
55842             return;
55843         }
55844         var sm = this;
55845         var walk = function(row, col, step){
55846             return g.walkCells(row, col, step, sm.isSelectable,  sm);
55847         };
55848         var k = e.getKey(), r = s.cell[0], c = s.cell[1];
55849         var newCell;
55850
55851       
55852
55853         switch(k){
55854             case e.TAB:
55855                 // handled by onEditorKey
55856                 if (g.isEditor && g.editing) {
55857                     return;
55858                 }
55859                 if(e.shiftKey) {
55860                     newCell = walk(r, c-1, -1);
55861                 } else {
55862                     newCell = walk(r, c+1, 1);
55863                 }
55864                 break;
55865             
55866             case e.DOWN:
55867                newCell = walk(r+1, c, 1);
55868                 break;
55869             
55870             case e.UP:
55871                 newCell = walk(r-1, c, -1);
55872                 break;
55873             
55874             case e.RIGHT:
55875                 newCell = walk(r, c+1, 1);
55876                 break;
55877             
55878             case e.LEFT:
55879                 newCell = walk(r, c-1, -1);
55880                 break;
55881             
55882             case e.ENTER:
55883                 
55884                 if(g.isEditor && !g.editing){
55885                    g.startEditing(r, c);
55886                    e.stopEvent();
55887                    return;
55888                 }
55889                 
55890                 
55891              break;
55892         };
55893         if(newCell){
55894             this.select(newCell[0], newCell[1]);
55895             e.stopEvent();
55896             
55897         }
55898     },
55899
55900     acceptsNav : function(row, col, cm){
55901         return !cm.isHidden(col) && cm.isCellEditable(col, row);
55902     },
55903     /**
55904      * Selects a cell.
55905      * @param {Number} field (not used) - as it's normally used as a listener
55906      * @param {Number} e - event - fake it by using
55907      *
55908      * var e = Roo.EventObjectImpl.prototype;
55909      * e.keyCode = e.TAB
55910      *
55911      * 
55912      */
55913     onEditorKey : function(field, e){
55914         
55915         var k = e.getKey(),
55916             newCell,
55917             g = this.grid,
55918             ed = g.activeEditor,
55919             forward = false;
55920         ///Roo.log('onEditorKey' + k);
55921         
55922         
55923         if (this.enter_is_tab && k == e.ENTER) {
55924             k = e.TAB;
55925         }
55926         
55927         if(k == e.TAB){
55928             if(e.shiftKey){
55929                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
55930             }else{
55931                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
55932                 forward = true;
55933             }
55934             
55935             e.stopEvent();
55936             
55937         } else if(k == e.ENTER &&  !e.ctrlKey){
55938             ed.completeEdit();
55939             e.stopEvent();
55940             newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
55941         
55942                 } else if(k == e.ESC){
55943             ed.cancelEdit();
55944         }
55945                 
55946         if (newCell) {
55947             var ecall = { cell : newCell, forward : forward };
55948             this.fireEvent('beforeeditnext', ecall );
55949             newCell = ecall.cell;
55950                         forward = ecall.forward;
55951         }
55952                 
55953         if(newCell){
55954             //Roo.log('next cell after edit');
55955             g.startEditing.defer(100, g, [newCell[0], newCell[1]]);
55956         } else if (forward) {
55957             // tabbed past last
55958             this.fireEvent.defer(100, this, ['tabend',this]);
55959         }
55960     }
55961 });/*
55962  * Based on:
55963  * Ext JS Library 1.1.1
55964  * Copyright(c) 2006-2007, Ext JS, LLC.
55965  *
55966  * Originally Released Under LGPL - original licence link has changed is not relivant.
55967  *
55968  * Fork - LGPL
55969  * <script type="text/javascript">
55970  */
55971  
55972 /**
55973  * @class Roo.grid.EditorGrid
55974  * @extends Roo.grid.Grid
55975  * Class for creating and editable grid.
55976  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered - 
55977  * The container MUST have some type of size defined for the grid to fill. The container will be 
55978  * automatically set to position relative if it isn't already.
55979  * @param {Object} dataSource The data model to bind to
55980  * @param {Object} colModel The column model with info about this grid's columns
55981  */
55982 Roo.grid.EditorGrid = function(container, config){
55983     Roo.grid.EditorGrid.superclass.constructor.call(this, container, config);
55984     this.getGridEl().addClass("xedit-grid");
55985
55986     if(!this.selModel){
55987         this.selModel = new Roo.grid.CellSelectionModel();
55988     }
55989
55990     this.activeEditor = null;
55991
55992         this.addEvents({
55993             /**
55994              * @event beforeedit
55995              * Fires before cell editing is triggered. The edit event object has the following properties <br />
55996              * <ul style="padding:5px;padding-left:16px;">
55997              * <li>grid - This grid</li>
55998              * <li>record - The record being edited</li>
55999              * <li>field - The field name being edited</li>
56000              * <li>value - The value for the field being edited.</li>
56001              * <li>row - The grid row index</li>
56002              * <li>column - The grid column index</li>
56003              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
56004              * </ul>
56005              * @param {Object} e An edit event (see above for description)
56006              */
56007             "beforeedit" : true,
56008             /**
56009              * @event afteredit
56010              * Fires after a cell is edited. <br />
56011              * <ul style="padding:5px;padding-left:16px;">
56012              * <li>grid - This grid</li>
56013              * <li>record - The record being edited</li>
56014              * <li>field - The field name being edited</li>
56015              * <li>value - The value being set</li>
56016              * <li>originalValue - The original value for the field, before the edit.</li>
56017              * <li>row - The grid row index</li>
56018              * <li>column - The grid column index</li>
56019              * </ul>
56020              * @param {Object} e An edit event (see above for description)
56021              */
56022             "afteredit" : true,
56023             /**
56024              * @event validateedit
56025              * Fires after a cell is edited, but before the value is set in the record. 
56026          * You can use this to modify the value being set in the field, Return false
56027              * to cancel the change. The edit event object has the following properties <br />
56028              * <ul style="padding:5px;padding-left:16px;">
56029          * <li>editor - This editor</li>
56030              * <li>grid - This grid</li>
56031              * <li>record - The record being edited</li>
56032              * <li>field - The field name being edited</li>
56033              * <li>value - The value being set</li>
56034              * <li>originalValue - The original value for the field, before the edit.</li>
56035              * <li>row - The grid row index</li>
56036              * <li>column - The grid column index</li>
56037              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
56038              * </ul>
56039              * @param {Object} e An edit event (see above for description)
56040              */
56041             "validateedit" : true
56042         });
56043     this.on("bodyscroll", this.stopEditing,  this);
56044     this.on(this.clicksToEdit == 1 ? "cellclick" : "celldblclick", this.onCellDblClick,  this);
56045 };
56046
56047 Roo.extend(Roo.grid.EditorGrid, Roo.grid.Grid, {
56048     /**
56049      * @cfg {Number} clicksToEdit
56050      * The number of clicks on a cell required to display the cell's editor (defaults to 2)
56051      */
56052     clicksToEdit: 2,
56053
56054     // private
56055     isEditor : true,
56056     // private
56057     trackMouseOver: false, // causes very odd FF errors
56058
56059     onCellDblClick : function(g, row, col){
56060         this.startEditing(row, col);
56061     },
56062
56063     onEditComplete : function(ed, value, startValue){
56064         this.editing = false;
56065         this.activeEditor = null;
56066         ed.un("specialkey", this.selModel.onEditorKey, this.selModel);
56067         var r = ed.record;
56068         var field = this.colModel.getDataIndex(ed.col);
56069         var e = {
56070             grid: this,
56071             record: r,
56072             field: field,
56073             originalValue: startValue,
56074             value: value,
56075             row: ed.row,
56076             column: ed.col,
56077             cancel:false,
56078             editor: ed
56079         };
56080         var cell = Roo.get(this.view.getCell(ed.row,ed.col))
56081         cell.show();
56082           
56083         if(String(value) !== String(startValue)){
56084             
56085             if(this.fireEvent("validateedit", e) !== false && !e.cancel){
56086                 r.set(field, e.value);
56087                 // if we are dealing with a combo box..
56088                 // then we also set the 'name' colum to be the displayField
56089                 if (ed.field.displayField && ed.field.name) {
56090                     r.set(ed.field.name, ed.field.el.dom.value);
56091                 }
56092                 
56093                 delete e.cancel; //?? why!!!
56094                 this.fireEvent("afteredit", e);
56095             }
56096         } else {
56097             this.fireEvent("afteredit", e); // always fire it!
56098         }
56099         this.view.focusCell(ed.row, ed.col);
56100     },
56101
56102     /**
56103      * Starts editing the specified for the specified row/column
56104      * @param {Number} rowIndex
56105      * @param {Number} colIndex
56106      */
56107     startEditing : function(row, col){
56108         this.stopEditing();
56109         if(this.colModel.isCellEditable(col, row)){
56110             this.view.ensureVisible(row, col, true);
56111           
56112             var r = this.dataSource.getAt(row);
56113             var field = this.colModel.getDataIndex(col);
56114             var cell = Roo.get(this.view.getCell(row,col));
56115             var e = {
56116                 grid: this,
56117                 record: r,
56118                 field: field,
56119                 value: r.data[field],
56120                 row: row,
56121                 column: col,
56122                 cancel:false 
56123             };
56124             if(this.fireEvent("beforeedit", e) !== false && !e.cancel){
56125                 this.editing = true;
56126                 var ed = this.colModel.getCellEditor(col, row);
56127                 
56128                 if (!ed) {
56129                     return;
56130                 }
56131                 if(!ed.rendered){
56132                     ed.render(ed.parentEl || document.body);
56133                 }
56134                 ed.field.reset();
56135                
56136                 cell.hide();
56137                 
56138                 (function(){ // complex but required for focus issues in safari, ie and opera
56139                     ed.row = row;
56140                     ed.col = col;
56141                     ed.record = r;
56142                     ed.on("complete",   this.onEditComplete,        this,       {single: true});
56143                     ed.on("specialkey", this.selModel.onEditorKey,  this.selModel);
56144                     this.activeEditor = ed;
56145                     var v = r.data[field];
56146                     ed.startEdit(this.view.getCell(row, col), v);
56147                     // combo's with 'displayField and name set
56148                     if (ed.field.displayField && ed.field.name) {
56149                         ed.field.el.dom.value = r.data[ed.field.name];
56150                     }
56151                     
56152                     
56153                 }).defer(50, this);
56154             }
56155         }
56156     },
56157         
56158     /**
56159      * Stops any active editing
56160      */
56161     stopEditing : function(){
56162         if(this.activeEditor){
56163             this.activeEditor.completeEdit();
56164         }
56165         this.activeEditor = null;
56166     },
56167         
56168          /**
56169      * Called to get grid's drag proxy text, by default returns this.ddText.
56170      * @return {String}
56171      */
56172     getDragDropText : function(){
56173         var count = this.selModel.getSelectedCell() ? 1 : 0;
56174         return String.format(this.ddText, count, count == 1 ? '' : 's');
56175     }
56176         
56177 });/*
56178  * Based on:
56179  * Ext JS Library 1.1.1
56180  * Copyright(c) 2006-2007, Ext JS, LLC.
56181  *
56182  * Originally Released Under LGPL - original licence link has changed is not relivant.
56183  *
56184  * Fork - LGPL
56185  * <script type="text/javascript">
56186  */
56187
56188 // private - not really -- you end up using it !
56189 // This is a support class used internally by the Grid components
56190
56191 /**
56192  * @class Roo.grid.GridEditor
56193  * @extends Roo.Editor
56194  * Class for creating and editable grid elements.
56195  * @param {Object} config any settings (must include field)
56196  */
56197 Roo.grid.GridEditor = function(field, config){
56198     if (!config && field.field) {
56199         config = field;
56200         field = Roo.factory(config.field, Roo.form);
56201     }
56202     Roo.grid.GridEditor.superclass.constructor.call(this, field, config);
56203     field.monitorTab = false;
56204 };
56205
56206 Roo.extend(Roo.grid.GridEditor, Roo.Editor, {
56207     
56208     /**
56209      * @cfg {Roo.form.Field} field Field to wrap (or xtyped)
56210      */
56211     
56212     alignment: "tl-tl",
56213     autoSize: "width",
56214     hideEl : false,
56215     cls: "x-small-editor x-grid-editor",
56216     shim:false,
56217     shadow:"frame"
56218 });/*
56219  * Based on:
56220  * Ext JS Library 1.1.1
56221  * Copyright(c) 2006-2007, Ext JS, LLC.
56222  *
56223  * Originally Released Under LGPL - original licence link has changed is not relivant.
56224  *
56225  * Fork - LGPL
56226  * <script type="text/javascript">
56227  */
56228   
56229
56230   
56231 Roo.grid.PropertyRecord = Roo.data.Record.create([
56232     {name:'name',type:'string'},  'value'
56233 ]);
56234
56235
56236 Roo.grid.PropertyStore = function(grid, source){
56237     this.grid = grid;
56238     this.store = new Roo.data.Store({
56239         recordType : Roo.grid.PropertyRecord
56240     });
56241     this.store.on('update', this.onUpdate,  this);
56242     if(source){
56243         this.setSource(source);
56244     }
56245     Roo.grid.PropertyStore.superclass.constructor.call(this);
56246 };
56247
56248
56249
56250 Roo.extend(Roo.grid.PropertyStore, Roo.util.Observable, {
56251     setSource : function(o){
56252         this.source = o;
56253         this.store.removeAll();
56254         var data = [];
56255         for(var k in o){
56256             if(this.isEditableValue(o[k])){
56257                 data.push(new Roo.grid.PropertyRecord({name: k, value: o[k]}, k));
56258             }
56259         }
56260         this.store.loadRecords({records: data}, {}, true);
56261     },
56262
56263     onUpdate : function(ds, record, type){
56264         if(type == Roo.data.Record.EDIT){
56265             var v = record.data['value'];
56266             var oldValue = record.modified['value'];
56267             if(this.grid.fireEvent('beforepropertychange', this.source, record.id, v, oldValue) !== false){
56268                 this.source[record.id] = v;
56269                 record.commit();
56270                 this.grid.fireEvent('propertychange', this.source, record.id, v, oldValue);
56271             }else{
56272                 record.reject();
56273             }
56274         }
56275     },
56276
56277     getProperty : function(row){
56278        return this.store.getAt(row);
56279     },
56280
56281     isEditableValue: function(val){
56282         if(val && val instanceof Date){
56283             return true;
56284         }else if(typeof val == 'object' || typeof val == 'function'){
56285             return false;
56286         }
56287         return true;
56288     },
56289
56290     setValue : function(prop, value){
56291         this.source[prop] = value;
56292         this.store.getById(prop).set('value', value);
56293     },
56294
56295     getSource : function(){
56296         return this.source;
56297     }
56298 });
56299
56300 Roo.grid.PropertyColumnModel = function(grid, store){
56301     this.grid = grid;
56302     var g = Roo.grid;
56303     g.PropertyColumnModel.superclass.constructor.call(this, [
56304         {header: this.nameText, sortable: true, dataIndex:'name', id: 'name'},
56305         {header: this.valueText, resizable:false, dataIndex: 'value', id: 'value'}
56306     ]);
56307     this.store = store;
56308     this.bselect = Roo.DomHelper.append(document.body, {
56309         tag: 'select', style:'display:none', cls: 'x-grid-editor', children: [
56310             {tag: 'option', value: 'true', html: 'true'},
56311             {tag: 'option', value: 'false', html: 'false'}
56312         ]
56313     });
56314     Roo.id(this.bselect);
56315     var f = Roo.form;
56316     this.editors = {
56317         'date' : new g.GridEditor(new f.DateField({selectOnFocus:true})),
56318         'string' : new g.GridEditor(new f.TextField({selectOnFocus:true})),
56319         'number' : new g.GridEditor(new f.NumberField({selectOnFocus:true, style:'text-align:left;'})),
56320         'int' : new g.GridEditor(new f.NumberField({selectOnFocus:true, allowDecimals:false, style:'text-align:left;'})),
56321         'boolean' : new g.GridEditor(new f.Field({el:this.bselect,selectOnFocus:true}))
56322     };
56323     this.renderCellDelegate = this.renderCell.createDelegate(this);
56324     this.renderPropDelegate = this.renderProp.createDelegate(this);
56325 };
56326
56327 Roo.extend(Roo.grid.PropertyColumnModel, Roo.grid.ColumnModel, {
56328     
56329     
56330     nameText : 'Name',
56331     valueText : 'Value',
56332     
56333     dateFormat : 'm/j/Y',
56334     
56335     
56336     renderDate : function(dateVal){
56337         return dateVal.dateFormat(this.dateFormat);
56338     },
56339
56340     renderBool : function(bVal){
56341         return bVal ? 'true' : 'false';
56342     },
56343
56344     isCellEditable : function(colIndex, rowIndex){
56345         return colIndex == 1;
56346     },
56347
56348     getRenderer : function(col){
56349         return col == 1 ?
56350             this.renderCellDelegate : this.renderPropDelegate;
56351     },
56352
56353     renderProp : function(v){
56354         return this.getPropertyName(v);
56355     },
56356
56357     renderCell : function(val){
56358         var rv = val;
56359         if(val instanceof Date){
56360             rv = this.renderDate(val);
56361         }else if(typeof val == 'boolean'){
56362             rv = this.renderBool(val);
56363         }
56364         return Roo.util.Format.htmlEncode(rv);
56365     },
56366
56367     getPropertyName : function(name){
56368         var pn = this.grid.propertyNames;
56369         return pn && pn[name] ? pn[name] : name;
56370     },
56371
56372     getCellEditor : function(colIndex, rowIndex){
56373         var p = this.store.getProperty(rowIndex);
56374         var n = p.data['name'], val = p.data['value'];
56375         
56376         if(typeof(this.grid.customEditors[n]) == 'string'){
56377             return this.editors[this.grid.customEditors[n]];
56378         }
56379         if(typeof(this.grid.customEditors[n]) != 'undefined'){
56380             return this.grid.customEditors[n];
56381         }
56382         if(val instanceof Date){
56383             return this.editors['date'];
56384         }else if(typeof val == 'number'){
56385             return this.editors['number'];
56386         }else if(typeof val == 'boolean'){
56387             return this.editors['boolean'];
56388         }else{
56389             return this.editors['string'];
56390         }
56391     }
56392 });
56393
56394 /**
56395  * @class Roo.grid.PropertyGrid
56396  * @extends Roo.grid.EditorGrid
56397  * This class represents the  interface of a component based property grid control.
56398  * <br><br>Usage:<pre><code>
56399  var grid = new Roo.grid.PropertyGrid("my-container-id", {
56400       
56401  });
56402  // set any options
56403  grid.render();
56404  * </code></pre>
56405   
56406  * @constructor
56407  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
56408  * The container MUST have some type of size defined for the grid to fill. The container will be
56409  * automatically set to position relative if it isn't already.
56410  * @param {Object} config A config object that sets properties on this grid.
56411  */
56412 Roo.grid.PropertyGrid = function(container, config){
56413     config = config || {};
56414     var store = new Roo.grid.PropertyStore(this);
56415     this.store = store;
56416     var cm = new Roo.grid.PropertyColumnModel(this, store);
56417     store.store.sort('name', 'ASC');
56418     Roo.grid.PropertyGrid.superclass.constructor.call(this, container, Roo.apply({
56419         ds: store.store,
56420         cm: cm,
56421         enableColLock:false,
56422         enableColumnMove:false,
56423         stripeRows:false,
56424         trackMouseOver: false,
56425         clicksToEdit:1
56426     }, config));
56427     this.getGridEl().addClass('x-props-grid');
56428     this.lastEditRow = null;
56429     this.on('columnresize', this.onColumnResize, this);
56430     this.addEvents({
56431          /**
56432              * @event beforepropertychange
56433              * Fires before a property changes (return false to stop?)
56434              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
56435              * @param {String} id Record Id
56436              * @param {String} newval New Value
56437          * @param {String} oldval Old Value
56438              */
56439         "beforepropertychange": true,
56440         /**
56441              * @event propertychange
56442              * Fires after a property changes
56443              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
56444              * @param {String} id Record Id
56445              * @param {String} newval New Value
56446          * @param {String} oldval Old Value
56447              */
56448         "propertychange": true
56449     });
56450     this.customEditors = this.customEditors || {};
56451 };
56452 Roo.extend(Roo.grid.PropertyGrid, Roo.grid.EditorGrid, {
56453     
56454      /**
56455      * @cfg {Object} customEditors map of colnames=> custom editors.
56456      * the custom editor can be one of the standard ones (date|string|number|int|boolean), or a
56457      * grid editor eg. Roo.grid.GridEditor(new Roo.form.TextArea({selectOnFocus:true})),
56458      * false disables editing of the field.
56459          */
56460     
56461       /**
56462      * @cfg {Object} propertyNames map of property Names to their displayed value
56463          */
56464     
56465     render : function(){
56466         Roo.grid.PropertyGrid.superclass.render.call(this);
56467         this.autoSize.defer(100, this);
56468     },
56469
56470     autoSize : function(){
56471         Roo.grid.PropertyGrid.superclass.autoSize.call(this);
56472         if(this.view){
56473             this.view.fitColumns();
56474         }
56475     },
56476
56477     onColumnResize : function(){
56478         this.colModel.setColumnWidth(1, this.container.getWidth(true)-this.colModel.getColumnWidth(0));
56479         this.autoSize();
56480     },
56481     /**
56482      * Sets the data for the Grid
56483      * accepts a Key => Value object of all the elements avaiable.
56484      * @param {Object} data  to appear in grid.
56485      */
56486     setSource : function(source){
56487         this.store.setSource(source);
56488         //this.autoSize();
56489     },
56490     /**
56491      * Gets all the data from the grid.
56492      * @return {Object} data  data stored in grid
56493      */
56494     getSource : function(){
56495         return this.store.getSource();
56496     }
56497 });/*
56498   
56499  * Licence LGPL
56500  
56501  */
56502  
56503 /**
56504  * @class Roo.grid.Calendar
56505  * @extends Roo.util.Grid
56506  * This class extends the Grid to provide a calendar widget
56507  * <br><br>Usage:<pre><code>
56508  var grid = new Roo.grid.Calendar("my-container-id", {
56509      ds: myDataStore,
56510      cm: myColModel,
56511      selModel: mySelectionModel,
56512      autoSizeColumns: true,
56513      monitorWindowResize: false,
56514      trackMouseOver: true
56515      eventstore : real data store..
56516  });
56517  // set any options
56518  grid.render();
56519   
56520   * @constructor
56521  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
56522  * The container MUST have some type of size defined for the grid to fill. The container will be
56523  * automatically set to position relative if it isn't already.
56524  * @param {Object} config A config object that sets properties on this grid.
56525  */
56526 Roo.grid.Calendar = function(container, config){
56527         // initialize the container
56528         this.container = Roo.get(container);
56529         this.container.update("");
56530         this.container.setStyle("overflow", "hidden");
56531     this.container.addClass('x-grid-container');
56532
56533     this.id = this.container.id;
56534
56535     Roo.apply(this, config);
56536     // check and correct shorthanded configs
56537     
56538     var rows = [];
56539     var d =1;
56540     for (var r = 0;r < 6;r++) {
56541         
56542         rows[r]=[];
56543         for (var c =0;c < 7;c++) {
56544             rows[r][c]= '';
56545         }
56546     }
56547     if (this.eventStore) {
56548         this.eventStore= Roo.factory(this.eventStore, Roo.data);
56549         this.eventStore.on('load',this.onLoad, this);
56550         this.eventStore.on('beforeload',this.clearEvents, this);
56551          
56552     }
56553     
56554     this.dataSource = new Roo.data.Store({
56555             proxy: new Roo.data.MemoryProxy(rows),
56556             reader: new Roo.data.ArrayReader({}, [
56557                    'weekday0', 'weekday1', 'weekday2', 'weekday3', 'weekday4', 'weekday5', 'weekday6' ])
56558     });
56559
56560     this.dataSource.load();
56561     this.ds = this.dataSource;
56562     this.ds.xmodule = this.xmodule || false;
56563     
56564     
56565     var cellRender = function(v,x,r)
56566     {
56567         return String.format(
56568             '<div class="fc-day  fc-widget-content"><div>' +
56569                 '<div class="fc-event-container"></div>' +
56570                 '<div class="fc-day-number">{0}</div>'+
56571                 
56572                 '<div class="fc-day-content"><div style="position:relative"></div></div>' +
56573             '</div></div>', v);
56574     
56575     }
56576     
56577     
56578     this.colModel = new Roo.grid.ColumnModel( [
56579         {
56580             xtype: 'ColumnModel',
56581             xns: Roo.grid,
56582             dataIndex : 'weekday0',
56583             header : 'Sunday',
56584             renderer : cellRender
56585         },
56586         {
56587             xtype: 'ColumnModel',
56588             xns: Roo.grid,
56589             dataIndex : 'weekday1',
56590             header : 'Monday',
56591             renderer : cellRender
56592         },
56593         {
56594             xtype: 'ColumnModel',
56595             xns: Roo.grid,
56596             dataIndex : 'weekday2',
56597             header : 'Tuesday',
56598             renderer : cellRender
56599         },
56600         {
56601             xtype: 'ColumnModel',
56602             xns: Roo.grid,
56603             dataIndex : 'weekday3',
56604             header : 'Wednesday',
56605             renderer : cellRender
56606         },
56607         {
56608             xtype: 'ColumnModel',
56609             xns: Roo.grid,
56610             dataIndex : 'weekday4',
56611             header : 'Thursday',
56612             renderer : cellRender
56613         },
56614         {
56615             xtype: 'ColumnModel',
56616             xns: Roo.grid,
56617             dataIndex : 'weekday5',
56618             header : 'Friday',
56619             renderer : cellRender
56620         },
56621         {
56622             xtype: 'ColumnModel',
56623             xns: Roo.grid,
56624             dataIndex : 'weekday6',
56625             header : 'Saturday',
56626             renderer : cellRender
56627         }
56628     ]);
56629     this.cm = this.colModel;
56630     this.cm.xmodule = this.xmodule || false;
56631  
56632         
56633           
56634     //this.selModel = new Roo.grid.CellSelectionModel();
56635     //this.sm = this.selModel;
56636     //this.selModel.init(this);
56637     
56638     
56639     if(this.width){
56640         this.container.setWidth(this.width);
56641     }
56642
56643     if(this.height){
56644         this.container.setHeight(this.height);
56645     }
56646     /** @private */
56647         this.addEvents({
56648         // raw events
56649         /**
56650          * @event click
56651          * The raw click event for the entire grid.
56652          * @param {Roo.EventObject} e
56653          */
56654         "click" : true,
56655         /**
56656          * @event dblclick
56657          * The raw dblclick event for the entire grid.
56658          * @param {Roo.EventObject} e
56659          */
56660         "dblclick" : true,
56661         /**
56662          * @event contextmenu
56663          * The raw contextmenu event for the entire grid.
56664          * @param {Roo.EventObject} e
56665          */
56666         "contextmenu" : true,
56667         /**
56668          * @event mousedown
56669          * The raw mousedown event for the entire grid.
56670          * @param {Roo.EventObject} e
56671          */
56672         "mousedown" : true,
56673         /**
56674          * @event mouseup
56675          * The raw mouseup event for the entire grid.
56676          * @param {Roo.EventObject} e
56677          */
56678         "mouseup" : true,
56679         /**
56680          * @event mouseover
56681          * The raw mouseover event for the entire grid.
56682          * @param {Roo.EventObject} e
56683          */
56684         "mouseover" : true,
56685         /**
56686          * @event mouseout
56687          * The raw mouseout event for the entire grid.
56688          * @param {Roo.EventObject} e
56689          */
56690         "mouseout" : true,
56691         /**
56692          * @event keypress
56693          * The raw keypress event for the entire grid.
56694          * @param {Roo.EventObject} e
56695          */
56696         "keypress" : true,
56697         /**
56698          * @event keydown
56699          * The raw keydown event for the entire grid.
56700          * @param {Roo.EventObject} e
56701          */
56702         "keydown" : true,
56703
56704         // custom events
56705
56706         /**
56707          * @event cellclick
56708          * Fires when a cell is clicked
56709          * @param {Grid} this
56710          * @param {Number} rowIndex
56711          * @param {Number} columnIndex
56712          * @param {Roo.EventObject} e
56713          */
56714         "cellclick" : true,
56715         /**
56716          * @event celldblclick
56717          * Fires when a cell is double clicked
56718          * @param {Grid} this
56719          * @param {Number} rowIndex
56720          * @param {Number} columnIndex
56721          * @param {Roo.EventObject} e
56722          */
56723         "celldblclick" : true,
56724         /**
56725          * @event rowclick
56726          * Fires when a row is clicked
56727          * @param {Grid} this
56728          * @param {Number} rowIndex
56729          * @param {Roo.EventObject} e
56730          */
56731         "rowclick" : true,
56732         /**
56733          * @event rowdblclick
56734          * Fires when a row is double clicked
56735          * @param {Grid} this
56736          * @param {Number} rowIndex
56737          * @param {Roo.EventObject} e
56738          */
56739         "rowdblclick" : true,
56740         /**
56741          * @event headerclick
56742          * Fires when a header is clicked
56743          * @param {Grid} this
56744          * @param {Number} columnIndex
56745          * @param {Roo.EventObject} e
56746          */
56747         "headerclick" : true,
56748         /**
56749          * @event headerdblclick
56750          * Fires when a header cell is double clicked
56751          * @param {Grid} this
56752          * @param {Number} columnIndex
56753          * @param {Roo.EventObject} e
56754          */
56755         "headerdblclick" : true,
56756         /**
56757          * @event rowcontextmenu
56758          * Fires when a row is right clicked
56759          * @param {Grid} this
56760          * @param {Number} rowIndex
56761          * @param {Roo.EventObject} e
56762          */
56763         "rowcontextmenu" : true,
56764         /**
56765          * @event cellcontextmenu
56766          * Fires when a cell is right clicked
56767          * @param {Grid} this
56768          * @param {Number} rowIndex
56769          * @param {Number} cellIndex
56770          * @param {Roo.EventObject} e
56771          */
56772          "cellcontextmenu" : true,
56773         /**
56774          * @event headercontextmenu
56775          * Fires when a header is right clicked
56776          * @param {Grid} this
56777          * @param {Number} columnIndex
56778          * @param {Roo.EventObject} e
56779          */
56780         "headercontextmenu" : true,
56781         /**
56782          * @event bodyscroll
56783          * Fires when the body element is scrolled
56784          * @param {Number} scrollLeft
56785          * @param {Number} scrollTop
56786          */
56787         "bodyscroll" : true,
56788         /**
56789          * @event columnresize
56790          * Fires when the user resizes a column
56791          * @param {Number} columnIndex
56792          * @param {Number} newSize
56793          */
56794         "columnresize" : true,
56795         /**
56796          * @event columnmove
56797          * Fires when the user moves a column
56798          * @param {Number} oldIndex
56799          * @param {Number} newIndex
56800          */
56801         "columnmove" : true,
56802         /**
56803          * @event startdrag
56804          * Fires when row(s) start being dragged
56805          * @param {Grid} this
56806          * @param {Roo.GridDD} dd The drag drop object
56807          * @param {event} e The raw browser event
56808          */
56809         "startdrag" : true,
56810         /**
56811          * @event enddrag
56812          * Fires when a drag operation is complete
56813          * @param {Grid} this
56814          * @param {Roo.GridDD} dd The drag drop object
56815          * @param {event} e The raw browser event
56816          */
56817         "enddrag" : true,
56818         /**
56819          * @event dragdrop
56820          * Fires when dragged row(s) are dropped on a valid DD target
56821          * @param {Grid} this
56822          * @param {Roo.GridDD} dd The drag drop object
56823          * @param {String} targetId The target drag drop object
56824          * @param {event} e The raw browser event
56825          */
56826         "dragdrop" : true,
56827         /**
56828          * @event dragover
56829          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
56830          * @param {Grid} this
56831          * @param {Roo.GridDD} dd The drag drop object
56832          * @param {String} targetId The target drag drop object
56833          * @param {event} e The raw browser event
56834          */
56835         "dragover" : true,
56836         /**
56837          * @event dragenter
56838          *  Fires when the dragged row(s) first cross another DD target while being dragged
56839          * @param {Grid} this
56840          * @param {Roo.GridDD} dd The drag drop object
56841          * @param {String} targetId The target drag drop object
56842          * @param {event} e The raw browser event
56843          */
56844         "dragenter" : true,
56845         /**
56846          * @event dragout
56847          * Fires when the dragged row(s) leave another DD target while being dragged
56848          * @param {Grid} this
56849          * @param {Roo.GridDD} dd The drag drop object
56850          * @param {String} targetId The target drag drop object
56851          * @param {event} e The raw browser event
56852          */
56853         "dragout" : true,
56854         /**
56855          * @event rowclass
56856          * Fires when a row is rendered, so you can change add a style to it.
56857          * @param {GridView} gridview   The grid view
56858          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
56859          */
56860         'rowclass' : true,
56861
56862         /**
56863          * @event render
56864          * Fires when the grid is rendered
56865          * @param {Grid} grid
56866          */
56867         'render' : true,
56868             /**
56869              * @event select
56870              * Fires when a date is selected
56871              * @param {DatePicker} this
56872              * @param {Date} date The selected date
56873              */
56874         'select': true,
56875         /**
56876              * @event monthchange
56877              * Fires when the displayed month changes 
56878              * @param {DatePicker} this
56879              * @param {Date} date The selected month
56880              */
56881         'monthchange': true,
56882         /**
56883              * @event evententer
56884              * Fires when mouse over an event
56885              * @param {Calendar} this
56886              * @param {event} Event
56887              */
56888         'evententer': true,
56889         /**
56890              * @event eventleave
56891              * Fires when the mouse leaves an
56892              * @param {Calendar} this
56893              * @param {event}
56894              */
56895         'eventleave': true,
56896         /**
56897              * @event eventclick
56898              * Fires when the mouse click an
56899              * @param {Calendar} this
56900              * @param {event}
56901              */
56902         'eventclick': true,
56903         /**
56904              * @event eventrender
56905              * Fires before each cell is rendered, so you can modify the contents, like cls / title / qtip
56906              * @param {Calendar} this
56907              * @param {data} data to be modified
56908              */
56909         'eventrender': true
56910         
56911     });
56912
56913     Roo.grid.Grid.superclass.constructor.call(this);
56914     this.on('render', function() {
56915         this.view.el.addClass('x-grid-cal'); 
56916         
56917         (function() { this.setDate(new Date()); }).defer(100,this); //default today..
56918
56919     },this);
56920     
56921     if (!Roo.grid.Calendar.style) {
56922         Roo.grid.Calendar.style = Roo.util.CSS.createStyleSheet({
56923             
56924             
56925             '.x-grid-cal .x-grid-col' :  {
56926                 height: 'auto !important',
56927                 'vertical-align': 'top'
56928             },
56929             '.x-grid-cal  .fc-event-hori' : {
56930                 height: '14px'
56931             }
56932              
56933             
56934         }, Roo.id());
56935     }
56936
56937     
56938     
56939 };
56940 Roo.extend(Roo.grid.Calendar, Roo.grid.Grid, {
56941     /**
56942      * @cfg {Store} eventStore The store that loads events.
56943      */
56944     eventStore : 25,
56945
56946      
56947     activeDate : false,
56948     startDay : 0,
56949     autoWidth : true,
56950     monitorWindowResize : false,
56951
56952     
56953     resizeColumns : function() {
56954         var col = (this.view.el.getWidth() / 7) - 3;
56955         // loop through cols, and setWidth
56956         for(var i =0 ; i < 7 ; i++){
56957             this.cm.setColumnWidth(i, col);
56958         }
56959     },
56960      setDate :function(date) {
56961         
56962         Roo.log('setDate?');
56963         
56964         this.resizeColumns();
56965         var vd = this.activeDate;
56966         this.activeDate = date;
56967 //        if(vd && this.el){
56968 //            var t = date.getTime();
56969 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
56970 //                Roo.log('using add remove');
56971 //                
56972 //                this.fireEvent('monthchange', this, date);
56973 //                
56974 //                this.cells.removeClass("fc-state-highlight");
56975 //                this.cells.each(function(c){
56976 //                   if(c.dateValue == t){
56977 //                       c.addClass("fc-state-highlight");
56978 //                       setTimeout(function(){
56979 //                            try{c.dom.firstChild.focus();}catch(e){}
56980 //                       }, 50);
56981 //                       return false;
56982 //                   }
56983 //                   return true;
56984 //                });
56985 //                return;
56986 //            }
56987 //        }
56988         
56989         var days = date.getDaysInMonth();
56990         
56991         var firstOfMonth = date.getFirstDateOfMonth();
56992         var startingPos = firstOfMonth.getDay()-this.startDay;
56993         
56994         if(startingPos < this.startDay){
56995             startingPos += 7;
56996         }
56997         
56998         var pm = date.add(Date.MONTH, -1);
56999         var prevStart = pm.getDaysInMonth()-startingPos;
57000 //        
57001         
57002         
57003         this.cells = this.view.el.select('.x-grid-row .x-grid-col',true);
57004         
57005         this.textNodes = this.view.el.query('.x-grid-row .x-grid-col .x-grid-cell-text');
57006         //this.cells.addClassOnOver('fc-state-hover');
57007         
57008         var cells = this.cells.elements;
57009         var textEls = this.textNodes;
57010         
57011         //Roo.each(cells, function(cell){
57012         //    cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
57013         //});
57014         
57015         days += startingPos;
57016
57017         // convert everything to numbers so it's fast
57018         var day = 86400000;
57019         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
57020         //Roo.log(d);
57021         //Roo.log(pm);
57022         //Roo.log(prevStart);
57023         
57024         var today = new Date().clearTime().getTime();
57025         var sel = date.clearTime().getTime();
57026         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
57027         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
57028         var ddMatch = this.disabledDatesRE;
57029         var ddText = this.disabledDatesText;
57030         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
57031         var ddaysText = this.disabledDaysText;
57032         var format = this.format;
57033         
57034         var setCellClass = function(cal, cell){
57035             
57036             //Roo.log('set Cell Class');
57037             cell.title = "";
57038             var t = d.getTime();
57039             
57040             //Roo.log(d);
57041             
57042             
57043             cell.dateValue = t;
57044             if(t == today){
57045                 cell.className += " fc-today";
57046                 cell.className += " fc-state-highlight";
57047                 cell.title = cal.todayText;
57048             }
57049             if(t == sel){
57050                 // disable highlight in other month..
57051                 cell.className += " fc-state-highlight";
57052                 
57053             }
57054             // disabling
57055             if(t < min) {
57056                 //cell.className = " fc-state-disabled";
57057                 cell.title = cal.minText;
57058                 return;
57059             }
57060             if(t > max) {
57061                 //cell.className = " fc-state-disabled";
57062                 cell.title = cal.maxText;
57063                 return;
57064             }
57065             if(ddays){
57066                 if(ddays.indexOf(d.getDay()) != -1){
57067                     // cell.title = ddaysText;
57068                    // cell.className = " fc-state-disabled";
57069                 }
57070             }
57071             if(ddMatch && format){
57072                 var fvalue = d.dateFormat(format);
57073                 if(ddMatch.test(fvalue)){
57074                     cell.title = ddText.replace("%0", fvalue);
57075                    cell.className = " fc-state-disabled";
57076                 }
57077             }
57078             
57079             if (!cell.initialClassName) {
57080                 cell.initialClassName = cell.dom.className;
57081             }
57082             
57083             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
57084         };
57085
57086         var i = 0;
57087         
57088         for(; i < startingPos; i++) {
57089             cells[i].dayName =  (++prevStart);
57090             Roo.log(textEls[i]);
57091             d.setDate(d.getDate()+1);
57092             
57093             //cells[i].className = "fc-past fc-other-month";
57094             setCellClass(this, cells[i]);
57095         }
57096         
57097         var intDay = 0;
57098         
57099         for(; i < days; i++){
57100             intDay = i - startingPos + 1;
57101             cells[i].dayName =  (intDay);
57102             d.setDate(d.getDate()+1);
57103             
57104             cells[i].className = ''; // "x-date-active";
57105             setCellClass(this, cells[i]);
57106         }
57107         var extraDays = 0;
57108         
57109         for(; i < 42; i++) {
57110             //textEls[i].innerHTML = (++extraDays);
57111             
57112             d.setDate(d.getDate()+1);
57113             cells[i].dayName = (++extraDays);
57114             cells[i].className = "fc-future fc-other-month";
57115             setCellClass(this, cells[i]);
57116         }
57117         
57118         //this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
57119         
57120         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
57121         
57122         // this will cause all the cells to mis
57123         var rows= [];
57124         var i =0;
57125         for (var r = 0;r < 6;r++) {
57126             for (var c =0;c < 7;c++) {
57127                 this.ds.getAt(r).set('weekday' + c ,cells[i++].dayName );
57128             }    
57129         }
57130         
57131         this.cells = this.view.el.select('.x-grid-row .x-grid-col',true);
57132         for(i=0;i<cells.length;i++) {
57133             
57134             this.cells.elements[i].dayName = cells[i].dayName ;
57135             this.cells.elements[i].className = cells[i].className;
57136             this.cells.elements[i].initialClassName = cells[i].initialClassName ;
57137             this.cells.elements[i].title = cells[i].title ;
57138             this.cells.elements[i].dateValue = cells[i].dateValue ;
57139         }
57140         
57141         
57142         
57143         
57144         //this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
57145         //this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
57146         
57147         ////if(totalRows != 6){
57148             //this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
57149            // this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
57150        // }
57151         
57152         this.fireEvent('monthchange', this, date);
57153         
57154         
57155     },
57156  /**
57157      * Returns the grid's SelectionModel.
57158      * @return {SelectionModel}
57159      */
57160     getSelectionModel : function(){
57161         if(!this.selModel){
57162             this.selModel = new Roo.grid.CellSelectionModel();
57163         }
57164         return this.selModel;
57165     },
57166
57167     load: function() {
57168         this.eventStore.load()
57169         
57170         
57171         
57172     },
57173     
57174     findCell : function(dt) {
57175         dt = dt.clearTime().getTime();
57176         var ret = false;
57177         this.cells.each(function(c){
57178             //Roo.log("check " +c.dateValue + '?=' + dt);
57179             if(c.dateValue == dt){
57180                 ret = c;
57181                 return false;
57182             }
57183             return true;
57184         });
57185         
57186         return ret;
57187     },
57188     
57189     findCells : function(rec) {
57190         var s = rec.data.start_dt.clone().clearTime().getTime();
57191        // Roo.log(s);
57192         var e= rec.data.end_dt.clone().clearTime().getTime();
57193        // Roo.log(e);
57194         var ret = [];
57195         this.cells.each(function(c){
57196              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
57197             
57198             if(c.dateValue > e){
57199                 return ;
57200             }
57201             if(c.dateValue < s){
57202                 return ;
57203             }
57204             ret.push(c);
57205         });
57206         
57207         return ret;    
57208     },
57209     
57210     findBestRow: function(cells)
57211     {
57212         var ret = 0;
57213         
57214         for (var i =0 ; i < cells.length;i++) {
57215             ret  = Math.max(cells[i].rows || 0,ret);
57216         }
57217         return ret;
57218         
57219     },
57220     
57221     
57222     addItem : function(rec)
57223     {
57224         // look for vertical location slot in
57225         var cells = this.findCells(rec);
57226         
57227         rec.row = this.findBestRow(cells);
57228         
57229         // work out the location.
57230         
57231         var crow = false;
57232         var rows = [];
57233         for(var i =0; i < cells.length; i++) {
57234             if (!crow) {
57235                 crow = {
57236                     start : cells[i],
57237                     end :  cells[i]
57238                 };
57239                 continue;
57240             }
57241             if (crow.start.getY() == cells[i].getY()) {
57242                 // on same row.
57243                 crow.end = cells[i];
57244                 continue;
57245             }
57246             // different row.
57247             rows.push(crow);
57248             crow = {
57249                 start: cells[i],
57250                 end : cells[i]
57251             };
57252             
57253         }
57254         
57255         rows.push(crow);
57256         rec.els = [];
57257         rec.rows = rows;
57258         rec.cells = cells;
57259         for (var i = 0; i < cells.length;i++) {
57260             cells[i].rows = Math.max(cells[i].rows || 0 , rec.row + 1 );
57261             
57262         }
57263         
57264         
57265     },
57266     
57267     clearEvents: function() {
57268         
57269         if (!this.eventStore.getCount()) {
57270             return;
57271         }
57272         // reset number of rows in cells.
57273         Roo.each(this.cells.elements, function(c){
57274             c.rows = 0;
57275         });
57276         
57277         this.eventStore.each(function(e) {
57278             this.clearEvent(e);
57279         },this);
57280         
57281     },
57282     
57283     clearEvent : function(ev)
57284     {
57285         if (ev.els) {
57286             Roo.each(ev.els, function(el) {
57287                 el.un('mouseenter' ,this.onEventEnter, this);
57288                 el.un('mouseleave' ,this.onEventLeave, this);
57289                 el.remove();
57290             },this);
57291             ev.els = [];
57292         }
57293     },
57294     
57295     
57296     renderEvent : function(ev,ctr) {
57297         if (!ctr) {
57298              ctr = this.view.el.select('.fc-event-container',true).first();
57299         }
57300         
57301          
57302         this.clearEvent(ev);
57303             //code
57304        
57305         
57306         
57307         ev.els = [];
57308         var cells = ev.cells;
57309         var rows = ev.rows;
57310         this.fireEvent('eventrender', this, ev);
57311         
57312         for(var i =0; i < rows.length; i++) {
57313             
57314             cls = '';
57315             if (i == 0) {
57316                 cls += ' fc-event-start';
57317             }
57318             if ((i+1) == rows.length) {
57319                 cls += ' fc-event-end';
57320             }
57321             
57322             //Roo.log(ev.data);
57323             // how many rows should it span..
57324             var cg = this.eventTmpl.append(ctr,Roo.apply({
57325                 fccls : cls
57326                 
57327             }, ev.data) , true);
57328             
57329             
57330             cg.on('mouseenter' ,this.onEventEnter, this, ev);
57331             cg.on('mouseleave' ,this.onEventLeave, this, ev);
57332             cg.on('click', this.onEventClick, this, ev);
57333             
57334             ev.els.push(cg);
57335             
57336             var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
57337             var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
57338             //Roo.log(cg);
57339              
57340             cg.setXY([sbox.x +2, sbox.y +(ev.row * 20)]);    
57341             cg.setWidth(ebox.right - sbox.x -2);
57342         }
57343     },
57344     
57345     renderEvents: function()
57346     {   
57347         // first make sure there is enough space..
57348         
57349         if (!this.eventTmpl) {
57350             this.eventTmpl = new Roo.Template(
57351                 '<div class="roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable {fccls} {cls}"  style="position: absolute" unselectable="on">' +
57352                     '<div class="fc-event-inner">' +
57353                         '<span class="fc-event-time">{time}</span>' +
57354                         '<span class="fc-event-title" qtip="{qtip}">{title}</span>' +
57355                     '</div>' +
57356                     '<div class="ui-resizable-heandle ui-resizable-e">&nbsp;&nbsp;&nbsp;</div>' +
57357                 '</div>'
57358             );
57359                 
57360         }
57361                
57362         
57363         
57364         this.cells.each(function(c) {
57365             //Roo.log(c.select('.fc-day-content div',true).first());
57366             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, (c.rows || 1) * 20));
57367         });
57368         
57369         var ctr = this.view.el.select('.fc-event-container',true).first();
57370         
57371         var cls;
57372         this.eventStore.each(function(ev){
57373             
57374             this.renderEvent(ev);
57375              
57376              
57377         }, this);
57378         this.view.layout();
57379         
57380     },
57381     
57382     onEventEnter: function (e, el,event,d) {
57383         this.fireEvent('evententer', this, el, event);
57384     },
57385     
57386     onEventLeave: function (e, el,event,d) {
57387         this.fireEvent('eventleave', this, el, event);
57388     },
57389     
57390     onEventClick: function (e, el,event,d) {
57391         this.fireEvent('eventclick', this, el, event);
57392     },
57393     
57394     onMonthChange: function () {
57395         this.store.load();
57396     },
57397     
57398     onLoad: function () {
57399         
57400         //Roo.log('calendar onload');
57401 //         
57402         if(this.eventStore.getCount() > 0){
57403             
57404            
57405             
57406             this.eventStore.each(function(d){
57407                 
57408                 
57409                 // FIXME..
57410                 var add =   d.data;
57411                 if (typeof(add.end_dt) == 'undefined')  {
57412                     Roo.log("Missing End time in calendar data: ");
57413                     Roo.log(d);
57414                     return;
57415                 }
57416                 if (typeof(add.start_dt) == 'undefined')  {
57417                     Roo.log("Missing Start time in calendar data: ");
57418                     Roo.log(d);
57419                     return;
57420                 }
57421                 add.start_dt = typeof(add.start_dt) == 'string' ? Date.parseDate(add.start_dt,'Y-m-d H:i:s') : add.start_dt,
57422                 add.end_dt = typeof(add.end_dt) == 'string' ? Date.parseDate(add.end_dt,'Y-m-d H:i:s') : add.end_dt,
57423                 add.id = add.id || d.id;
57424                 add.title = add.title || '??';
57425                 
57426                 this.addItem(d);
57427                 
57428              
57429             },this);
57430         }
57431         
57432         this.renderEvents();
57433     }
57434     
57435
57436 });
57437 /*
57438  grid : {
57439                 xtype: 'Grid',
57440                 xns: Roo.grid,
57441                 listeners : {
57442                     render : function ()
57443                     {
57444                         _this.grid = this;
57445                         
57446                         if (!this.view.el.hasClass('course-timesheet')) {
57447                             this.view.el.addClass('course-timesheet');
57448                         }
57449                         if (this.tsStyle) {
57450                             this.ds.load({});
57451                             return; 
57452                         }
57453                         Roo.log('width');
57454                         Roo.log(_this.grid.view.el.getWidth());
57455                         
57456                         
57457                         this.tsStyle =  Roo.util.CSS.createStyleSheet({
57458                             '.course-timesheet .x-grid-row' : {
57459                                 height: '80px'
57460                             },
57461                             '.x-grid-row td' : {
57462                                 'vertical-align' : 0
57463                             },
57464                             '.course-edit-link' : {
57465                                 'color' : 'blue',
57466                                 'text-overflow' : 'ellipsis',
57467                                 'overflow' : 'hidden',
57468                                 'white-space' : 'nowrap',
57469                                 'cursor' : 'pointer'
57470                             },
57471                             '.sub-link' : {
57472                                 'color' : 'green'
57473                             },
57474                             '.de-act-sup-link' : {
57475                                 'color' : 'purple',
57476                                 'text-decoration' : 'line-through'
57477                             },
57478                             '.de-act-link' : {
57479                                 'color' : 'red',
57480                                 'text-decoration' : 'line-through'
57481                             },
57482                             '.course-timesheet .course-highlight' : {
57483                                 'border-top-style': 'dashed !important',
57484                                 'border-bottom-bottom': 'dashed !important'
57485                             },
57486                             '.course-timesheet .course-item' : {
57487                                 'font-family'   : 'tahoma, arial, helvetica',
57488                                 'font-size'     : '11px',
57489                                 'overflow'      : 'hidden',
57490                                 'padding-left'  : '10px',
57491                                 'padding-right' : '10px',
57492                                 'padding-top' : '10px' 
57493                             }
57494                             
57495                         }, Roo.id());
57496                                 this.ds.load({});
57497                     }
57498                 },
57499                 autoWidth : true,
57500                 monitorWindowResize : false,
57501                 cellrenderer : function(v,x,r)
57502                 {
57503                     return v;
57504                 },
57505                 sm : {
57506                     xtype: 'CellSelectionModel',
57507                     xns: Roo.grid
57508                 },
57509                 dataSource : {
57510                     xtype: 'Store',
57511                     xns: Roo.data,
57512                     listeners : {
57513                         beforeload : function (_self, options)
57514                         {
57515                             options.params = options.params || {};
57516                             options.params._month = _this.monthField.getValue();
57517                             options.params.limit = 9999;
57518                             options.params['sort'] = 'when_dt';    
57519                             options.params['dir'] = 'ASC';    
57520                             this.proxy.loadResponse = this.loadResponse;
57521                             Roo.log("load?");
57522                             //this.addColumns();
57523                         },
57524                         load : function (_self, records, options)
57525                         {
57526                             _this.grid.view.el.select('.course-edit-link', true).on('click', function() {
57527                                 // if you click on the translation.. you can edit it...
57528                                 var el = Roo.get(this);
57529                                 var id = el.dom.getAttribute('data-id');
57530                                 var d = el.dom.getAttribute('data-date');
57531                                 var t = el.dom.getAttribute('data-time');
57532                                 //var id = this.child('span').dom.textContent;
57533                                 
57534                                 //Roo.log(this);
57535                                 Pman.Dialog.CourseCalendar.show({
57536                                     id : id,
57537                                     when_d : d,
57538                                     when_t : t,
57539                                     productitem_active : id ? 1 : 0
57540                                 }, function() {
57541                                     _this.grid.ds.load({});
57542                                 });
57543                            
57544                            });
57545                            
57546                            _this.panel.fireEvent('resize', [ '', '' ]);
57547                         }
57548                     },
57549                     loadResponse : function(o, success, response){
57550                             // this is overridden on before load..
57551                             
57552                             Roo.log("our code?");       
57553                             //Roo.log(success);
57554                             //Roo.log(response)
57555                             delete this.activeRequest;
57556                             if(!success){
57557                                 this.fireEvent("loadexception", this, o, response);
57558                                 o.request.callback.call(o.request.scope, null, o.request.arg, false);
57559                                 return;
57560                             }
57561                             var result;
57562                             try {
57563                                 result = o.reader.read(response);
57564                             }catch(e){
57565                                 Roo.log("load exception?");
57566                                 this.fireEvent("loadexception", this, o, response, e);
57567                                 o.request.callback.call(o.request.scope, null, o.request.arg, false);
57568                                 return;
57569                             }
57570                             Roo.log("ready...");        
57571                             // loop through result.records;
57572                             // and set this.tdate[date] = [] << array of records..
57573                             _this.tdata  = {};
57574                             Roo.each(result.records, function(r){
57575                                 //Roo.log(r.data);
57576                                 if(typeof(_this.tdata[r.data.when_dt.format('j')]) == 'undefined'){
57577                                     _this.tdata[r.data.when_dt.format('j')] = [];
57578                                 }
57579                                 _this.tdata[r.data.when_dt.format('j')].push(r.data);
57580                             });
57581                             
57582                             //Roo.log(_this.tdata);
57583                             
57584                             result.records = [];
57585                             result.totalRecords = 6;
57586                     
57587                             // let's generate some duumy records for the rows.
57588                             //var st = _this.dateField.getValue();
57589                             
57590                             // work out monday..
57591                             //st = st.add(Date.DAY, -1 * st.format('w'));
57592                             
57593                             var date = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
57594                             
57595                             var firstOfMonth = date.getFirstDayOfMonth();
57596                             var days = date.getDaysInMonth();
57597                             var d = 1;
57598                             var firstAdded = false;
57599                             for (var i = 0; i < result.totalRecords ; i++) {
57600                                 //var d= st.add(Date.DAY, i);
57601                                 var row = {};
57602                                 var added = 0;
57603                                 for(var w = 0 ; w < 7 ; w++){
57604                                     if(!firstAdded && firstOfMonth != w){
57605                                         continue;
57606                                     }
57607                                     if(d > days){
57608                                         continue;
57609                                     }
57610                                     firstAdded = true;
57611                                     var dd = (d > 0 && d < 10) ? "0"+d : d;
57612                                     row['weekday'+w] = String.format(
57613                                                     '<span style="font-size: 16px;"><b>{0}</b></span>'+
57614                                                     '<span class="course-edit-link" style="color:blue;" data-id="0" data-date="{1}"> Add New</span>',
57615                                                     d,
57616                                                     date.format('Y-m-')+dd
57617                                                 );
57618                                     added++;
57619                                     if(typeof(_this.tdata[d]) != 'undefined'){
57620                                         Roo.each(_this.tdata[d], function(r){
57621                                             var is_sub = '';
57622                                             var deactive = '';
57623                                             var id = r.id;
57624                                             var desc = (r.productitem_id_descrip) ? r.productitem_id_descrip : '';
57625                                             if(r.parent_id*1>0){
57626                                                 is_sub = (r.productitem_id_visible*1 < 1) ? 'de-act-sup-link' :'sub-link';
57627                                                 id = r.parent_id;
57628                                             }
57629                                             if(r.productitem_id_visible*1 < 1 && r.parent_id*1 < 1){
57630                                                 deactive = 'de-act-link';
57631                                             }
57632                                             
57633                                             row['weekday'+w] += String.format(
57634                                                     '<br /><span class="course-edit-link {3} {4}" qtip="{5}" data-id="{0}">{2} - {1}</span>',
57635                                                     id, //0
57636                                                     r.product_id_name, //1
57637                                                     r.when_dt.format('h:ia'), //2
57638                                                     is_sub, //3
57639                                                     deactive, //4
57640                                                     desc // 5
57641                                             );
57642                                         });
57643                                     }
57644                                     d++;
57645                                 }
57646                                 
57647                                 // only do this if something added..
57648                                 if(added > 0){ 
57649                                     result.records.push(_this.grid.dataSource.reader.newRow(row));
57650                                 }
57651                                 
57652                                 
57653                                 // push it twice. (second one with an hour..
57654                                 
57655                             }
57656                             //Roo.log(result);
57657                             this.fireEvent("load", this, o, o.request.arg);
57658                             o.request.callback.call(o.request.scope, result, o.request.arg, true);
57659                         },
57660                     sortInfo : {field: 'when_dt', direction : 'ASC' },
57661                     proxy : {
57662                         xtype: 'HttpProxy',
57663                         xns: Roo.data,
57664                         method : 'GET',
57665                         url : baseURL + '/Roo/Shop_course.php'
57666                     },
57667                     reader : {
57668                         xtype: 'JsonReader',
57669                         xns: Roo.data,
57670                         id : 'id',
57671                         fields : [
57672                             {
57673                                 'name': 'id',
57674                                 'type': 'int'
57675                             },
57676                             {
57677                                 'name': 'when_dt',
57678                                 'type': 'string'
57679                             },
57680                             {
57681                                 'name': 'end_dt',
57682                                 'type': 'string'
57683                             },
57684                             {
57685                                 'name': 'parent_id',
57686                                 'type': 'int'
57687                             },
57688                             {
57689                                 'name': 'product_id',
57690                                 'type': 'int'
57691                             },
57692                             {
57693                                 'name': 'productitem_id',
57694                                 'type': 'int'
57695                             },
57696                             {
57697                                 'name': 'guid',
57698                                 'type': 'int'
57699                             }
57700                         ]
57701                     }
57702                 },
57703                 toolbar : {
57704                     xtype: 'Toolbar',
57705                     xns: Roo,
57706                     items : [
57707                         {
57708                             xtype: 'Button',
57709                             xns: Roo.Toolbar,
57710                             listeners : {
57711                                 click : function (_self, e)
57712                                 {
57713                                     var sd = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
57714                                     sd.setMonth(sd.getMonth()-1);
57715                                     _this.monthField.setValue(sd.format('Y-m-d'));
57716                                     _this.grid.ds.load({});
57717                                 }
57718                             },
57719                             text : "Back"
57720                         },
57721                         {
57722                             xtype: 'Separator',
57723                             xns: Roo.Toolbar
57724                         },
57725                         {
57726                             xtype: 'MonthField',
57727                             xns: Roo.form,
57728                             listeners : {
57729                                 render : function (_self)
57730                                 {
57731                                     _this.monthField = _self;
57732                                    // _this.monthField.set  today
57733                                 },
57734                                 select : function (combo, date)
57735                                 {
57736                                     _this.grid.ds.load({});
57737                                 }
57738                             },
57739                             value : (function() { return new Date(); })()
57740                         },
57741                         {
57742                             xtype: 'Separator',
57743                             xns: Roo.Toolbar
57744                         },
57745                         {
57746                             xtype: 'TextItem',
57747                             xns: Roo.Toolbar,
57748                             text : "Blue: in-active, green: in-active sup-event, red: de-active, purple: de-active sup-event"
57749                         },
57750                         {
57751                             xtype: 'Fill',
57752                             xns: Roo.Toolbar
57753                         },
57754                         {
57755                             xtype: 'Button',
57756                             xns: Roo.Toolbar,
57757                             listeners : {
57758                                 click : function (_self, e)
57759                                 {
57760                                     var sd = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
57761                                     sd.setMonth(sd.getMonth()+1);
57762                                     _this.monthField.setValue(sd.format('Y-m-d'));
57763                                     _this.grid.ds.load({});
57764                                 }
57765                             },
57766                             text : "Next"
57767                         }
57768                     ]
57769                 },
57770                  
57771             }
57772         };
57773         
57774         *//*
57775  * Based on:
57776  * Ext JS Library 1.1.1
57777  * Copyright(c) 2006-2007, Ext JS, LLC.
57778  *
57779  * Originally Released Under LGPL - original licence link has changed is not relivant.
57780  *
57781  * Fork - LGPL
57782  * <script type="text/javascript">
57783  */
57784  
57785 /**
57786  * @class Roo.LoadMask
57787  * A simple utility class for generically masking elements while loading data.  If the element being masked has
57788  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
57789  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
57790  * element's UpdateManager load indicator and will be destroyed after the initial load.
57791  * @constructor
57792  * Create a new LoadMask
57793  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
57794  * @param {Object} config The config object
57795  */
57796 Roo.LoadMask = function(el, config){
57797     this.el = Roo.get(el);
57798     Roo.apply(this, config);
57799     if(this.store){
57800         this.store.on('beforeload', this.onBeforeLoad, this);
57801         this.store.on('load', this.onLoad, this);
57802         this.store.on('loadexception', this.onLoadException, this);
57803         this.removeMask = false;
57804     }else{
57805         var um = this.el.getUpdateManager();
57806         um.showLoadIndicator = false; // disable the default indicator
57807         um.on('beforeupdate', this.onBeforeLoad, this);
57808         um.on('update', this.onLoad, this);
57809         um.on('failure', this.onLoad, this);
57810         this.removeMask = true;
57811     }
57812 };
57813
57814 Roo.LoadMask.prototype = {
57815     /**
57816      * @cfg {Boolean} removeMask
57817      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
57818      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
57819      */
57820     /**
57821      * @cfg {String} msg
57822      * The text to display in a centered loading message box (defaults to 'Loading...')
57823      */
57824     msg : 'Loading...',
57825     /**
57826      * @cfg {String} msgCls
57827      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
57828      */
57829     msgCls : 'x-mask-loading',
57830
57831     /**
57832      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
57833      * @type Boolean
57834      */
57835     disabled: false,
57836
57837     /**
57838      * Disables the mask to prevent it from being displayed
57839      */
57840     disable : function(){
57841        this.disabled = true;
57842     },
57843
57844     /**
57845      * Enables the mask so that it can be displayed
57846      */
57847     enable : function(){
57848         this.disabled = false;
57849     },
57850     
57851     onLoadException : function()
57852     {
57853         Roo.log(arguments);
57854         
57855         if (typeof(arguments[3]) != 'undefined') {
57856             Roo.MessageBox.alert("Error loading",arguments[3]);
57857         } 
57858         /*
57859         try {
57860             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
57861                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
57862             }   
57863         } catch(e) {
57864             
57865         }
57866         */
57867     
57868         
57869         
57870         this.el.unmask(this.removeMask);
57871     },
57872     // private
57873     onLoad : function()
57874     {
57875         this.el.unmask(this.removeMask);
57876     },
57877
57878     // private
57879     onBeforeLoad : function(){
57880         if(!this.disabled){
57881             this.el.mask(this.msg, this.msgCls);
57882         }
57883     },
57884
57885     // private
57886     destroy : function(){
57887         if(this.store){
57888             this.store.un('beforeload', this.onBeforeLoad, this);
57889             this.store.un('load', this.onLoad, this);
57890             this.store.un('loadexception', this.onLoadException, this);
57891         }else{
57892             var um = this.el.getUpdateManager();
57893             um.un('beforeupdate', this.onBeforeLoad, this);
57894             um.un('update', this.onLoad, this);
57895             um.un('failure', this.onLoad, this);
57896         }
57897     }
57898 };/*
57899  * Based on:
57900  * Ext JS Library 1.1.1
57901  * Copyright(c) 2006-2007, Ext JS, LLC.
57902  *
57903  * Originally Released Under LGPL - original licence link has changed is not relivant.
57904  *
57905  * Fork - LGPL
57906  * <script type="text/javascript">
57907  */
57908
57909
57910 /**
57911  * @class Roo.XTemplate
57912  * @extends Roo.Template
57913  * Provides a template that can have nested templates for loops or conditionals. The syntax is:
57914 <pre><code>
57915 var t = new Roo.XTemplate(
57916         '&lt;select name="{name}"&gt;',
57917                 '&lt;tpl for="options"&gt;&lt;option value="{value:trim}"&gt;{text:ellipsis(10)}&lt;/option&gt;&lt;/tpl&gt;',
57918         '&lt;/select&gt;'
57919 );
57920  
57921 // then append, applying the master template values
57922  </code></pre>
57923  *
57924  * Supported features:
57925  *
57926  *  Tags:
57927
57928 <pre><code>
57929       {a_variable} - output encoded.
57930       {a_variable.format:("Y-m-d")} - call a method on the variable
57931       {a_variable:raw} - unencoded output
57932       {a_variable:toFixed(1,2)} - Roo.util.Format."toFixed"
57933       {a_variable:this.method_on_template(...)} - call a method on the template object.
57934  
57935 </code></pre>
57936  *  The tpl tag:
57937 <pre><code>
57938         &lt;tpl for="a_variable or condition.."&gt;&lt;/tpl&gt;
57939         &lt;tpl if="a_variable or condition"&gt;&lt;/tpl&gt;
57940         &lt;tpl exec="some javascript"&gt;&lt;/tpl&gt;
57941         &lt;tpl name="named_template"&gt;&lt;/tpl&gt; (experimental)
57942   
57943         &lt;tpl for="."&gt;&lt;/tpl&gt; - just iterate the property..
57944         &lt;tpl for=".."&gt;&lt;/tpl&gt; - iterates with the parent (probably the template) 
57945 </code></pre>
57946  *      
57947  */
57948 Roo.XTemplate = function()
57949 {
57950     Roo.XTemplate.superclass.constructor.apply(this, arguments);
57951     if (this.html) {
57952         this.compile();
57953     }
57954 };
57955
57956
57957 Roo.extend(Roo.XTemplate, Roo.Template, {
57958
57959     /**
57960      * The various sub templates
57961      */
57962     tpls : false,
57963     /**
57964      *
57965      * basic tag replacing syntax
57966      * WORD:WORD()
57967      *
57968      * // you can fake an object call by doing this
57969      *  x.t:(test,tesT) 
57970      * 
57971      */
57972     re : /\{([\w-\.]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
57973
57974     /**
57975      * compile the template
57976      *
57977      * This is not recursive, so I'm not sure how nested templates are really going to be handled..
57978      *
57979      */
57980     compile: function()
57981     {
57982         var s = this.html;
57983      
57984         s = ['<tpl>', s, '</tpl>'].join('');
57985     
57986         var re     = /<tpl\b[^>]*>((?:(?=([^<]+))\2|<(?!tpl\b[^>]*>))*?)<\/tpl>/,
57987             nameRe = /^<tpl\b[^>]*?for="(.*?)"/,
57988             ifRe   = /^<tpl\b[^>]*?if="(.*?)"/,
57989             execRe = /^<tpl\b[^>]*?exec="(.*?)"/,
57990             namedRe = /^<tpl\b[^>]*?name="(\w+)"/,  // named templates..
57991             m,
57992             id     = 0,
57993             tpls   = [];
57994     
57995         while(true == !!(m = s.match(re))){
57996             var forMatch   = m[0].match(nameRe),
57997                 ifMatch   = m[0].match(ifRe),
57998                 execMatch   = m[0].match(execRe),
57999                 namedMatch   = m[0].match(namedRe),
58000                 
58001                 exp  = null, 
58002                 fn   = null,
58003                 exec = null,
58004                 name = forMatch && forMatch[1] ? forMatch[1] : '';
58005                 
58006             if (ifMatch) {
58007                 // if - puts fn into test..
58008                 exp = ifMatch && ifMatch[1] ? ifMatch[1] : null;
58009                 if(exp){
58010                    fn = new Function('values', 'parent', 'with(values){ return '+(Roo.util.Format.htmlDecode(exp))+'; }');
58011                 }
58012             }
58013             
58014             if (execMatch) {
58015                 // exec - calls a function... returns empty if true is  returned.
58016                 exp = execMatch && execMatch[1] ? execMatch[1] : null;
58017                 if(exp){
58018                    exec = new Function('values', 'parent', 'with(values){ '+(Roo.util.Format.htmlDecode(exp))+'; }');
58019                 }
58020             }
58021             
58022             
58023             if (name) {
58024                 // for = 
58025                 switch(name){
58026                     case '.':  name = new Function('values', 'parent', 'with(values){ return values; }'); break;
58027                     case '..': name = new Function('values', 'parent', 'with(values){ return parent; }'); break;
58028                     default:   name = new Function('values', 'parent', 'with(values){ return '+name+'; }');
58029                 }
58030             }
58031             var uid = namedMatch ? namedMatch[1] : id;
58032             
58033             
58034             tpls.push({
58035                 id:     namedMatch ? namedMatch[1] : id,
58036                 target: name,
58037                 exec:   exec,
58038                 test:   fn,
58039                 body:   m[1] || ''
58040             });
58041             if (namedMatch) {
58042                 s = s.replace(m[0], '');
58043             } else { 
58044                 s = s.replace(m[0], '{xtpl'+ id + '}');
58045             }
58046             ++id;
58047         }
58048         this.tpls = [];
58049         for(var i = tpls.length-1; i >= 0; --i){
58050             this.compileTpl(tpls[i]);
58051             this.tpls[tpls[i].id] = tpls[i];
58052         }
58053         this.master = tpls[tpls.length-1];
58054         return this;
58055     },
58056     /**
58057      * same as applyTemplate, except it's done to one of the subTemplates
58058      * when using named templates, you can do:
58059      *
58060      * var str = pl.applySubTemplate('your-name', values);
58061      *
58062      * 
58063      * @param {Number} id of the template
58064      * @param {Object} values to apply to template
58065      * @param {Object} parent (normaly the instance of this object)
58066      */
58067     applySubTemplate : function(id, values, parent)
58068     {
58069         
58070         
58071         var t = this.tpls[id];
58072         
58073         
58074         try { 
58075             if(t.test && !t.test.call(this, values, parent)){
58076                 return '';
58077             }
58078         } catch(e) {
58079             Roo.log("Xtemplate.applySubTemplate 'test': Exception thrown");
58080             Roo.log(e.toString());
58081             Roo.log(t.test);
58082             return ''
58083         }
58084         try { 
58085             
58086             if(t.exec && t.exec.call(this, values, parent)){
58087                 return '';
58088             }
58089         } catch(e) {
58090             Roo.log("Xtemplate.applySubTemplate 'exec': Exception thrown");
58091             Roo.log(e.toString());
58092             Roo.log(t.exec);
58093             return ''
58094         }
58095         try {
58096             var vs = t.target ? t.target.call(this, values, parent) : values;
58097             parent = t.target ? values : parent;
58098             if(t.target && vs instanceof Array){
58099                 var buf = [];
58100                 for(var i = 0, len = vs.length; i < len; i++){
58101                     buf[buf.length] = t.compiled.call(this, vs[i], parent);
58102                 }
58103                 return buf.join('');
58104             }
58105             return t.compiled.call(this, vs, parent);
58106         } catch (e) {
58107             Roo.log("Xtemplate.applySubTemplate : Exception thrown");
58108             Roo.log(e.toString());
58109             Roo.log(t.compiled);
58110             return '';
58111         }
58112     },
58113
58114     compileTpl : function(tpl)
58115     {
58116         var fm = Roo.util.Format;
58117         var useF = this.disableFormats !== true;
58118         var sep = Roo.isGecko ? "+" : ",";
58119         var undef = function(str) {
58120             Roo.log("Property not found :"  + str);
58121             return '';
58122         };
58123         
58124         var fn = function(m, name, format, args)
58125         {
58126             //Roo.log(arguments);
58127             args = args ? args.replace(/\\'/g,"'") : args;
58128             //["{TEST:(a,b,c)}", "TEST", "", "a,b,c", 0, "{TEST:(a,b,c)}"]
58129             if (typeof(format) == 'undefined') {
58130                 format= 'htmlEncode';
58131             }
58132             if (format == 'raw' ) {
58133                 format = false;
58134             }
58135             
58136             if(name.substr(0, 4) == 'xtpl'){
58137                 return "'"+ sep +'this.applySubTemplate('+name.substr(4)+', values, parent)'+sep+"'";
58138             }
58139             
58140             // build an array of options to determine if value is undefined..
58141             
58142             // basically get 'xxxx.yyyy' then do
58143             // (typeof(xxxx) == 'undefined' || typeof(xxx.yyyy) == 'undefined') ?
58144             //    (function () { Roo.log("Property not found"); return ''; })() :
58145             //    ......
58146             
58147             var udef_ar = [];
58148             var lookfor = '';
58149             Roo.each(name.split('.'), function(st) {
58150                 lookfor += (lookfor.length ? '.': '') + st;
58151                 udef_ar.push(  "(typeof(" + lookfor + ") == 'undefined')"  );
58152             });
58153             
58154             var udef_st = '((' + udef_ar.join(" || ") +") ? undef('" + name + "') : "; // .. needs )
58155             
58156             
58157             if(format && useF){
58158                 
58159                 args = args ? ',' + args : "";
58160                  
58161                 if(format.substr(0, 5) != "this."){
58162                     format = "fm." + format + '(';
58163                 }else{
58164                     format = 'this.call("'+ format.substr(5) + '", ';
58165                     args = ", values";
58166                 }
58167                 
58168                 return "'"+ sep +   udef_st   +    format + name + args + "))"+sep+"'";
58169             }
58170              
58171             if (args.length) {
58172                 // called with xxyx.yuu:(test,test)
58173                 // change to ()
58174                 return "'"+ sep + udef_st  + name + '(' +  args + "))"+sep+"'";
58175             }
58176             // raw.. - :raw modifier..
58177             return "'"+ sep + udef_st  + name + ")"+sep+"'";
58178             
58179         };
58180         var body;
58181         // branched to use + in gecko and [].join() in others
58182         if(Roo.isGecko){
58183             body = "tpl.compiled = function(values, parent){  with(values) { return '" +
58184                    tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
58185                     "';};};";
58186         }else{
58187             body = ["tpl.compiled = function(values, parent){  with (values) { return ['"];
58188             body.push(tpl.body.replace(/(\r\n|\n)/g,
58189                             '\\n').replace(/'/g, "\\'").replace(this.re, fn));
58190             body.push("'].join('');};};");
58191             body = body.join('');
58192         }
58193         
58194         Roo.debug && Roo.log(body.replace(/\\n/,'\n'));
58195        
58196         /** eval:var:tpl eval:var:fm eval:var:useF eval:var:undef  */
58197         eval(body);
58198         
58199         return this;
58200     },
58201
58202     applyTemplate : function(values){
58203         return this.master.compiled.call(this, values, {});
58204         //var s = this.subs;
58205     },
58206
58207     apply : function(){
58208         return this.applyTemplate.apply(this, arguments);
58209     }
58210
58211  });
58212
58213 Roo.XTemplate.from = function(el){
58214     el = Roo.getDom(el);
58215     return new Roo.XTemplate(el.value || el.innerHTML);
58216 };