roojs-core.js
[roojs1] / roojs-debug.js
1 /*
2  * Based on:
3  * Ext JS Library 1.1.1
4  * Copyright(c) 2006-2007, Ext JS, LLC.
5  *
6  * Originally Released Under LGPL - original licence link has changed is not relivant.
7  *
8  * Fork - LGPL
9  * <script type="text/javascript">
10  */
11  
12
13
14
15
16 // for old browsers
17 window["undefined"] = window["undefined"];
18
19 /**
20  * @class Roo
21  * Roo core utilities and functions.
22  * @singleton
23  */
24 var Roo = {}; 
25 /**
26  * Copies all the properties of config to obj.
27  * @param {Object} obj The receiver of the properties
28  * @param {Object} config The source of the properties
29  * @param {Object} defaults A different object that will also be applied for default values
30  * @return {Object} returns obj
31  * @member Roo apply
32  */
33
34  
35 Roo.apply = function(o, c, defaults){
36     if(defaults){
37         // no "this" reference for friendly out of scope calls
38         Roo.apply(o, defaults);
39     }
40     if(o && c && typeof c == 'object'){
41         for(var p in c){
42             o[p] = c[p];
43         }
44     }
45     return o;
46 };
47
48
49 (function(){
50     var idSeed = 0;
51     var ua = navigator.userAgent.toLowerCase();
52
53     var isStrict = document.compatMode == "CSS1Compat",
54         isOpera = ua.indexOf("opera") > -1,
55         isSafari = (/webkit|khtml/).test(ua),
56         isIE = ua.indexOf("msie") > -1,
57         isIE7 = ua.indexOf("msie 7") > -1,
58         isGecko = !isSafari && ua.indexOf("gecko") > -1,
59         isBorderBox = isIE && !isStrict,
60         isWindows = (ua.indexOf("windows") != -1 || ua.indexOf("win32") != -1),
61         isMac = (ua.indexOf("macintosh") != -1 || ua.indexOf("mac os x") != -1),
62         isLinux = (ua.indexOf("linux") != -1),
63         isSecure = window.location.href.toLowerCase().indexOf("https") === 0,
64         isTouch =  'ontouchstart' in window || window.DocumentTouch && document instanceof DocumentTouch;
65     // remove css image flicker
66         if(isIE && !isIE7){
67         try{
68             document.execCommand("BackgroundImageCache", false, true);
69         }catch(e){}
70     }
71     
72     Roo.apply(Roo, {
73         /**
74          * True if the browser is in strict mode
75          * @type Boolean
76          */
77         isStrict : isStrict,
78         /**
79          * True if the page is running over SSL
80          * @type Boolean
81          */
82         isSecure : isSecure,
83         /**
84          * True when the document is fully initialized and ready for action
85          * @type Boolean
86          */
87         isReady : false,
88         /**
89          * Turn on debugging output (currently only the factory uses this)
90          * @type Boolean
91          */
92         
93         debug: false,
94
95         /**
96          * True to automatically uncache orphaned Roo.Elements periodically (defaults to true)
97          * @type Boolean
98          */
99         enableGarbageCollector : true,
100
101         /**
102          * True to automatically purge event listeners after uncaching an element (defaults to false).
103          * Note: this only happens if enableGarbageCollector is true.
104          * @type Boolean
105          */
106         enableListenerCollection:false,
107
108         /**
109          * URL to a blank file used by Roo when in secure mode for iframe src and onReady src to prevent
110          * the IE insecure content warning (defaults to javascript:false).
111          * @type String
112          */
113         SSL_SECURE_URL : "javascript:false",
114
115         /**
116          * URL to a 1x1 transparent gif image used by Roo to create inline icons with CSS background images. (Defaults to
117          * "http://Roojs.com/s.gif" and you should change this to a URL on your server).
118          * @type String
119          */
120         BLANK_IMAGE_URL : "http:/"+"/localhost/s.gif",
121
122         emptyFn : function(){},
123         
124         /**
125          * Copies all the properties of config to obj if they don't already exist.
126          * @param {Object} obj The receiver of the properties
127          * @param {Object} config The source of the properties
128          * @return {Object} returns obj
129          */
130         applyIf : function(o, c){
131             if(o && c){
132                 for(var p in c){
133                     if(typeof o[p] == "undefined"){ o[p] = c[p]; }
134                 }
135             }
136             return o;
137         },
138
139         /**
140          * Applies event listeners to elements by selectors when the document is ready.
141          * The event name is specified with an @ suffix.
142 <pre><code>
143 Roo.addBehaviors({
144    // add a listener for click on all anchors in element with id foo
145    '#foo a@click' : function(e, t){
146        // do something
147    },
148
149    // add the same listener to multiple selectors (separated by comma BEFORE the @)
150    '#foo a, #bar span.some-class@mouseover' : function(){
151        // do something
152    }
153 });
154 </code></pre>
155          * @param {Object} obj The list of behaviors to apply
156          */
157         addBehaviors : function(o){
158             if(!Roo.isReady){
159                 Roo.onReady(function(){
160                     Roo.addBehaviors(o);
161                 });
162                 return;
163             }
164             var cache = {}; // simple cache for applying multiple behaviors to same selector does query multiple times
165             for(var b in o){
166                 var parts = b.split('@');
167                 if(parts[1]){ // for Object prototype breakers
168                     var s = parts[0];
169                     if(!cache[s]){
170                         cache[s] = Roo.select(s);
171                     }
172                     cache[s].on(parts[1], o[b]);
173                 }
174             }
175             cache = null;
176         },
177
178         /**
179          * Generates unique ids. If the element already has an id, it is unchanged
180          * @param {String/HTMLElement/Element} el (optional) The element to generate an id for
181          * @param {String} prefix (optional) Id prefix (defaults "Roo-gen")
182          * @return {String} The generated Id.
183          */
184         id : function(el, prefix){
185             prefix = prefix || "roo-gen";
186             el = Roo.getDom(el);
187             var id = prefix + (++idSeed);
188             return el ? (el.id ? el.id : (el.id = id)) : id;
189         },
190          
191        
192         /**
193          * Extends one class with another class and optionally overrides members with the passed literal. This class
194          * also adds the function "override()" to the class that can be used to override
195          * members on an instance.
196          * @param {Object} subclass The class inheriting the functionality
197          * @param {Object} superclass The class being extended
198          * @param {Object} overrides (optional) A literal with members
199          * @method extend
200          */
201         extend : function(){
202             // inline overrides
203             var io = function(o){
204                 for(var m in o){
205                     this[m] = o[m];
206                 }
207             };
208             return function(sb, sp, overrides){
209                 if(typeof sp == 'object'){ // eg. prototype, rather than function constructor..
210                     overrides = sp;
211                     sp = sb;
212                     sb = function(){sp.apply(this, arguments);};
213                 }
214                 var F = function(){}, sbp, spp = sp.prototype;
215                 F.prototype = spp;
216                 sbp = sb.prototype = new F();
217                 sbp.constructor=sb;
218                 sb.superclass=spp;
219                 
220                 if(spp.constructor == Object.prototype.constructor){
221                     spp.constructor=sp;
222                    
223                 }
224                 
225                 sb.override = function(o){
226                     Roo.override(sb, o);
227                 };
228                 sbp.override = io;
229                 Roo.override(sb, overrides);
230                 return sb;
231             };
232         }(),
233
234         /**
235          * Adds a list of functions to the prototype of an existing class, overwriting any existing methods with the same name.
236          * Usage:<pre><code>
237 Roo.override(MyClass, {
238     newMethod1: function(){
239         // etc.
240     },
241     newMethod2: function(foo){
242         // etc.
243     }
244 });
245  </code></pre>
246          * @param {Object} origclass The class to override
247          * @param {Object} overrides The list of functions to add to origClass.  This should be specified as an object literal
248          * containing one or more methods.
249          * @method override
250          */
251         override : function(origclass, overrides){
252             if(overrides){
253                 var p = origclass.prototype;
254                 for(var method in overrides){
255                     p[method] = overrides[method];
256                 }
257             }
258         },
259         /**
260          * Creates namespaces to be used for scoping variables and classes so that they are not global.  Usage:
261          * <pre><code>
262 Roo.namespace('Company', 'Company.data');
263 Company.Widget = function() { ... }
264 Company.data.CustomStore = function(config) { ... }
265 </code></pre>
266          * @param {String} namespace1
267          * @param {String} namespace2
268          * @param {String} etc
269          * @method namespace
270          */
271         namespace : function(){
272             var a=arguments, o=null, i, j, d, rt;
273             for (i=0; i<a.length; ++i) {
274                 d=a[i].split(".");
275                 rt = d[0];
276                 /** eval:var:o */
277                 eval('if (typeof ' + rt + ' == "undefined"){' + rt + ' = {};} o = ' + rt + ';');
278                 for (j=1; j<d.length; ++j) {
279                     o[d[j]]=o[d[j]] || {};
280                     o=o[d[j]];
281                 }
282             }
283         },
284         /**
285          * Creates namespaces to be used for scoping variables and classes so that they are not global.  Usage:
286          * <pre><code>
287 Roo.factory({ xns: Roo.data, xtype : 'Store', .....});
288 Roo.factory(conf, Roo.data);
289 </code></pre>
290          * @param {String} classname
291          * @param {String} namespace (optional)
292          * @method factory
293          */
294          
295         factory : function(c, ns)
296         {
297             // no xtype, no ns or c.xns - or forced off by c.xns
298             if (!c.xtype   || (!ns && !c.xns) ||  (c.xns === false)) { // not enough info...
299                 return c;
300             }
301             ns = c.xns ? c.xns : ns; // if c.xns is set, then use that..
302             if (c.constructor == ns[c.xtype]) {// already created...
303                 return c;
304             }
305             if (ns[c.xtype]) {
306                 if (Roo.debug) Roo.log("Roo.Factory(" + c.xtype + ")");
307                 var ret = new ns[c.xtype](c);
308                 ret.xns = false;
309                 return ret;
310             }
311             c.xns = false; // prevent recursion..
312             return c;
313         },
314          /**
315          * Logs to console if it can.
316          *
317          * @param {String|Object} string
318          * @method log
319          */
320         log : function(s)
321         {
322             if ((typeof(console) == 'undefined') || (typeof(console.log) == 'undefined')) {
323                 return; // alerT?
324             }
325             console.log(s);
326             
327         },
328         /**
329          * Takes an object and converts it to an encoded URL. e.g. Roo.urlEncode({foo: 1, bar: 2}); would return "foo=1&bar=2".  Optionally, property values can be arrays, instead of keys and the resulting string that's returned will contain a name/value pair for each array value.
330          * @param {Object} o
331          * @return {String}
332          */
333         urlEncode : function(o){
334             if(!o){
335                 return "";
336             }
337             var buf = [];
338             for(var key in o){
339                 var ov = o[key], k = Roo.encodeURIComponent(key);
340                 var type = typeof ov;
341                 if(type == 'undefined'){
342                     buf.push(k, "=&");
343                 }else if(type != "function" && type != "object"){
344                     buf.push(k, "=", Roo.encodeURIComponent(ov), "&");
345                 }else if(ov instanceof Array){
346                     if (ov.length) {
347                             for(var i = 0, len = ov.length; i < len; i++) {
348                                 buf.push(k, "=", Roo.encodeURIComponent(ov[i] === undefined ? '' : ov[i]), "&");
349                             }
350                         } else {
351                             buf.push(k, "=&");
352                         }
353                 }
354             }
355             buf.pop();
356             return buf.join("");
357         },
358          /**
359          * Safe version of encodeURIComponent
360          * @param {String} data 
361          * @return {String} 
362          */
363         
364         encodeURIComponent : function (data)
365         {
366             try {
367                 return encodeURIComponent(data);
368             } catch(e) {} // should be an uri encode error.
369             
370             if (data == '' || data == null){
371                return '';
372             }
373             // http://stackoverflow.com/questions/2596483/unicode-and-uri-encoding-decoding-and-escaping-in-javascript
374             function nibble_to_hex(nibble){
375                 var chars = '0123456789ABCDEF';
376                 return chars.charAt(nibble);
377             }
378             data = data.toString();
379             var buffer = '';
380             for(var i=0; i<data.length; i++){
381                 var c = data.charCodeAt(i);
382                 var bs = new Array();
383                 if (c > 0x10000){
384                         // 4 bytes
385                     bs[0] = 0xF0 | ((c & 0x1C0000) >>> 18);
386                     bs[1] = 0x80 | ((c & 0x3F000) >>> 12);
387                     bs[2] = 0x80 | ((c & 0xFC0) >>> 6);
388                     bs[3] = 0x80 | (c & 0x3F);
389                 }else if (c > 0x800){
390                          // 3 bytes
391                     bs[0] = 0xE0 | ((c & 0xF000) >>> 12);
392                     bs[1] = 0x80 | ((c & 0xFC0) >>> 6);
393                     bs[2] = 0x80 | (c & 0x3F);
394                 }else if (c > 0x80){
395                        // 2 bytes
396                     bs[0] = 0xC0 | ((c & 0x7C0) >>> 6);
397                     bs[1] = 0x80 | (c & 0x3F);
398                 }else{
399                         // 1 byte
400                     bs[0] = c;
401                 }
402                 for(var j=0; j<bs.length; j++){
403                     var b = bs[j];
404                     var hex = nibble_to_hex((b & 0xF0) >>> 4) 
405                             + nibble_to_hex(b &0x0F);
406                     buffer += '%'+hex;
407                }
408             }
409             return buffer;    
410              
411         },
412
413         /**
414          * Takes an encoded URL and and converts it to an object. e.g. Roo.urlDecode("foo=1&bar=2"); would return {foo: 1, bar: 2} or Roo.urlDecode("foo=1&bar=2&bar=3&bar=4", true); would return {foo: 1, bar: [2, 3, 4]}.
415          * @param {String} string
416          * @param {Boolean} overwrite (optional) Items of the same name will overwrite previous values instead of creating an an array (Defaults to false).
417          * @return {Object} A literal with members
418          */
419         urlDecode : function(string, overwrite){
420             if(!string || !string.length){
421                 return {};
422             }
423             var obj = {};
424             var pairs = string.split('&');
425             var pair, name, value;
426             for(var i = 0, len = pairs.length; i < len; i++){
427                 pair = pairs[i].split('=');
428                 name = decodeURIComponent(pair[0]);
429                 value = decodeURIComponent(pair[1]);
430                 if(overwrite !== true){
431                     if(typeof obj[name] == "undefined"){
432                         obj[name] = value;
433                     }else if(typeof obj[name] == "string"){
434                         obj[name] = [obj[name]];
435                         obj[name].push(value);
436                     }else{
437                         obj[name].push(value);
438                     }
439                 }else{
440                     obj[name] = value;
441                 }
442             }
443             return obj;
444         },
445
446         /**
447          * Iterates an array calling the passed function with each item, stopping if your function returns false. If the
448          * passed array is not really an array, your function is called once with it.
449          * The supplied function is called with (Object item, Number index, Array allItems).
450          * @param {Array/NodeList/Mixed} array
451          * @param {Function} fn
452          * @param {Object} scope
453          */
454         each : function(array, fn, scope){
455             if(typeof array.length == "undefined" || typeof array == "string"){
456                 array = [array];
457             }
458             for(var i = 0, len = array.length; i < len; i++){
459                 if(fn.call(scope || array[i], array[i], i, array) === false){ return i; };
460             }
461         },
462
463         // deprecated
464         combine : function(){
465             var as = arguments, l = as.length, r = [];
466             for(var i = 0; i < l; i++){
467                 var a = as[i];
468                 if(a instanceof Array){
469                     r = r.concat(a);
470                 }else if(a.length !== undefined && !a.substr){
471                     r = r.concat(Array.prototype.slice.call(a, 0));
472                 }else{
473                     r.push(a);
474                 }
475             }
476             return r;
477         },
478
479         /**
480          * Escapes the passed string for use in a regular expression
481          * @param {String} str
482          * @return {String}
483          */
484         escapeRe : function(s) {
485             return s.replace(/([.*+?^${}()|[\]\/\\])/g, "\\$1");
486         },
487
488         // internal
489         callback : function(cb, scope, args, delay){
490             if(typeof cb == "function"){
491                 if(delay){
492                     cb.defer(delay, scope, args || []);
493                 }else{
494                     cb.apply(scope, args || []);
495                 }
496             }
497         },
498
499         /**
500          * Return the dom node for the passed string (id), dom node, or Roo.Element
501          * @param {String/HTMLElement/Roo.Element} el
502          * @return HTMLElement
503          */
504         getDom : function(el){
505             if(!el){
506                 return null;
507             }
508             return el.dom ? el.dom : (typeof el == 'string' ? document.getElementById(el) : el);
509         },
510
511         /**
512         * Shorthand for {@link Roo.ComponentMgr#get}
513         * @param {String} id
514         * @return Roo.Component
515         */
516         getCmp : function(id){
517             return Roo.ComponentMgr.get(id);
518         },
519          
520         num : function(v, defaultValue){
521             if(typeof v != 'number'){
522                 return defaultValue;
523             }
524             return v;
525         },
526
527         destroy : function(){
528             for(var i = 0, a = arguments, len = a.length; i < len; i++) {
529                 var as = a[i];
530                 if(as){
531                     if(as.dom){
532                         as.removeAllListeners();
533                         as.remove();
534                         continue;
535                     }
536                     if(typeof as.purgeListeners == 'function'){
537                         as.purgeListeners();
538                     }
539                     if(typeof as.destroy == 'function'){
540                         as.destroy();
541                     }
542                 }
543             }
544         },
545
546         // inpired by a similar function in mootools library
547         /**
548          * Returns the type of object that is passed in. If the object passed in is null or undefined it
549          * return false otherwise it returns one of the following values:<ul>
550          * <li><b>string</b>: If the object passed is a string</li>
551          * <li><b>number</b>: If the object passed is a number</li>
552          * <li><b>boolean</b>: If the object passed is a boolean value</li>
553          * <li><b>function</b>: If the object passed is a function reference</li>
554          * <li><b>object</b>: If the object passed is an object</li>
555          * <li><b>array</b>: If the object passed is an array</li>
556          * <li><b>regexp</b>: If the object passed is a regular expression</li>
557          * <li><b>element</b>: If the object passed is a DOM Element</li>
558          * <li><b>nodelist</b>: If the object passed is a DOM NodeList</li>
559          * <li><b>textnode</b>: If the object passed is a DOM text node and contains something other than whitespace</li>
560          * <li><b>whitespace</b>: If the object passed is a DOM text node and contains only whitespace</li>
561          * @param {Mixed} object
562          * @return {String}
563          */
564         type : function(o){
565             if(o === undefined || o === null){
566                 return false;
567             }
568             if(o.htmlElement){
569                 return 'element';
570             }
571             var t = typeof o;
572             if(t == 'object' && o.nodeName) {
573                 switch(o.nodeType) {
574                     case 1: return 'element';
575                     case 3: return (/\S/).test(o.nodeValue) ? 'textnode' : 'whitespace';
576                 }
577             }
578             if(t == 'object' || t == 'function') {
579                 switch(o.constructor) {
580                     case Array: return 'array';
581                     case RegExp: return 'regexp';
582                 }
583                 if(typeof o.length == 'number' && typeof o.item == 'function') {
584                     return 'nodelist';
585                 }
586             }
587             return t;
588         },
589
590         /**
591          * Returns true if the passed value is null, undefined or an empty string (optional).
592          * @param {Mixed} value The value to test
593          * @param {Boolean} allowBlank (optional) Pass true if an empty string is not considered empty
594          * @return {Boolean}
595          */
596         isEmpty : function(v, allowBlank){
597             return v === null || v === undefined || (!allowBlank ? v === '' : false);
598         },
599         
600         /** @type Boolean */
601         isOpera : isOpera,
602         /** @type Boolean */
603         isSafari : isSafari,
604         /** @type Boolean */
605         isIE : isIE,
606         /** @type Boolean */
607         isIE7 : isIE7,
608         /** @type Boolean */
609         isGecko : isGecko,
610         /** @type Boolean */
611         isBorderBox : isBorderBox,
612         /** @type Boolean */
613         isWindows : isWindows,
614         /** @type Boolean */
615         isLinux : isLinux,
616         /** @type Boolean */
617         isMac : isMac,
618         /** @type Boolean */
619         isTouch : isTouch,
620
621         /**
622          * By default, Ext intelligently decides whether floating elements should be shimmed. If you are using flash,
623          * you may want to set this to true.
624          * @type Boolean
625          */
626         useShims : ((isIE && !isIE7) || (isGecko && isMac)),
627         
628         
629                 
630         /**
631          * Selects a single element as a Roo Element
632          * This is about as close as you can get to jQuery's $('do crazy stuff')
633          * @param {String} selector The selector/xpath query
634          * @param {Node} root (optional) The start of the query (defaults to document).
635          * @return {Roo.Element}
636          */
637         selectNode : function(selector, root) 
638         {
639             var node = Roo.DomQuery.selectNode(selector,root);
640             return node ? Roo.get(node) : new Roo.Element(false);
641         }
642         
643     });
644
645
646 })();
647
648 Roo.namespace("Roo", "Roo.util", "Roo.grid", "Roo.dd", "Roo.tree", "Roo.data",
649                 "Roo.form", "Roo.menu", "Roo.state", "Roo.lib", "Roo.layout",
650                 "Roo.app", "Roo.ux",
651                 "Roo.bootstrap",
652                 "Roo.bootstrap.dash");
653 /*
654  * Based on:
655  * Ext JS Library 1.1.1
656  * Copyright(c) 2006-2007, Ext JS, LLC.
657  *
658  * Originally Released Under LGPL - original licence link has changed is not relivant.
659  *
660  * Fork - LGPL
661  * <script type="text/javascript">
662  */
663
664 (function() {    
665     // wrappedn so fnCleanup is not in global scope...
666     if(Roo.isIE) {
667         function fnCleanUp() {
668             var p = Function.prototype;
669             delete p.createSequence;
670             delete p.defer;
671             delete p.createDelegate;
672             delete p.createCallback;
673             delete p.createInterceptor;
674
675             window.detachEvent("onunload", fnCleanUp);
676         }
677         window.attachEvent("onunload", fnCleanUp);
678     }
679 })();
680
681
682 /**
683  * @class Function
684  * These functions are available on every Function object (any JavaScript function).
685  */
686 Roo.apply(Function.prototype, {
687      /**
688      * Creates a callback that passes arguments[0], arguments[1], arguments[2], ...
689      * Call directly on any function. Example: <code>myFunction.createCallback(myarg, myarg2)</code>
690      * Will create a function that is bound to those 2 args.
691      * @return {Function} The new function
692     */
693     createCallback : function(/*args...*/){
694         // make args available, in function below
695         var args = arguments;
696         var method = this;
697         return function() {
698             return method.apply(window, args);
699         };
700     },
701
702     /**
703      * Creates a delegate (callback) that sets the scope to obj.
704      * Call directly on any function. Example: <code>this.myFunction.createDelegate(this)</code>
705      * Will create a function that is automatically scoped to this.
706      * @param {Object} obj (optional) The object for which the scope is set
707      * @param {Array} args (optional) Overrides arguments for the call. (Defaults to the arguments passed by the caller)
708      * @param {Boolean/Number} appendArgs (optional) if True args are appended to call args instead of overriding,
709      *                                             if a number the args are inserted at the specified position
710      * @return {Function} The new function
711      */
712     createDelegate : function(obj, args, appendArgs){
713         var method = this;
714         return function() {
715             var callArgs = args || arguments;
716             if(appendArgs === true){
717                 callArgs = Array.prototype.slice.call(arguments, 0);
718                 callArgs = callArgs.concat(args);
719             }else if(typeof appendArgs == "number"){
720                 callArgs = Array.prototype.slice.call(arguments, 0); // copy arguments first
721                 var applyArgs = [appendArgs, 0].concat(args); // create method call params
722                 Array.prototype.splice.apply(callArgs, applyArgs); // splice them in
723             }
724             return method.apply(obj || window, callArgs);
725         };
726     },
727
728     /**
729      * Calls this function after the number of millseconds specified.
730      * @param {Number} millis The number of milliseconds for the setTimeout call (if 0 the function is executed immediately)
731      * @param {Object} obj (optional) The object for which the scope is set
732      * @param {Array} args (optional) Overrides arguments for the call. (Defaults to the arguments passed by the caller)
733      * @param {Boolean/Number} appendArgs (optional) if True args are appended to call args instead of overriding,
734      *                                             if a number the args are inserted at the specified position
735      * @return {Number} The timeout id that can be used with clearTimeout
736      */
737     defer : function(millis, obj, args, appendArgs){
738         var fn = this.createDelegate(obj, args, appendArgs);
739         if(millis){
740             return setTimeout(fn, millis);
741         }
742         fn();
743         return 0;
744     },
745     /**
746      * Create a combined function call sequence of the original function + the passed function.
747      * The resulting function returns the results of the original function.
748      * The passed fcn is called with the parameters of the original function
749      * @param {Function} fcn The function to sequence
750      * @param {Object} scope (optional) The scope of the passed fcn (Defaults to scope of original function or window)
751      * @return {Function} The new function
752      */
753     createSequence : function(fcn, scope){
754         if(typeof fcn != "function"){
755             return this;
756         }
757         var method = this;
758         return function() {
759             var retval = method.apply(this || window, arguments);
760             fcn.apply(scope || this || window, arguments);
761             return retval;
762         };
763     },
764
765     /**
766      * Creates an interceptor function. The passed fcn is called before the original one. If it returns false, the original one is not called.
767      * The resulting function returns the results of the original function.
768      * The passed fcn is called with the parameters of the original function.
769      * @addon
770      * @param {Function} fcn The function to call before the original
771      * @param {Object} scope (optional) The scope of the passed fcn (Defaults to scope of original function or window)
772      * @return {Function} The new function
773      */
774     createInterceptor : function(fcn, scope){
775         if(typeof fcn != "function"){
776             return this;
777         }
778         var method = this;
779         return function() {
780             fcn.target = this;
781             fcn.method = method;
782             if(fcn.apply(scope || this || window, arguments) === false){
783                 return;
784             }
785             return method.apply(this || window, arguments);
786         };
787     }
788 });
789 /*
790  * Based on:
791  * Ext JS Library 1.1.1
792  * Copyright(c) 2006-2007, Ext JS, LLC.
793  *
794  * Originally Released Under LGPL - original licence link has changed is not relivant.
795  *
796  * Fork - LGPL
797  * <script type="text/javascript">
798  */
799
800 Roo.applyIf(String, {
801     
802     /** @scope String */
803     
804     /**
805      * Escapes the passed string for ' and \
806      * @param {String} string The string to escape
807      * @return {String} The escaped string
808      * @static
809      */
810     escape : function(string) {
811         return string.replace(/('|\\)/g, "\\$1");
812     },
813
814     /**
815      * Pads the left side of a string with a specified character.  This is especially useful
816      * for normalizing number and date strings.  Example usage:
817      * <pre><code>
818 var s = String.leftPad('123', 5, '0');
819 // s now contains the string: '00123'
820 </code></pre>
821      * @param {String} string The original string
822      * @param {Number} size The total length of the output string
823      * @param {String} char (optional) The character with which to pad the original string (defaults to empty string " ")
824      * @return {String} The padded string
825      * @static
826      */
827     leftPad : function (val, size, ch) {
828         var result = new String(val);
829         if(ch === null || ch === undefined || ch === '') {
830             ch = " ";
831         }
832         while (result.length < size) {
833             result = ch + result;
834         }
835         return result;
836     },
837
838     /**
839      * Allows you to define a tokenized string and pass an arbitrary number of arguments to replace the tokens.  Each
840      * token must be unique, and must increment in the format {0}, {1}, etc.  Example usage:
841      * <pre><code>
842 var cls = 'my-class', text = 'Some text';
843 var s = String.format('<div class="{0}">{1}</div>', cls, text);
844 // s now contains the string: '<div class="my-class">Some text</div>'
845 </code></pre>
846      * @param {String} string The tokenized string to be formatted
847      * @param {String} value1 The value to replace token {0}
848      * @param {String} value2 Etc...
849      * @return {String} The formatted string
850      * @static
851      */
852     format : function(format){
853         var args = Array.prototype.slice.call(arguments, 1);
854         return format.replace(/\{(\d+)\}/g, function(m, i){
855             return Roo.util.Format.htmlEncode(args[i]);
856         });
857     }
858 });
859
860 /**
861  * Utility function that allows you to easily switch a string between two alternating values.  The passed value
862  * is compared to the current string, and if they are equal, the other value that was passed in is returned.  If
863  * they are already different, the first value passed in is returned.  Note that this method returns the new value
864  * but does not change the current string.
865  * <pre><code>
866 // alternate sort directions
867 sort = sort.toggle('ASC', 'DESC');
868
869 // instead of conditional logic:
870 sort = (sort == 'ASC' ? 'DESC' : 'ASC');
871 </code></pre>
872  * @param {String} value The value to compare to the current string
873  * @param {String} other The new value to use if the string already equals the first value passed in
874  * @return {String} The new value
875  */
876  
877 String.prototype.toggle = function(value, other){
878     return this == value ? other : value;
879 };/*
880  * Based on:
881  * Ext JS Library 1.1.1
882  * Copyright(c) 2006-2007, Ext JS, LLC.
883  *
884  * Originally Released Under LGPL - original licence link has changed is not relivant.
885  *
886  * Fork - LGPL
887  * <script type="text/javascript">
888  */
889
890  /**
891  * @class Number
892  */
893 Roo.applyIf(Number.prototype, {
894     /**
895      * Checks whether or not the current number is within a desired range.  If the number is already within the
896      * range it is returned, otherwise the min or max value is returned depending on which side of the range is
897      * exceeded.  Note that this method returns the constrained value but does not change the current number.
898      * @param {Number} min The minimum number in the range
899      * @param {Number} max The maximum number in the range
900      * @return {Number} The constrained value if outside the range, otherwise the current value
901      */
902     constrain : function(min, max){
903         return Math.min(Math.max(this, min), max);
904     }
905 });/*
906  * Based on:
907  * Ext JS Library 1.1.1
908  * Copyright(c) 2006-2007, Ext JS, LLC.
909  *
910  * Originally Released Under LGPL - original licence link has changed is not relivant.
911  *
912  * Fork - LGPL
913  * <script type="text/javascript">
914  */
915  /**
916  * @class Array
917  */
918 Roo.applyIf(Array.prototype, {
919     /**
920      * Checks whether or not the specified object exists in the array.
921      * @param {Object} o The object to check for
922      * @return {Number} The index of o in the array (or -1 if it is not found)
923      */
924     indexOf : function(o){
925        for (var i = 0, len = this.length; i < len; i++){
926               if(this[i] == o) return i;
927        }
928            return -1;
929     },
930
931     /**
932      * Removes the specified object from the array.  If the object is not found nothing happens.
933      * @param {Object} o The object to remove
934      */
935     remove : function(o){
936        var index = this.indexOf(o);
937        if(index != -1){
938            this.splice(index, 1);
939        }
940     },
941     /**
942      * Map (JS 1.6 compatibility)
943      * @param {Function} function  to call
944      */
945     map : function(fun )
946     {
947         var len = this.length >>> 0;
948         if (typeof fun != "function")
949             throw new TypeError();
950
951         var res = new Array(len);
952         var thisp = arguments[1];
953         for (var i = 0; i < len; i++)
954         {
955             if (i in this)
956                 res[i] = fun.call(thisp, this[i], i, this);
957         }
958
959         return res;
960     }
961     
962 });
963
964
965  /*
966  * Based on:
967  * Ext JS Library 1.1.1
968  * Copyright(c) 2006-2007, Ext JS, LLC.
969  *
970  * Originally Released Under LGPL - original licence link has changed is not relivant.
971  *
972  * Fork - LGPL
973  * <script type="text/javascript">
974  */
975
976 /**
977  * @class Date
978  *
979  * The date parsing and format syntax is a subset of
980  * <a href="http://www.php.net/date">PHP's date() function</a>, and the formats that are
981  * supported will provide results equivalent to their PHP versions.
982  *
983  * Following is the list of all currently supported formats:
984  *<pre>
985 Sample date:
986 'Wed Jan 10 2007 15:05:01 GMT-0600 (Central Standard Time)'
987
988 Format  Output      Description
989 ------  ----------  --------------------------------------------------------------
990   d      10         Day of the month, 2 digits with leading zeros
991   D      Wed        A textual representation of a day, three letters
992   j      10         Day of the month without leading zeros
993   l      Wednesday  A full textual representation of the day of the week
994   S      th         English ordinal day of month suffix, 2 chars (use with j)
995   w      3          Numeric representation of the day of the week
996   z      9          The julian date, or day of the year (0-365)
997   W      01         ISO-8601 2-digit week number of year, weeks starting on Monday (00-52)
998   F      January    A full textual representation of the month
999   m      01         Numeric representation of a month, with leading zeros
1000   M      Jan        Month name abbreviation, three letters
1001   n      1          Numeric representation of a month, without leading zeros
1002   t      31         Number of days in the given month
1003   L      0          Whether it's a leap year (1 if it is a leap year, else 0)
1004   Y      2007       A full numeric representation of a year, 4 digits
1005   y      07         A two digit representation of a year
1006   a      pm         Lowercase Ante meridiem and Post meridiem
1007   A      PM         Uppercase Ante meridiem and Post meridiem
1008   g      3          12-hour format of an hour without leading zeros
1009   G      15         24-hour format of an hour without leading zeros
1010   h      03         12-hour format of an hour with leading zeros
1011   H      15         24-hour format of an hour with leading zeros
1012   i      05         Minutes with leading zeros
1013   s      01         Seconds, with leading zeros
1014   O      -0600      Difference to Greenwich time (GMT) in hours (Allows +08, without minutes)
1015   P      -06:00     Difference to Greenwich time (GMT) with colon between hours and minutes
1016   T      CST        Timezone setting of the machine running the code
1017   Z      -21600     Timezone offset in seconds (negative if west of UTC, positive if east)
1018 </pre>
1019  *
1020  * Example usage (note that you must escape format specifiers with '\\' to render them as character literals):
1021  * <pre><code>
1022 var dt = new Date('1/10/2007 03:05:01 PM GMT-0600');
1023 document.write(dt.format('Y-m-d'));                         //2007-01-10
1024 document.write(dt.format('F j, Y, g:i a'));                 //January 10, 2007, 3:05 pm
1025 document.write(dt.format('l, \\t\\he dS of F Y h:i:s A'));  //Wednesday, the 10th of January 2007 03:05:01 PM
1026  </code></pre>
1027  *
1028  * Here are some standard date/time patterns that you might find helpful.  They
1029  * are not part of the source of Date.js, but to use them you can simply copy this
1030  * block of code into any script that is included after Date.js and they will also become
1031  * globally available on the Date object.  Feel free to add or remove patterns as needed in your code.
1032  * <pre><code>
1033 Date.patterns = {
1034     ISO8601Long:"Y-m-d H:i:s",
1035     ISO8601Short:"Y-m-d",
1036     ShortDate: "n/j/Y",
1037     LongDate: "l, F d, Y",
1038     FullDateTime: "l, F d, Y g:i:s A",
1039     MonthDay: "F d",
1040     ShortTime: "g:i A",
1041     LongTime: "g:i:s A",
1042     SortableDateTime: "Y-m-d\\TH:i:s",
1043     UniversalSortableDateTime: "Y-m-d H:i:sO",
1044     YearMonth: "F, Y"
1045 };
1046 </code></pre>
1047  *
1048  * Example usage:
1049  * <pre><code>
1050 var dt = new Date();
1051 document.write(dt.format(Date.patterns.ShortDate));
1052  </code></pre>
1053  */
1054
1055 /*
1056  * Most of the date-formatting functions below are the excellent work of Baron Schwartz.
1057  * They generate precompiled functions from date formats instead of parsing and
1058  * processing the pattern every time you format a date.  These functions are available
1059  * on every Date object (any javascript function).
1060  *
1061  * The original article and download are here:
1062  * http://www.xaprb.com/blog/2005/12/12/javascript-closures-for-runtime-efficiency/
1063  *
1064  */
1065  
1066  
1067  // was in core
1068 /**
1069  Returns the number of milliseconds between this date and date
1070  @param {Date} date (optional) Defaults to now
1071  @return {Number} The diff in milliseconds
1072  @member Date getElapsed
1073  */
1074 Date.prototype.getElapsed = function(date) {
1075         return Math.abs((date || new Date()).getTime()-this.getTime());
1076 };
1077 // was in date file..
1078
1079
1080 // private
1081 Date.parseFunctions = {count:0};
1082 // private
1083 Date.parseRegexes = [];
1084 // private
1085 Date.formatFunctions = {count:0};
1086
1087 // private
1088 Date.prototype.dateFormat = function(format) {
1089     if (Date.formatFunctions[format] == null) {
1090         Date.createNewFormat(format);
1091     }
1092     var func = Date.formatFunctions[format];
1093     return this[func]();
1094 };
1095
1096
1097 /**
1098  * Formats a date given the supplied format string
1099  * @param {String} format The format string
1100  * @return {String} The formatted date
1101  * @method
1102  */
1103 Date.prototype.format = Date.prototype.dateFormat;
1104
1105 // private
1106 Date.createNewFormat = function(format) {
1107     var funcName = "format" + Date.formatFunctions.count++;
1108     Date.formatFunctions[format] = funcName;
1109     var code = "Date.prototype." + funcName + " = function(){return ";
1110     var special = false;
1111     var ch = '';
1112     for (var i = 0; i < format.length; ++i) {
1113         ch = format.charAt(i);
1114         if (!special && ch == "\\") {
1115             special = true;
1116         }
1117         else if (special) {
1118             special = false;
1119             code += "'" + String.escape(ch) + "' + ";
1120         }
1121         else {
1122             code += Date.getFormatCode(ch);
1123         }
1124     }
1125     /** eval:var:zzzzzzzzzzzzz */
1126     eval(code.substring(0, code.length - 3) + ";}");
1127 };
1128
1129 // private
1130 Date.getFormatCode = function(character) {
1131     switch (character) {
1132     case "d":
1133         return "String.leftPad(this.getDate(), 2, '0') + ";
1134     case "D":
1135         return "Date.dayNames[this.getDay()].substring(0, 3) + ";
1136     case "j":
1137         return "this.getDate() + ";
1138     case "l":
1139         return "Date.dayNames[this.getDay()] + ";
1140     case "S":
1141         return "this.getSuffix() + ";
1142     case "w":
1143         return "this.getDay() + ";
1144     case "z":
1145         return "this.getDayOfYear() + ";
1146     case "W":
1147         return "this.getWeekOfYear() + ";
1148     case "F":
1149         return "Date.monthNames[this.getMonth()] + ";
1150     case "m":
1151         return "String.leftPad(this.getMonth() + 1, 2, '0') + ";
1152     case "M":
1153         return "Date.monthNames[this.getMonth()].substring(0, 3) + ";
1154     case "n":
1155         return "(this.getMonth() + 1) + ";
1156     case "t":
1157         return "this.getDaysInMonth() + ";
1158     case "L":
1159         return "(this.isLeapYear() ? 1 : 0) + ";
1160     case "Y":
1161         return "this.getFullYear() + ";
1162     case "y":
1163         return "('' + this.getFullYear()).substring(2, 4) + ";
1164     case "a":
1165         return "(this.getHours() < 12 ? 'am' : 'pm') + ";
1166     case "A":
1167         return "(this.getHours() < 12 ? 'AM' : 'PM') + ";
1168     case "g":
1169         return "((this.getHours() % 12) ? this.getHours() % 12 : 12) + ";
1170     case "G":
1171         return "this.getHours() + ";
1172     case "h":
1173         return "String.leftPad((this.getHours() % 12) ? this.getHours() % 12 : 12, 2, '0') + ";
1174     case "H":
1175         return "String.leftPad(this.getHours(), 2, '0') + ";
1176     case "i":
1177         return "String.leftPad(this.getMinutes(), 2, '0') + ";
1178     case "s":
1179         return "String.leftPad(this.getSeconds(), 2, '0') + ";
1180     case "O":
1181         return "this.getGMTOffset() + ";
1182     case "P":
1183         return "this.getGMTColonOffset() + ";
1184     case "T":
1185         return "this.getTimezone() + ";
1186     case "Z":
1187         return "(this.getTimezoneOffset() * -60) + ";
1188     default:
1189         return "'" + String.escape(character) + "' + ";
1190     }
1191 };
1192
1193 /**
1194  * Parses the passed string using the specified format. Note that this function expects dates in normal calendar
1195  * format, meaning that months are 1-based (1 = January) and not zero-based like in JavaScript dates.  Any part of
1196  * the date format that is not specified will default to the current date value for that part.  Time parts can also
1197  * be specified, but default to 0.  Keep in mind that the input date string must precisely match the specified format
1198  * string or the parse operation will fail.
1199  * Example Usage:
1200 <pre><code>
1201 //dt = Fri May 25 2007 (current date)
1202 var dt = new Date();
1203
1204 //dt = Thu May 25 2006 (today's month/day in 2006)
1205 dt = Date.parseDate("2006", "Y");
1206
1207 //dt = Sun Jan 15 2006 (all date parts specified)
1208 dt = Date.parseDate("2006-1-15", "Y-m-d");
1209
1210 //dt = Sun Jan 15 2006 15:20:01 GMT-0600 (CST)
1211 dt = Date.parseDate("2006-1-15 3:20:01 PM", "Y-m-d h:i:s A" );
1212 </code></pre>
1213  * @param {String} input The unparsed date as a string
1214  * @param {String} format The format the date is in
1215  * @return {Date} The parsed date
1216  * @static
1217  */
1218 Date.parseDate = function(input, format) {
1219     if (Date.parseFunctions[format] == null) {
1220         Date.createParser(format);
1221     }
1222     var func = Date.parseFunctions[format];
1223     return Date[func](input);
1224 };
1225 /**
1226  * @private
1227  */
1228 Date.createParser = function(format) {
1229     var funcName = "parse" + Date.parseFunctions.count++;
1230     var regexNum = Date.parseRegexes.length;
1231     var currentGroup = 1;
1232     Date.parseFunctions[format] = funcName;
1233
1234     var code = "Date." + funcName + " = function(input){\n"
1235         + "var y = -1, m = -1, d = -1, h = -1, i = -1, s = -1, o, z, v;\n"
1236         + "var d = new Date();\n"
1237         + "y = d.getFullYear();\n"
1238         + "m = d.getMonth();\n"
1239         + "d = d.getDate();\n"
1240         + "var results = input.match(Date.parseRegexes[" + regexNum + "]);\n"
1241         + "if (results && results.length > 0) {";
1242     var regex = "";
1243
1244     var special = false;
1245     var ch = '';
1246     for (var i = 0; i < format.length; ++i) {
1247         ch = format.charAt(i);
1248         if (!special && ch == "\\") {
1249             special = true;
1250         }
1251         else if (special) {
1252             special = false;
1253             regex += String.escape(ch);
1254         }
1255         else {
1256             var obj = Date.formatCodeToRegex(ch, currentGroup);
1257             currentGroup += obj.g;
1258             regex += obj.s;
1259             if (obj.g && obj.c) {
1260                 code += obj.c;
1261             }
1262         }
1263     }
1264
1265     code += "if (y >= 0 && m >= 0 && d > 0 && h >= 0 && i >= 0 && s >= 0)\n"
1266         + "{v = new Date(y, m, d, h, i, s);}\n"
1267         + "else if (y >= 0 && m >= 0 && d > 0 && h >= 0 && i >= 0)\n"
1268         + "{v = new Date(y, m, d, h, i);}\n"
1269         + "else if (y >= 0 && m >= 0 && d > 0 && h >= 0)\n"
1270         + "{v = new Date(y, m, d, h);}\n"
1271         + "else if (y >= 0 && m >= 0 && d > 0)\n"
1272         + "{v = new Date(y, m, d);}\n"
1273         + "else if (y >= 0 && m >= 0)\n"
1274         + "{v = new Date(y, m);}\n"
1275         + "else if (y >= 0)\n"
1276         + "{v = new Date(y);}\n"
1277         + "}return (v && (z || o))?\n" // favour UTC offset over GMT offset
1278         + "    ((z)? v.add(Date.SECOND, (v.getTimezoneOffset() * 60) + (z*1)) :\n" // reset to UTC, then add offset
1279         + "        v.add(Date.HOUR, (v.getGMTOffset() / 100) + (o / -100))) : v\n" // reset to GMT, then add offset
1280         + ";}";
1281
1282     Date.parseRegexes[regexNum] = new RegExp("^" + regex + "$");
1283     /** eval:var:zzzzzzzzzzzzz */
1284     eval(code);
1285 };
1286
1287 // private
1288 Date.formatCodeToRegex = function(character, currentGroup) {
1289     switch (character) {
1290     case "D":
1291         return {g:0,
1292         c:null,
1293         s:"(?:Sun|Mon|Tue|Wed|Thu|Fri|Sat)"};
1294     case "j":
1295         return {g:1,
1296             c:"d = parseInt(results[" + currentGroup + "], 10);\n",
1297             s:"(\\d{1,2})"}; // day of month without leading zeroes
1298     case "d":
1299         return {g:1,
1300             c:"d = parseInt(results[" + currentGroup + "], 10);\n",
1301             s:"(\\d{2})"}; // day of month with leading zeroes
1302     case "l":
1303         return {g:0,
1304             c:null,
1305             s:"(?:" + Date.dayNames.join("|") + ")"};
1306     case "S":
1307         return {g:0,
1308             c:null,
1309             s:"(?:st|nd|rd|th)"};
1310     case "w":
1311         return {g:0,
1312             c:null,
1313             s:"\\d"};
1314     case "z":
1315         return {g:0,
1316             c:null,
1317             s:"(?:\\d{1,3})"};
1318     case "W":
1319         return {g:0,
1320             c:null,
1321             s:"(?:\\d{2})"};
1322     case "F":
1323         return {g:1,
1324             c:"m = parseInt(Date.monthNumbers[results[" + currentGroup + "].substring(0, 3)], 10);\n",
1325             s:"(" + Date.monthNames.join("|") + ")"};
1326     case "M":
1327         return {g:1,
1328             c:"m = parseInt(Date.monthNumbers[results[" + currentGroup + "]], 10);\n",
1329             s:"(Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)"};
1330     case "n":
1331         return {g:1,
1332             c:"m = parseInt(results[" + currentGroup + "], 10) - 1;\n",
1333             s:"(\\d{1,2})"}; // Numeric representation of a month, without leading zeros
1334     case "m":
1335         return {g:1,
1336             c:"m = parseInt(results[" + currentGroup + "], 10) - 1;\n",
1337             s:"(\\d{2})"}; // Numeric representation of a month, with leading zeros
1338     case "t":
1339         return {g:0,
1340             c:null,
1341             s:"\\d{1,2}"};
1342     case "L":
1343         return {g:0,
1344             c:null,
1345             s:"(?:1|0)"};
1346     case "Y":
1347         return {g:1,
1348             c:"y = parseInt(results[" + currentGroup + "], 10);\n",
1349             s:"(\\d{4})"};
1350     case "y":
1351         return {g:1,
1352             c:"var ty = parseInt(results[" + currentGroup + "], 10);\n"
1353                 + "y = ty > Date.y2kYear ? 1900 + ty : 2000 + ty;\n",
1354             s:"(\\d{1,2})"};
1355     case "a":
1356         return {g:1,
1357             c:"if (results[" + currentGroup + "] == 'am') {\n"
1358                 + "if (h == 12) { h = 0; }\n"
1359                 + "} else { if (h < 12) { h += 12; }}",
1360             s:"(am|pm)"};
1361     case "A":
1362         return {g:1,
1363             c:"if (results[" + currentGroup + "] == 'AM') {\n"
1364                 + "if (h == 12) { h = 0; }\n"
1365                 + "} else { if (h < 12) { h += 12; }}",
1366             s:"(AM|PM)"};
1367     case "g":
1368     case "G":
1369         return {g:1,
1370             c:"h = parseInt(results[" + currentGroup + "], 10);\n",
1371             s:"(\\d{1,2})"}; // 12/24-hr format  format of an hour without leading zeroes
1372     case "h":
1373     case "H":
1374         return {g:1,
1375             c:"h = parseInt(results[" + currentGroup + "], 10);\n",
1376             s:"(\\d{2})"}; //  12/24-hr format  format of an hour with leading zeroes
1377     case "i":
1378         return {g:1,
1379             c:"i = parseInt(results[" + currentGroup + "], 10);\n",
1380             s:"(\\d{2})"};
1381     case "s":
1382         return {g:1,
1383             c:"s = parseInt(results[" + currentGroup + "], 10);\n",
1384             s:"(\\d{2})"};
1385     case "O":
1386         return {g:1,
1387             c:[
1388                 "o = results[", currentGroup, "];\n",
1389                 "var sn = o.substring(0,1);\n", // get + / - sign
1390                 "var hr = o.substring(1,3)*1 + Math.floor(o.substring(3,5) / 60);\n", // get hours (performs minutes-to-hour conversion also)
1391                 "var mn = o.substring(3,5) % 60;\n", // get minutes
1392                 "o = ((-12 <= (hr*60 + mn)/60) && ((hr*60 + mn)/60 <= 14))?\n", // -12hrs <= GMT offset <= 14hrs
1393                 "    (sn + String.leftPad(hr, 2, 0) + String.leftPad(mn, 2, 0)) : null;\n"
1394             ].join(""),
1395             s:"([+\-]\\d{2,4})"};
1396     
1397     
1398     case "P":
1399         return {g:1,
1400                 c:[
1401                    "o = results[", currentGroup, "];\n",
1402                    "var sn = o.substring(0,1);\n",
1403                    "var hr = o.substring(1,3)*1 + Math.floor(o.substring(4,6) / 60);\n",
1404                    "var mn = o.substring(4,6) % 60;\n",
1405                    "o = ((-12 <= (hr*60 + mn)/60) && ((hr*60 + mn)/60 <= 14))?\n",
1406                         "    (sn + String.leftPad(hr, 2, 0) + String.leftPad(mn, 2, 0)) : null;\n"
1407             ].join(""),
1408             s:"([+\-]\\d{4})"};
1409     case "T":
1410         return {g:0,
1411             c:null,
1412             s:"[A-Z]{1,4}"}; // timezone abbrev. may be between 1 - 4 chars
1413     case "Z":
1414         return {g:1,
1415             c:"z = results[" + currentGroup + "];\n" // -43200 <= UTC offset <= 50400
1416                   + "z = (-43200 <= z*1 && z*1 <= 50400)? z : null;\n",
1417             s:"([+\-]?\\d{1,5})"}; // leading '+' sign is optional for UTC offset
1418     default:
1419         return {g:0,
1420             c:null,
1421             s:String.escape(character)};
1422     }
1423 };
1424
1425 /**
1426  * Get the timezone abbreviation of the current date (equivalent to the format specifier 'T').
1427  * @return {String} The abbreviated timezone name (e.g. 'CST')
1428  */
1429 Date.prototype.getTimezone = function() {
1430     return this.toString().replace(/^.*? ([A-Z]{1,4})[\-+][0-9]{4} .*$/, "$1");
1431 };
1432
1433 /**
1434  * Get the offset from GMT of the current date (equivalent to the format specifier 'O').
1435  * @return {String} The 4-character offset string prefixed with + or - (e.g. '-0600')
1436  */
1437 Date.prototype.getGMTOffset = function() {
1438     return (this.getTimezoneOffset() > 0 ? "-" : "+")
1439         + String.leftPad(Math.abs(Math.floor(this.getTimezoneOffset() / 60)), 2, "0")
1440         + String.leftPad(this.getTimezoneOffset() % 60, 2, "0");
1441 };
1442
1443 /**
1444  * Get the offset from GMT of the current date (equivalent to the format specifier 'P').
1445  * @return {String} 2-characters representing hours and 2-characters representing minutes
1446  * seperated by a colon and prefixed with + or - (e.g. '-06:00')
1447  */
1448 Date.prototype.getGMTColonOffset = function() {
1449         return (this.getTimezoneOffset() > 0 ? "-" : "+")
1450                 + String.leftPad(Math.abs(Math.floor(this.getTimezoneOffset() / 60)), 2, "0")
1451                 + ":"
1452                 + String.leftPad(this.getTimezoneOffset() %60, 2, "0");
1453 }
1454
1455 /**
1456  * Get the numeric day number of the year, adjusted for leap year.
1457  * @return {Number} 0 through 364 (365 in leap years)
1458  */
1459 Date.prototype.getDayOfYear = function() {
1460     var num = 0;
1461     Date.daysInMonth[1] = this.isLeapYear() ? 29 : 28;
1462     for (var i = 0; i < this.getMonth(); ++i) {
1463         num += Date.daysInMonth[i];
1464     }
1465     return num + this.getDate() - 1;
1466 };
1467
1468 /**
1469  * Get the string representation of the numeric week number of the year
1470  * (equivalent to the format specifier 'W').
1471  * @return {String} '00' through '52'
1472  */
1473 Date.prototype.getWeekOfYear = function() {
1474     // Skip to Thursday of this week
1475     var now = this.getDayOfYear() + (4 - this.getDay());
1476     // Find the first Thursday of the year
1477     var jan1 = new Date(this.getFullYear(), 0, 1);
1478     var then = (7 - jan1.getDay() + 4);
1479     return String.leftPad(((now - then) / 7) + 1, 2, "0");
1480 };
1481
1482 /**
1483  * Whether or not the current date is in a leap year.
1484  * @return {Boolean} True if the current date is in a leap year, else false
1485  */
1486 Date.prototype.isLeapYear = function() {
1487     var year = this.getFullYear();
1488     return ((year & 3) == 0 && (year % 100 || (year % 400 == 0 && year)));
1489 };
1490
1491 /**
1492  * Get the first day of the current month, adjusted for leap year.  The returned value
1493  * is the numeric day index within the week (0-6) which can be used in conjunction with
1494  * the {@link #monthNames} array to retrieve the textual day name.
1495  * Example:
1496  *<pre><code>
1497 var dt = new Date('1/10/2007');
1498 document.write(Date.dayNames[dt.getFirstDayOfMonth()]); //output: 'Monday'
1499 </code></pre>
1500  * @return {Number} The day number (0-6)
1501  */
1502 Date.prototype.getFirstDayOfMonth = function() {
1503     var day = (this.getDay() - (this.getDate() - 1)) % 7;
1504     return (day < 0) ? (day + 7) : day;
1505 };
1506
1507 /**
1508  * Get the last day of the current month, adjusted for leap year.  The returned value
1509  * is the numeric day index within the week (0-6) which can be used in conjunction with
1510  * the {@link #monthNames} array to retrieve the textual day name.
1511  * Example:
1512  *<pre><code>
1513 var dt = new Date('1/10/2007');
1514 document.write(Date.dayNames[dt.getLastDayOfMonth()]); //output: 'Wednesday'
1515 </code></pre>
1516  * @return {Number} The day number (0-6)
1517  */
1518 Date.prototype.getLastDayOfMonth = function() {
1519     var day = (this.getDay() + (Date.daysInMonth[this.getMonth()] - this.getDate())) % 7;
1520     return (day < 0) ? (day + 7) : day;
1521 };
1522
1523
1524 /**
1525  * Get the first date of this date's month
1526  * @return {Date}
1527  */
1528 Date.prototype.getFirstDateOfMonth = function() {
1529     return new Date(this.getFullYear(), this.getMonth(), 1);
1530 };
1531
1532 /**
1533  * Get the last date of this date's month
1534  * @return {Date}
1535  */
1536 Date.prototype.getLastDateOfMonth = function() {
1537     return new Date(this.getFullYear(), this.getMonth(), this.getDaysInMonth());
1538 };
1539 /**
1540  * Get the number of days in the current month, adjusted for leap year.
1541  * @return {Number} The number of days in the month
1542  */
1543 Date.prototype.getDaysInMonth = function() {
1544     Date.daysInMonth[1] = this.isLeapYear() ? 29 : 28;
1545     return Date.daysInMonth[this.getMonth()];
1546 };
1547
1548 /**
1549  * Get the English ordinal suffix of the current day (equivalent to the format specifier 'S').
1550  * @return {String} 'st, 'nd', 'rd' or 'th'
1551  */
1552 Date.prototype.getSuffix = function() {
1553     switch (this.getDate()) {
1554         case 1:
1555         case 21:
1556         case 31:
1557             return "st";
1558         case 2:
1559         case 22:
1560             return "nd";
1561         case 3:
1562         case 23:
1563             return "rd";
1564         default:
1565             return "th";
1566     }
1567 };
1568
1569 // private
1570 Date.daysInMonth = [31,28,31,30,31,30,31,31,30,31,30,31];
1571
1572 /**
1573  * An array of textual month names.
1574  * Override these values for international dates, for example...
1575  * Date.monthNames = ['JanInYourLang', 'FebInYourLang', ...];
1576  * @type Array
1577  * @static
1578  */
1579 Date.monthNames =
1580    ["January",
1581     "February",
1582     "March",
1583     "April",
1584     "May",
1585     "June",
1586     "July",
1587     "August",
1588     "September",
1589     "October",
1590     "November",
1591     "December"];
1592
1593 /**
1594  * An array of textual day names.
1595  * Override these values for international dates, for example...
1596  * Date.dayNames = ['SundayInYourLang', 'MondayInYourLang', ...];
1597  * @type Array
1598  * @static
1599  */
1600 Date.dayNames =
1601    ["Sunday",
1602     "Monday",
1603     "Tuesday",
1604     "Wednesday",
1605     "Thursday",
1606     "Friday",
1607     "Saturday"];
1608
1609 // private
1610 Date.y2kYear = 50;
1611 // private
1612 Date.monthNumbers = {
1613     Jan:0,
1614     Feb:1,
1615     Mar:2,
1616     Apr:3,
1617     May:4,
1618     Jun:5,
1619     Jul:6,
1620     Aug:7,
1621     Sep:8,
1622     Oct:9,
1623     Nov:10,
1624     Dec:11};
1625
1626 /**
1627  * Creates and returns a new Date instance with the exact same date value as the called instance.
1628  * Dates are copied and passed by reference, so if a copied date variable is modified later, the original
1629  * variable will also be changed.  When the intention is to create a new variable that will not
1630  * modify the original instance, you should create a clone.
1631  *
1632  * Example of correctly cloning a date:
1633  * <pre><code>
1634 //wrong way:
1635 var orig = new Date('10/1/2006');
1636 var copy = orig;
1637 copy.setDate(5);
1638 document.write(orig);  //returns 'Thu Oct 05 2006'!
1639
1640 //correct way:
1641 var orig = new Date('10/1/2006');
1642 var copy = orig.clone();
1643 copy.setDate(5);
1644 document.write(orig);  //returns 'Thu Oct 01 2006'
1645 </code></pre>
1646  * @return {Date} The new Date instance
1647  */
1648 Date.prototype.clone = function() {
1649         return new Date(this.getTime());
1650 };
1651
1652 /**
1653  * Clears any time information from this date
1654  @param {Boolean} clone true to create a clone of this date, clear the time and return it
1655  @return {Date} this or the clone
1656  */
1657 Date.prototype.clearTime = function(clone){
1658     if(clone){
1659         return this.clone().clearTime();
1660     }
1661     this.setHours(0);
1662     this.setMinutes(0);
1663     this.setSeconds(0);
1664     this.setMilliseconds(0);
1665     return this;
1666 };
1667
1668 // private
1669 // safari setMonth is broken
1670 if(Roo.isSafari){
1671     Date.brokenSetMonth = Date.prototype.setMonth;
1672         Date.prototype.setMonth = function(num){
1673                 if(num <= -1){
1674                         var n = Math.ceil(-num);
1675                         var back_year = Math.ceil(n/12);
1676                         var month = (n % 12) ? 12 - n % 12 : 0 ;
1677                         this.setFullYear(this.getFullYear() - back_year);
1678                         return Date.brokenSetMonth.call(this, month);
1679                 } else {
1680                         return Date.brokenSetMonth.apply(this, arguments);
1681                 }
1682         };
1683 }
1684
1685 /** Date interval constant 
1686 * @static 
1687 * @type String */
1688 Date.MILLI = "ms";
1689 /** Date interval constant 
1690 * @static 
1691 * @type String */
1692 Date.SECOND = "s";
1693 /** Date interval constant 
1694 * @static 
1695 * @type String */
1696 Date.MINUTE = "mi";
1697 /** Date interval constant 
1698 * @static 
1699 * @type String */
1700 Date.HOUR = "h";
1701 /** Date interval constant 
1702 * @static 
1703 * @type String */
1704 Date.DAY = "d";
1705 /** Date interval constant 
1706 * @static 
1707 * @type String */
1708 Date.MONTH = "mo";
1709 /** Date interval constant 
1710 * @static 
1711 * @type String */
1712 Date.YEAR = "y";
1713
1714 /**
1715  * Provides a convenient method of performing basic date arithmetic.  This method
1716  * does not modify the Date instance being called - it creates and returns
1717  * a new Date instance containing the resulting date value.
1718  *
1719  * Examples:
1720  * <pre><code>
1721 //Basic usage:
1722 var dt = new Date('10/29/2006').add(Date.DAY, 5);
1723 document.write(dt); //returns 'Fri Oct 06 2006 00:00:00'
1724
1725 //Negative values will subtract correctly:
1726 var dt2 = new Date('10/1/2006').add(Date.DAY, -5);
1727 document.write(dt2); //returns 'Tue Sep 26 2006 00:00:00'
1728
1729 //You can even chain several calls together in one line!
1730 var dt3 = new Date('10/1/2006').add(Date.DAY, 5).add(Date.HOUR, 8).add(Date.MINUTE, -30);
1731 document.write(dt3); //returns 'Fri Oct 06 2006 07:30:00'
1732  </code></pre>
1733  *
1734  * @param {String} interval   A valid date interval enum value
1735  * @param {Number} value      The amount to add to the current date
1736  * @return {Date} The new Date instance
1737  */
1738 Date.prototype.add = function(interval, value){
1739   var d = this.clone();
1740   if (!interval || value === 0) return d;
1741   switch(interval.toLowerCase()){
1742     case Date.MILLI:
1743       d.setMilliseconds(this.getMilliseconds() + value);
1744       break;
1745     case Date.SECOND:
1746       d.setSeconds(this.getSeconds() + value);
1747       break;
1748     case Date.MINUTE:
1749       d.setMinutes(this.getMinutes() + value);
1750       break;
1751     case Date.HOUR:
1752       d.setHours(this.getHours() + value);
1753       break;
1754     case Date.DAY:
1755       d.setDate(this.getDate() + value);
1756       break;
1757     case Date.MONTH:
1758       var day = this.getDate();
1759       if(day > 28){
1760           day = Math.min(day, this.getFirstDateOfMonth().add('mo', value).getLastDateOfMonth().getDate());
1761       }
1762       d.setDate(day);
1763       d.setMonth(this.getMonth() + value);
1764       break;
1765     case Date.YEAR:
1766       d.setFullYear(this.getFullYear() + value);
1767       break;
1768   }
1769   return d;
1770 };
1771 /*
1772  * Based on:
1773  * Ext JS Library 1.1.1
1774  * Copyright(c) 2006-2007, Ext JS, LLC.
1775  *
1776  * Originally Released Under LGPL - original licence link has changed is not relivant.
1777  *
1778  * Fork - LGPL
1779  * <script type="text/javascript">
1780  */
1781
1782 /**
1783  * @class Roo.lib.Dom
1784  * @static
1785  * 
1786  * Dom utils (from YIU afaik)
1787  * 
1788  **/
1789 Roo.lib.Dom = {
1790     /**
1791      * Get the view width
1792      * @param {Boolean} full True will get the full document, otherwise it's the view width
1793      * @return {Number} The width
1794      */
1795      
1796     getViewWidth : function(full) {
1797         return full ? this.getDocumentWidth() : this.getViewportWidth();
1798     },
1799     /**
1800      * Get the view height
1801      * @param {Boolean} full True will get the full document, otherwise it's the view height
1802      * @return {Number} The height
1803      */
1804     getViewHeight : function(full) {
1805         return full ? this.getDocumentHeight() : this.getViewportHeight();
1806     },
1807
1808     getDocumentHeight: function() {
1809         var scrollHeight = (document.compatMode != "CSS1Compat") ? document.body.scrollHeight : document.documentElement.scrollHeight;
1810         return Math.max(scrollHeight, this.getViewportHeight());
1811     },
1812
1813     getDocumentWidth: function() {
1814         var scrollWidth = (document.compatMode != "CSS1Compat") ? document.body.scrollWidth : document.documentElement.scrollWidth;
1815         return Math.max(scrollWidth, this.getViewportWidth());
1816     },
1817
1818     getViewportHeight: function() {
1819         var height = self.innerHeight;
1820         var mode = document.compatMode;
1821
1822         if ((mode || Roo.isIE) && !Roo.isOpera) {
1823             height = (mode == "CSS1Compat") ?
1824                      document.documentElement.clientHeight :
1825                      document.body.clientHeight;
1826         }
1827
1828         return height;
1829     },
1830
1831     getViewportWidth: function() {
1832         var width = self.innerWidth;
1833         var mode = document.compatMode;
1834
1835         if (mode || Roo.isIE) {
1836             width = (mode == "CSS1Compat") ?
1837                     document.documentElement.clientWidth :
1838                     document.body.clientWidth;
1839         }
1840         return width;
1841     },
1842
1843     isAncestor : function(p, c) {
1844         p = Roo.getDom(p);
1845         c = Roo.getDom(c);
1846         if (!p || !c) {
1847             return false;
1848         }
1849
1850         if (p.contains && !Roo.isSafari) {
1851             return p.contains(c);
1852         } else if (p.compareDocumentPosition) {
1853             return !!(p.compareDocumentPosition(c) & 16);
1854         } else {
1855             var parent = c.parentNode;
1856             while (parent) {
1857                 if (parent == p) {
1858                     return true;
1859                 }
1860                 else if (!parent.tagName || parent.tagName.toUpperCase() == "HTML") {
1861                     return false;
1862                 }
1863                 parent = parent.parentNode;
1864             }
1865             return false;
1866         }
1867     },
1868
1869     getRegion : function(el) {
1870         return Roo.lib.Region.getRegion(el);
1871     },
1872
1873     getY : function(el) {
1874         return this.getXY(el)[1];
1875     },
1876
1877     getX : function(el) {
1878         return this.getXY(el)[0];
1879     },
1880
1881     getXY : function(el) {
1882         var p, pe, b, scroll, bd = document.body;
1883         el = Roo.getDom(el);
1884         var fly = Roo.lib.AnimBase.fly;
1885         if (el.getBoundingClientRect) {
1886             b = el.getBoundingClientRect();
1887             scroll = fly(document).getScroll();
1888             return [b.left + scroll.left, b.top + scroll.top];
1889         }
1890         var x = 0, y = 0;
1891
1892         p = el;
1893
1894         var hasAbsolute = fly(el).getStyle("position") == "absolute";
1895
1896         while (p) {
1897
1898             x += p.offsetLeft;
1899             y += p.offsetTop;
1900
1901             if (!hasAbsolute && fly(p).getStyle("position") == "absolute") {
1902                 hasAbsolute = true;
1903             }
1904
1905             if (Roo.isGecko) {
1906                 pe = fly(p);
1907
1908                 var bt = parseInt(pe.getStyle("borderTopWidth"), 10) || 0;
1909                 var bl = parseInt(pe.getStyle("borderLeftWidth"), 10) || 0;
1910
1911
1912                 x += bl;
1913                 y += bt;
1914
1915
1916                 if (p != el && pe.getStyle('overflow') != 'visible') {
1917                     x += bl;
1918                     y += bt;
1919                 }
1920             }
1921             p = p.offsetParent;
1922         }
1923
1924         if (Roo.isSafari && hasAbsolute) {
1925             x -= bd.offsetLeft;
1926             y -= bd.offsetTop;
1927         }
1928
1929         if (Roo.isGecko && !hasAbsolute) {
1930             var dbd = fly(bd);
1931             x += parseInt(dbd.getStyle("borderLeftWidth"), 10) || 0;
1932             y += parseInt(dbd.getStyle("borderTopWidth"), 10) || 0;
1933         }
1934
1935         p = el.parentNode;
1936         while (p && p != bd) {
1937             if (!Roo.isOpera || (p.tagName != 'TR' && fly(p).getStyle("display") != "inline")) {
1938                 x -= p.scrollLeft;
1939                 y -= p.scrollTop;
1940             }
1941             p = p.parentNode;
1942         }
1943         return [x, y];
1944     },
1945  
1946   
1947
1948
1949     setXY : function(el, xy) {
1950         el = Roo.fly(el, '_setXY');
1951         el.position();
1952         var pts = el.translatePoints(xy);
1953         if (xy[0] !== false) {
1954             el.dom.style.left = pts.left + "px";
1955         }
1956         if (xy[1] !== false) {
1957             el.dom.style.top = pts.top + "px";
1958         }
1959     },
1960
1961     setX : function(el, x) {
1962         this.setXY(el, [x, false]);
1963     },
1964
1965     setY : function(el, y) {
1966         this.setXY(el, [false, y]);
1967     }
1968 };
1969 /*
1970  * Portions of this file are based on pieces of Yahoo User Interface Library
1971  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
1972  * YUI licensed under the BSD License:
1973  * http://developer.yahoo.net/yui/license.txt
1974  * <script type="text/javascript">
1975  *
1976  */
1977
1978 Roo.lib.Event = function() {
1979     var loadComplete = false;
1980     var listeners = [];
1981     var unloadListeners = [];
1982     var retryCount = 0;
1983     var onAvailStack = [];
1984     var counter = 0;
1985     var lastError = null;
1986
1987     return {
1988         POLL_RETRYS: 200,
1989         POLL_INTERVAL: 20,
1990         EL: 0,
1991         TYPE: 1,
1992         FN: 2,
1993         WFN: 3,
1994         OBJ: 3,
1995         ADJ_SCOPE: 4,
1996         _interval: null,
1997
1998         startInterval: function() {
1999             if (!this._interval) {
2000                 var self = this;
2001                 var callback = function() {
2002                     self._tryPreloadAttach();
2003                 };
2004                 this._interval = setInterval(callback, this.POLL_INTERVAL);
2005
2006             }
2007         },
2008
2009         onAvailable: function(p_id, p_fn, p_obj, p_override) {
2010             onAvailStack.push({ id:         p_id,
2011                 fn:         p_fn,
2012                 obj:        p_obj,
2013                 override:   p_override,
2014                 checkReady: false    });
2015
2016             retryCount = this.POLL_RETRYS;
2017             this.startInterval();
2018         },
2019
2020
2021         addListener: function(el, eventName, fn) {
2022             el = Roo.getDom(el);
2023             if (!el || !fn) {
2024                 return false;
2025             }
2026
2027             if ("unload" == eventName) {
2028                 unloadListeners[unloadListeners.length] =
2029                 [el, eventName, fn];
2030                 return true;
2031             }
2032
2033             var wrappedFn = function(e) {
2034                 return fn(Roo.lib.Event.getEvent(e));
2035             };
2036
2037             var li = [el, eventName, fn, wrappedFn];
2038
2039             var index = listeners.length;
2040             listeners[index] = li;
2041
2042             this.doAdd(el, eventName, wrappedFn, false);
2043             return true;
2044
2045         },
2046
2047
2048         removeListener: function(el, eventName, fn) {
2049             var i, len;
2050
2051             el = Roo.getDom(el);
2052
2053             if(!fn) {
2054                 return this.purgeElement(el, false, eventName);
2055             }
2056
2057
2058             if ("unload" == eventName) {
2059
2060                 for (i = 0,len = unloadListeners.length; i < len; i++) {
2061                     var li = unloadListeners[i];
2062                     if (li &&
2063                         li[0] == el &&
2064                         li[1] == eventName &&
2065                         li[2] == fn) {
2066                         unloadListeners.splice(i, 1);
2067                         return true;
2068                     }
2069                 }
2070
2071                 return false;
2072             }
2073
2074             var cacheItem = null;
2075
2076
2077             var index = arguments[3];
2078
2079             if ("undefined" == typeof index) {
2080                 index = this._getCacheIndex(el, eventName, fn);
2081             }
2082
2083             if (index >= 0) {
2084                 cacheItem = listeners[index];
2085             }
2086
2087             if (!el || !cacheItem) {
2088                 return false;
2089             }
2090
2091             this.doRemove(el, eventName, cacheItem[this.WFN], false);
2092
2093             delete listeners[index][this.WFN];
2094             delete listeners[index][this.FN];
2095             listeners.splice(index, 1);
2096
2097             return true;
2098
2099         },
2100
2101
2102         getTarget: function(ev, resolveTextNode) {
2103             ev = ev.browserEvent || ev;
2104             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2105             var t = ev.target || ev.srcElement;
2106             return this.resolveTextNode(t);
2107         },
2108
2109
2110         resolveTextNode: function(node) {
2111             if (Roo.isSafari && node && 3 == node.nodeType) {
2112                 return node.parentNode;
2113             } else {
2114                 return node;
2115             }
2116         },
2117
2118
2119         getPageX: function(ev) {
2120             ev = ev.browserEvent || ev;
2121             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2122             var x = ev.pageX;
2123             if (!x && 0 !== x) {
2124                 x = ev.clientX || 0;
2125
2126                 if (Roo.isIE) {
2127                     x += this.getScroll()[1];
2128                 }
2129             }
2130
2131             return x;
2132         },
2133
2134
2135         getPageY: function(ev) {
2136             ev = ev.browserEvent || ev;
2137             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2138             var y = ev.pageY;
2139             if (!y && 0 !== y) {
2140                 y = ev.clientY || 0;
2141
2142                 if (Roo.isIE) {
2143                     y += this.getScroll()[0];
2144                 }
2145             }
2146
2147
2148             return y;
2149         },
2150
2151
2152         getXY: function(ev) {
2153             ev = ev.browserEvent || ev;
2154             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2155             return [this.getPageX(ev), this.getPageY(ev)];
2156         },
2157
2158
2159         getRelatedTarget: function(ev) {
2160             ev = ev.browserEvent || ev;
2161             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2162             var t = ev.relatedTarget;
2163             if (!t) {
2164                 if (ev.type == "mouseout") {
2165                     t = ev.toElement;
2166                 } else if (ev.type == "mouseover") {
2167                     t = ev.fromElement;
2168                 }
2169             }
2170
2171             return this.resolveTextNode(t);
2172         },
2173
2174
2175         getTime: function(ev) {
2176             ev = ev.browserEvent || ev;
2177             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2178             if (!ev.time) {
2179                 var t = new Date().getTime();
2180                 try {
2181                     ev.time = t;
2182                 } catch(ex) {
2183                     this.lastError = ex;
2184                     return t;
2185                 }
2186             }
2187
2188             return ev.time;
2189         },
2190
2191
2192         stopEvent: function(ev) {
2193             this.stopPropagation(ev);
2194             this.preventDefault(ev);
2195         },
2196
2197
2198         stopPropagation: function(ev) {
2199             ev = ev.browserEvent || ev;
2200             if (ev.stopPropagation) {
2201                 ev.stopPropagation();
2202             } else {
2203                 ev.cancelBubble = true;
2204             }
2205         },
2206
2207
2208         preventDefault: function(ev) {
2209             ev = ev.browserEvent || ev;
2210             if(ev.preventDefault) {
2211                 ev.preventDefault();
2212             } else {
2213                 ev.returnValue = false;
2214             }
2215         },
2216
2217
2218         getEvent: function(e) {
2219             var ev = e || window.event;
2220             if (!ev) {
2221                 var c = this.getEvent.caller;
2222                 while (c) {
2223                     ev = c.arguments[0];
2224                     if (ev && Event == ev.constructor) {
2225                         break;
2226                     }
2227                     c = c.caller;
2228                 }
2229             }
2230             return ev;
2231         },
2232
2233
2234         getCharCode: function(ev) {
2235             ev = ev.browserEvent || ev;
2236             return ev.charCode || ev.keyCode || 0;
2237         },
2238
2239
2240         _getCacheIndex: function(el, eventName, fn) {
2241             for (var i = 0,len = listeners.length; i < len; ++i) {
2242                 var li = listeners[i];
2243                 if (li &&
2244                     li[this.FN] == fn &&
2245                     li[this.EL] == el &&
2246                     li[this.TYPE] == eventName) {
2247                     return i;
2248                 }
2249             }
2250
2251             return -1;
2252         },
2253
2254
2255         elCache: {},
2256
2257
2258         getEl: function(id) {
2259             return document.getElementById(id);
2260         },
2261
2262
2263         clearCache: function() {
2264         },
2265
2266
2267         _load: function(e) {
2268             loadComplete = true;
2269             var EU = Roo.lib.Event;
2270
2271
2272             if (Roo.isIE) {
2273                 EU.doRemove(window, "load", EU._load);
2274             }
2275         },
2276
2277
2278         _tryPreloadAttach: function() {
2279
2280             if (this.locked) {
2281                 return false;
2282             }
2283
2284             this.locked = true;
2285
2286
2287             var tryAgain = !loadComplete;
2288             if (!tryAgain) {
2289                 tryAgain = (retryCount > 0);
2290             }
2291
2292
2293             var notAvail = [];
2294             for (var i = 0,len = onAvailStack.length; i < len; ++i) {
2295                 var item = onAvailStack[i];
2296                 if (item) {
2297                     var el = this.getEl(item.id);
2298
2299                     if (el) {
2300                         if (!item.checkReady ||
2301                             loadComplete ||
2302                             el.nextSibling ||
2303                             (document && document.body)) {
2304
2305                             var scope = el;
2306                             if (item.override) {
2307                                 if (item.override === true) {
2308                                     scope = item.obj;
2309                                 } else {
2310                                     scope = item.override;
2311                                 }
2312                             }
2313                             item.fn.call(scope, item.obj);
2314                             onAvailStack[i] = null;
2315                         }
2316                     } else {
2317                         notAvail.push(item);
2318                     }
2319                 }
2320             }
2321
2322             retryCount = (notAvail.length === 0) ? 0 : retryCount - 1;
2323
2324             if (tryAgain) {
2325
2326                 this.startInterval();
2327             } else {
2328                 clearInterval(this._interval);
2329                 this._interval = null;
2330             }
2331
2332             this.locked = false;
2333
2334             return true;
2335
2336         },
2337
2338
2339         purgeElement: function(el, recurse, eventName) {
2340             var elListeners = this.getListeners(el, eventName);
2341             if (elListeners) {
2342                 for (var i = 0,len = elListeners.length; i < len; ++i) {
2343                     var l = elListeners[i];
2344                     this.removeListener(el, l.type, l.fn);
2345                 }
2346             }
2347
2348             if (recurse && el && el.childNodes) {
2349                 for (i = 0,len = el.childNodes.length; i < len; ++i) {
2350                     this.purgeElement(el.childNodes[i], recurse, eventName);
2351                 }
2352             }
2353         },
2354
2355
2356         getListeners: function(el, eventName) {
2357             var results = [], searchLists;
2358             if (!eventName) {
2359                 searchLists = [listeners, unloadListeners];
2360             } else if (eventName == "unload") {
2361                 searchLists = [unloadListeners];
2362             } else {
2363                 searchLists = [listeners];
2364             }
2365
2366             for (var j = 0; j < searchLists.length; ++j) {
2367                 var searchList = searchLists[j];
2368                 if (searchList && searchList.length > 0) {
2369                     for (var i = 0,len = searchList.length; i < len; ++i) {
2370                         var l = searchList[i];
2371                         if (l && l[this.EL] === el &&
2372                             (!eventName || eventName === l[this.TYPE])) {
2373                             results.push({
2374                                 type:   l[this.TYPE],
2375                                 fn:     l[this.FN],
2376                                 obj:    l[this.OBJ],
2377                                 adjust: l[this.ADJ_SCOPE],
2378                                 index:  i
2379                             });
2380                         }
2381                     }
2382                 }
2383             }
2384
2385             return (results.length) ? results : null;
2386         },
2387
2388
2389         _unload: function(e) {
2390
2391             var EU = Roo.lib.Event, i, j, l, len, index;
2392
2393             for (i = 0,len = unloadListeners.length; i < len; ++i) {
2394                 l = unloadListeners[i];
2395                 if (l) {
2396                     var scope = window;
2397                     if (l[EU.ADJ_SCOPE]) {
2398                         if (l[EU.ADJ_SCOPE] === true) {
2399                             scope = l[EU.OBJ];
2400                         } else {
2401                             scope = l[EU.ADJ_SCOPE];
2402                         }
2403                     }
2404                     l[EU.FN].call(scope, EU.getEvent(e), l[EU.OBJ]);
2405                     unloadListeners[i] = null;
2406                     l = null;
2407                     scope = null;
2408                 }
2409             }
2410
2411             unloadListeners = null;
2412
2413             if (listeners && listeners.length > 0) {
2414                 j = listeners.length;
2415                 while (j) {
2416                     index = j - 1;
2417                     l = listeners[index];
2418                     if (l) {
2419                         EU.removeListener(l[EU.EL], l[EU.TYPE],
2420                                 l[EU.FN], index);
2421                     }
2422                     j = j - 1;
2423                 }
2424                 l = null;
2425
2426                 EU.clearCache();
2427             }
2428
2429             EU.doRemove(window, "unload", EU._unload);
2430
2431         },
2432
2433
2434         getScroll: function() {
2435             var dd = document.documentElement, db = document.body;
2436             if (dd && (dd.scrollTop || dd.scrollLeft)) {
2437                 return [dd.scrollTop, dd.scrollLeft];
2438             } else if (db) {
2439                 return [db.scrollTop, db.scrollLeft];
2440             } else {
2441                 return [0, 0];
2442             }
2443         },
2444
2445
2446         doAdd: function () {
2447             if (window.addEventListener) {
2448                 return function(el, eventName, fn, capture) {
2449                     el.addEventListener(eventName, fn, (capture));
2450                 };
2451             } else if (window.attachEvent) {
2452                 return function(el, eventName, fn, capture) {
2453                     el.attachEvent("on" + eventName, fn);
2454                 };
2455             } else {
2456                 return function() {
2457                 };
2458             }
2459         }(),
2460
2461
2462         doRemove: function() {
2463             if (window.removeEventListener) {
2464                 return function (el, eventName, fn, capture) {
2465                     el.removeEventListener(eventName, fn, (capture));
2466                 };
2467             } else if (window.detachEvent) {
2468                 return function (el, eventName, fn) {
2469                     el.detachEvent("on" + eventName, fn);
2470                 };
2471             } else {
2472                 return function() {
2473                 };
2474             }
2475         }()
2476     };
2477     
2478 }();
2479 (function() {     
2480    
2481     var E = Roo.lib.Event;
2482     E.on = E.addListener;
2483     E.un = E.removeListener;
2484
2485     if (document && document.body) {
2486         E._load();
2487     } else {
2488         E.doAdd(window, "load", E._load);
2489     }
2490     E.doAdd(window, "unload", E._unload);
2491     E._tryPreloadAttach();
2492 })();
2493
2494 /*
2495  * Portions of this file are based on pieces of Yahoo User Interface Library
2496  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
2497  * YUI licensed under the BSD License:
2498  * http://developer.yahoo.net/yui/license.txt
2499  * <script type="text/javascript">
2500  *
2501  */
2502
2503 (function() {
2504     /**
2505      * @class Roo.lib.Ajax
2506      *
2507      */
2508     Roo.lib.Ajax = {
2509         /**
2510          * @static 
2511          */
2512         request : function(method, uri, cb, data, options) {
2513             if(options){
2514                 var hs = options.headers;
2515                 if(hs){
2516                     for(var h in hs){
2517                         if(hs.hasOwnProperty(h)){
2518                             this.initHeader(h, hs[h], false);
2519                         }
2520                     }
2521                 }
2522                 if(options.xmlData){
2523                     this.initHeader('Content-Type', 'text/xml', false);
2524                     method = 'POST';
2525                     data = options.xmlData;
2526                 }
2527             }
2528
2529             return this.asyncRequest(method, uri, cb, data);
2530         },
2531
2532         serializeForm : function(form) {
2533             if(typeof form == 'string') {
2534                 form = (document.getElementById(form) || document.forms[form]);
2535             }
2536
2537             var el, name, val, disabled, data = '', hasSubmit = false;
2538             for (var i = 0; i < form.elements.length; i++) {
2539                 el = form.elements[i];
2540                 disabled = form.elements[i].disabled;
2541                 name = form.elements[i].name;
2542                 val = form.elements[i].value;
2543
2544                 if (!disabled && name){
2545                     switch (el.type)
2546                             {
2547                         case 'select-one':
2548                         case 'select-multiple':
2549                             for (var j = 0; j < el.options.length; j++) {
2550                                 if (el.options[j].selected) {
2551                                     if (Roo.isIE) {
2552                                         data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(el.options[j].attributes['value'].specified ? el.options[j].value : el.options[j].text) + '&';
2553                                     }
2554                                     else {
2555                                         data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(el.options[j].hasAttribute('value') ? el.options[j].value : el.options[j].text) + '&';
2556                                     }
2557                                 }
2558                             }
2559                             break;
2560                         case 'radio':
2561                         case 'checkbox':
2562                             if (el.checked) {
2563                                 data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(val) + '&';
2564                             }
2565                             break;
2566                         case 'file':
2567
2568                         case undefined:
2569
2570                         case 'reset':
2571
2572                         case 'button':
2573
2574                             break;
2575                         case 'submit':
2576                             if(hasSubmit == false) {
2577                                 data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(val) + '&';
2578                                 hasSubmit = true;
2579                             }
2580                             break;
2581                         default:
2582                             data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(val) + '&';
2583                             break;
2584                     }
2585                 }
2586             }
2587             data = data.substr(0, data.length - 1);
2588             return data;
2589         },
2590
2591         headers:{},
2592
2593         hasHeaders:false,
2594
2595         useDefaultHeader:true,
2596
2597         defaultPostHeader:'application/x-www-form-urlencoded',
2598
2599         useDefaultXhrHeader:true,
2600
2601         defaultXhrHeader:'XMLHttpRequest',
2602
2603         hasDefaultHeaders:true,
2604
2605         defaultHeaders:{},
2606
2607         poll:{},
2608
2609         timeout:{},
2610
2611         pollInterval:50,
2612
2613         transactionId:0,
2614
2615         setProgId:function(id)
2616         {
2617             this.activeX.unshift(id);
2618         },
2619
2620         setDefaultPostHeader:function(b)
2621         {
2622             this.useDefaultHeader = b;
2623         },
2624
2625         setDefaultXhrHeader:function(b)
2626         {
2627             this.useDefaultXhrHeader = b;
2628         },
2629
2630         setPollingInterval:function(i)
2631         {
2632             if (typeof i == 'number' && isFinite(i)) {
2633                 this.pollInterval = i;
2634             }
2635         },
2636
2637         createXhrObject:function(transactionId)
2638         {
2639             var obj,http;
2640             try
2641             {
2642
2643                 http = new XMLHttpRequest();
2644
2645                 obj = { conn:http, tId:transactionId };
2646             }
2647             catch(e)
2648             {
2649                 for (var i = 0; i < this.activeX.length; ++i) {
2650                     try
2651                     {
2652
2653                         http = new ActiveXObject(this.activeX[i]);
2654
2655                         obj = { conn:http, tId:transactionId };
2656                         break;
2657                     }
2658                     catch(e) {
2659                     }
2660                 }
2661             }
2662             finally
2663             {
2664                 return obj;
2665             }
2666         },
2667
2668         getConnectionObject:function()
2669         {
2670             var o;
2671             var tId = this.transactionId;
2672
2673             try
2674             {
2675                 o = this.createXhrObject(tId);
2676                 if (o) {
2677                     this.transactionId++;
2678                 }
2679             }
2680             catch(e) {
2681             }
2682             finally
2683             {
2684                 return o;
2685             }
2686         },
2687
2688         asyncRequest:function(method, uri, callback, postData)
2689         {
2690             var o = this.getConnectionObject();
2691
2692             if (!o) {
2693                 return null;
2694             }
2695             else {
2696                 o.conn.open(method, uri, true);
2697
2698                 if (this.useDefaultXhrHeader) {
2699                     if (!this.defaultHeaders['X-Requested-With']) {
2700                         this.initHeader('X-Requested-With', this.defaultXhrHeader, true);
2701                     }
2702                 }
2703
2704                 if(postData && this.useDefaultHeader){
2705                     this.initHeader('Content-Type', this.defaultPostHeader);
2706                 }
2707
2708                  if (this.hasDefaultHeaders || this.hasHeaders) {
2709                     this.setHeader(o);
2710                 }
2711
2712                 this.handleReadyState(o, callback);
2713                 o.conn.send(postData || null);
2714
2715                 return o;
2716             }
2717         },
2718
2719         handleReadyState:function(o, callback)
2720         {
2721             var oConn = this;
2722
2723             if (callback && callback.timeout) {
2724                 
2725                 this.timeout[o.tId] = window.setTimeout(function() {
2726                     oConn.abort(o, callback, true);
2727                 }, callback.timeout);
2728             }
2729
2730             this.poll[o.tId] = window.setInterval(
2731                     function() {
2732                         if (o.conn && o.conn.readyState == 4) {
2733                             window.clearInterval(oConn.poll[o.tId]);
2734                             delete oConn.poll[o.tId];
2735
2736                             if(callback && callback.timeout) {
2737                                 window.clearTimeout(oConn.timeout[o.tId]);
2738                                 delete oConn.timeout[o.tId];
2739                             }
2740
2741                             oConn.handleTransactionResponse(o, callback);
2742                         }
2743                     }
2744                     , this.pollInterval);
2745         },
2746
2747         handleTransactionResponse:function(o, callback, isAbort)
2748         {
2749
2750             if (!callback) {
2751                 this.releaseObject(o);
2752                 return;
2753             }
2754
2755             var httpStatus, responseObject;
2756
2757             try
2758             {
2759                 if (o.conn.status !== undefined && o.conn.status != 0) {
2760                     httpStatus = o.conn.status;
2761                 }
2762                 else {
2763                     httpStatus = 13030;
2764                 }
2765             }
2766             catch(e) {
2767
2768
2769                 httpStatus = 13030;
2770             }
2771
2772             if (httpStatus >= 200 && httpStatus < 300) {
2773                 responseObject = this.createResponseObject(o, callback.argument);
2774                 if (callback.success) {
2775                     if (!callback.scope) {
2776                         callback.success(responseObject);
2777                     }
2778                     else {
2779
2780
2781                         callback.success.apply(callback.scope, [responseObject]);
2782                     }
2783                 }
2784             }
2785             else {
2786                 switch (httpStatus) {
2787
2788                     case 12002:
2789                     case 12029:
2790                     case 12030:
2791                     case 12031:
2792                     case 12152:
2793                     case 13030:
2794                         responseObject = this.createExceptionObject(o.tId, callback.argument, (isAbort ? isAbort : false));
2795                         if (callback.failure) {
2796                             if (!callback.scope) {
2797                                 callback.failure(responseObject);
2798                             }
2799                             else {
2800                                 callback.failure.apply(callback.scope, [responseObject]);
2801                             }
2802                         }
2803                         break;
2804                     default:
2805                         responseObject = this.createResponseObject(o, callback.argument);
2806                         if (callback.failure) {
2807                             if (!callback.scope) {
2808                                 callback.failure(responseObject);
2809                             }
2810                             else {
2811                                 callback.failure.apply(callback.scope, [responseObject]);
2812                             }
2813                         }
2814                 }
2815             }
2816
2817             this.releaseObject(o);
2818             responseObject = null;
2819         },
2820
2821         createResponseObject:function(o, callbackArg)
2822         {
2823             var obj = {};
2824             var headerObj = {};
2825
2826             try
2827             {
2828                 var headerStr = o.conn.getAllResponseHeaders();
2829                 var header = headerStr.split('\n');
2830                 for (var i = 0; i < header.length; i++) {
2831                     var delimitPos = header[i].indexOf(':');
2832                     if (delimitPos != -1) {
2833                         headerObj[header[i].substring(0, delimitPos)] = header[i].substring(delimitPos + 2);
2834                     }
2835                 }
2836             }
2837             catch(e) {
2838             }
2839
2840             obj.tId = o.tId;
2841             obj.status = o.conn.status;
2842             obj.statusText = o.conn.statusText;
2843             obj.getResponseHeader = headerObj;
2844             obj.getAllResponseHeaders = headerStr;
2845             obj.responseText = o.conn.responseText;
2846             obj.responseXML = o.conn.responseXML;
2847
2848             if (typeof callbackArg !== undefined) {
2849                 obj.argument = callbackArg;
2850             }
2851
2852             return obj;
2853         },
2854
2855         createExceptionObject:function(tId, callbackArg, isAbort)
2856         {
2857             var COMM_CODE = 0;
2858             var COMM_ERROR = 'communication failure';
2859             var ABORT_CODE = -1;
2860             var ABORT_ERROR = 'transaction aborted';
2861
2862             var obj = {};
2863
2864             obj.tId = tId;
2865             if (isAbort) {
2866                 obj.status = ABORT_CODE;
2867                 obj.statusText = ABORT_ERROR;
2868             }
2869             else {
2870                 obj.status = COMM_CODE;
2871                 obj.statusText = COMM_ERROR;
2872             }
2873
2874             if (callbackArg) {
2875                 obj.argument = callbackArg;
2876             }
2877
2878             return obj;
2879         },
2880
2881         initHeader:function(label, value, isDefault)
2882         {
2883             var headerObj = (isDefault) ? this.defaultHeaders : this.headers;
2884
2885             if (headerObj[label] === undefined) {
2886                 headerObj[label] = value;
2887             }
2888             else {
2889
2890
2891                 headerObj[label] = value + "," + headerObj[label];
2892             }
2893
2894             if (isDefault) {
2895                 this.hasDefaultHeaders = true;
2896             }
2897             else {
2898                 this.hasHeaders = true;
2899             }
2900         },
2901
2902
2903         setHeader:function(o)
2904         {
2905             if (this.hasDefaultHeaders) {
2906                 for (var prop in this.defaultHeaders) {
2907                     if (this.defaultHeaders.hasOwnProperty(prop)) {
2908                         o.conn.setRequestHeader(prop, this.defaultHeaders[prop]);
2909                     }
2910                 }
2911             }
2912
2913             if (this.hasHeaders) {
2914                 for (var prop in this.headers) {
2915                     if (this.headers.hasOwnProperty(prop)) {
2916                         o.conn.setRequestHeader(prop, this.headers[prop]);
2917                     }
2918                 }
2919                 this.headers = {};
2920                 this.hasHeaders = false;
2921             }
2922         },
2923
2924         resetDefaultHeaders:function() {
2925             delete this.defaultHeaders;
2926             this.defaultHeaders = {};
2927             this.hasDefaultHeaders = false;
2928         },
2929
2930         abort:function(o, callback, isTimeout)
2931         {
2932             if(this.isCallInProgress(o)) {
2933                 o.conn.abort();
2934                 window.clearInterval(this.poll[o.tId]);
2935                 delete this.poll[o.tId];
2936                 if (isTimeout) {
2937                     delete this.timeout[o.tId];
2938                 }
2939
2940                 this.handleTransactionResponse(o, callback, true);
2941
2942                 return true;
2943             }
2944             else {
2945                 return false;
2946             }
2947         },
2948
2949
2950         isCallInProgress:function(o)
2951         {
2952             if (o && o.conn) {
2953                 return o.conn.readyState != 4 && o.conn.readyState != 0;
2954             }
2955             else {
2956
2957                 return false;
2958             }
2959         },
2960
2961
2962         releaseObject:function(o)
2963         {
2964
2965             o.conn = null;
2966
2967             o = null;
2968         },
2969
2970         activeX:[
2971         'MSXML2.XMLHTTP.3.0',
2972         'MSXML2.XMLHTTP',
2973         'Microsoft.XMLHTTP'
2974         ]
2975
2976
2977     };
2978 })();/*
2979  * Portions of this file are based on pieces of Yahoo User Interface Library
2980  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
2981  * YUI licensed under the BSD License:
2982  * http://developer.yahoo.net/yui/license.txt
2983  * <script type="text/javascript">
2984  *
2985  */
2986
2987 Roo.lib.Region = function(t, r, b, l) {
2988     this.top = t;
2989     this[1] = t;
2990     this.right = r;
2991     this.bottom = b;
2992     this.left = l;
2993     this[0] = l;
2994 };
2995
2996
2997 Roo.lib.Region.prototype = {
2998     contains : function(region) {
2999         return ( region.left >= this.left &&
3000                  region.right <= this.right &&
3001                  region.top >= this.top &&
3002                  region.bottom <= this.bottom    );
3003
3004     },
3005
3006     getArea : function() {
3007         return ( (this.bottom - this.top) * (this.right - this.left) );
3008     },
3009
3010     intersect : function(region) {
3011         var t = Math.max(this.top, region.top);
3012         var r = Math.min(this.right, region.right);
3013         var b = Math.min(this.bottom, region.bottom);
3014         var l = Math.max(this.left, region.left);
3015
3016         if (b >= t && r >= l) {
3017             return new Roo.lib.Region(t, r, b, l);
3018         } else {
3019             return null;
3020         }
3021     },
3022     union : function(region) {
3023         var t = Math.min(this.top, region.top);
3024         var r = Math.max(this.right, region.right);
3025         var b = Math.max(this.bottom, region.bottom);
3026         var l = Math.min(this.left, region.left);
3027
3028         return new Roo.lib.Region(t, r, b, l);
3029     },
3030
3031     adjust : function(t, l, b, r) {
3032         this.top += t;
3033         this.left += l;
3034         this.right += r;
3035         this.bottom += b;
3036         return this;
3037     }
3038 };
3039
3040 Roo.lib.Region.getRegion = function(el) {
3041     var p = Roo.lib.Dom.getXY(el);
3042
3043     var t = p[1];
3044     var r = p[0] + el.offsetWidth;
3045     var b = p[1] + el.offsetHeight;
3046     var l = p[0];
3047
3048     return new Roo.lib.Region(t, r, b, l);
3049 };
3050 /*
3051  * Portions of this file are based on pieces of Yahoo User Interface Library
3052  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3053  * YUI licensed under the BSD License:
3054  * http://developer.yahoo.net/yui/license.txt
3055  * <script type="text/javascript">
3056  *
3057  */
3058 //@@dep Roo.lib.Region
3059
3060
3061 Roo.lib.Point = function(x, y) {
3062     if (x instanceof Array) {
3063         y = x[1];
3064         x = x[0];
3065     }
3066     this.x = this.right = this.left = this[0] = x;
3067     this.y = this.top = this.bottom = this[1] = y;
3068 };
3069
3070 Roo.lib.Point.prototype = new Roo.lib.Region();
3071 /*
3072  * Portions of this file are based on pieces of Yahoo User Interface Library
3073  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3074  * YUI licensed under the BSD License:
3075  * http://developer.yahoo.net/yui/license.txt
3076  * <script type="text/javascript">
3077  *
3078  */
3079  
3080 (function() {   
3081
3082     Roo.lib.Anim = {
3083         scroll : function(el, args, duration, easing, cb, scope) {
3084             this.run(el, args, duration, easing, cb, scope, Roo.lib.Scroll);
3085         },
3086
3087         motion : function(el, args, duration, easing, cb, scope) {
3088             this.run(el, args, duration, easing, cb, scope, Roo.lib.Motion);
3089         },
3090
3091         color : function(el, args, duration, easing, cb, scope) {
3092             this.run(el, args, duration, easing, cb, scope, Roo.lib.ColorAnim);
3093         },
3094
3095         run : function(el, args, duration, easing, cb, scope, type) {
3096             type = type || Roo.lib.AnimBase;
3097             if (typeof easing == "string") {
3098                 easing = Roo.lib.Easing[easing];
3099             }
3100             var anim = new type(el, args, duration, easing);
3101             anim.animateX(function() {
3102                 Roo.callback(cb, scope);
3103             });
3104             return anim;
3105         }
3106     };
3107 })();/*
3108  * Portions of this file are based on pieces of Yahoo User Interface Library
3109  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3110  * YUI licensed under the BSD License:
3111  * http://developer.yahoo.net/yui/license.txt
3112  * <script type="text/javascript">
3113  *
3114  */
3115
3116 (function() {    
3117     var libFlyweight;
3118     
3119     function fly(el) {
3120         if (!libFlyweight) {
3121             libFlyweight = new Roo.Element.Flyweight();
3122         }
3123         libFlyweight.dom = el;
3124         return libFlyweight;
3125     }
3126
3127     // since this uses fly! - it cant be in DOM (which does not have fly yet..)
3128     
3129    
3130     
3131     Roo.lib.AnimBase = function(el, attributes, duration, method) {
3132         if (el) {
3133             this.init(el, attributes, duration, method);
3134         }
3135     };
3136
3137     Roo.lib.AnimBase.fly = fly;
3138     
3139     
3140     
3141     Roo.lib.AnimBase.prototype = {
3142
3143         toString: function() {
3144             var el = this.getEl();
3145             var id = el.id || el.tagName;
3146             return ("Anim " + id);
3147         },
3148
3149         patterns: {
3150             noNegatives:        /width|height|opacity|padding/i,
3151             offsetAttribute:  /^((width|height)|(top|left))$/,
3152             defaultUnit:        /width|height|top$|bottom$|left$|right$/i,
3153             offsetUnit:         /\d+(em|%|en|ex|pt|in|cm|mm|pc)$/i
3154         },
3155
3156
3157         doMethod: function(attr, start, end) {
3158             return this.method(this.currentFrame, start, end - start, this.totalFrames);
3159         },
3160
3161
3162         setAttribute: function(attr, val, unit) {
3163             if (this.patterns.noNegatives.test(attr)) {
3164                 val = (val > 0) ? val : 0;
3165             }
3166
3167             Roo.fly(this.getEl(), '_anim').setStyle(attr, val + unit);
3168         },
3169
3170
3171         getAttribute: function(attr) {
3172             var el = this.getEl();
3173             var val = fly(el).getStyle(attr);
3174
3175             if (val !== 'auto' && !this.patterns.offsetUnit.test(val)) {
3176                 return parseFloat(val);
3177             }
3178
3179             var a = this.patterns.offsetAttribute.exec(attr) || [];
3180             var pos = !!( a[3] );
3181             var box = !!( a[2] );
3182
3183
3184             if (box || (fly(el).getStyle('position') == 'absolute' && pos)) {
3185                 val = el['offset' + a[0].charAt(0).toUpperCase() + a[0].substr(1)];
3186             } else {
3187                 val = 0;
3188             }
3189
3190             return val;
3191         },
3192
3193
3194         getDefaultUnit: function(attr) {
3195             if (this.patterns.defaultUnit.test(attr)) {
3196                 return 'px';
3197             }
3198
3199             return '';
3200         },
3201
3202         animateX : function(callback, scope) {
3203             var f = function() {
3204                 this.onComplete.removeListener(f);
3205                 if (typeof callback == "function") {
3206                     callback.call(scope || this, this);
3207                 }
3208             };
3209             this.onComplete.addListener(f, this);
3210             this.animate();
3211         },
3212
3213
3214         setRuntimeAttribute: function(attr) {
3215             var start;
3216             var end;
3217             var attributes = this.attributes;
3218
3219             this.runtimeAttributes[attr] = {};
3220
3221             var isset = function(prop) {
3222                 return (typeof prop !== 'undefined');
3223             };
3224
3225             if (!isset(attributes[attr]['to']) && !isset(attributes[attr]['by'])) {
3226                 return false;
3227             }
3228
3229             start = ( isset(attributes[attr]['from']) ) ? attributes[attr]['from'] : this.getAttribute(attr);
3230
3231
3232             if (isset(attributes[attr]['to'])) {
3233                 end = attributes[attr]['to'];
3234             } else if (isset(attributes[attr]['by'])) {
3235                 if (start.constructor == Array) {
3236                     end = [];
3237                     for (var i = 0, len = start.length; i < len; ++i) {
3238                         end[i] = start[i] + attributes[attr]['by'][i];
3239                     }
3240                 } else {
3241                     end = start + attributes[attr]['by'];
3242                 }
3243             }
3244
3245             this.runtimeAttributes[attr].start = start;
3246             this.runtimeAttributes[attr].end = end;
3247
3248
3249             this.runtimeAttributes[attr].unit = ( isset(attributes[attr].unit) ) ? attributes[attr]['unit'] : this.getDefaultUnit(attr);
3250         },
3251
3252
3253         init: function(el, attributes, duration, method) {
3254
3255             var isAnimated = false;
3256
3257
3258             var startTime = null;
3259
3260
3261             var actualFrames = 0;
3262
3263
3264             el = Roo.getDom(el);
3265
3266
3267             this.attributes = attributes || {};
3268
3269
3270             this.duration = duration || 1;
3271
3272
3273             this.method = method || Roo.lib.Easing.easeNone;
3274
3275
3276             this.useSeconds = true;
3277
3278
3279             this.currentFrame = 0;
3280
3281
3282             this.totalFrames = Roo.lib.AnimMgr.fps;
3283
3284
3285             this.getEl = function() {
3286                 return el;
3287             };
3288
3289
3290             this.isAnimated = function() {
3291                 return isAnimated;
3292             };
3293
3294
3295             this.getStartTime = function() {
3296                 return startTime;
3297             };
3298
3299             this.runtimeAttributes = {};
3300
3301
3302             this.animate = function() {
3303                 if (this.isAnimated()) {
3304                     return false;
3305                 }
3306
3307                 this.currentFrame = 0;
3308
3309                 this.totalFrames = ( this.useSeconds ) ? Math.ceil(Roo.lib.AnimMgr.fps * this.duration) : this.duration;
3310
3311                 Roo.lib.AnimMgr.registerElement(this);
3312             };
3313
3314
3315             this.stop = function(finish) {
3316                 if (finish) {
3317                     this.currentFrame = this.totalFrames;
3318                     this._onTween.fire();
3319                 }
3320                 Roo.lib.AnimMgr.stop(this);
3321             };
3322
3323             var onStart = function() {
3324                 this.onStart.fire();
3325
3326                 this.runtimeAttributes = {};
3327                 for (var attr in this.attributes) {
3328                     this.setRuntimeAttribute(attr);
3329                 }
3330
3331                 isAnimated = true;
3332                 actualFrames = 0;
3333                 startTime = new Date();
3334             };
3335
3336
3337             var onTween = function() {
3338                 var data = {
3339                     duration: new Date() - this.getStartTime(),
3340                     currentFrame: this.currentFrame
3341                 };
3342
3343                 data.toString = function() {
3344                     return (
3345                             'duration: ' + data.duration +
3346                             ', currentFrame: ' + data.currentFrame
3347                             );
3348                 };
3349
3350                 this.onTween.fire(data);
3351
3352                 var runtimeAttributes = this.runtimeAttributes;
3353
3354                 for (var attr in runtimeAttributes) {
3355                     this.setAttribute(attr, this.doMethod(attr, runtimeAttributes[attr].start, runtimeAttributes[attr].end), runtimeAttributes[attr].unit);
3356                 }
3357
3358                 actualFrames += 1;
3359             };
3360
3361             var onComplete = function() {
3362                 var actual_duration = (new Date() - startTime) / 1000 ;
3363
3364                 var data = {
3365                     duration: actual_duration,
3366                     frames: actualFrames,
3367                     fps: actualFrames / actual_duration
3368                 };
3369
3370                 data.toString = function() {
3371                     return (
3372                             'duration: ' + data.duration +
3373                             ', frames: ' + data.frames +
3374                             ', fps: ' + data.fps
3375                             );
3376                 };
3377
3378                 isAnimated = false;
3379                 actualFrames = 0;
3380                 this.onComplete.fire(data);
3381             };
3382
3383
3384             this._onStart = new Roo.util.Event(this);
3385             this.onStart = new Roo.util.Event(this);
3386             this.onTween = new Roo.util.Event(this);
3387             this._onTween = new Roo.util.Event(this);
3388             this.onComplete = new Roo.util.Event(this);
3389             this._onComplete = new Roo.util.Event(this);
3390             this._onStart.addListener(onStart);
3391             this._onTween.addListener(onTween);
3392             this._onComplete.addListener(onComplete);
3393         }
3394     };
3395 })();
3396 /*
3397  * Portions of this file are based on pieces of Yahoo User Interface Library
3398  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3399  * YUI licensed under the BSD License:
3400  * http://developer.yahoo.net/yui/license.txt
3401  * <script type="text/javascript">
3402  *
3403  */
3404
3405 Roo.lib.AnimMgr = new function() {
3406
3407     var thread = null;
3408
3409
3410     var queue = [];
3411
3412
3413     var tweenCount = 0;
3414
3415
3416     this.fps = 1000;
3417
3418
3419     this.delay = 1;
3420
3421
3422     this.registerElement = function(tween) {
3423         queue[queue.length] = tween;
3424         tweenCount += 1;
3425         tween._onStart.fire();
3426         this.start();
3427     };
3428
3429
3430     this.unRegister = function(tween, index) {
3431         tween._onComplete.fire();
3432         index = index || getIndex(tween);
3433         if (index != -1) {
3434             queue.splice(index, 1);
3435         }
3436
3437         tweenCount -= 1;
3438         if (tweenCount <= 0) {
3439             this.stop();
3440         }
3441     };
3442
3443
3444     this.start = function() {
3445         if (thread === null) {
3446             thread = setInterval(this.run, this.delay);
3447         }
3448     };
3449
3450
3451     this.stop = function(tween) {
3452         if (!tween) {
3453             clearInterval(thread);
3454
3455             for (var i = 0, len = queue.length; i < len; ++i) {
3456                 if (queue[0].isAnimated()) {
3457                     this.unRegister(queue[0], 0);
3458                 }
3459             }
3460
3461             queue = [];
3462             thread = null;
3463             tweenCount = 0;
3464         }
3465         else {
3466             this.unRegister(tween);
3467         }
3468     };
3469
3470
3471     this.run = function() {
3472         for (var i = 0, len = queue.length; i < len; ++i) {
3473             var tween = queue[i];
3474             if (!tween || !tween.isAnimated()) {
3475                 continue;
3476             }
3477
3478             if (tween.currentFrame < tween.totalFrames || tween.totalFrames === null)
3479             {
3480                 tween.currentFrame += 1;
3481
3482                 if (tween.useSeconds) {
3483                     correctFrame(tween);
3484                 }
3485                 tween._onTween.fire();
3486             }
3487             else {
3488                 Roo.lib.AnimMgr.stop(tween, i);
3489             }
3490         }
3491     };
3492
3493     var getIndex = function(anim) {
3494         for (var i = 0, len = queue.length; i < len; ++i) {
3495             if (queue[i] == anim) {
3496                 return i;
3497             }
3498         }
3499         return -1;
3500     };
3501
3502
3503     var correctFrame = function(tween) {
3504         var frames = tween.totalFrames;
3505         var frame = tween.currentFrame;
3506         var expected = (tween.currentFrame * tween.duration * 1000 / tween.totalFrames);
3507         var elapsed = (new Date() - tween.getStartTime());
3508         var tweak = 0;
3509
3510         if (elapsed < tween.duration * 1000) {
3511             tweak = Math.round((elapsed / expected - 1) * tween.currentFrame);
3512         } else {
3513             tweak = frames - (frame + 1);
3514         }
3515         if (tweak > 0 && isFinite(tweak)) {
3516             if (tween.currentFrame + tweak >= frames) {
3517                 tweak = frames - (frame + 1);
3518             }
3519
3520             tween.currentFrame += tweak;
3521         }
3522     };
3523 };
3524
3525     /*
3526  * Portions of this file are based on pieces of Yahoo User Interface Library
3527  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3528  * YUI licensed under the BSD License:
3529  * http://developer.yahoo.net/yui/license.txt
3530  * <script type="text/javascript">
3531  *
3532  */
3533 Roo.lib.Bezier = new function() {
3534
3535         this.getPosition = function(points, t) {
3536             var n = points.length;
3537             var tmp = [];
3538
3539             for (var i = 0; i < n; ++i) {
3540                 tmp[i] = [points[i][0], points[i][1]];
3541             }
3542
3543             for (var j = 1; j < n; ++j) {
3544                 for (i = 0; i < n - j; ++i) {
3545                     tmp[i][0] = (1 - t) * tmp[i][0] + t * tmp[parseInt(i + 1, 10)][0];
3546                     tmp[i][1] = (1 - t) * tmp[i][1] + t * tmp[parseInt(i + 1, 10)][1];
3547                 }
3548             }
3549
3550             return [ tmp[0][0], tmp[0][1] ];
3551
3552         };
3553     };/*
3554  * Portions of this file are based on pieces of Yahoo User Interface Library
3555  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3556  * YUI licensed under the BSD License:
3557  * http://developer.yahoo.net/yui/license.txt
3558  * <script type="text/javascript">
3559  *
3560  */
3561 (function() {
3562
3563     Roo.lib.ColorAnim = function(el, attributes, duration, method) {
3564         Roo.lib.ColorAnim.superclass.constructor.call(this, el, attributes, duration, method);
3565     };
3566
3567     Roo.extend(Roo.lib.ColorAnim, Roo.lib.AnimBase);
3568
3569     var fly = Roo.lib.AnimBase.fly;
3570     var Y = Roo.lib;
3571     var superclass = Y.ColorAnim.superclass;
3572     var proto = Y.ColorAnim.prototype;
3573
3574     proto.toString = function() {
3575         var el = this.getEl();
3576         var id = el.id || el.tagName;
3577         return ("ColorAnim " + id);
3578     };
3579
3580     proto.patterns.color = /color$/i;
3581     proto.patterns.rgb = /^rgb\(([0-9]+)\s*,\s*([0-9]+)\s*,\s*([0-9]+)\)$/i;
3582     proto.patterns.hex = /^#?([0-9A-F]{2})([0-9A-F]{2})([0-9A-F]{2})$/i;
3583     proto.patterns.hex3 = /^#?([0-9A-F]{1})([0-9A-F]{1})([0-9A-F]{1})$/i;
3584     proto.patterns.transparent = /^transparent|rgba\(0, 0, 0, 0\)$/;
3585
3586
3587     proto.parseColor = function(s) {
3588         if (s.length == 3) {
3589             return s;
3590         }
3591
3592         var c = this.patterns.hex.exec(s);
3593         if (c && c.length == 4) {
3594             return [ parseInt(c[1], 16), parseInt(c[2], 16), parseInt(c[3], 16) ];
3595         }
3596
3597         c = this.patterns.rgb.exec(s);
3598         if (c && c.length == 4) {
3599             return [ parseInt(c[1], 10), parseInt(c[2], 10), parseInt(c[3], 10) ];
3600         }
3601
3602         c = this.patterns.hex3.exec(s);
3603         if (c && c.length == 4) {
3604             return [ parseInt(c[1] + c[1], 16), parseInt(c[2] + c[2], 16), parseInt(c[3] + c[3], 16) ];
3605         }
3606
3607         return null;
3608     };
3609     // since this uses fly! - it cant be in ColorAnim (which does not have fly yet..)
3610     proto.getAttribute = function(attr) {
3611         var el = this.getEl();
3612         if (this.patterns.color.test(attr)) {
3613             var val = fly(el).getStyle(attr);
3614
3615             if (this.patterns.transparent.test(val)) {
3616                 var parent = el.parentNode;
3617                 val = fly(parent).getStyle(attr);
3618
3619                 while (parent && this.patterns.transparent.test(val)) {
3620                     parent = parent.parentNode;
3621                     val = fly(parent).getStyle(attr);
3622                     if (parent.tagName.toUpperCase() == 'HTML') {
3623                         val = '#fff';
3624                     }
3625                 }
3626             }
3627         } else {
3628             val = superclass.getAttribute.call(this, attr);
3629         }
3630
3631         return val;
3632     };
3633     proto.getAttribute = function(attr) {
3634         var el = this.getEl();
3635         if (this.patterns.color.test(attr)) {
3636             var val = fly(el).getStyle(attr);
3637
3638             if (this.patterns.transparent.test(val)) {
3639                 var parent = el.parentNode;
3640                 val = fly(parent).getStyle(attr);
3641
3642                 while (parent && this.patterns.transparent.test(val)) {
3643                     parent = parent.parentNode;
3644                     val = fly(parent).getStyle(attr);
3645                     if (parent.tagName.toUpperCase() == 'HTML') {
3646                         val = '#fff';
3647                     }
3648                 }
3649             }
3650         } else {
3651             val = superclass.getAttribute.call(this, attr);
3652         }
3653
3654         return val;
3655     };
3656
3657     proto.doMethod = function(attr, start, end) {
3658         var val;
3659
3660         if (this.patterns.color.test(attr)) {
3661             val = [];
3662             for (var i = 0, len = start.length; i < len; ++i) {
3663                 val[i] = superclass.doMethod.call(this, attr, start[i], end[i]);
3664             }
3665
3666             val = 'rgb(' + Math.floor(val[0]) + ',' + Math.floor(val[1]) + ',' + Math.floor(val[2]) + ')';
3667         }
3668         else {
3669             val = superclass.doMethod.call(this, attr, start, end);
3670         }
3671
3672         return val;
3673     };
3674
3675     proto.setRuntimeAttribute = function(attr) {
3676         superclass.setRuntimeAttribute.call(this, attr);
3677
3678         if (this.patterns.color.test(attr)) {
3679             var attributes = this.attributes;
3680             var start = this.parseColor(this.runtimeAttributes[attr].start);
3681             var end = this.parseColor(this.runtimeAttributes[attr].end);
3682
3683             if (typeof attributes[attr]['to'] === 'undefined' && typeof attributes[attr]['by'] !== 'undefined') {
3684                 end = this.parseColor(attributes[attr].by);
3685
3686                 for (var i = 0, len = start.length; i < len; ++i) {
3687                     end[i] = start[i] + end[i];
3688                 }
3689             }
3690
3691             this.runtimeAttributes[attr].start = start;
3692             this.runtimeAttributes[attr].end = end;
3693         }
3694     };
3695 })();
3696
3697 /*
3698  * Portions of this file are based on pieces of Yahoo User Interface Library
3699  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3700  * YUI licensed under the BSD License:
3701  * http://developer.yahoo.net/yui/license.txt
3702  * <script type="text/javascript">
3703  *
3704  */
3705 Roo.lib.Easing = {
3706
3707
3708     easeNone: function (t, b, c, d) {
3709         return c * t / d + b;
3710     },
3711
3712
3713     easeIn: function (t, b, c, d) {
3714         return c * (t /= d) * t + b;
3715     },
3716
3717
3718     easeOut: function (t, b, c, d) {
3719         return -c * (t /= d) * (t - 2) + b;
3720     },
3721
3722
3723     easeBoth: function (t, b, c, d) {
3724         if ((t /= d / 2) < 1) {
3725             return c / 2 * t * t + b;
3726         }
3727
3728         return -c / 2 * ((--t) * (t - 2) - 1) + b;
3729     },
3730
3731
3732     easeInStrong: function (t, b, c, d) {
3733         return c * (t /= d) * t * t * t + b;
3734     },
3735
3736
3737     easeOutStrong: function (t, b, c, d) {
3738         return -c * ((t = t / d - 1) * t * t * t - 1) + b;
3739     },
3740
3741
3742     easeBothStrong: function (t, b, c, d) {
3743         if ((t /= d / 2) < 1) {
3744             return c / 2 * t * t * t * t + b;
3745         }
3746
3747         return -c / 2 * ((t -= 2) * t * t * t - 2) + b;
3748     },
3749
3750
3751
3752     elasticIn: function (t, b, c, d, a, p) {
3753         if (t == 0) {
3754             return b;
3755         }
3756         if ((t /= d) == 1) {
3757             return b + c;
3758         }
3759         if (!p) {
3760             p = d * .3;
3761         }
3762
3763         if (!a || a < Math.abs(c)) {
3764             a = c;
3765             var s = p / 4;
3766         }
3767         else {
3768             var s = p / (2 * Math.PI) * Math.asin(c / a);
3769         }
3770
3771         return -(a * Math.pow(2, 10 * (t -= 1)) * Math.sin((t * d - s) * (2 * Math.PI) / p)) + b;
3772     },
3773
3774
3775     elasticOut: function (t, b, c, d, a, p) {
3776         if (t == 0) {
3777             return b;
3778         }
3779         if ((t /= d) == 1) {
3780             return b + c;
3781         }
3782         if (!p) {
3783             p = d * .3;
3784         }
3785
3786         if (!a || a < Math.abs(c)) {
3787             a = c;
3788             var s = p / 4;
3789         }
3790         else {
3791             var s = p / (2 * Math.PI) * Math.asin(c / a);
3792         }
3793
3794         return a * Math.pow(2, -10 * t) * Math.sin((t * d - s) * (2 * Math.PI) / p) + c + b;
3795     },
3796
3797
3798     elasticBoth: function (t, b, c, d, a, p) {
3799         if (t == 0) {
3800             return b;
3801         }
3802
3803         if ((t /= d / 2) == 2) {
3804             return b + c;
3805         }
3806
3807         if (!p) {
3808             p = d * (.3 * 1.5);
3809         }
3810
3811         if (!a || a < Math.abs(c)) {
3812             a = c;
3813             var s = p / 4;
3814         }
3815         else {
3816             var s = p / (2 * Math.PI) * Math.asin(c / a);
3817         }
3818
3819         if (t < 1) {
3820             return -.5 * (a * Math.pow(2, 10 * (t -= 1)) *
3821                           Math.sin((t * d - s) * (2 * Math.PI) / p)) + b;
3822         }
3823         return a * Math.pow(2, -10 * (t -= 1)) *
3824                Math.sin((t * d - s) * (2 * Math.PI) / p) * .5 + c + b;
3825     },
3826
3827
3828
3829     backIn: function (t, b, c, d, s) {
3830         if (typeof s == 'undefined') {
3831             s = 1.70158;
3832         }
3833         return c * (t /= d) * t * ((s + 1) * t - s) + b;
3834     },
3835
3836
3837     backOut: function (t, b, c, d, s) {
3838         if (typeof s == 'undefined') {
3839             s = 1.70158;
3840         }
3841         return c * ((t = t / d - 1) * t * ((s + 1) * t + s) + 1) + b;
3842     },
3843
3844
3845     backBoth: function (t, b, c, d, s) {
3846         if (typeof s == 'undefined') {
3847             s = 1.70158;
3848         }
3849
3850         if ((t /= d / 2 ) < 1) {
3851             return c / 2 * (t * t * (((s *= (1.525)) + 1) * t - s)) + b;
3852         }
3853         return c / 2 * ((t -= 2) * t * (((s *= (1.525)) + 1) * t + s) + 2) + b;
3854     },
3855
3856
3857     bounceIn: function (t, b, c, d) {
3858         return c - Roo.lib.Easing.bounceOut(d - t, 0, c, d) + b;
3859     },
3860
3861
3862     bounceOut: function (t, b, c, d) {
3863         if ((t /= d) < (1 / 2.75)) {
3864             return c * (7.5625 * t * t) + b;
3865         } else if (t < (2 / 2.75)) {
3866             return c * (7.5625 * (t -= (1.5 / 2.75)) * t + .75) + b;
3867         } else if (t < (2.5 / 2.75)) {
3868             return c * (7.5625 * (t -= (2.25 / 2.75)) * t + .9375) + b;
3869         }
3870         return c * (7.5625 * (t -= (2.625 / 2.75)) * t + .984375) + b;
3871     },
3872
3873
3874     bounceBoth: function (t, b, c, d) {
3875         if (t < d / 2) {
3876             return Roo.lib.Easing.bounceIn(t * 2, 0, c, d) * .5 + b;
3877         }
3878         return Roo.lib.Easing.bounceOut(t * 2 - d, 0, c, d) * .5 + c * .5 + b;
3879     }
3880 };/*
3881  * Portions of this file are based on pieces of Yahoo User Interface Library
3882  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3883  * YUI licensed under the BSD License:
3884  * http://developer.yahoo.net/yui/license.txt
3885  * <script type="text/javascript">
3886  *
3887  */
3888     (function() {
3889         Roo.lib.Motion = function(el, attributes, duration, method) {
3890             if (el) {
3891                 Roo.lib.Motion.superclass.constructor.call(this, el, attributes, duration, method);
3892             }
3893         };
3894
3895         Roo.extend(Roo.lib.Motion, Roo.lib.ColorAnim);
3896
3897
3898         var Y = Roo.lib;
3899         var superclass = Y.Motion.superclass;
3900         var proto = Y.Motion.prototype;
3901
3902         proto.toString = function() {
3903             var el = this.getEl();
3904             var id = el.id || el.tagName;
3905             return ("Motion " + id);
3906         };
3907
3908         proto.patterns.points = /^points$/i;
3909
3910         proto.setAttribute = function(attr, val, unit) {
3911             if (this.patterns.points.test(attr)) {
3912                 unit = unit || 'px';
3913                 superclass.setAttribute.call(this, 'left', val[0], unit);
3914                 superclass.setAttribute.call(this, 'top', val[1], unit);
3915             } else {
3916                 superclass.setAttribute.call(this, attr, val, unit);
3917             }
3918         };
3919
3920         proto.getAttribute = function(attr) {
3921             if (this.patterns.points.test(attr)) {
3922                 var val = [
3923                         superclass.getAttribute.call(this, 'left'),
3924                         superclass.getAttribute.call(this, 'top')
3925                         ];
3926             } else {
3927                 val = superclass.getAttribute.call(this, attr);
3928             }
3929
3930             return val;
3931         };
3932
3933         proto.doMethod = function(attr, start, end) {
3934             var val = null;
3935
3936             if (this.patterns.points.test(attr)) {
3937                 var t = this.method(this.currentFrame, 0, 100, this.totalFrames) / 100;
3938                 val = Y.Bezier.getPosition(this.runtimeAttributes[attr], t);
3939             } else {
3940                 val = superclass.doMethod.call(this, attr, start, end);
3941             }
3942             return val;
3943         };
3944
3945         proto.setRuntimeAttribute = function(attr) {
3946             if (this.patterns.points.test(attr)) {
3947                 var el = this.getEl();
3948                 var attributes = this.attributes;
3949                 var start;
3950                 var control = attributes['points']['control'] || [];
3951                 var end;
3952                 var i, len;
3953
3954                 if (control.length > 0 && !(control[0] instanceof Array)) {
3955                     control = [control];
3956                 } else {
3957                     var tmp = [];
3958                     for (i = 0,len = control.length; i < len; ++i) {
3959                         tmp[i] = control[i];
3960                     }
3961                     control = tmp;
3962                 }
3963
3964                 Roo.fly(el).position();
3965
3966                 if (isset(attributes['points']['from'])) {
3967                     Roo.lib.Dom.setXY(el, attributes['points']['from']);
3968                 }
3969                 else {
3970                     Roo.lib.Dom.setXY(el, Roo.lib.Dom.getXY(el));
3971                 }
3972
3973                 start = this.getAttribute('points');
3974
3975
3976                 if (isset(attributes['points']['to'])) {
3977                     end = translateValues.call(this, attributes['points']['to'], start);
3978
3979                     var pageXY = Roo.lib.Dom.getXY(this.getEl());
3980                     for (i = 0,len = control.length; i < len; ++i) {
3981                         control[i] = translateValues.call(this, control[i], start);
3982                     }
3983
3984
3985                 } else if (isset(attributes['points']['by'])) {
3986                     end = [ start[0] + attributes['points']['by'][0], start[1] + attributes['points']['by'][1] ];
3987
3988                     for (i = 0,len = control.length; i < len; ++i) {
3989                         control[i] = [ start[0] + control[i][0], start[1] + control[i][1] ];
3990                     }
3991                 }
3992
3993                 this.runtimeAttributes[attr] = [start];
3994
3995                 if (control.length > 0) {
3996                     this.runtimeAttributes[attr] = this.runtimeAttributes[attr].concat(control);
3997                 }
3998
3999                 this.runtimeAttributes[attr][this.runtimeAttributes[attr].length] = end;
4000             }
4001             else {
4002                 superclass.setRuntimeAttribute.call(this, attr);
4003             }
4004         };
4005
4006         var translateValues = function(val, start) {
4007             var pageXY = Roo.lib.Dom.getXY(this.getEl());
4008             val = [ val[0] - pageXY[0] + start[0], val[1] - pageXY[1] + start[1] ];
4009
4010             return val;
4011         };
4012
4013         var isset = function(prop) {
4014             return (typeof prop !== 'undefined');
4015         };
4016     })();
4017 /*
4018  * Portions of this file are based on pieces of Yahoo User Interface Library
4019  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
4020  * YUI licensed under the BSD License:
4021  * http://developer.yahoo.net/yui/license.txt
4022  * <script type="text/javascript">
4023  *
4024  */
4025     (function() {
4026         Roo.lib.Scroll = function(el, attributes, duration, method) {
4027             if (el) {
4028                 Roo.lib.Scroll.superclass.constructor.call(this, el, attributes, duration, method);
4029             }
4030         };
4031
4032         Roo.extend(Roo.lib.Scroll, Roo.lib.ColorAnim);
4033
4034
4035         var Y = Roo.lib;
4036         var superclass = Y.Scroll.superclass;
4037         var proto = Y.Scroll.prototype;
4038
4039         proto.toString = function() {
4040             var el = this.getEl();
4041             var id = el.id || el.tagName;
4042             return ("Scroll " + id);
4043         };
4044
4045         proto.doMethod = function(attr, start, end) {
4046             var val = null;
4047
4048             if (attr == 'scroll') {
4049                 val = [
4050                         this.method(this.currentFrame, start[0], end[0] - start[0], this.totalFrames),
4051                         this.method(this.currentFrame, start[1], end[1] - start[1], this.totalFrames)
4052                         ];
4053
4054             } else {
4055                 val = superclass.doMethod.call(this, attr, start, end);
4056             }
4057             return val;
4058         };
4059
4060         proto.getAttribute = function(attr) {
4061             var val = null;
4062             var el = this.getEl();
4063
4064             if (attr == 'scroll') {
4065                 val = [ el.scrollLeft, el.scrollTop ];
4066             } else {
4067                 val = superclass.getAttribute.call(this, attr);
4068             }
4069
4070             return val;
4071         };
4072
4073         proto.setAttribute = function(attr, val, unit) {
4074             var el = this.getEl();
4075
4076             if (attr == 'scroll') {
4077                 el.scrollLeft = val[0];
4078                 el.scrollTop = val[1];
4079             } else {
4080                 superclass.setAttribute.call(this, attr, val, unit);
4081             }
4082         };
4083     })();
4084 /*
4085  * Based on:
4086  * Ext JS Library 1.1.1
4087  * Copyright(c) 2006-2007, Ext JS, LLC.
4088  *
4089  * Originally Released Under LGPL - original licence link has changed is not relivant.
4090  *
4091  * Fork - LGPL
4092  * <script type="text/javascript">
4093  */
4094
4095
4096 // nasty IE9 hack - what a pile of crap that is..
4097
4098  if (typeof Range != "undefined" && typeof Range.prototype.createContextualFragment == "undefined") {
4099     Range.prototype.createContextualFragment = function (html) {
4100         var doc = window.document;
4101         var container = doc.createElement("div");
4102         container.innerHTML = html;
4103         var frag = doc.createDocumentFragment(), n;
4104         while ((n = container.firstChild)) {
4105             frag.appendChild(n);
4106         }
4107         return frag;
4108     };
4109 }
4110
4111 /**
4112  * @class Roo.DomHelper
4113  * Utility class for working with DOM and/or Templates. It transparently supports using HTML fragments or DOM.
4114  * For more information see <a href="http://web.archive.org/web/20071221063734/http://www.jackslocum.com/blog/2006/10/06/domhelper-create-elements-using-dom-html-fragments-or-templates/">this blog post with examples</a>.
4115  * @singleton
4116  */
4117 Roo.DomHelper = function(){
4118     var tempTableEl = null;
4119     var emptyTags = /^(?:br|frame|hr|img|input|link|meta|range|spacer|wbr|area|param|col)$/i;
4120     var tableRe = /^table|tbody|tr|td$/i;
4121     var xmlns = {};
4122     // build as innerHTML where available
4123     /** @ignore */
4124     var createHtml = function(o){
4125         if(typeof o == 'string'){
4126             return o;
4127         }
4128         var b = "";
4129         if(!o.tag){
4130             o.tag = "div";
4131         }
4132         b += "<" + o.tag;
4133         for(var attr in o){
4134             if(attr == "tag" || attr == "children" || attr == "cn" || attr == "html" || typeof o[attr] == "function") continue;
4135             if(attr == "style"){
4136                 var s = o["style"];
4137                 if(typeof s == "function"){
4138                     s = s.call();
4139                 }
4140                 if(typeof s == "string"){
4141                     b += ' style="' + s + '"';
4142                 }else if(typeof s == "object"){
4143                     b += ' style="';
4144                     for(var key in s){
4145                         if(typeof s[key] != "function"){
4146                             b += key + ":" + s[key] + ";";
4147                         }
4148                     }
4149                     b += '"';
4150                 }
4151             }else{
4152                 if(attr == "cls"){
4153                     b += ' class="' + o["cls"] + '"';
4154                 }else if(attr == "htmlFor"){
4155                     b += ' for="' + o["htmlFor"] + '"';
4156                 }else{
4157                     b += " " + attr + '="' + o[attr] + '"';
4158                 }
4159             }
4160         }
4161         if(emptyTags.test(o.tag)){
4162             b += "/>";
4163         }else{
4164             b += ">";
4165             var cn = o.children || o.cn;
4166             if(cn){
4167                 //http://bugs.kde.org/show_bug.cgi?id=71506
4168                 if((cn instanceof Array) || (Roo.isSafari && typeof(cn.join) == "function")){
4169                     for(var i = 0, len = cn.length; i < len; i++) {
4170                         b += createHtml(cn[i], b);
4171                     }
4172                 }else{
4173                     b += createHtml(cn, b);
4174                 }
4175             }
4176             if(o.html){
4177                 b += o.html;
4178             }
4179             b += "</" + o.tag + ">";
4180         }
4181         return b;
4182     };
4183
4184     // build as dom
4185     /** @ignore */
4186     var createDom = function(o, parentNode){
4187          
4188         // defininition craeted..
4189         var ns = false;
4190         if (o.ns && o.ns != 'html') {
4191                
4192             if (o.xmlns && typeof(xmlns[o.ns]) == 'undefined') {
4193                 xmlns[o.ns] = o.xmlns;
4194                 ns = o.xmlns;
4195             }
4196             if (typeof(xmlns[o.ns]) == 'undefined') {
4197                 console.log("Trying to create namespace element " + o.ns + ", however no xmlns was sent to builder previously");
4198             }
4199             ns = xmlns[o.ns];
4200         }
4201         
4202         
4203         if (typeof(o) == 'string') {
4204             return parentNode.appendChild(document.createTextNode(o));
4205         }
4206         o.tag = o.tag || div;
4207         if (o.ns && Roo.isIE) {
4208             ns = false;
4209             o.tag = o.ns + ':' + o.tag;
4210             
4211         }
4212         var el = ns ? document.createElementNS( ns, o.tag||'div') :  document.createElement(o.tag||'div');
4213         var useSet = el.setAttribute ? true : false; // In IE some elements don't have setAttribute
4214         for(var attr in o){
4215             
4216             if(attr == "tag" || attr == "ns" ||attr == "xmlns" ||attr == "children" || attr == "cn" || attr == "html" || 
4217                     attr == "style" || typeof o[attr] == "function") continue;
4218                     
4219             if(attr=="cls" && Roo.isIE){
4220                 el.className = o["cls"];
4221             }else{
4222                 if(useSet) el.setAttribute(attr=="cls" ? 'class' : attr, o[attr]);
4223                 else el[attr] = o[attr];
4224             }
4225         }
4226         Roo.DomHelper.applyStyles(el, o.style);
4227         var cn = o.children || o.cn;
4228         if(cn){
4229             //http://bugs.kde.org/show_bug.cgi?id=71506
4230              if((cn instanceof Array) || (Roo.isSafari && typeof(cn.join) == "function")){
4231                 for(var i = 0, len = cn.length; i < len; i++) {
4232                     createDom(cn[i], el);
4233                 }
4234             }else{
4235                 createDom(cn, el);
4236             }
4237         }
4238         if(o.html){
4239             el.innerHTML = o.html;
4240         }
4241         if(parentNode){
4242            parentNode.appendChild(el);
4243         }
4244         return el;
4245     };
4246
4247     var ieTable = function(depth, s, h, e){
4248         tempTableEl.innerHTML = [s, h, e].join('');
4249         var i = -1, el = tempTableEl;
4250         while(++i < depth){
4251             el = el.firstChild;
4252         }
4253         return el;
4254     };
4255
4256     // kill repeat to save bytes
4257     var ts = '<table>',
4258         te = '</table>',
4259         tbs = ts+'<tbody>',
4260         tbe = '</tbody>'+te,
4261         trs = tbs + '<tr>',
4262         tre = '</tr>'+tbe;
4263
4264     /**
4265      * @ignore
4266      * Nasty code for IE's broken table implementation
4267      */
4268     var insertIntoTable = function(tag, where, el, html){
4269         if(!tempTableEl){
4270             tempTableEl = document.createElement('div');
4271         }
4272         var node;
4273         var before = null;
4274         if(tag == 'td'){
4275             if(where == 'afterbegin' || where == 'beforeend'){ // INTO a TD
4276                 return;
4277             }
4278             if(where == 'beforebegin'){
4279                 before = el;
4280                 el = el.parentNode;
4281             } else{
4282                 before = el.nextSibling;
4283                 el = el.parentNode;
4284             }
4285             node = ieTable(4, trs, html, tre);
4286         }
4287         else if(tag == 'tr'){
4288             if(where == 'beforebegin'){
4289                 before = el;
4290                 el = el.parentNode;
4291                 node = ieTable(3, tbs, html, tbe);
4292             } else if(where == 'afterend'){
4293                 before = el.nextSibling;
4294                 el = el.parentNode;
4295                 node = ieTable(3, tbs, html, tbe);
4296             } else{ // INTO a TR
4297                 if(where == 'afterbegin'){
4298                     before = el.firstChild;
4299                 }
4300                 node = ieTable(4, trs, html, tre);
4301             }
4302         } else if(tag == 'tbody'){
4303             if(where == 'beforebegin'){
4304                 before = el;
4305                 el = el.parentNode;
4306                 node = ieTable(2, ts, html, te);
4307             } else if(where == 'afterend'){
4308                 before = el.nextSibling;
4309                 el = el.parentNode;
4310                 node = ieTable(2, ts, html, te);
4311             } else{
4312                 if(where == 'afterbegin'){
4313                     before = el.firstChild;
4314                 }
4315                 node = ieTable(3, tbs, html, tbe);
4316             }
4317         } else{ // TABLE
4318             if(where == 'beforebegin' || where == 'afterend'){ // OUTSIDE the table
4319                 return;
4320             }
4321             if(where == 'afterbegin'){
4322                 before = el.firstChild;
4323             }
4324             node = ieTable(2, ts, html, te);
4325         }
4326         el.insertBefore(node, before);
4327         return node;
4328     };
4329
4330     return {
4331     /** True to force the use of DOM instead of html fragments @type Boolean */
4332     useDom : false,
4333
4334     /**
4335      * Returns the markup for the passed Element(s) config
4336      * @param {Object} o The Dom object spec (and children)
4337      * @return {String}
4338      */
4339     markup : function(o){
4340         return createHtml(o);
4341     },
4342
4343     /**
4344      * Applies a style specification to an element
4345      * @param {String/HTMLElement} el The element to apply styles to
4346      * @param {String/Object/Function} styles A style specification string eg "width:100px", or object in the form {width:"100px"}, or
4347      * a function which returns such a specification.
4348      */
4349     applyStyles : function(el, styles){
4350         if(styles){
4351            el = Roo.fly(el);
4352            if(typeof styles == "string"){
4353                var re = /\s?([a-z\-]*)\:\s?([^;]*);?/gi;
4354                var matches;
4355                while ((matches = re.exec(styles)) != null){
4356                    el.setStyle(matches[1], matches[2]);
4357                }
4358            }else if (typeof styles == "object"){
4359                for (var style in styles){
4360                   el.setStyle(style, styles[style]);
4361                }
4362            }else if (typeof styles == "function"){
4363                 Roo.DomHelper.applyStyles(el, styles.call());
4364            }
4365         }
4366     },
4367
4368     /**
4369      * Inserts an HTML fragment into the Dom
4370      * @param {String} where Where to insert the html in relation to el - beforeBegin, afterBegin, beforeEnd, afterEnd.
4371      * @param {HTMLElement} el The context element
4372      * @param {String} html The HTML fragmenet
4373      * @return {HTMLElement} The new node
4374      */
4375     insertHtml : function(where, el, html){
4376         where = where.toLowerCase();
4377         if(el.insertAdjacentHTML){
4378             if(tableRe.test(el.tagName)){
4379                 var rs;
4380                 if(rs = insertIntoTable(el.tagName.toLowerCase(), where, el, html)){
4381                     return rs;
4382                 }
4383             }
4384             switch(where){
4385                 case "beforebegin":
4386                     el.insertAdjacentHTML('BeforeBegin', html);
4387                     return el.previousSibling;
4388                 case "afterbegin":
4389                     el.insertAdjacentHTML('AfterBegin', html);
4390                     return el.firstChild;
4391                 case "beforeend":
4392                     el.insertAdjacentHTML('BeforeEnd', html);
4393                     return el.lastChild;
4394                 case "afterend":
4395                     el.insertAdjacentHTML('AfterEnd', html);
4396                     return el.nextSibling;
4397             }
4398             throw 'Illegal insertion point -> "' + where + '"';
4399         }
4400         var range = el.ownerDocument.createRange();
4401         var frag;
4402         switch(where){
4403              case "beforebegin":
4404                 range.setStartBefore(el);
4405                 frag = range.createContextualFragment(html);
4406                 el.parentNode.insertBefore(frag, el);
4407                 return el.previousSibling;
4408              case "afterbegin":
4409                 if(el.firstChild){
4410                     range.setStartBefore(el.firstChild);
4411                     frag = range.createContextualFragment(html);
4412                     el.insertBefore(frag, el.firstChild);
4413                     return el.firstChild;
4414                 }else{
4415                     el.innerHTML = html;
4416                     return el.firstChild;
4417                 }
4418             case "beforeend":
4419                 if(el.lastChild){
4420                     range.setStartAfter(el.lastChild);
4421                     frag = range.createContextualFragment(html);
4422                     el.appendChild(frag);
4423                     return el.lastChild;
4424                 }else{
4425                     el.innerHTML = html;
4426                     return el.lastChild;
4427                 }
4428             case "afterend":
4429                 range.setStartAfter(el);
4430                 frag = range.createContextualFragment(html);
4431                 el.parentNode.insertBefore(frag, el.nextSibling);
4432                 return el.nextSibling;
4433             }
4434             throw 'Illegal insertion point -> "' + where + '"';
4435     },
4436
4437     /**
4438      * Creates new Dom element(s) and inserts them before el
4439      * @param {String/HTMLElement/Element} el The context element
4440      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
4441      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4442      * @return {HTMLElement/Roo.Element} The new node
4443      */
4444     insertBefore : function(el, o, returnElement){
4445         return this.doInsert(el, o, returnElement, "beforeBegin");
4446     },
4447
4448     /**
4449      * Creates new Dom element(s) and inserts them after el
4450      * @param {String/HTMLElement/Element} el The context element
4451      * @param {Object} o The Dom object spec (and children)
4452      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4453      * @return {HTMLElement/Roo.Element} The new node
4454      */
4455     insertAfter : function(el, o, returnElement){
4456         return this.doInsert(el, o, returnElement, "afterEnd", "nextSibling");
4457     },
4458
4459     /**
4460      * Creates new Dom element(s) and inserts them as the first child of el
4461      * @param {String/HTMLElement/Element} el The context element
4462      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
4463      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4464      * @return {HTMLElement/Roo.Element} The new node
4465      */
4466     insertFirst : function(el, o, returnElement){
4467         return this.doInsert(el, o, returnElement, "afterBegin");
4468     },
4469
4470     // private
4471     doInsert : function(el, o, returnElement, pos, sibling){
4472         el = Roo.getDom(el);
4473         var newNode;
4474         if(this.useDom || o.ns){
4475             newNode = createDom(o, null);
4476             el.parentNode.insertBefore(newNode, sibling ? el[sibling] : el);
4477         }else{
4478             var html = createHtml(o);
4479             newNode = this.insertHtml(pos, el, html);
4480         }
4481         return returnElement ? Roo.get(newNode, true) : newNode;
4482     },
4483
4484     /**
4485      * Creates new Dom element(s) and appends them to el
4486      * @param {String/HTMLElement/Element} el The context element
4487      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
4488      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4489      * @return {HTMLElement/Roo.Element} The new node
4490      */
4491     append : function(el, o, returnElement){
4492         el = Roo.getDom(el);
4493         var newNode;
4494         if(this.useDom || o.ns){
4495             newNode = createDom(o, null);
4496             el.appendChild(newNode);
4497         }else{
4498             var html = createHtml(o);
4499             newNode = this.insertHtml("beforeEnd", el, html);
4500         }
4501         return returnElement ? Roo.get(newNode, true) : newNode;
4502     },
4503
4504     /**
4505      * Creates new Dom element(s) and overwrites the contents of el with them
4506      * @param {String/HTMLElement/Element} el The context element
4507      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
4508      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4509      * @return {HTMLElement/Roo.Element} The new node
4510      */
4511     overwrite : function(el, o, returnElement){
4512         el = Roo.getDom(el);
4513         if (o.ns) {
4514           
4515             while (el.childNodes.length) {
4516                 el.removeChild(el.firstChild);
4517             }
4518             createDom(o, el);
4519         } else {
4520             el.innerHTML = createHtml(o);   
4521         }
4522         
4523         return returnElement ? Roo.get(el.firstChild, true) : el.firstChild;
4524     },
4525
4526     /**
4527      * Creates a new Roo.DomHelper.Template from the Dom object spec
4528      * @param {Object} o The Dom object spec (and children)
4529      * @return {Roo.DomHelper.Template} The new template
4530      */
4531     createTemplate : function(o){
4532         var html = createHtml(o);
4533         return new Roo.Template(html);
4534     }
4535     };
4536 }();
4537 /*
4538  * Based on:
4539  * Ext JS Library 1.1.1
4540  * Copyright(c) 2006-2007, Ext JS, LLC.
4541  *
4542  * Originally Released Under LGPL - original licence link has changed is not relivant.
4543  *
4544  * Fork - LGPL
4545  * <script type="text/javascript">
4546  */
4547  
4548 /**
4549 * @class Roo.Template
4550 * Represents an HTML fragment template. Templates can be precompiled for greater performance.
4551 * For a list of available format functions, see {@link Roo.util.Format}.<br />
4552 * Usage:
4553 <pre><code>
4554 var t = new Roo.Template({
4555     html :  '&lt;div name="{id}"&gt;' + 
4556         '&lt;span class="{cls}"&gt;{name:trim} {someval:this.myformat}{value:ellipsis(10)}&lt;/span&gt;' +
4557         '&lt;/div&gt;',
4558     myformat: function (value, allValues) {
4559         return 'XX' + value;
4560     }
4561 });
4562 t.append('some-element', {id: 'myid', cls: 'myclass', name: 'foo', value: 'bar'});
4563 </code></pre>
4564 * For more information see this blog post with examples:
4565 *  <a href="http://www.cnitblog.com/seeyeah/archive/2011/12/30/38728.html/">DomHelper
4566      - Create Elements using DOM, HTML fragments and Templates</a>. 
4567 * @constructor
4568 * @param {Object} cfg - Configuration object.
4569 */
4570 Roo.Template = function(cfg){
4571     // BC!
4572     if(cfg instanceof Array){
4573         cfg = cfg.join("");
4574     }else if(arguments.length > 1){
4575         cfg = Array.prototype.join.call(arguments, "");
4576     }
4577     
4578     
4579     if (typeof(cfg) == 'object') {
4580         Roo.apply(this,cfg)
4581     } else {
4582         // bc
4583         this.html = cfg;
4584     }
4585     if (this.url) {
4586         this.load();
4587     }
4588     
4589 };
4590 Roo.Template.prototype = {
4591     
4592     /**
4593      * @cfg {String} url  The Url to load the template from. beware if you are loading from a url, the data may not be ready if you use it instantly..
4594      *                    it should be fixed so that template is observable...
4595      */
4596     url : false,
4597     /**
4598      * @cfg {String} html  The HTML fragment or an array of fragments to join("") or multiple arguments to join("")
4599      */
4600     html : '',
4601     /**
4602      * Returns an HTML fragment of this template with the specified values applied.
4603      * @param {Object} values The template values. Can be an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'})
4604      * @return {String} The HTML fragment
4605      */
4606     applyTemplate : function(values){
4607         try {
4608            
4609             if(this.compiled){
4610                 return this.compiled(values);
4611             }
4612             var useF = this.disableFormats !== true;
4613             var fm = Roo.util.Format, tpl = this;
4614             var fn = function(m, name, format, args){
4615                 if(format && useF){
4616                     if(format.substr(0, 5) == "this."){
4617                         return tpl.call(format.substr(5), values[name], values);
4618                     }else{
4619                         if(args){
4620                             // quoted values are required for strings in compiled templates, 
4621                             // but for non compiled we need to strip them
4622                             // quoted reversed for jsmin
4623                             var re = /^\s*['"](.*)["']\s*$/;
4624                             args = args.split(',');
4625                             for(var i = 0, len = args.length; i < len; i++){
4626                                 args[i] = args[i].replace(re, "$1");
4627                             }
4628                             args = [values[name]].concat(args);
4629                         }else{
4630                             args = [values[name]];
4631                         }
4632                         return fm[format].apply(fm, args);
4633                     }
4634                 }else{
4635                     return values[name] !== undefined ? values[name] : "";
4636                 }
4637             };
4638             return this.html.replace(this.re, fn);
4639         } catch (e) {
4640             Roo.log(e);
4641             throw e;
4642         }
4643          
4644     },
4645     
4646     loading : false,
4647       
4648     load : function ()
4649     {
4650          
4651         if (this.loading) {
4652             return;
4653         }
4654         var _t = this;
4655         
4656         this.loading = true;
4657         this.compiled = false;
4658         
4659         var cx = new Roo.data.Connection();
4660         cx.request({
4661             url : this.url,
4662             method : 'GET',
4663             success : function (response) {
4664                 _t.loading = false;
4665                 _t.html = response.responseText;
4666                 _t.url = false;
4667                 _t.compile();
4668              },
4669             failure : function(response) {
4670                 Roo.log("Template failed to load from " + _t.url);
4671                 _t.loading = false;
4672             }
4673         });
4674     },
4675
4676     /**
4677      * Sets the HTML used as the template and optionally compiles it.
4678      * @param {String} html
4679      * @param {Boolean} compile (optional) True to compile the template (defaults to undefined)
4680      * @return {Roo.Template} this
4681      */
4682     set : function(html, compile){
4683         this.html = html;
4684         this.compiled = null;
4685         if(compile){
4686             this.compile();
4687         }
4688         return this;
4689     },
4690     
4691     /**
4692      * True to disable format functions (defaults to false)
4693      * @type Boolean
4694      */
4695     disableFormats : false,
4696     
4697     /**
4698     * The regular expression used to match template variables 
4699     * @type RegExp
4700     * @property 
4701     */
4702     re : /\{([\w-]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
4703     
4704     /**
4705      * Compiles the template into an internal function, eliminating the RegEx overhead.
4706      * @return {Roo.Template} this
4707      */
4708     compile : function(){
4709         var fm = Roo.util.Format;
4710         var useF = this.disableFormats !== true;
4711         var sep = Roo.isGecko ? "+" : ",";
4712         var fn = function(m, name, format, args){
4713             if(format && useF){
4714                 args = args ? ',' + args : "";
4715                 if(format.substr(0, 5) != "this."){
4716                     format = "fm." + format + '(';
4717                 }else{
4718                     format = 'this.call("'+ format.substr(5) + '", ';
4719                     args = ", values";
4720                 }
4721             }else{
4722                 args= ''; format = "(values['" + name + "'] == undefined ? '' : ";
4723             }
4724             return "'"+ sep + format + "values['" + name + "']" + args + ")"+sep+"'";
4725         };
4726         var body;
4727         // branched to use + in gecko and [].join() in others
4728         if(Roo.isGecko){
4729             body = "this.compiled = function(values){ return '" +
4730                    this.html.replace(/\\/g, '\\\\').replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
4731                     "';};";
4732         }else{
4733             body = ["this.compiled = function(values){ return ['"];
4734             body.push(this.html.replace(/\\/g, '\\\\').replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn));
4735             body.push("'].join('');};");
4736             body = body.join('');
4737         }
4738         /**
4739          * eval:var:values
4740          * eval:var:fm
4741          */
4742         eval(body);
4743         return this;
4744     },
4745     
4746     // private function used to call members
4747     call : function(fnName, value, allValues){
4748         return this[fnName](value, allValues);
4749     },
4750     
4751     /**
4752      * Applies the supplied values to the template and inserts the new node(s) as the first child of el.
4753      * @param {String/HTMLElement/Roo.Element} el The context element
4754      * @param {Object} values The template values. Can be an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'})
4755      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4756      * @return {HTMLElement/Roo.Element} The new node or Element
4757      */
4758     insertFirst: function(el, values, returnElement){
4759         return this.doInsert('afterBegin', el, values, returnElement);
4760     },
4761
4762     /**
4763      * Applies the supplied values to the template and inserts the new node(s) before el.
4764      * @param {String/HTMLElement/Roo.Element} el The context element
4765      * @param {Object} values The template values. Can be an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'})
4766      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4767      * @return {HTMLElement/Roo.Element} The new node or Element
4768      */
4769     insertBefore: function(el, values, returnElement){
4770         return this.doInsert('beforeBegin', el, values, returnElement);
4771     },
4772
4773     /**
4774      * Applies the supplied values to the template and inserts the new node(s) after el.
4775      * @param {String/HTMLElement/Roo.Element} el The context element
4776      * @param {Object} values The template values. Can be an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'})
4777      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4778      * @return {HTMLElement/Roo.Element} The new node or Element
4779      */
4780     insertAfter : function(el, values, returnElement){
4781         return this.doInsert('afterEnd', el, values, returnElement);
4782     },
4783     
4784     /**
4785      * Applies the supplied values to the template and appends the new node(s) to el.
4786      * @param {String/HTMLElement/Roo.Element} el The context element
4787      * @param {Object} values The template values. Can be an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'})
4788      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4789      * @return {HTMLElement/Roo.Element} The new node or Element
4790      */
4791     append : function(el, values, returnElement){
4792         return this.doInsert('beforeEnd', el, values, returnElement);
4793     },
4794
4795     doInsert : function(where, el, values, returnEl){
4796         el = Roo.getDom(el);
4797         var newNode = Roo.DomHelper.insertHtml(where, el, this.applyTemplate(values));
4798         return returnEl ? Roo.get(newNode, true) : newNode;
4799     },
4800
4801     /**
4802      * Applies the supplied values to the template and overwrites the content of el with the new node(s).
4803      * @param {String/HTMLElement/Roo.Element} el The context element
4804      * @param {Object} values The template values. Can be an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'})
4805      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4806      * @return {HTMLElement/Roo.Element} The new node or Element
4807      */
4808     overwrite : function(el, values, returnElement){
4809         el = Roo.getDom(el);
4810         el.innerHTML = this.applyTemplate(values);
4811         return returnElement ? Roo.get(el.firstChild, true) : el.firstChild;
4812     }
4813 };
4814 /**
4815  * Alias for {@link #applyTemplate}
4816  * @method
4817  */
4818 Roo.Template.prototype.apply = Roo.Template.prototype.applyTemplate;
4819
4820 // backwards compat
4821 Roo.DomHelper.Template = Roo.Template;
4822
4823 /**
4824  * Creates a template from the passed element's value (<i>display:none</i> textarea, preferred) or innerHTML.
4825  * @param {String/HTMLElement} el A DOM element or its id
4826  * @returns {Roo.Template} The created template
4827  * @static
4828  */
4829 Roo.Template.from = function(el){
4830     el = Roo.getDom(el);
4831     return new Roo.Template(el.value || el.innerHTML);
4832 };/*
4833  * Based on:
4834  * Ext JS Library 1.1.1
4835  * Copyright(c) 2006-2007, Ext JS, LLC.
4836  *
4837  * Originally Released Under LGPL - original licence link has changed is not relivant.
4838  *
4839  * Fork - LGPL
4840  * <script type="text/javascript">
4841  */
4842  
4843
4844 /*
4845  * This is code is also distributed under MIT license for use
4846  * with jQuery and prototype JavaScript libraries.
4847  */
4848 /**
4849  * @class Roo.DomQuery
4850 Provides high performance selector/xpath processing by compiling queries into reusable functions. New pseudo classes and matchers can be plugged. It works on HTML and XML documents (if a content node is passed in).
4851 <p>
4852 DomQuery supports most of the <a href="http://www.w3.org/TR/2005/WD-css3-selectors-20051215/">CSS3 selectors spec</a>, along with some custom selectors and basic XPath.</p>
4853
4854 <p>
4855 All selectors, attribute filters and pseudos below can be combined infinitely in any order. For example "div.foo:nth-child(odd)[@foo=bar].bar:first" would be a perfectly valid selector. Node filters are processed in the order in which they appear, which allows you to optimize your queries for your document structure.
4856 </p>
4857 <h4>Element Selectors:</h4>
4858 <ul class="list">
4859     <li> <b>*</b> any element</li>
4860     <li> <b>E</b> an element with the tag E</li>
4861     <li> <b>E F</b> All descendent elements of E that have the tag F</li>
4862     <li> <b>E > F</b> or <b>E/F</b> all direct children elements of E that have the tag F</li>
4863     <li> <b>E + F</b> all elements with the tag F that are immediately preceded by an element with the tag E</li>
4864     <li> <b>E ~ F</b> all elements with the tag F that are preceded by a sibling element with the tag E</li>
4865 </ul>
4866 <h4>Attribute Selectors:</h4>
4867 <p>The use of @ and quotes are optional. For example, div[@foo='bar'] is also a valid attribute selector.</p>
4868 <ul class="list">
4869     <li> <b>E[foo]</b> has an attribute "foo"</li>
4870     <li> <b>E[foo=bar]</b> has an attribute "foo" that equals "bar"</li>
4871     <li> <b>E[foo^=bar]</b> has an attribute "foo" that starts with "bar"</li>
4872     <li> <b>E[foo$=bar]</b> has an attribute "foo" that ends with "bar"</li>
4873     <li> <b>E[foo*=bar]</b> has an attribute "foo" that contains the substring "bar"</li>
4874     <li> <b>E[foo%=2]</b> has an attribute "foo" that is evenly divisible by 2</li>
4875     <li> <b>E[foo!=bar]</b> has an attribute "foo" that does not equal "bar"</li>
4876 </ul>
4877 <h4>Pseudo Classes:</h4>
4878 <ul class="list">
4879     <li> <b>E:first-child</b> E is the first child of its parent</li>
4880     <li> <b>E:last-child</b> E is the last child of its parent</li>
4881     <li> <b>E:nth-child(<i>n</i>)</b> E is the <i>n</i>th child of its parent (1 based as per the spec)</li>
4882     <li> <b>E:nth-child(odd)</b> E is an odd child of its parent</li>
4883     <li> <b>E:nth-child(even)</b> E is an even child of its parent</li>
4884     <li> <b>E:only-child</b> E is the only child of its parent</li>
4885     <li> <b>E:checked</b> E is an element that is has a checked attribute that is true (e.g. a radio or checkbox) </li>
4886     <li> <b>E:first</b> the first E in the resultset</li>
4887     <li> <b>E:last</b> the last E in the resultset</li>
4888     <li> <b>E:nth(<i>n</i>)</b> the <i>n</i>th E in the resultset (1 based)</li>
4889     <li> <b>E:odd</b> shortcut for :nth-child(odd)</li>
4890     <li> <b>E:even</b> shortcut for :nth-child(even)</li>
4891     <li> <b>E:contains(foo)</b> E's innerHTML contains the substring "foo"</li>
4892     <li> <b>E:nodeValue(foo)</b> E contains a textNode with a nodeValue that equals "foo"</li>
4893     <li> <b>E:not(S)</b> an E element that does not match simple selector S</li>
4894     <li> <b>E:has(S)</b> an E element that has a descendent that matches simple selector S</li>
4895     <li> <b>E:next(S)</b> an E element whose next sibling matches simple selector S</li>
4896     <li> <b>E:prev(S)</b> an E element whose previous sibling matches simple selector S</li>
4897 </ul>
4898 <h4>CSS Value Selectors:</h4>
4899 <ul class="list">
4900     <li> <b>E{display=none}</b> css value "display" that equals "none"</li>
4901     <li> <b>E{display^=none}</b> css value "display" that starts with "none"</li>
4902     <li> <b>E{display$=none}</b> css value "display" that ends with "none"</li>
4903     <li> <b>E{display*=none}</b> css value "display" that contains the substring "none"</li>
4904     <li> <b>E{display%=2}</b> css value "display" that is evenly divisible by 2</li>
4905     <li> <b>E{display!=none}</b> css value "display" that does not equal "none"</li>
4906 </ul>
4907  * @singleton
4908  */
4909 Roo.DomQuery = function(){
4910     var cache = {}, simpleCache = {}, valueCache = {};
4911     var nonSpace = /\S/;
4912     var trimRe = /^\s+|\s+$/g;
4913     var tplRe = /\{(\d+)\}/g;
4914     var modeRe = /^(\s?[\/>+~]\s?|\s|$)/;
4915     var tagTokenRe = /^(#)?([\w-\*]+)/;
4916     var nthRe = /(\d*)n\+?(\d*)/, nthRe2 = /\D/;
4917
4918     function child(p, index){
4919         var i = 0;
4920         var n = p.firstChild;
4921         while(n){
4922             if(n.nodeType == 1){
4923                if(++i == index){
4924                    return n;
4925                }
4926             }
4927             n = n.nextSibling;
4928         }
4929         return null;
4930     };
4931
4932     function next(n){
4933         while((n = n.nextSibling) && n.nodeType != 1);
4934         return n;
4935     };
4936
4937     function prev(n){
4938         while((n = n.previousSibling) && n.nodeType != 1);
4939         return n;
4940     };
4941
4942     function children(d){
4943         var n = d.firstChild, ni = -1;
4944             while(n){
4945                 var nx = n.nextSibling;
4946                 if(n.nodeType == 3 && !nonSpace.test(n.nodeValue)){
4947                     d.removeChild(n);
4948                 }else{
4949                     n.nodeIndex = ++ni;
4950                 }
4951                 n = nx;
4952             }
4953             return this;
4954         };
4955
4956     function byClassName(c, a, v){
4957         if(!v){
4958             return c;
4959         }
4960         var r = [], ri = -1, cn;
4961         for(var i = 0, ci; ci = c[i]; i++){
4962             if((' '+ci.className+' ').indexOf(v) != -1){
4963                 r[++ri] = ci;
4964             }
4965         }
4966         return r;
4967     };
4968
4969     function attrValue(n, attr){
4970         if(!n.tagName && typeof n.length != "undefined"){
4971             n = n[0];
4972         }
4973         if(!n){
4974             return null;
4975         }
4976         if(attr == "for"){
4977             return n.htmlFor;
4978         }
4979         if(attr == "class" || attr == "className"){
4980             return n.className;
4981         }
4982         return n.getAttribute(attr) || n[attr];
4983
4984     };
4985
4986     function getNodes(ns, mode, tagName){
4987         var result = [], ri = -1, cs;
4988         if(!ns){
4989             return result;
4990         }
4991         tagName = tagName || "*";
4992         if(typeof ns.getElementsByTagName != "undefined"){
4993             ns = [ns];
4994         }
4995         if(!mode){
4996             for(var i = 0, ni; ni = ns[i]; i++){
4997                 cs = ni.getElementsByTagName(tagName);
4998                 for(var j = 0, ci; ci = cs[j]; j++){
4999                     result[++ri] = ci;
5000                 }
5001             }
5002         }else if(mode == "/" || mode == ">"){
5003             var utag = tagName.toUpperCase();
5004             for(var i = 0, ni, cn; ni = ns[i]; i++){
5005                 cn = ni.children || ni.childNodes;
5006                 for(var j = 0, cj; cj = cn[j]; j++){
5007                     if(cj.nodeName == utag || cj.nodeName == tagName  || tagName == '*'){
5008                         result[++ri] = cj;
5009                     }
5010                 }
5011             }
5012         }else if(mode == "+"){
5013             var utag = tagName.toUpperCase();
5014             for(var i = 0, n; n = ns[i]; i++){
5015                 while((n = n.nextSibling) && n.nodeType != 1);
5016                 if(n && (n.nodeName == utag || n.nodeName == tagName || tagName == '*')){
5017                     result[++ri] = n;
5018                 }
5019             }
5020         }else if(mode == "~"){
5021             for(var i = 0, n; n = ns[i]; i++){
5022                 while((n = n.nextSibling) && (n.nodeType != 1 || (tagName == '*' || n.tagName.toLowerCase()!=tagName)));
5023                 if(n){
5024                     result[++ri] = n;
5025                 }
5026             }
5027         }
5028         return result;
5029     };
5030
5031     function concat(a, b){
5032         if(b.slice){
5033             return a.concat(b);
5034         }
5035         for(var i = 0, l = b.length; i < l; i++){
5036             a[a.length] = b[i];
5037         }
5038         return a;
5039     }
5040
5041     function byTag(cs, tagName){
5042         if(cs.tagName || cs == document){
5043             cs = [cs];
5044         }
5045         if(!tagName){
5046             return cs;
5047         }
5048         var r = [], ri = -1;
5049         tagName = tagName.toLowerCase();
5050         for(var i = 0, ci; ci = cs[i]; i++){
5051             if(ci.nodeType == 1 && ci.tagName.toLowerCase()==tagName){
5052                 r[++ri] = ci;
5053             }
5054         }
5055         return r;
5056     };
5057
5058     function byId(cs, attr, id){
5059         if(cs.tagName || cs == document){
5060             cs = [cs];
5061         }
5062         if(!id){
5063             return cs;
5064         }
5065         var r = [], ri = -1;
5066         for(var i = 0,ci; ci = cs[i]; i++){
5067             if(ci && ci.id == id){
5068                 r[++ri] = ci;
5069                 return r;
5070             }
5071         }
5072         return r;
5073     };
5074
5075     function byAttribute(cs, attr, value, op, custom){
5076         var r = [], ri = -1, st = custom=="{";
5077         var f = Roo.DomQuery.operators[op];
5078         for(var i = 0, ci; ci = cs[i]; i++){
5079             var a;
5080             if(st){
5081                 a = Roo.DomQuery.getStyle(ci, attr);
5082             }
5083             else if(attr == "class" || attr == "className"){
5084                 a = ci.className;
5085             }else if(attr == "for"){
5086                 a = ci.htmlFor;
5087             }else if(attr == "href"){
5088                 a = ci.getAttribute("href", 2);
5089             }else{
5090                 a = ci.getAttribute(attr);
5091             }
5092             if((f && f(a, value)) || (!f && a)){
5093                 r[++ri] = ci;
5094             }
5095         }
5096         return r;
5097     };
5098
5099     function byPseudo(cs, name, value){
5100         return Roo.DomQuery.pseudos[name](cs, value);
5101     };
5102
5103     // This is for IE MSXML which does not support expandos.
5104     // IE runs the same speed using setAttribute, however FF slows way down
5105     // and Safari completely fails so they need to continue to use expandos.
5106     var isIE = window.ActiveXObject ? true : false;
5107
5108     // this eval is stop the compressor from
5109     // renaming the variable to something shorter
5110     
5111     /** eval:var:batch */
5112     var batch = 30803; 
5113
5114     var key = 30803;
5115
5116     function nodupIEXml(cs){
5117         var d = ++key;
5118         cs[0].setAttribute("_nodup", d);
5119         var r = [cs[0]];
5120         for(var i = 1, len = cs.length; i < len; i++){
5121             var c = cs[i];
5122             if(!c.getAttribute("_nodup") != d){
5123                 c.setAttribute("_nodup", d);
5124                 r[r.length] = c;
5125             }
5126         }
5127         for(var i = 0, len = cs.length; i < len; i++){
5128             cs[i].removeAttribute("_nodup");
5129         }
5130         return r;
5131     }
5132
5133     function nodup(cs){
5134         if(!cs){
5135             return [];
5136         }
5137         var len = cs.length, c, i, r = cs, cj, ri = -1;
5138         if(!len || typeof cs.nodeType != "undefined" || len == 1){
5139             return cs;
5140         }
5141         if(isIE && typeof cs[0].selectSingleNode != "undefined"){
5142             return nodupIEXml(cs);
5143         }
5144         var d = ++key;
5145         cs[0]._nodup = d;
5146         for(i = 1; c = cs[i]; i++){
5147             if(c._nodup != d){
5148                 c._nodup = d;
5149             }else{
5150                 r = [];
5151                 for(var j = 0; j < i; j++){
5152                     r[++ri] = cs[j];
5153                 }
5154                 for(j = i+1; cj = cs[j]; j++){
5155                     if(cj._nodup != d){
5156                         cj._nodup = d;
5157                         r[++ri] = cj;
5158                     }
5159                 }
5160                 return r;
5161             }
5162         }
5163         return r;
5164     }
5165
5166     function quickDiffIEXml(c1, c2){
5167         var d = ++key;
5168         for(var i = 0, len = c1.length; i < len; i++){
5169             c1[i].setAttribute("_qdiff", d);
5170         }
5171         var r = [];
5172         for(var i = 0, len = c2.length; i < len; i++){
5173             if(c2[i].getAttribute("_qdiff") != d){
5174                 r[r.length] = c2[i];
5175             }
5176         }
5177         for(var i = 0, len = c1.length; i < len; i++){
5178            c1[i].removeAttribute("_qdiff");
5179         }
5180         return r;
5181     }
5182
5183     function quickDiff(c1, c2){
5184         var len1 = c1.length;
5185         if(!len1){
5186             return c2;
5187         }
5188         if(isIE && c1[0].selectSingleNode){
5189             return quickDiffIEXml(c1, c2);
5190         }
5191         var d = ++key;
5192         for(var i = 0; i < len1; i++){
5193             c1[i]._qdiff = d;
5194         }
5195         var r = [];
5196         for(var i = 0, len = c2.length; i < len; i++){
5197             if(c2[i]._qdiff != d){
5198                 r[r.length] = c2[i];
5199             }
5200         }
5201         return r;
5202     }
5203
5204     function quickId(ns, mode, root, id){
5205         if(ns == root){
5206            var d = root.ownerDocument || root;
5207            return d.getElementById(id);
5208         }
5209         ns = getNodes(ns, mode, "*");
5210         return byId(ns, null, id);
5211     }
5212
5213     return {
5214         getStyle : function(el, name){
5215             return Roo.fly(el).getStyle(name);
5216         },
5217         /**
5218          * Compiles a selector/xpath query into a reusable function. The returned function
5219          * takes one parameter "root" (optional), which is the context node from where the query should start.
5220          * @param {String} selector The selector/xpath query
5221          * @param {String} type (optional) Either "select" (the default) or "simple" for a simple selector match
5222          * @return {Function}
5223          */
5224         compile : function(path, type){
5225             type = type || "select";
5226             
5227             var fn = ["var f = function(root){\n var mode; ++batch; var n = root || document;\n"];
5228             var q = path, mode, lq;
5229             var tk = Roo.DomQuery.matchers;
5230             var tklen = tk.length;
5231             var mm;
5232
5233             // accept leading mode switch
5234             var lmode = q.match(modeRe);
5235             if(lmode && lmode[1]){
5236                 fn[fn.length] = 'mode="'+lmode[1].replace(trimRe, "")+'";';
5237                 q = q.replace(lmode[1], "");
5238             }
5239             // strip leading slashes
5240             while(path.substr(0, 1)=="/"){
5241                 path = path.substr(1);
5242             }
5243
5244             while(q && lq != q){
5245                 lq = q;
5246                 var tm = q.match(tagTokenRe);
5247                 if(type == "select"){
5248                     if(tm){
5249                         if(tm[1] == "#"){
5250                             fn[fn.length] = 'n = quickId(n, mode, root, "'+tm[2]+'");';
5251                         }else{
5252                             fn[fn.length] = 'n = getNodes(n, mode, "'+tm[2]+'");';
5253                         }
5254                         q = q.replace(tm[0], "");
5255                     }else if(q.substr(0, 1) != '@'){
5256                         fn[fn.length] = 'n = getNodes(n, mode, "*");';
5257                     }
5258                 }else{
5259                     if(tm){
5260                         if(tm[1] == "#"){
5261                             fn[fn.length] = 'n = byId(n, null, "'+tm[2]+'");';
5262                         }else{
5263                             fn[fn.length] = 'n = byTag(n, "'+tm[2]+'");';
5264                         }
5265                         q = q.replace(tm[0], "");
5266                     }
5267                 }
5268                 while(!(mm = q.match(modeRe))){
5269                     var matched = false;
5270                     for(var j = 0; j < tklen; j++){
5271                         var t = tk[j];
5272                         var m = q.match(t.re);
5273                         if(m){
5274                             fn[fn.length] = t.select.replace(tplRe, function(x, i){
5275                                                     return m[i];
5276                                                 });
5277                             q = q.replace(m[0], "");
5278                             matched = true;
5279                             break;
5280                         }
5281                     }
5282                     // prevent infinite loop on bad selector
5283                     if(!matched){
5284                         throw 'Error parsing selector, parsing failed at "' + q + '"';
5285                     }
5286                 }
5287                 if(mm[1]){
5288                     fn[fn.length] = 'mode="'+mm[1].replace(trimRe, "")+'";';
5289                     q = q.replace(mm[1], "");
5290                 }
5291             }
5292             fn[fn.length] = "return nodup(n);\n}";
5293             
5294              /** 
5295               * list of variables that need from compression as they are used by eval.
5296              *  eval:var:batch 
5297              *  eval:var:nodup
5298              *  eval:var:byTag
5299              *  eval:var:ById
5300              *  eval:var:getNodes
5301              *  eval:var:quickId
5302              *  eval:var:mode
5303              *  eval:var:root
5304              *  eval:var:n
5305              *  eval:var:byClassName
5306              *  eval:var:byPseudo
5307              *  eval:var:byAttribute
5308              *  eval:var:attrValue
5309              * 
5310              **/ 
5311             eval(fn.join(""));
5312             return f;
5313         },
5314
5315         /**
5316          * Selects a group of elements.
5317          * @param {String} selector The selector/xpath query (can be a comma separated list of selectors)
5318          * @param {Node} root (optional) The start of the query (defaults to document).
5319          * @return {Array}
5320          */
5321         select : function(path, root, type){
5322             if(!root || root == document){
5323                 root = document;
5324             }
5325             if(typeof root == "string"){
5326                 root = document.getElementById(root);
5327             }
5328             var paths = path.split(",");
5329             var results = [];
5330             for(var i = 0, len = paths.length; i < len; i++){
5331                 var p = paths[i].replace(trimRe, "");
5332                 if(!cache[p]){
5333                     cache[p] = Roo.DomQuery.compile(p);
5334                     if(!cache[p]){
5335                         throw p + " is not a valid selector";
5336                     }
5337                 }
5338                 var result = cache[p](root);
5339                 if(result && result != document){
5340                     results = results.concat(result);
5341                 }
5342             }
5343             if(paths.length > 1){
5344                 return nodup(results);
5345             }
5346             return results;
5347         },
5348
5349         /**
5350          * Selects a single element.
5351          * @param {String} selector The selector/xpath query
5352          * @param {Node} root (optional) The start of the query (defaults to document).
5353          * @return {Element}
5354          */
5355         selectNode : function(path, root){
5356             return Roo.DomQuery.select(path, root)[0];
5357         },
5358
5359         /**
5360          * Selects the value of a node, optionally replacing null with the defaultValue.
5361          * @param {String} selector The selector/xpath query
5362          * @param {Node} root (optional) The start of the query (defaults to document).
5363          * @param {String} defaultValue
5364          */
5365         selectValue : function(path, root, defaultValue){
5366             path = path.replace(trimRe, "");
5367             if(!valueCache[path]){
5368                 valueCache[path] = Roo.DomQuery.compile(path, "select");
5369             }
5370             var n = valueCache[path](root);
5371             n = n[0] ? n[0] : n;
5372             var v = (n && n.firstChild ? n.firstChild.nodeValue : null);
5373             return ((v === null||v === undefined||v==='') ? defaultValue : v);
5374         },
5375
5376         /**
5377          * Selects the value of a node, parsing integers and floats.
5378          * @param {String} selector The selector/xpath query
5379          * @param {Node} root (optional) The start of the query (defaults to document).
5380          * @param {Number} defaultValue
5381          * @return {Number}
5382          */
5383         selectNumber : function(path, root, defaultValue){
5384             var v = Roo.DomQuery.selectValue(path, root, defaultValue || 0);
5385             return parseFloat(v);
5386         },
5387
5388         /**
5389          * Returns true if the passed element(s) match the passed simple selector (e.g. div.some-class or span:first-child)
5390          * @param {String/HTMLElement/Array} el An element id, element or array of elements
5391          * @param {String} selector The simple selector to test
5392          * @return {Boolean}
5393          */
5394         is : function(el, ss){
5395             if(typeof el == "string"){
5396                 el = document.getElementById(el);
5397             }
5398             var isArray = (el instanceof Array);
5399             var result = Roo.DomQuery.filter(isArray ? el : [el], ss);
5400             return isArray ? (result.length == el.length) : (result.length > 0);
5401         },
5402
5403         /**
5404          * Filters an array of elements to only include matches of a simple selector (e.g. div.some-class or span:first-child)
5405          * @param {Array} el An array of elements to filter
5406          * @param {String} selector The simple selector to test
5407          * @param {Boolean} nonMatches If true, it returns the elements that DON'T match
5408          * the selector instead of the ones that match
5409          * @return {Array}
5410          */
5411         filter : function(els, ss, nonMatches){
5412             ss = ss.replace(trimRe, "");
5413             if(!simpleCache[ss]){
5414                 simpleCache[ss] = Roo.DomQuery.compile(ss, "simple");
5415             }
5416             var result = simpleCache[ss](els);
5417             return nonMatches ? quickDiff(result, els) : result;
5418         },
5419
5420         /**
5421          * Collection of matching regular expressions and code snippets.
5422          */
5423         matchers : [{
5424                 re: /^\.([\w-]+)/,
5425                 select: 'n = byClassName(n, null, " {1} ");'
5426             }, {
5427                 re: /^\:([\w-]+)(?:\(((?:[^\s>\/]*|.*?))\))?/,
5428                 select: 'n = byPseudo(n, "{1}", "{2}");'
5429             },{
5430                 re: /^(?:([\[\{])(?:@)?([\w-]+)\s?(?:(=|.=)\s?['"]?(.*?)["']?)?[\]\}])/,
5431                 select: 'n = byAttribute(n, "{2}", "{4}", "{3}", "{1}");'
5432             }, {
5433                 re: /^#([\w-]+)/,
5434                 select: 'n = byId(n, null, "{1}");'
5435             },{
5436                 re: /^@([\w-]+)/,
5437                 select: 'return {firstChild:{nodeValue:attrValue(n, "{1}")}};'
5438             }
5439         ],
5440
5441         /**
5442          * Collection of operator comparison functions. The default operators are =, !=, ^=, $=, *=, %=, |= and ~=.
5443          * New operators can be added as long as the match the format <i>c</i>= where <i>c</i> is any character other than space, &gt; &lt;.
5444          */
5445         operators : {
5446             "=" : function(a, v){
5447                 return a == v;
5448             },
5449             "!=" : function(a, v){
5450                 return a != v;
5451             },
5452             "^=" : function(a, v){
5453                 return a && a.substr(0, v.length) == v;
5454             },
5455             "$=" : function(a, v){
5456                 return a && a.substr(a.length-v.length) == v;
5457             },
5458             "*=" : function(a, v){
5459                 return a && a.indexOf(v) !== -1;
5460             },
5461             "%=" : function(a, v){
5462                 return (a % v) == 0;
5463             },
5464             "|=" : function(a, v){
5465                 return a && (a == v || a.substr(0, v.length+1) == v+'-');
5466             },
5467             "~=" : function(a, v){
5468                 return a && (' '+a+' ').indexOf(' '+v+' ') != -1;
5469             }
5470         },
5471
5472         /**
5473          * Collection of "pseudo class" processors. Each processor is passed the current nodeset (array)
5474          * and the argument (if any) supplied in the selector.
5475          */
5476         pseudos : {
5477             "first-child" : function(c){
5478                 var r = [], ri = -1, n;
5479                 for(var i = 0, ci; ci = n = c[i]; i++){
5480                     while((n = n.previousSibling) && n.nodeType != 1);
5481                     if(!n){
5482                         r[++ri] = ci;
5483                     }
5484                 }
5485                 return r;
5486             },
5487
5488             "last-child" : function(c){
5489                 var r = [], ri = -1, n;
5490                 for(var i = 0, ci; ci = n = c[i]; i++){
5491                     while((n = n.nextSibling) && n.nodeType != 1);
5492                     if(!n){
5493                         r[++ri] = ci;
5494                     }
5495                 }
5496                 return r;
5497             },
5498
5499             "nth-child" : function(c, a) {
5500                 var r = [], ri = -1;
5501                 var m = nthRe.exec(a == "even" && "2n" || a == "odd" && "2n+1" || !nthRe2.test(a) && "n+" + a || a);
5502                 var f = (m[1] || 1) - 0, l = m[2] - 0;
5503                 for(var i = 0, n; n = c[i]; i++){
5504                     var pn = n.parentNode;
5505                     if (batch != pn._batch) {
5506                         var j = 0;
5507                         for(var cn = pn.firstChild; cn; cn = cn.nextSibling){
5508                             if(cn.nodeType == 1){
5509                                cn.nodeIndex = ++j;
5510                             }
5511                         }
5512                         pn._batch = batch;
5513                     }
5514                     if (f == 1) {
5515                         if (l == 0 || n.nodeIndex == l){
5516                             r[++ri] = n;
5517                         }
5518                     } else if ((n.nodeIndex + l) % f == 0){
5519                         r[++ri] = n;
5520                     }
5521                 }
5522
5523                 return r;
5524             },
5525
5526             "only-child" : function(c){
5527                 var r = [], ri = -1;;
5528                 for(var i = 0, ci; ci = c[i]; i++){
5529                     if(!prev(ci) && !next(ci)){
5530                         r[++ri] = ci;
5531                     }
5532                 }
5533                 return r;
5534             },
5535
5536             "empty" : function(c){
5537                 var r = [], ri = -1;
5538                 for(var i = 0, ci; ci = c[i]; i++){
5539                     var cns = ci.childNodes, j = 0, cn, empty = true;
5540                     while(cn = cns[j]){
5541                         ++j;
5542                         if(cn.nodeType == 1 || cn.nodeType == 3){
5543                             empty = false;
5544                             break;
5545                         }
5546                     }
5547                     if(empty){
5548                         r[++ri] = ci;
5549                     }
5550                 }
5551                 return r;
5552             },
5553
5554             "contains" : function(c, v){
5555                 var r = [], ri = -1;
5556                 for(var i = 0, ci; ci = c[i]; i++){
5557                     if((ci.textContent||ci.innerText||'').indexOf(v) != -1){
5558                         r[++ri] = ci;
5559                     }
5560                 }
5561                 return r;
5562             },
5563
5564             "nodeValue" : function(c, v){
5565                 var r = [], ri = -1;
5566                 for(var i = 0, ci; ci = c[i]; i++){
5567                     if(ci.firstChild && ci.firstChild.nodeValue == v){
5568                         r[++ri] = ci;
5569                     }
5570                 }
5571                 return r;
5572             },
5573
5574             "checked" : function(c){
5575                 var r = [], ri = -1;
5576                 for(var i = 0, ci; ci = c[i]; i++){
5577                     if(ci.checked == true){
5578                         r[++ri] = ci;
5579                     }
5580                 }
5581                 return r;
5582             },
5583
5584             "not" : function(c, ss){
5585                 return Roo.DomQuery.filter(c, ss, true);
5586             },
5587
5588             "odd" : function(c){
5589                 return this["nth-child"](c, "odd");
5590             },
5591
5592             "even" : function(c){
5593                 return this["nth-child"](c, "even");
5594             },
5595
5596             "nth" : function(c, a){
5597                 return c[a-1] || [];
5598             },
5599
5600             "first" : function(c){
5601                 return c[0] || [];
5602             },
5603
5604             "last" : function(c){
5605                 return c[c.length-1] || [];
5606             },
5607
5608             "has" : function(c, ss){
5609                 var s = Roo.DomQuery.select;
5610                 var r = [], ri = -1;
5611                 for(var i = 0, ci; ci = c[i]; i++){
5612                     if(s(ss, ci).length > 0){
5613                         r[++ri] = ci;
5614                     }
5615                 }
5616                 return r;
5617             },
5618
5619             "next" : function(c, ss){
5620                 var is = Roo.DomQuery.is;
5621                 var r = [], ri = -1;
5622                 for(var i = 0, ci; ci = c[i]; i++){
5623                     var n = next(ci);
5624                     if(n && is(n, ss)){
5625                         r[++ri] = ci;
5626                     }
5627                 }
5628                 return r;
5629             },
5630
5631             "prev" : function(c, ss){
5632                 var is = Roo.DomQuery.is;
5633                 var r = [], ri = -1;
5634                 for(var i = 0, ci; ci = c[i]; i++){
5635                     var n = prev(ci);
5636                     if(n && is(n, ss)){
5637                         r[++ri] = ci;
5638                     }
5639                 }
5640                 return r;
5641             }
5642         }
5643     };
5644 }();
5645
5646 /**
5647  * Selects an array of DOM nodes by CSS/XPath selector. Shorthand of {@link Roo.DomQuery#select}
5648  * @param {String} path The selector/xpath query
5649  * @param {Node} root (optional) The start of the query (defaults to document).
5650  * @return {Array}
5651  * @member Roo
5652  * @method query
5653  */
5654 Roo.query = Roo.DomQuery.select;
5655 /*
5656  * Based on:
5657  * Ext JS Library 1.1.1
5658  * Copyright(c) 2006-2007, Ext JS, LLC.
5659  *
5660  * Originally Released Under LGPL - original licence link has changed is not relivant.
5661  *
5662  * Fork - LGPL
5663  * <script type="text/javascript">
5664  */
5665
5666 /**
5667  * @class Roo.util.Observable
5668  * Base class that provides a common interface for publishing events. Subclasses are expected to
5669  * to have a property "events" with all the events defined.<br>
5670  * For example:
5671  * <pre><code>
5672  Employee = function(name){
5673     this.name = name;
5674     this.addEvents({
5675         "fired" : true,
5676         "quit" : true
5677     });
5678  }
5679  Roo.extend(Employee, Roo.util.Observable);
5680 </code></pre>
5681  * @param {Object} config properties to use (incuding events / listeners)
5682  */
5683
5684 Roo.util.Observable = function(cfg){
5685     
5686     cfg = cfg|| {};
5687     this.addEvents(cfg.events || {});
5688     if (cfg.events) {
5689         delete cfg.events; // make sure
5690     }
5691      
5692     Roo.apply(this, cfg);
5693     
5694     if(this.listeners){
5695         this.on(this.listeners);
5696         delete this.listeners;
5697     }
5698 };
5699 Roo.util.Observable.prototype = {
5700     /** 
5701  * @cfg {Object} listeners  list of events and functions to call for this object, 
5702  * For example :
5703  * <pre><code>
5704     listeners :  { 
5705        'click' : function(e) {
5706            ..... 
5707         } ,
5708         .... 
5709     } 
5710   </code></pre>
5711  */
5712     
5713     
5714     /**
5715      * Fires the specified event with the passed parameters (minus the event name).
5716      * @param {String} eventName
5717      * @param {Object...} args Variable number of parameters are passed to handlers
5718      * @return {Boolean} returns false if any of the handlers return false otherwise it returns true
5719      */
5720     fireEvent : function(){
5721         var ce = this.events[arguments[0].toLowerCase()];
5722         if(typeof ce == "object"){
5723             return ce.fire.apply(ce, Array.prototype.slice.call(arguments, 1));
5724         }else{
5725             return true;
5726         }
5727     },
5728
5729     // private
5730     filterOptRe : /^(?:scope|delay|buffer|single)$/,
5731
5732     /**
5733      * Appends an event handler to this component
5734      * @param {String}   eventName The type of event to listen for
5735      * @param {Function} handler The method the event invokes
5736      * @param {Object}   scope (optional) The scope in which to execute the handler
5737      * function. The handler function's "this" context.
5738      * @param {Object}   options (optional) An object containing handler configuration
5739      * properties. This may contain any of the following properties:<ul>
5740      * <li>scope {Object} The scope in which to execute the handler function. The handler function's "this" context.</li>
5741      * <li>delay {Number} The number of milliseconds to delay the invocation of the handler after te event fires.</li>
5742      * <li>single {Boolean} True to add a handler to handle just the next firing of the event, and then remove itself.</li>
5743      * <li>buffer {Number} Causes the handler to be scheduled to run in an {@link Roo.util.DelayedTask} delayed
5744      * by the specified number of milliseconds. If the event fires again within that time, the original
5745      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>
5746      * </ul><br>
5747      * <p>
5748      * <b>Combining Options</b><br>
5749      * Using the options argument, it is possible to combine different types of listeners:<br>
5750      * <br>
5751      * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)
5752                 <pre><code>
5753                 el.on('click', this.onClick, this, {
5754                         single: true,
5755                 delay: 100,
5756                 forumId: 4
5757                 });
5758                 </code></pre>
5759      * <p>
5760      * <b>Attaching multiple handlers in 1 call</b><br>
5761      * The method also allows for a single argument to be passed which is a config object containing properties
5762      * which specify multiple handlers.
5763      * <pre><code>
5764                 el.on({
5765                         'click': {
5766                         fn: this.onClick,
5767                         scope: this,
5768                         delay: 100
5769                 }, 
5770                 'mouseover': {
5771                         fn: this.onMouseOver,
5772                         scope: this
5773                 },
5774                 'mouseout': {
5775                         fn: this.onMouseOut,
5776                         scope: this
5777                 }
5778                 });
5779                 </code></pre>
5780      * <p>
5781      * Or a shorthand syntax which passes the same scope object to all handlers:
5782         <pre><code>
5783                 el.on({
5784                         'click': this.onClick,
5785                 'mouseover': this.onMouseOver,
5786                 'mouseout': this.onMouseOut,
5787                 scope: this
5788                 });
5789                 </code></pre>
5790      */
5791     addListener : function(eventName, fn, scope, o){
5792         if(typeof eventName == "object"){
5793             o = eventName;
5794             for(var e in o){
5795                 if(this.filterOptRe.test(e)){
5796                     continue;
5797                 }
5798                 if(typeof o[e] == "function"){
5799                     // shared options
5800                     this.addListener(e, o[e], o.scope,  o);
5801                 }else{
5802                     // individual options
5803                     this.addListener(e, o[e].fn, o[e].scope, o[e]);
5804                 }
5805             }
5806             return;
5807         }
5808         o = (!o || typeof o == "boolean") ? {} : o;
5809         eventName = eventName.toLowerCase();
5810         var ce = this.events[eventName] || true;
5811         if(typeof ce == "boolean"){
5812             ce = new Roo.util.Event(this, eventName);
5813             this.events[eventName] = ce;
5814         }
5815         ce.addListener(fn, scope, o);
5816     },
5817
5818     /**
5819      * Removes a listener
5820      * @param {String}   eventName     The type of event to listen for
5821      * @param {Function} handler        The handler to remove
5822      * @param {Object}   scope  (optional) The scope (this object) for the handler
5823      */
5824     removeListener : function(eventName, fn, scope){
5825         var ce = this.events[eventName.toLowerCase()];
5826         if(typeof ce == "object"){
5827             ce.removeListener(fn, scope);
5828         }
5829     },
5830
5831     /**
5832      * Removes all listeners for this object
5833      */
5834     purgeListeners : function(){
5835         for(var evt in this.events){
5836             if(typeof this.events[evt] == "object"){
5837                  this.events[evt].clearListeners();
5838             }
5839         }
5840     },
5841
5842     relayEvents : function(o, events){
5843         var createHandler = function(ename){
5844             return function(){
5845                 return this.fireEvent.apply(this, Roo.combine(ename, Array.prototype.slice.call(arguments, 0)));
5846             };
5847         };
5848         for(var i = 0, len = events.length; i < len; i++){
5849             var ename = events[i];
5850             if(!this.events[ename]){ this.events[ename] = true; };
5851             o.on(ename, createHandler(ename), this);
5852         }
5853     },
5854
5855     /**
5856      * Used to define events on this Observable
5857      * @param {Object} object The object with the events defined
5858      */
5859     addEvents : function(o){
5860         if(!this.events){
5861             this.events = {};
5862         }
5863         Roo.applyIf(this.events, o);
5864     },
5865
5866     /**
5867      * Checks to see if this object has any listeners for a specified event
5868      * @param {String} eventName The name of the event to check for
5869      * @return {Boolean} True if the event is being listened for, else false
5870      */
5871     hasListener : function(eventName){
5872         var e = this.events[eventName];
5873         return typeof e == "object" && e.listeners.length > 0;
5874     }
5875 };
5876 /**
5877  * Appends an event handler to this element (shorthand for addListener)
5878  * @param {String}   eventName     The type of event to listen for
5879  * @param {Function} handler        The method the event invokes
5880  * @param {Object}   scope (optional) The scope in which to execute the handler
5881  * function. The handler function's "this" context.
5882  * @param {Object}   options  (optional)
5883  * @method
5884  */
5885 Roo.util.Observable.prototype.on = Roo.util.Observable.prototype.addListener;
5886 /**
5887  * Removes a listener (shorthand for removeListener)
5888  * @param {String}   eventName     The type of event to listen for
5889  * @param {Function} handler        The handler to remove
5890  * @param {Object}   scope  (optional) The scope (this object) for the handler
5891  * @method
5892  */
5893 Roo.util.Observable.prototype.un = Roo.util.Observable.prototype.removeListener;
5894
5895 /**
5896  * Starts capture on the specified Observable. All events will be passed
5897  * to the supplied function with the event name + standard signature of the event
5898  * <b>before</b> the event is fired. If the supplied function returns false,
5899  * the event will not fire.
5900  * @param {Observable} o The Observable to capture
5901  * @param {Function} fn The function to call
5902  * @param {Object} scope (optional) The scope (this object) for the fn
5903  * @static
5904  */
5905 Roo.util.Observable.capture = function(o, fn, scope){
5906     o.fireEvent = o.fireEvent.createInterceptor(fn, scope);
5907 };
5908
5909 /**
5910  * Removes <b>all</b> added captures from the Observable.
5911  * @param {Observable} o The Observable to release
5912  * @static
5913  */
5914 Roo.util.Observable.releaseCapture = function(o){
5915     o.fireEvent = Roo.util.Observable.prototype.fireEvent;
5916 };
5917
5918 (function(){
5919
5920     var createBuffered = function(h, o, scope){
5921         var task = new Roo.util.DelayedTask();
5922         return function(){
5923             task.delay(o.buffer, h, scope, Array.prototype.slice.call(arguments, 0));
5924         };
5925     };
5926
5927     var createSingle = function(h, e, fn, scope){
5928         return function(){
5929             e.removeListener(fn, scope);
5930             return h.apply(scope, arguments);
5931         };
5932     };
5933
5934     var createDelayed = function(h, o, scope){
5935         return function(){
5936             var args = Array.prototype.slice.call(arguments, 0);
5937             setTimeout(function(){
5938                 h.apply(scope, args);
5939             }, o.delay || 10);
5940         };
5941     };
5942
5943     Roo.util.Event = function(obj, name){
5944         this.name = name;
5945         this.obj = obj;
5946         this.listeners = [];
5947     };
5948
5949     Roo.util.Event.prototype = {
5950         addListener : function(fn, scope, options){
5951             var o = options || {};
5952             scope = scope || this.obj;
5953             if(!this.isListening(fn, scope)){
5954                 var l = {fn: fn, scope: scope, options: o};
5955                 var h = fn;
5956                 if(o.delay){
5957                     h = createDelayed(h, o, scope);
5958                 }
5959                 if(o.single){
5960                     h = createSingle(h, this, fn, scope);
5961                 }
5962                 if(o.buffer){
5963                     h = createBuffered(h, o, scope);
5964                 }
5965                 l.fireFn = h;
5966                 if(!this.firing){ // if we are currently firing this event, don't disturb the listener loop
5967                     this.listeners.push(l);
5968                 }else{
5969                     this.listeners = this.listeners.slice(0);
5970                     this.listeners.push(l);
5971                 }
5972             }
5973         },
5974
5975         findListener : function(fn, scope){
5976             scope = scope || this.obj;
5977             var ls = this.listeners;
5978             for(var i = 0, len = ls.length; i < len; i++){
5979                 var l = ls[i];
5980                 if(l.fn == fn && l.scope == scope){
5981                     return i;
5982                 }
5983             }
5984             return -1;
5985         },
5986
5987         isListening : function(fn, scope){
5988             return this.findListener(fn, scope) != -1;
5989         },
5990
5991         removeListener : function(fn, scope){
5992             var index;
5993             if((index = this.findListener(fn, scope)) != -1){
5994                 if(!this.firing){
5995                     this.listeners.splice(index, 1);
5996                 }else{
5997                     this.listeners = this.listeners.slice(0);
5998                     this.listeners.splice(index, 1);
5999                 }
6000                 return true;
6001             }
6002             return false;
6003         },
6004
6005         clearListeners : function(){
6006             this.listeners = [];
6007         },
6008
6009         fire : function(){
6010             var ls = this.listeners, scope, len = ls.length;
6011             if(len > 0){
6012                 this.firing = true;
6013                 var args = Array.prototype.slice.call(arguments, 0);
6014                 for(var i = 0; i < len; i++){
6015                     var l = ls[i];
6016                     if(l.fireFn.apply(l.scope||this.obj||window, arguments) === false){
6017                         this.firing = false;
6018                         return false;
6019                     }
6020                 }
6021                 this.firing = false;
6022             }
6023             return true;
6024         }
6025     };
6026 })();/*
6027  * Based on:
6028  * Ext JS Library 1.1.1
6029  * Copyright(c) 2006-2007, Ext JS, LLC.
6030  *
6031  * Originally Released Under LGPL - original licence link has changed is not relivant.
6032  *
6033  * Fork - LGPL
6034  * <script type="text/javascript">
6035  */
6036
6037 /**
6038  * @class Roo.EventManager
6039  * Registers event handlers that want to receive a normalized EventObject instead of the standard browser event and provides 
6040  * several useful events directly.
6041  * See {@link Roo.EventObject} for more details on normalized event objects.
6042  * @singleton
6043  */
6044 Roo.EventManager = function(){
6045     var docReadyEvent, docReadyProcId, docReadyState = false;
6046     var resizeEvent, resizeTask, textEvent, textSize;
6047     var E = Roo.lib.Event;
6048     var D = Roo.lib.Dom;
6049
6050     
6051     
6052
6053     var fireDocReady = function(){
6054         if(!docReadyState){
6055             docReadyState = true;
6056             Roo.isReady = true;
6057             if(docReadyProcId){
6058                 clearInterval(docReadyProcId);
6059             }
6060             if(Roo.isGecko || Roo.isOpera) {
6061                 document.removeEventListener("DOMContentLoaded", fireDocReady, false);
6062             }
6063             if(Roo.isIE){
6064                 var defer = document.getElementById("ie-deferred-loader");
6065                 if(defer){
6066                     defer.onreadystatechange = null;
6067                     defer.parentNode.removeChild(defer);
6068                 }
6069             }
6070             if(docReadyEvent){
6071                 docReadyEvent.fire();
6072                 docReadyEvent.clearListeners();
6073             }
6074         }
6075     };
6076     
6077     var initDocReady = function(){
6078         docReadyEvent = new Roo.util.Event();
6079         if(Roo.isGecko || Roo.isOpera) {
6080             document.addEventListener("DOMContentLoaded", fireDocReady, false);
6081         }else if(Roo.isIE){
6082             document.write("<s"+'cript id="ie-deferred-loader" defer="defer" src="/'+'/:"></s'+"cript>");
6083             var defer = document.getElementById("ie-deferred-loader");
6084             defer.onreadystatechange = function(){
6085                 if(this.readyState == "complete"){
6086                     fireDocReady();
6087                 }
6088             };
6089         }else if(Roo.isSafari){ 
6090             docReadyProcId = setInterval(function(){
6091                 var rs = document.readyState;
6092                 if(rs == "complete") {
6093                     fireDocReady();     
6094                  }
6095             }, 10);
6096         }
6097         // no matter what, make sure it fires on load
6098         E.on(window, "load", fireDocReady);
6099     };
6100
6101     var createBuffered = function(h, o){
6102         var task = new Roo.util.DelayedTask(h);
6103         return function(e){
6104             // create new event object impl so new events don't wipe out properties
6105             e = new Roo.EventObjectImpl(e);
6106             task.delay(o.buffer, h, null, [e]);
6107         };
6108     };
6109
6110     var createSingle = function(h, el, ename, fn){
6111         return function(e){
6112             Roo.EventManager.removeListener(el, ename, fn);
6113             h(e);
6114         };
6115     };
6116
6117     var createDelayed = function(h, o){
6118         return function(e){
6119             // create new event object impl so new events don't wipe out properties
6120             e = new Roo.EventObjectImpl(e);
6121             setTimeout(function(){
6122                 h(e);
6123             }, o.delay || 10);
6124         };
6125     };
6126     var transitionEndVal = false;
6127     
6128     var transitionEnd = funciton()
6129     {
6130         if (transitionEndVal) {
6131             return transitionEndVal;
6132         }
6133         var el = document.createElement('div')
6134
6135         var transEndEventNames = {
6136             WebkitTransition : 'webkitTransitionEnd',
6137             MozTransition    : 'transitionend',
6138             OTransition      : 'oTransitionEnd otransitionend',
6139             transition       : 'transitionend'
6140         }
6141     
6142         for (var name in transEndEventNames) {
6143             if (el.style[name] !== undefined) {
6144                 transitionEndVal = transEndEventNames[name];
6145                 return  transitionEndVal ;
6146             }
6147         }
6148     }
6149     
6150
6151     var listen = function(element, ename, opt, fn, scope){
6152         var o = (!opt || typeof opt == "boolean") ? {} : opt;
6153         fn = fn || o.fn; scope = scope || o.scope;
6154         var el = Roo.getDom(element);
6155         if(!el){
6156             throw "Error listening for \"" + ename + '\". Element "' + element + '" doesn\'t exist.';
6157         }
6158         var h = function(e){
6159             e = Roo.EventObject.setEvent(e);
6160             var t;
6161             if(o.delegate){
6162                 t = e.getTarget(o.delegate, el);
6163                 if(!t){
6164                     return;
6165                 }
6166             }else{
6167                 t = e.target;
6168             }
6169             if(o.stopEvent === true){
6170                 e.stopEvent();
6171             }
6172             if(o.preventDefault === true){
6173                e.preventDefault();
6174             }
6175             if(o.stopPropagation === true){
6176                 e.stopPropagation();
6177             }
6178
6179             if(o.normalized === false){
6180                 e = e.browserEvent;
6181             }
6182
6183             fn.call(scope || el, e, t, o);
6184         };
6185         if(o.delay){
6186             h = createDelayed(h, o);
6187         }
6188         if(o.single){
6189             h = createSingle(h, el, ename, fn);
6190         }
6191         if(o.buffer){
6192             h = createBuffered(h, o);
6193         }
6194         fn._handlers = fn._handlers || [];
6195         fn._handlers.push([Roo.id(el), ename, h]);
6196         
6197         if (ename == 'transitionend') {
6198             ename = transitionEnd();
6199         }
6200          
6201         E.on(el, ename, h);
6202         if(ename == "mousewheel" && el.addEventListener){ // workaround for jQuery
6203             el.addEventListener("DOMMouseScroll", h, false);
6204             E.on(window, 'unload', function(){
6205                 el.removeEventListener("DOMMouseScroll", h, false);
6206             });
6207         }
6208         if(ename == "mousedown" && el == document){ // fix stopped mousedowns on the document
6209             Roo.EventManager.stoppedMouseDownEvent.addListener(h);
6210         }
6211         return h;
6212     };
6213
6214     var stopListening = function(el, ename, fn){
6215         var id = Roo.id(el), hds = fn._handlers, hd = fn;
6216         if(hds){
6217             for(var i = 0, len = hds.length; i < len; i++){
6218                 var h = hds[i];
6219                 if(h[0] == id && h[1] == ename){
6220                     hd = h[2];
6221                     hds.splice(i, 1);
6222                     break;
6223                 }
6224             }
6225         }
6226         E.un(el, ename, hd);
6227         el = Roo.getDom(el);
6228         if(ename == "mousewheel" && el.addEventListener){
6229             el.removeEventListener("DOMMouseScroll", hd, false);
6230         }
6231         if(ename == "mousedown" && el == document){ // fix stopped mousedowns on the document
6232             Roo.EventManager.stoppedMouseDownEvent.removeListener(hd);
6233         }
6234     };
6235
6236     var propRe = /^(?:scope|delay|buffer|single|stopEvent|preventDefault|stopPropagation|normalized|args|delegate)$/;
6237     
6238     var pub = {
6239         
6240         
6241         /** 
6242          * Fix for doc tools
6243          * @scope Roo.EventManager
6244          */
6245         
6246         
6247         /** 
6248          * This is no longer needed and is deprecated. Places a simple wrapper around an event handler to override the browser event
6249          * object with a Roo.EventObject
6250          * @param {Function} fn        The method the event invokes
6251          * @param {Object}   scope    An object that becomes the scope of the handler
6252          * @param {boolean}  override If true, the obj passed in becomes
6253          *                             the execution scope of the listener
6254          * @return {Function} The wrapped function
6255          * @deprecated
6256          */
6257         wrap : function(fn, scope, override){
6258             return function(e){
6259                 Roo.EventObject.setEvent(e);
6260                 fn.call(override ? scope || window : window, Roo.EventObject, scope);
6261             };
6262         },
6263         
6264         /**
6265      * Appends an event handler to an element (shorthand for addListener)
6266      * @param {String/HTMLElement}   element        The html element or id to assign the
6267      * @param {String}   eventName The type of event to listen for
6268      * @param {Function} handler The method the event invokes
6269      * @param {Object}   scope (optional) The scope in which to execute the handler
6270      * function. The handler function's "this" context.
6271      * @param {Object}   options (optional) An object containing handler configuration
6272      * properties. This may contain any of the following properties:<ul>
6273      * <li>scope {Object} The scope in which to execute the handler function. The handler function's "this" context.</li>
6274      * <li>delegate {String} A simple selector to filter the target or look for a descendant of the target</li>
6275      * <li>stopEvent {Boolean} True to stop the event. That is stop propagation, and prevent the default action.</li>
6276      * <li>preventDefault {Boolean} True to prevent the default action</li>
6277      * <li>stopPropagation {Boolean} True to prevent event propagation</li>
6278      * <li>normalized {Boolean} False to pass a browser event to the handler function instead of an Roo.EventObject</li>
6279      * <li>delay {Number} The number of milliseconds to delay the invocation of the handler after te event fires.</li>
6280      * <li>single {Boolean} True to add a handler to handle just the next firing of the event, and then remove itself.</li>
6281      * <li>buffer {Number} Causes the handler to be scheduled to run in an {@link Roo.util.DelayedTask} delayed
6282      * by the specified number of milliseconds. If the event fires again within that time, the original
6283      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>
6284      * </ul><br>
6285      * <p>
6286      * <b>Combining Options</b><br>
6287      * Using the options argument, it is possible to combine different types of listeners:<br>
6288      * <br>
6289      * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)<div style="margin: 5px 20px 20px;">
6290      * Code:<pre><code>
6291 el.on('click', this.onClick, this, {
6292     single: true,
6293     delay: 100,
6294     stopEvent : true,
6295     forumId: 4
6296 });</code></pre>
6297      * <p>
6298      * <b>Attaching multiple handlers in 1 call</b><br>
6299       * The method also allows for a single argument to be passed which is a config object containing properties
6300      * which specify multiple handlers.
6301      * <p>
6302      * Code:<pre><code>
6303 el.on({
6304     'click' : {
6305         fn: this.onClick
6306         scope: this,
6307         delay: 100
6308     },
6309     'mouseover' : {
6310         fn: this.onMouseOver
6311         scope: this
6312     },
6313     'mouseout' : {
6314         fn: this.onMouseOut
6315         scope: this
6316     }
6317 });</code></pre>
6318      * <p>
6319      * Or a shorthand syntax:<br>
6320      * Code:<pre><code>
6321 el.on({
6322     'click' : this.onClick,
6323     'mouseover' : this.onMouseOver,
6324     'mouseout' : this.onMouseOut
6325     scope: this
6326 });</code></pre>
6327      */
6328         addListener : function(element, eventName, fn, scope, options){
6329             if(typeof eventName == "object"){
6330                 var o = eventName;
6331                 for(var e in o){
6332                     if(propRe.test(e)){
6333                         continue;
6334                     }
6335                     if(typeof o[e] == "function"){
6336                         // shared options
6337                         listen(element, e, o, o[e], o.scope);
6338                     }else{
6339                         // individual options
6340                         listen(element, e, o[e]);
6341                     }
6342                 }
6343                 return;
6344             }
6345             return listen(element, eventName, options, fn, scope);
6346         },
6347         
6348         /**
6349          * Removes an event handler
6350          *
6351          * @param {String/HTMLElement}   element        The id or html element to remove the 
6352          *                             event from
6353          * @param {String}   eventName     The type of event
6354          * @param {Function} fn
6355          * @return {Boolean} True if a listener was actually removed
6356          */
6357         removeListener : function(element, eventName, fn){
6358             return stopListening(element, eventName, fn);
6359         },
6360         
6361         /**
6362          * Fires when the document is ready (before onload and before images are loaded). Can be 
6363          * accessed shorthanded Roo.onReady().
6364          * @param {Function} fn        The method the event invokes
6365          * @param {Object}   scope    An  object that becomes the scope of the handler
6366          * @param {boolean}  options
6367          */
6368         onDocumentReady : function(fn, scope, options){
6369             if(docReadyState){ // if it already fired
6370                 docReadyEvent.addListener(fn, scope, options);
6371                 docReadyEvent.fire();
6372                 docReadyEvent.clearListeners();
6373                 return;
6374             }
6375             if(!docReadyEvent){
6376                 initDocReady();
6377             }
6378             docReadyEvent.addListener(fn, scope, options);
6379         },
6380         
6381         /**
6382          * Fires when the window is resized and provides resize event buffering (50 milliseconds), passes new viewport width and height to handlers.
6383          * @param {Function} fn        The method the event invokes
6384          * @param {Object}   scope    An object that becomes the scope of the handler
6385          * @param {boolean}  options
6386          */
6387         onWindowResize : function(fn, scope, options){
6388             if(!resizeEvent){
6389                 resizeEvent = new Roo.util.Event();
6390                 resizeTask = new Roo.util.DelayedTask(function(){
6391                     resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
6392                 });
6393                 E.on(window, "resize", function(){
6394                     if(Roo.isIE){
6395                         resizeTask.delay(50);
6396                     }else{
6397                         resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
6398                     }
6399                 });
6400             }
6401             resizeEvent.addListener(fn, scope, options);
6402         },
6403
6404         /**
6405          * Fires when the user changes the active text size. Handler gets called with 2 params, the old size and the new size.
6406          * @param {Function} fn        The method the event invokes
6407          * @param {Object}   scope    An object that becomes the scope of the handler
6408          * @param {boolean}  options
6409          */
6410         onTextResize : function(fn, scope, options){
6411             if(!textEvent){
6412                 textEvent = new Roo.util.Event();
6413                 var textEl = new Roo.Element(document.createElement('div'));
6414                 textEl.dom.className = 'x-text-resize';
6415                 textEl.dom.innerHTML = 'X';
6416                 textEl.appendTo(document.body);
6417                 textSize = textEl.dom.offsetHeight;
6418                 setInterval(function(){
6419                     if(textEl.dom.offsetHeight != textSize){
6420                         textEvent.fire(textSize, textSize = textEl.dom.offsetHeight);
6421                     }
6422                 }, this.textResizeInterval);
6423             }
6424             textEvent.addListener(fn, scope, options);
6425         },
6426
6427         /**
6428          * Removes the passed window resize listener.
6429          * @param {Function} fn        The method the event invokes
6430          * @param {Object}   scope    The scope of handler
6431          */
6432         removeResizeListener : function(fn, scope){
6433             if(resizeEvent){
6434                 resizeEvent.removeListener(fn, scope);
6435             }
6436         },
6437
6438         // private
6439         fireResize : function(){
6440             if(resizeEvent){
6441                 resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
6442             }   
6443         },
6444         /**
6445          * Url used for onDocumentReady with using SSL (defaults to Roo.SSL_SECURE_URL)
6446          */
6447         ieDeferSrc : false,
6448         /**
6449          * The frequency, in milliseconds, to check for text resize events (defaults to 50)
6450          */
6451         textResizeInterval : 50
6452     };
6453     
6454     /**
6455      * Fix for doc tools
6456      * @scopeAlias pub=Roo.EventManager
6457      */
6458     
6459      /**
6460      * Appends an event handler to an element (shorthand for addListener)
6461      * @param {String/HTMLElement}   element        The html element or id to assign the
6462      * @param {String}   eventName The type of event to listen for
6463      * @param {Function} handler The method the event invokes
6464      * @param {Object}   scope (optional) The scope in which to execute the handler
6465      * function. The handler function's "this" context.
6466      * @param {Object}   options (optional) An object containing handler configuration
6467      * properties. This may contain any of the following properties:<ul>
6468      * <li>scope {Object} The scope in which to execute the handler function. The handler function's "this" context.</li>
6469      * <li>delegate {String} A simple selector to filter the target or look for a descendant of the target</li>
6470      * <li>stopEvent {Boolean} True to stop the event. That is stop propagation, and prevent the default action.</li>
6471      * <li>preventDefault {Boolean} True to prevent the default action</li>
6472      * <li>stopPropagation {Boolean} True to prevent event propagation</li>
6473      * <li>normalized {Boolean} False to pass a browser event to the handler function instead of an Roo.EventObject</li>
6474      * <li>delay {Number} The number of milliseconds to delay the invocation of the handler after te event fires.</li>
6475      * <li>single {Boolean} True to add a handler to handle just the next firing of the event, and then remove itself.</li>
6476      * <li>buffer {Number} Causes the handler to be scheduled to run in an {@link Roo.util.DelayedTask} delayed
6477      * by the specified number of milliseconds. If the event fires again within that time, the original
6478      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>
6479      * </ul><br>
6480      * <p>
6481      * <b>Combining Options</b><br>
6482      * Using the options argument, it is possible to combine different types of listeners:<br>
6483      * <br>
6484      * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)<div style="margin: 5px 20px 20px;">
6485      * Code:<pre><code>
6486 el.on('click', this.onClick, this, {
6487     single: true,
6488     delay: 100,
6489     stopEvent : true,
6490     forumId: 4
6491 });</code></pre>
6492      * <p>
6493      * <b>Attaching multiple handlers in 1 call</b><br>
6494       * The method also allows for a single argument to be passed which is a config object containing properties
6495      * which specify multiple handlers.
6496      * <p>
6497      * Code:<pre><code>
6498 el.on({
6499     'click' : {
6500         fn: this.onClick
6501         scope: this,
6502         delay: 100
6503     },
6504     'mouseover' : {
6505         fn: this.onMouseOver
6506         scope: this
6507     },
6508     'mouseout' : {
6509         fn: this.onMouseOut
6510         scope: this
6511     }
6512 });</code></pre>
6513      * <p>
6514      * Or a shorthand syntax:<br>
6515      * Code:<pre><code>
6516 el.on({
6517     'click' : this.onClick,
6518     'mouseover' : this.onMouseOver,
6519     'mouseout' : this.onMouseOut
6520     scope: this
6521 });</code></pre>
6522      */
6523     pub.on = pub.addListener;
6524     pub.un = pub.removeListener;
6525
6526     pub.stoppedMouseDownEvent = new Roo.util.Event();
6527     return pub;
6528 }();
6529 /**
6530   * Fires when the document is ready (before onload and before images are loaded).  Shorthand of {@link Roo.EventManager#onDocumentReady}.
6531   * @param {Function} fn        The method the event invokes
6532   * @param {Object}   scope    An  object that becomes the scope of the handler
6533   * @param {boolean}  override If true, the obj passed in becomes
6534   *                             the execution scope of the listener
6535   * @member Roo
6536   * @method onReady
6537  */
6538 Roo.onReady = Roo.EventManager.onDocumentReady;
6539
6540 Roo.onReady(function(){
6541     var bd = Roo.get(document.body);
6542     if(!bd){ return; }
6543
6544     var cls = [
6545             Roo.isIE ? "roo-ie"
6546             : Roo.isGecko ? "roo-gecko"
6547             : Roo.isOpera ? "roo-opera"
6548             : Roo.isSafari ? "roo-safari" : ""];
6549
6550     if(Roo.isMac){
6551         cls.push("roo-mac");
6552     }
6553     if(Roo.isLinux){
6554         cls.push("roo-linux");
6555     }
6556     if(Roo.isBorderBox){
6557         cls.push('roo-border-box');
6558     }
6559     if(Roo.isStrict){ // add to the parent to allow for selectors like ".ext-strict .ext-ie"
6560         var p = bd.dom.parentNode;
6561         if(p){
6562             p.className += ' roo-strict';
6563         }
6564     }
6565     bd.addClass(cls.join(' '));
6566 });
6567
6568 /**
6569  * @class Roo.EventObject
6570  * EventObject exposes the Yahoo! UI Event functionality directly on the object
6571  * passed to your event handler. It exists mostly for convenience. It also fixes the annoying null checks automatically to cleanup your code 
6572  * Example:
6573  * <pre><code>
6574  function handleClick(e){ // e is not a standard event object, it is a Roo.EventObject
6575     e.preventDefault();
6576     var target = e.getTarget();
6577     ...
6578  }
6579  var myDiv = Roo.get("myDiv");
6580  myDiv.on("click", handleClick);
6581  //or
6582  Roo.EventManager.on("myDiv", 'click', handleClick);
6583  Roo.EventManager.addListener("myDiv", 'click', handleClick);
6584  </code></pre>
6585  * @singleton
6586  */
6587 Roo.EventObject = function(){
6588     
6589     var E = Roo.lib.Event;
6590     
6591     // safari keypress events for special keys return bad keycodes
6592     var safariKeys = {
6593         63234 : 37, // left
6594         63235 : 39, // right
6595         63232 : 38, // up
6596         63233 : 40, // down
6597         63276 : 33, // page up
6598         63277 : 34, // page down
6599         63272 : 46, // delete
6600         63273 : 36, // home
6601         63275 : 35  // end
6602     };
6603
6604     // normalize button clicks
6605     var btnMap = Roo.isIE ? {1:0,4:1,2:2} :
6606                 (Roo.isSafari ? {1:0,2:1,3:2} : {0:0,1:1,2:2});
6607
6608     Roo.EventObjectImpl = function(e){
6609         if(e){
6610             this.setEvent(e.browserEvent || e);
6611         }
6612     };
6613     Roo.EventObjectImpl.prototype = {
6614         /**
6615          * Used to fix doc tools.
6616          * @scope Roo.EventObject.prototype
6617          */
6618             
6619
6620         
6621         
6622         /** The normal browser event */
6623         browserEvent : null,
6624         /** The button pressed in a mouse event */
6625         button : -1,
6626         /** True if the shift key was down during the event */
6627         shiftKey : false,
6628         /** True if the control key was down during the event */
6629         ctrlKey : false,
6630         /** True if the alt key was down during the event */
6631         altKey : false,
6632
6633         /** Key constant 
6634         * @type Number */
6635         BACKSPACE : 8,
6636         /** Key constant 
6637         * @type Number */
6638         TAB : 9,
6639         /** Key constant 
6640         * @type Number */
6641         RETURN : 13,
6642         /** Key constant 
6643         * @type Number */
6644         ENTER : 13,
6645         /** Key constant 
6646         * @type Number */
6647         SHIFT : 16,
6648         /** Key constant 
6649         * @type Number */
6650         CONTROL : 17,
6651         /** Key constant 
6652         * @type Number */
6653         ESC : 27,
6654         /** Key constant 
6655         * @type Number */
6656         SPACE : 32,
6657         /** Key constant 
6658         * @type Number */
6659         PAGEUP : 33,
6660         /** Key constant 
6661         * @type Number */
6662         PAGEDOWN : 34,
6663         /** Key constant 
6664         * @type Number */
6665         END : 35,
6666         /** Key constant 
6667         * @type Number */
6668         HOME : 36,
6669         /** Key constant 
6670         * @type Number */
6671         LEFT : 37,
6672         /** Key constant 
6673         * @type Number */
6674         UP : 38,
6675         /** Key constant 
6676         * @type Number */
6677         RIGHT : 39,
6678         /** Key constant 
6679         * @type Number */
6680         DOWN : 40,
6681         /** Key constant 
6682         * @type Number */
6683         DELETE : 46,
6684         /** Key constant 
6685         * @type Number */
6686         F5 : 116,
6687
6688            /** @private */
6689         setEvent : function(e){
6690             if(e == this || (e && e.browserEvent)){ // already wrapped
6691                 return e;
6692             }
6693             this.browserEvent = e;
6694             if(e){
6695                 // normalize buttons
6696                 this.button = e.button ? btnMap[e.button] : (e.which ? e.which-1 : -1);
6697                 if(e.type == 'click' && this.button == -1){
6698                     this.button = 0;
6699                 }
6700                 this.type = e.type;
6701                 this.shiftKey = e.shiftKey;
6702                 // mac metaKey behaves like ctrlKey
6703                 this.ctrlKey = e.ctrlKey || e.metaKey;
6704                 this.altKey = e.altKey;
6705                 // in getKey these will be normalized for the mac
6706                 this.keyCode = e.keyCode;
6707                 // keyup warnings on firefox.
6708                 this.charCode = (e.type == 'keyup' || e.type == 'keydown') ? 0 : e.charCode;
6709                 // cache the target for the delayed and or buffered events
6710                 this.target = E.getTarget(e);
6711                 // same for XY
6712                 this.xy = E.getXY(e);
6713             }else{
6714                 this.button = -1;
6715                 this.shiftKey = false;
6716                 this.ctrlKey = false;
6717                 this.altKey = false;
6718                 this.keyCode = 0;
6719                 this.charCode =0;
6720                 this.target = null;
6721                 this.xy = [0, 0];
6722             }
6723             return this;
6724         },
6725
6726         /**
6727          * Stop the event (preventDefault and stopPropagation)
6728          */
6729         stopEvent : function(){
6730             if(this.browserEvent){
6731                 if(this.browserEvent.type == 'mousedown'){
6732                     Roo.EventManager.stoppedMouseDownEvent.fire(this);
6733                 }
6734                 E.stopEvent(this.browserEvent);
6735             }
6736         },
6737
6738         /**
6739          * Prevents the browsers default handling of the event.
6740          */
6741         preventDefault : function(){
6742             if(this.browserEvent){
6743                 E.preventDefault(this.browserEvent);
6744             }
6745         },
6746
6747         /** @private */
6748         isNavKeyPress : function(){
6749             var k = this.keyCode;
6750             k = Roo.isSafari ? (safariKeys[k] || k) : k;
6751             return (k >= 33 && k <= 40) || k == this.RETURN || k == this.TAB || k == this.ESC;
6752         },
6753
6754         isSpecialKey : function(){
6755             var k = this.keyCode;
6756             return (this.type == 'keypress' && this.ctrlKey) || k == 9 || k == 13  || k == 40 || k == 27 ||
6757             (k == 16) || (k == 17) ||
6758             (k >= 18 && k <= 20) ||
6759             (k >= 33 && k <= 35) ||
6760             (k >= 36 && k <= 39) ||
6761             (k >= 44 && k <= 45);
6762         },
6763         /**
6764          * Cancels bubbling of the event.
6765          */
6766         stopPropagation : function(){
6767             if(this.browserEvent){
6768                 if(this.type == 'mousedown'){
6769                     Roo.EventManager.stoppedMouseDownEvent.fire(this);
6770                 }
6771                 E.stopPropagation(this.browserEvent);
6772             }
6773         },
6774
6775         /**
6776          * Gets the key code for the event.
6777          * @return {Number}
6778          */
6779         getCharCode : function(){
6780             return this.charCode || this.keyCode;
6781         },
6782
6783         /**
6784          * Returns a normalized keyCode for the event.
6785          * @return {Number} The key code
6786          */
6787         getKey : function(){
6788             var k = this.keyCode || this.charCode;
6789             return Roo.isSafari ? (safariKeys[k] || k) : k;
6790         },
6791
6792         /**
6793          * Gets the x coordinate of the event.
6794          * @return {Number}
6795          */
6796         getPageX : function(){
6797             return this.xy[0];
6798         },
6799
6800         /**
6801          * Gets the y coordinate of the event.
6802          * @return {Number}
6803          */
6804         getPageY : function(){
6805             return this.xy[1];
6806         },
6807
6808         /**
6809          * Gets the time of the event.
6810          * @return {Number}
6811          */
6812         getTime : function(){
6813             if(this.browserEvent){
6814                 return E.getTime(this.browserEvent);
6815             }
6816             return null;
6817         },
6818
6819         /**
6820          * Gets the page coordinates of the event.
6821          * @return {Array} The xy values like [x, y]
6822          */
6823         getXY : function(){
6824             return this.xy;
6825         },
6826
6827         /**
6828          * Gets the target for the event.
6829          * @param {String} selector (optional) A simple selector to filter the target or look for an ancestor of the target
6830          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
6831                 search as a number or element (defaults to 10 || document.body)
6832          * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
6833          * @return {HTMLelement}
6834          */
6835         getTarget : function(selector, maxDepth, returnEl){
6836             return selector ? Roo.fly(this.target).findParent(selector, maxDepth, returnEl) : this.target;
6837         },
6838         /**
6839          * Gets the related target.
6840          * @return {HTMLElement}
6841          */
6842         getRelatedTarget : function(){
6843             if(this.browserEvent){
6844                 return E.getRelatedTarget(this.browserEvent);
6845             }
6846             return null;
6847         },
6848
6849         /**
6850          * Normalizes mouse wheel delta across browsers
6851          * @return {Number} The delta
6852          */
6853         getWheelDelta : function(){
6854             var e = this.browserEvent;
6855             var delta = 0;
6856             if(e.wheelDelta){ /* IE/Opera. */
6857                 delta = e.wheelDelta/120;
6858             }else if(e.detail){ /* Mozilla case. */
6859                 delta = -e.detail/3;
6860             }
6861             return delta;
6862         },
6863
6864         /**
6865          * Returns true if the control, meta, shift or alt key was pressed during this event.
6866          * @return {Boolean}
6867          */
6868         hasModifier : function(){
6869             return !!((this.ctrlKey || this.altKey) || this.shiftKey);
6870         },
6871
6872         /**
6873          * Returns true if the target of this event equals el or is a child of el
6874          * @param {String/HTMLElement/Element} el
6875          * @param {Boolean} related (optional) true to test if the related target is within el instead of the target
6876          * @return {Boolean}
6877          */
6878         within : function(el, related){
6879             var t = this[related ? "getRelatedTarget" : "getTarget"]();
6880             return t && Roo.fly(el).contains(t);
6881         },
6882
6883         getPoint : function(){
6884             return new Roo.lib.Point(this.xy[0], this.xy[1]);
6885         }
6886     };
6887
6888     return new Roo.EventObjectImpl();
6889 }();
6890             
6891     /*
6892  * Based on:
6893  * Ext JS Library 1.1.1
6894  * Copyright(c) 2006-2007, Ext JS, LLC.
6895  *
6896  * Originally Released Under LGPL - original licence link has changed is not relivant.
6897  *
6898  * Fork - LGPL
6899  * <script type="text/javascript">
6900  */
6901
6902  
6903 // was in Composite Element!??!?!
6904  
6905 (function(){
6906     var D = Roo.lib.Dom;
6907     var E = Roo.lib.Event;
6908     var A = Roo.lib.Anim;
6909
6910     // local style camelizing for speed
6911     var propCache = {};
6912     var camelRe = /(-[a-z])/gi;
6913     var camelFn = function(m, a){ return a.charAt(1).toUpperCase(); };
6914     var view = document.defaultView;
6915
6916 /**
6917  * @class Roo.Element
6918  * Represents an Element in the DOM.<br><br>
6919  * Usage:<br>
6920 <pre><code>
6921 var el = Roo.get("my-div");
6922
6923 // or with getEl
6924 var el = getEl("my-div");
6925
6926 // or with a DOM element
6927 var el = Roo.get(myDivElement);
6928 </code></pre>
6929  * Using Roo.get() or getEl() instead of calling the constructor directly ensures you get the same object
6930  * each call instead of constructing a new one.<br><br>
6931  * <b>Animations</b><br />
6932  * Many of the functions for manipulating an element have an optional "animate" parameter. The animate parameter
6933  * should either be a boolean (true) or an object literal with animation options. The animation options are:
6934 <pre>
6935 Option    Default   Description
6936 --------- --------  ---------------------------------------------
6937 duration  .35       The duration of the animation in seconds
6938 easing    easeOut   The YUI easing method
6939 callback  none      A function to execute when the anim completes
6940 scope     this      The scope (this) of the callback function
6941 </pre>
6942 * Also, the Anim object being used for the animation will be set on your options object as "anim", which allows you to stop or
6943 * manipulate the animation. Here's an example:
6944 <pre><code>
6945 var el = Roo.get("my-div");
6946
6947 // no animation
6948 el.setWidth(100);
6949
6950 // default animation
6951 el.setWidth(100, true);
6952
6953 // animation with some options set
6954 el.setWidth(100, {
6955     duration: 1,
6956     callback: this.foo,
6957     scope: this
6958 });
6959
6960 // using the "anim" property to get the Anim object
6961 var opt = {
6962     duration: 1,
6963     callback: this.foo,
6964     scope: this
6965 };
6966 el.setWidth(100, opt);
6967 ...
6968 if(opt.anim.isAnimated()){
6969     opt.anim.stop();
6970 }
6971 </code></pre>
6972 * <b> Composite (Collections of) Elements</b><br />
6973  * For working with collections of Elements, see <a href="Roo.CompositeElement.html">Roo.CompositeElement</a>
6974  * @constructor Create a new Element directly.
6975  * @param {String/HTMLElement} element
6976  * @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).
6977  */
6978     Roo.Element = function(element, forceNew){
6979         var dom = typeof element == "string" ?
6980                 document.getElementById(element) : element;
6981         if(!dom){ // invalid id/element
6982             return null;
6983         }
6984         var id = dom.id;
6985         if(forceNew !== true && id && Roo.Element.cache[id]){ // element object already exists
6986             return Roo.Element.cache[id];
6987         }
6988
6989         /**
6990          * The DOM element
6991          * @type HTMLElement
6992          */
6993         this.dom = dom;
6994
6995         /**
6996          * The DOM element ID
6997          * @type String
6998          */
6999         this.id = id || Roo.id(dom);
7000     };
7001
7002     var El = Roo.Element;
7003
7004     El.prototype = {
7005         /**
7006          * The element's default display mode  (defaults to "")
7007          * @type String
7008          */
7009         originalDisplay : "",
7010
7011         visibilityMode : 1,
7012         /**
7013          * The default unit to append to CSS values where a unit isn't provided (defaults to px).
7014          * @type String
7015          */
7016         defaultUnit : "px",
7017         /**
7018          * Sets the element's visibility mode. When setVisible() is called it
7019          * will use this to determine whether to set the visibility or the display property.
7020          * @param visMode Element.VISIBILITY or Element.DISPLAY
7021          * @return {Roo.Element} this
7022          */
7023         setVisibilityMode : function(visMode){
7024             this.visibilityMode = visMode;
7025             return this;
7026         },
7027         /**
7028          * Convenience method for setVisibilityMode(Element.DISPLAY)
7029          * @param {String} display (optional) What to set display to when visible
7030          * @return {Roo.Element} this
7031          */
7032         enableDisplayMode : function(display){
7033             this.setVisibilityMode(El.DISPLAY);
7034             if(typeof display != "undefined") this.originalDisplay = display;
7035             return this;
7036         },
7037
7038         /**
7039          * 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)
7040          * @param {String} selector The simple selector to test
7041          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
7042                 search as a number or element (defaults to 10 || document.body)
7043          * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
7044          * @return {HTMLElement} The matching DOM node (or null if no match was found)
7045          */
7046         findParent : function(simpleSelector, maxDepth, returnEl){
7047             var p = this.dom, b = document.body, depth = 0, dq = Roo.DomQuery, stopEl;
7048             maxDepth = maxDepth || 50;
7049             if(typeof maxDepth != "number"){
7050                 stopEl = Roo.getDom(maxDepth);
7051                 maxDepth = 10;
7052             }
7053             while(p && p.nodeType == 1 && depth < maxDepth && p != b && p != stopEl){
7054                 if(dq.is(p, simpleSelector)){
7055                     return returnEl ? Roo.get(p) : p;
7056                 }
7057                 depth++;
7058                 p = p.parentNode;
7059             }
7060             return null;
7061         },
7062
7063
7064         /**
7065          * Looks at parent nodes for a match of the passed simple selector (e.g. div.some-class or span:first-child)
7066          * @param {String} selector The simple selector to test
7067          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
7068                 search as a number or element (defaults to 10 || document.body)
7069          * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
7070          * @return {HTMLElement} The matching DOM node (or null if no match was found)
7071          */
7072         findParentNode : function(simpleSelector, maxDepth, returnEl){
7073             var p = Roo.fly(this.dom.parentNode, '_internal');
7074             return p ? p.findParent(simpleSelector, maxDepth, returnEl) : null;
7075         },
7076
7077         /**
7078          * Walks up the dom looking for a parent node that matches the passed simple selector (e.g. div.some-class or span:first-child).
7079          * This is a shortcut for findParentNode() that always returns an Roo.Element.
7080          * @param {String} selector The simple selector to test
7081          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
7082                 search as a number or element (defaults to 10 || document.body)
7083          * @return {Roo.Element} The matching DOM node (or null if no match was found)
7084          */
7085         up : function(simpleSelector, maxDepth){
7086             return this.findParentNode(simpleSelector, maxDepth, true);
7087         },
7088
7089
7090
7091         /**
7092          * Returns true if this element matches the passed simple selector (e.g. div.some-class or span:first-child)
7093          * @param {String} selector The simple selector to test
7094          * @return {Boolean} True if this element matches the selector, else false
7095          */
7096         is : function(simpleSelector){
7097             return Roo.DomQuery.is(this.dom, simpleSelector);
7098         },
7099
7100         /**
7101          * Perform animation on this element.
7102          * @param {Object} args The YUI animation control args
7103          * @param {Float} duration (optional) How long the animation lasts in seconds (defaults to .35)
7104          * @param {Function} onComplete (optional) Function to call when animation completes
7105          * @param {String} easing (optional) Easing method to use (defaults to 'easeOut')
7106          * @param {String} animType (optional) 'run' is the default. Can also be 'color', 'motion', or 'scroll'
7107          * @return {Roo.Element} this
7108          */
7109         animate : function(args, duration, onComplete, easing, animType){
7110             this.anim(args, {duration: duration, callback: onComplete, easing: easing}, animType);
7111             return this;
7112         },
7113
7114         /*
7115          * @private Internal animation call
7116          */
7117         anim : function(args, opt, animType, defaultDur, defaultEase, cb){
7118             animType = animType || 'run';
7119             opt = opt || {};
7120             var anim = Roo.lib.Anim[animType](
7121                 this.dom, args,
7122                 (opt.duration || defaultDur) || .35,
7123                 (opt.easing || defaultEase) || 'easeOut',
7124                 function(){
7125                     Roo.callback(cb, this);
7126                     Roo.callback(opt.callback, opt.scope || this, [this, opt]);
7127                 },
7128                 this
7129             );
7130             opt.anim = anim;
7131             return anim;
7132         },
7133
7134         // private legacy anim prep
7135         preanim : function(a, i){
7136             return !a[i] ? false : (typeof a[i] == "object" ? a[i]: {duration: a[i+1], callback: a[i+2], easing: a[i+3]});
7137         },
7138
7139         /**
7140          * Removes worthless text nodes
7141          * @param {Boolean} forceReclean (optional) By default the element
7142          * keeps track if it has been cleaned already so
7143          * you can call this over and over. However, if you update the element and
7144          * need to force a reclean, you can pass true.
7145          */
7146         clean : function(forceReclean){
7147             if(this.isCleaned && forceReclean !== true){
7148                 return this;
7149             }
7150             var ns = /\S/;
7151             var d = this.dom, n = d.firstChild, ni = -1;
7152             while(n){
7153                 var nx = n.nextSibling;
7154                 if(n.nodeType == 3 && !ns.test(n.nodeValue)){
7155                     d.removeChild(n);
7156                 }else{
7157                     n.nodeIndex = ++ni;
7158                 }
7159                 n = nx;
7160             }
7161             this.isCleaned = true;
7162             return this;
7163         },
7164
7165         // private
7166         calcOffsetsTo : function(el){
7167             el = Roo.get(el);
7168             var d = el.dom;
7169             var restorePos = false;
7170             if(el.getStyle('position') == 'static'){
7171                 el.position('relative');
7172                 restorePos = true;
7173             }
7174             var x = 0, y =0;
7175             var op = this.dom;
7176             while(op && op != d && op.tagName != 'HTML'){
7177                 x+= op.offsetLeft;
7178                 y+= op.offsetTop;
7179                 op = op.offsetParent;
7180             }
7181             if(restorePos){
7182                 el.position('static');
7183             }
7184             return [x, y];
7185         },
7186
7187         /**
7188          * Scrolls this element into view within the passed container.
7189          * @param {String/HTMLElement/Element} container (optional) The container element to scroll (defaults to document.body)
7190          * @param {Boolean} hscroll (optional) False to disable horizontal scroll (defaults to true)
7191          * @return {Roo.Element} this
7192          */
7193         scrollIntoView : function(container, hscroll){
7194             var c = Roo.getDom(container) || document.body;
7195             var el = this.dom;
7196
7197             var o = this.calcOffsetsTo(c),
7198                 l = o[0],
7199                 t = o[1],
7200                 b = t+el.offsetHeight,
7201                 r = l+el.offsetWidth;
7202
7203             var ch = c.clientHeight;
7204             var ct = parseInt(c.scrollTop, 10);
7205             var cl = parseInt(c.scrollLeft, 10);
7206             var cb = ct + ch;
7207             var cr = cl + c.clientWidth;
7208
7209             if(t < ct){
7210                 c.scrollTop = t;
7211             }else if(b > cb){
7212                 c.scrollTop = b-ch;
7213             }
7214
7215             if(hscroll !== false){
7216                 if(l < cl){
7217                     c.scrollLeft = l;
7218                 }else if(r > cr){
7219                     c.scrollLeft = r-c.clientWidth;
7220                 }
7221             }
7222             return this;
7223         },
7224
7225         // private
7226         scrollChildIntoView : function(child, hscroll){
7227             Roo.fly(child, '_scrollChildIntoView').scrollIntoView(this, hscroll);
7228         },
7229
7230         /**
7231          * Measures the element's content height and updates height to match. Note: this function uses setTimeout so
7232          * the new height may not be available immediately.
7233          * @param {Boolean} animate (optional) Animate the transition (defaults to false)
7234          * @param {Float} duration (optional) Length of the animation in seconds (defaults to .35)
7235          * @param {Function} onComplete (optional) Function to call when animation completes
7236          * @param {String} easing (optional) Easing method to use (defaults to easeOut)
7237          * @return {Roo.Element} this
7238          */
7239         autoHeight : function(animate, duration, onComplete, easing){
7240             var oldHeight = this.getHeight();
7241             this.clip();
7242             this.setHeight(1); // force clipping
7243             setTimeout(function(){
7244                 var height = parseInt(this.dom.scrollHeight, 10); // parseInt for Safari
7245                 if(!animate){
7246                     this.setHeight(height);
7247                     this.unclip();
7248                     if(typeof onComplete == "function"){
7249                         onComplete();
7250                     }
7251                 }else{
7252                     this.setHeight(oldHeight); // restore original height
7253                     this.setHeight(height, animate, duration, function(){
7254                         this.unclip();
7255                         if(typeof onComplete == "function") onComplete();
7256                     }.createDelegate(this), easing);
7257                 }
7258             }.createDelegate(this), 0);
7259             return this;
7260         },
7261
7262         /**
7263          * Returns true if this element is an ancestor of the passed element
7264          * @param {HTMLElement/String} el The element to check
7265          * @return {Boolean} True if this element is an ancestor of el, else false
7266          */
7267         contains : function(el){
7268             if(!el){return false;}
7269             return D.isAncestor(this.dom, el.dom ? el.dom : el);
7270         },
7271
7272         /**
7273          * Checks whether the element is currently visible using both visibility and display properties.
7274          * @param {Boolean} deep (optional) True to walk the dom and see if parent elements are hidden (defaults to false)
7275          * @return {Boolean} True if the element is currently visible, else false
7276          */
7277         isVisible : function(deep) {
7278             var vis = !(this.getStyle("visibility") == "hidden" || this.getStyle("display") == "none");
7279             if(deep !== true || !vis){
7280                 return vis;
7281             }
7282             var p = this.dom.parentNode;
7283             while(p && p.tagName.toLowerCase() != "body"){
7284                 if(!Roo.fly(p, '_isVisible').isVisible()){
7285                     return false;
7286                 }
7287                 p = p.parentNode;
7288             }
7289             return true;
7290         },
7291
7292         /**
7293          * Creates a {@link Roo.CompositeElement} for child nodes based on the passed CSS selector (the selector should not contain an id).
7294          * @param {String} selector The CSS selector
7295          * @param {Boolean} unique (optional) True to create a unique Roo.Element for each child (defaults to false, which creates a single shared flyweight object)
7296          * @return {CompositeElement/CompositeElementLite} The composite element
7297          */
7298         select : function(selector, unique){
7299             return El.select(selector, unique, this.dom);
7300         },
7301
7302         /**
7303          * Selects child nodes based on the passed CSS selector (the selector should not contain an id).
7304          * @param {String} selector The CSS selector
7305          * @return {Array} An array of the matched nodes
7306          */
7307         query : function(selector, unique){
7308             return Roo.DomQuery.select(selector, this.dom);
7309         },
7310
7311         /**
7312          * Selects a single child at any depth below this element based on the passed CSS selector (the selector should not contain an id).
7313          * @param {String} selector The CSS selector
7314          * @param {Boolean} returnDom (optional) True to return the DOM node instead of Roo.Element (defaults to false)
7315          * @return {HTMLElement/Roo.Element} The child Roo.Element (or DOM node if returnDom = true)
7316          */
7317         child : function(selector, returnDom){
7318             var n = Roo.DomQuery.selectNode(selector, this.dom);
7319             return returnDom ? n : Roo.get(n);
7320         },
7321
7322         /**
7323          * Selects a single *direct* child based on the passed CSS selector (the selector should not contain an id).
7324          * @param {String} selector The CSS selector
7325          * @param {Boolean} returnDom (optional) True to return the DOM node instead of Roo.Element (defaults to false)
7326          * @return {HTMLElement/Roo.Element} The child Roo.Element (or DOM node if returnDom = true)
7327          */
7328         down : function(selector, returnDom){
7329             var n = Roo.DomQuery.selectNode(" > " + selector, this.dom);
7330             return returnDom ? n : Roo.get(n);
7331         },
7332
7333         /**
7334          * Initializes a {@link Roo.dd.DD} drag drop object for this element.
7335          * @param {String} group The group the DD object is member of
7336          * @param {Object} config The DD config object
7337          * @param {Object} overrides An object containing methods to override/implement on the DD object
7338          * @return {Roo.dd.DD} The DD object
7339          */
7340         initDD : function(group, config, overrides){
7341             var dd = new Roo.dd.DD(Roo.id(this.dom), group, config);
7342             return Roo.apply(dd, overrides);
7343         },
7344
7345         /**
7346          * Initializes a {@link Roo.dd.DDProxy} object for this element.
7347          * @param {String} group The group the DDProxy object is member of
7348          * @param {Object} config The DDProxy config object
7349          * @param {Object} overrides An object containing methods to override/implement on the DDProxy object
7350          * @return {Roo.dd.DDProxy} The DDProxy object
7351          */
7352         initDDProxy : function(group, config, overrides){
7353             var dd = new Roo.dd.DDProxy(Roo.id(this.dom), group, config);
7354             return Roo.apply(dd, overrides);
7355         },
7356
7357         /**
7358          * Initializes a {@link Roo.dd.DDTarget} object for this element.
7359          * @param {String} group The group the DDTarget object is member of
7360          * @param {Object} config The DDTarget config object
7361          * @param {Object} overrides An object containing methods to override/implement on the DDTarget object
7362          * @return {Roo.dd.DDTarget} The DDTarget object
7363          */
7364         initDDTarget : function(group, config, overrides){
7365             var dd = new Roo.dd.DDTarget(Roo.id(this.dom), group, config);
7366             return Roo.apply(dd, overrides);
7367         },
7368
7369         /**
7370          * Sets the visibility of the element (see details). If the visibilityMode is set to Element.DISPLAY, it will use
7371          * the display property to hide the element, otherwise it uses visibility. The default is to hide and show using the visibility property.
7372          * @param {Boolean} visible Whether the element is visible
7373          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7374          * @return {Roo.Element} this
7375          */
7376          setVisible : function(visible, animate){
7377             if(!animate || !A){
7378                 if(this.visibilityMode == El.DISPLAY){
7379                     this.setDisplayed(visible);
7380                 }else{
7381                     this.fixDisplay();
7382                     this.dom.style.visibility = visible ? "visible" : "hidden";
7383                 }
7384             }else{
7385                 // closure for composites
7386                 var dom = this.dom;
7387                 var visMode = this.visibilityMode;
7388                 if(visible){
7389                     this.setOpacity(.01);
7390                     this.setVisible(true);
7391                 }
7392                 this.anim({opacity: { to: (visible?1:0) }},
7393                       this.preanim(arguments, 1),
7394                       null, .35, 'easeIn', function(){
7395                          if(!visible){
7396                              if(visMode == El.DISPLAY){
7397                                  dom.style.display = "none";
7398                              }else{
7399                                  dom.style.visibility = "hidden";
7400                              }
7401                              Roo.get(dom).setOpacity(1);
7402                          }
7403                      });
7404             }
7405             return this;
7406         },
7407
7408         /**
7409          * Returns true if display is not "none"
7410          * @return {Boolean}
7411          */
7412         isDisplayed : function() {
7413             return this.getStyle("display") != "none";
7414         },
7415
7416         /**
7417          * Toggles the element's visibility or display, depending on visibility mode.
7418          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7419          * @return {Roo.Element} this
7420          */
7421         toggle : function(animate){
7422             this.setVisible(!this.isVisible(), this.preanim(arguments, 0));
7423             return this;
7424         },
7425
7426         /**
7427          * Sets the CSS display property. Uses originalDisplay if the specified value is a boolean true.
7428          * @param {Boolean} value Boolean value to display the element using its default display, or a string to set the display directly
7429          * @return {Roo.Element} this
7430          */
7431         setDisplayed : function(value) {
7432             if(typeof value == "boolean"){
7433                value = value ? this.originalDisplay : "none";
7434             }
7435             this.setStyle("display", value);
7436             return this;
7437         },
7438
7439         /**
7440          * Tries to focus the element. Any exceptions are caught and ignored.
7441          * @return {Roo.Element} this
7442          */
7443         focus : function() {
7444             try{
7445                 this.dom.focus();
7446             }catch(e){}
7447             return this;
7448         },
7449
7450         /**
7451          * Tries to blur the element. Any exceptions are caught and ignored.
7452          * @return {Roo.Element} this
7453          */
7454         blur : function() {
7455             try{
7456                 this.dom.blur();
7457             }catch(e){}
7458             return this;
7459         },
7460
7461         /**
7462          * Adds one or more CSS classes to the element. Duplicate classes are automatically filtered out.
7463          * @param {String/Array} className The CSS class to add, or an array of classes
7464          * @return {Roo.Element} this
7465          */
7466         addClass : function(className){
7467             if(className instanceof Array){
7468                 for(var i = 0, len = className.length; i < len; i++) {
7469                     this.addClass(className[i]);
7470                 }
7471             }else{
7472                 if(className && !this.hasClass(className)){
7473                     this.dom.className = this.dom.className + " " + className;
7474                 }
7475             }
7476             return this;
7477         },
7478
7479         /**
7480          * Adds one or more CSS classes to this element and removes the same class(es) from all siblings.
7481          * @param {String/Array} className The CSS class to add, or an array of classes
7482          * @return {Roo.Element} this
7483          */
7484         radioClass : function(className){
7485             var siblings = this.dom.parentNode.childNodes;
7486             for(var i = 0; i < siblings.length; i++) {
7487                 var s = siblings[i];
7488                 if(s.nodeType == 1){
7489                     Roo.get(s).removeClass(className);
7490                 }
7491             }
7492             this.addClass(className);
7493             return this;
7494         },
7495
7496         /**
7497          * Removes one or more CSS classes from the element.
7498          * @param {String/Array} className The CSS class to remove, or an array of classes
7499          * @return {Roo.Element} this
7500          */
7501         removeClass : function(className){
7502             if(!className || !this.dom.className){
7503                 return this;
7504             }
7505             if(className instanceof Array){
7506                 for(var i = 0, len = className.length; i < len; i++) {
7507                     this.removeClass(className[i]);
7508                 }
7509             }else{
7510                 if(this.hasClass(className)){
7511                     var re = this.classReCache[className];
7512                     if (!re) {
7513                        re = new RegExp('(?:^|\\s+)' + className + '(?:\\s+|$)', "g");
7514                        this.classReCache[className] = re;
7515                     }
7516                     this.dom.className =
7517                         this.dom.className.replace(re, " ");
7518                 }
7519             }
7520             return this;
7521         },
7522
7523         // private
7524         classReCache: {},
7525
7526         /**
7527          * Toggles the specified CSS class on this element (removes it if it already exists, otherwise adds it).
7528          * @param {String} className The CSS class to toggle
7529          * @return {Roo.Element} this
7530          */
7531         toggleClass : function(className){
7532             if(this.hasClass(className)){
7533                 this.removeClass(className);
7534             }else{
7535                 this.addClass(className);
7536             }
7537             return this;
7538         },
7539
7540         /**
7541          * Checks if the specified CSS class exists on this element's DOM node.
7542          * @param {String} className The CSS class to check for
7543          * @return {Boolean} True if the class exists, else false
7544          */
7545         hasClass : function(className){
7546             return className && (' '+this.dom.className+' ').indexOf(' '+className+' ') != -1;
7547         },
7548
7549         /**
7550          * Replaces a CSS class on the element with another.  If the old name does not exist, the new name will simply be added.
7551          * @param {String} oldClassName The CSS class to replace
7552          * @param {String} newClassName The replacement CSS class
7553          * @return {Roo.Element} this
7554          */
7555         replaceClass : function(oldClassName, newClassName){
7556             this.removeClass(oldClassName);
7557             this.addClass(newClassName);
7558             return this;
7559         },
7560
7561         /**
7562          * Returns an object with properties matching the styles requested.
7563          * For example, el.getStyles('color', 'font-size', 'width') might return
7564          * {'color': '#FFFFFF', 'font-size': '13px', 'width': '100px'}.
7565          * @param {String} style1 A style name
7566          * @param {String} style2 A style name
7567          * @param {String} etc.
7568          * @return {Object} The style object
7569          */
7570         getStyles : function(){
7571             var a = arguments, len = a.length, r = {};
7572             for(var i = 0; i < len; i++){
7573                 r[a[i]] = this.getStyle(a[i]);
7574             }
7575             return r;
7576         },
7577
7578         /**
7579          * Normalizes currentStyle and computedStyle. This is not YUI getStyle, it is an optimised version.
7580          * @param {String} property The style property whose value is returned.
7581          * @return {String} The current value of the style property for this element.
7582          */
7583         getStyle : function(){
7584             return view && view.getComputedStyle ?
7585                 function(prop){
7586                     var el = this.dom, v, cs, camel;
7587                     if(prop == 'float'){
7588                         prop = "cssFloat";
7589                     }
7590                     if(el.style && (v = el.style[prop])){
7591                         return v;
7592                     }
7593                     if(cs = view.getComputedStyle(el, "")){
7594                         if(!(camel = propCache[prop])){
7595                             camel = propCache[prop] = prop.replace(camelRe, camelFn);
7596                         }
7597                         return cs[camel];
7598                     }
7599                     return null;
7600                 } :
7601                 function(prop){
7602                     var el = this.dom, v, cs, camel;
7603                     if(prop == 'opacity'){
7604                         if(typeof el.style.filter == 'string'){
7605                             var m = el.style.filter.match(/alpha\(opacity=(.*)\)/i);
7606                             if(m){
7607                                 var fv = parseFloat(m[1]);
7608                                 if(!isNaN(fv)){
7609                                     return fv ? fv / 100 : 0;
7610                                 }
7611                             }
7612                         }
7613                         return 1;
7614                     }else if(prop == 'float'){
7615                         prop = "styleFloat";
7616                     }
7617                     if(!(camel = propCache[prop])){
7618                         camel = propCache[prop] = prop.replace(camelRe, camelFn);
7619                     }
7620                     if(v = el.style[camel]){
7621                         return v;
7622                     }
7623                     if(cs = el.currentStyle){
7624                         return cs[camel];
7625                     }
7626                     return null;
7627                 };
7628         }(),
7629
7630         /**
7631          * Wrapper for setting style properties, also takes single object parameter of multiple styles.
7632          * @param {String/Object} property The style property to be set, or an object of multiple styles.
7633          * @param {String} value (optional) The value to apply to the given property, or null if an object was passed.
7634          * @return {Roo.Element} this
7635          */
7636         setStyle : function(prop, value){
7637             if(typeof prop == "string"){
7638                 
7639                 if (prop == 'float') {
7640                     this.setStyle(Roo.isIE ? 'styleFloat'  : 'cssFloat', value);
7641                     return this;
7642                 }
7643                 
7644                 var camel;
7645                 if(!(camel = propCache[prop])){
7646                     camel = propCache[prop] = prop.replace(camelRe, camelFn);
7647                 }
7648                 
7649                 if(camel == 'opacity') {
7650                     this.setOpacity(value);
7651                 }else{
7652                     this.dom.style[camel] = value;
7653                 }
7654             }else{
7655                 for(var style in prop){
7656                     if(typeof prop[style] != "function"){
7657                        this.setStyle(style, prop[style]);
7658                     }
7659                 }
7660             }
7661             return this;
7662         },
7663
7664         /**
7665          * More flexible version of {@link #setStyle} for setting style properties.
7666          * @param {String/Object/Function} styles A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
7667          * a function which returns such a specification.
7668          * @return {Roo.Element} this
7669          */
7670         applyStyles : function(style){
7671             Roo.DomHelper.applyStyles(this.dom, style);
7672             return this;
7673         },
7674
7675         /**
7676           * 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).
7677           * @return {Number} The X position of the element
7678           */
7679         getX : function(){
7680             return D.getX(this.dom);
7681         },
7682
7683         /**
7684           * 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).
7685           * @return {Number} The Y position of the element
7686           */
7687         getY : function(){
7688             return D.getY(this.dom);
7689         },
7690
7691         /**
7692           * 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).
7693           * @return {Array} The XY position of the element
7694           */
7695         getXY : function(){
7696             return D.getXY(this.dom);
7697         },
7698
7699         /**
7700          * 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).
7701          * @param {Number} The X position of the element
7702          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7703          * @return {Roo.Element} this
7704          */
7705         setX : function(x, animate){
7706             if(!animate || !A){
7707                 D.setX(this.dom, x);
7708             }else{
7709                 this.setXY([x, this.getY()], this.preanim(arguments, 1));
7710             }
7711             return this;
7712         },
7713
7714         /**
7715          * 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).
7716          * @param {Number} The Y position of the element
7717          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7718          * @return {Roo.Element} this
7719          */
7720         setY : function(y, animate){
7721             if(!animate || !A){
7722                 D.setY(this.dom, y);
7723             }else{
7724                 this.setXY([this.getX(), y], this.preanim(arguments, 1));
7725             }
7726             return this;
7727         },
7728
7729         /**
7730          * Sets the element's left position directly using CSS style (instead of {@link #setX}).
7731          * @param {String} left The left CSS property value
7732          * @return {Roo.Element} this
7733          */
7734         setLeft : function(left){
7735             this.setStyle("left", this.addUnits(left));
7736             return this;
7737         },
7738
7739         /**
7740          * Sets the element's top position directly using CSS style (instead of {@link #setY}).
7741          * @param {String} top The top CSS property value
7742          * @return {Roo.Element} this
7743          */
7744         setTop : function(top){
7745             this.setStyle("top", this.addUnits(top));
7746             return this;
7747         },
7748
7749         /**
7750          * Sets the element's CSS right style.
7751          * @param {String} right The right CSS property value
7752          * @return {Roo.Element} this
7753          */
7754         setRight : function(right){
7755             this.setStyle("right", this.addUnits(right));
7756             return this;
7757         },
7758
7759         /**
7760          * Sets the element's CSS bottom style.
7761          * @param {String} bottom The bottom CSS property value
7762          * @return {Roo.Element} this
7763          */
7764         setBottom : function(bottom){
7765             this.setStyle("bottom", this.addUnits(bottom));
7766             return this;
7767         },
7768
7769         /**
7770          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
7771          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7772          * @param {Array} pos Contains X & Y [x, y] values 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         setXY : function(pos, animate){
7777             if(!animate || !A){
7778                 D.setXY(this.dom, pos);
7779             }else{
7780                 this.anim({points: {to: pos}}, this.preanim(arguments, 1), 'motion');
7781             }
7782             return this;
7783         },
7784
7785         /**
7786          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
7787          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7788          * @param {Number} x X value for new position (coordinates are page-based)
7789          * @param {Number} y Y value for new position (coordinates are page-based)
7790          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7791          * @return {Roo.Element} this
7792          */
7793         setLocation : function(x, y, animate){
7794             this.setXY([x, y], this.preanim(arguments, 2));
7795             return this;
7796         },
7797
7798         /**
7799          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
7800          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7801          * @param {Number} x X value for new position (coordinates are page-based)
7802          * @param {Number} y Y value for new position (coordinates are page-based)
7803          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7804          * @return {Roo.Element} this
7805          */
7806         moveTo : function(x, y, animate){
7807             this.setXY([x, y], this.preanim(arguments, 2));
7808             return this;
7809         },
7810
7811         /**
7812          * Returns the region of the given element.
7813          * The element must be part of the DOM tree to have a region (display:none or elements not appended return false).
7814          * @return {Region} A Roo.lib.Region containing "top, left, bottom, right" member data.
7815          */
7816         getRegion : function(){
7817             return D.getRegion(this.dom);
7818         },
7819
7820         /**
7821          * Returns the offset height of the element
7822          * @param {Boolean} contentHeight (optional) true to get the height minus borders and padding
7823          * @return {Number} The element's height
7824          */
7825         getHeight : function(contentHeight){
7826             var h = this.dom.offsetHeight || 0;
7827             return contentHeight !== true ? h : h-this.getBorderWidth("tb")-this.getPadding("tb");
7828         },
7829
7830         /**
7831          * Returns the offset width of the element
7832          * @param {Boolean} contentWidth (optional) true to get the width minus borders and padding
7833          * @return {Number} The element's width
7834          */
7835         getWidth : function(contentWidth){
7836             var w = this.dom.offsetWidth || 0;
7837             return contentWidth !== true ? w : w-this.getBorderWidth("lr")-this.getPadding("lr");
7838         },
7839
7840         /**
7841          * Returns either the offsetHeight or the height of this element based on CSS height adjusted by padding or borders
7842          * when needed to simulate offsetHeight when offsets aren't available. This may not work on display:none elements
7843          * if a height has not been set using CSS.
7844          * @return {Number}
7845          */
7846         getComputedHeight : function(){
7847             var h = Math.max(this.dom.offsetHeight, this.dom.clientHeight);
7848             if(!h){
7849                 h = parseInt(this.getStyle('height'), 10) || 0;
7850                 if(!this.isBorderBox()){
7851                     h += this.getFrameWidth('tb');
7852                 }
7853             }
7854             return h;
7855         },
7856
7857         /**
7858          * Returns either the offsetWidth or the width of this element based on CSS width adjusted by padding or borders
7859          * when needed to simulate offsetWidth when offsets aren't available. This may not work on display:none elements
7860          * if a width has not been set using CSS.
7861          * @return {Number}
7862          */
7863         getComputedWidth : function(){
7864             var w = Math.max(this.dom.offsetWidth, this.dom.clientWidth);
7865             if(!w){
7866                 w = parseInt(this.getStyle('width'), 10) || 0;
7867                 if(!this.isBorderBox()){
7868                     w += this.getFrameWidth('lr');
7869                 }
7870             }
7871             return w;
7872         },
7873
7874         /**
7875          * Returns the size of the element.
7876          * @param {Boolean} contentSize (optional) true to get the width/size minus borders and padding
7877          * @return {Object} An object containing the element's size {width: (element width), height: (element height)}
7878          */
7879         getSize : function(contentSize){
7880             return {width: this.getWidth(contentSize), height: this.getHeight(contentSize)};
7881         },
7882
7883         /**
7884          * Returns the width and height of the viewport.
7885          * @return {Object} An object containing the viewport's size {width: (viewport width), height: (viewport height)}
7886          */
7887         getViewSize : function(){
7888             var d = this.dom, doc = document, aw = 0, ah = 0;
7889             if(d == doc || d == doc.body){
7890                 return {width : D.getViewWidth(), height: D.getViewHeight()};
7891             }else{
7892                 return {
7893                     width : d.clientWidth,
7894                     height: d.clientHeight
7895                 };
7896             }
7897         },
7898
7899         /**
7900          * Returns the value of the "value" attribute
7901          * @param {Boolean} asNumber true to parse the value as a number
7902          * @return {String/Number}
7903          */
7904         getValue : function(asNumber){
7905             return asNumber ? parseInt(this.dom.value, 10) : this.dom.value;
7906         },
7907
7908         // private
7909         adjustWidth : function(width){
7910             if(typeof width == "number"){
7911                 if(this.autoBoxAdjust && !this.isBorderBox()){
7912                    width -= (this.getBorderWidth("lr") + this.getPadding("lr"));
7913                 }
7914                 if(width < 0){
7915                     width = 0;
7916                 }
7917             }
7918             return width;
7919         },
7920
7921         // private
7922         adjustHeight : function(height){
7923             if(typeof height == "number"){
7924                if(this.autoBoxAdjust && !this.isBorderBox()){
7925                    height -= (this.getBorderWidth("tb") + this.getPadding("tb"));
7926                }
7927                if(height < 0){
7928                    height = 0;
7929                }
7930             }
7931             return height;
7932         },
7933
7934         /**
7935          * Set the width of the element
7936          * @param {Number} width The new width
7937          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7938          * @return {Roo.Element} this
7939          */
7940         setWidth : function(width, animate){
7941             width = this.adjustWidth(width);
7942             if(!animate || !A){
7943                 this.dom.style.width = this.addUnits(width);
7944             }else{
7945                 this.anim({width: {to: width}}, this.preanim(arguments, 1));
7946             }
7947             return this;
7948         },
7949
7950         /**
7951          * Set the height of the element
7952          * @param {Number} height The new height
7953          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7954          * @return {Roo.Element} this
7955          */
7956          setHeight : function(height, animate){
7957             height = this.adjustHeight(height);
7958             if(!animate || !A){
7959                 this.dom.style.height = this.addUnits(height);
7960             }else{
7961                 this.anim({height: {to: height}}, this.preanim(arguments, 1));
7962             }
7963             return this;
7964         },
7965
7966         /**
7967          * Set the size of the element. If animation is true, both width an height will be animated concurrently.
7968          * @param {Number} width The new width
7969          * @param {Number} height The new height
7970          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7971          * @return {Roo.Element} this
7972          */
7973          setSize : function(width, height, animate){
7974             if(typeof width == "object"){ // in case of object from getSize()
7975                 height = width.height; width = width.width;
7976             }
7977             width = this.adjustWidth(width); height = this.adjustHeight(height);
7978             if(!animate || !A){
7979                 this.dom.style.width = this.addUnits(width);
7980                 this.dom.style.height = this.addUnits(height);
7981             }else{
7982                 this.anim({width: {to: width}, height: {to: height}}, this.preanim(arguments, 2));
7983             }
7984             return this;
7985         },
7986
7987         /**
7988          * Sets the element's position and size in one shot. If animation is true then width, height, x and y will be animated concurrently.
7989          * @param {Number} x X value for new position (coordinates are page-based)
7990          * @param {Number} y Y value for new position (coordinates are page-based)
7991          * @param {Number} width The new width
7992          * @param {Number} height The new height
7993          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7994          * @return {Roo.Element} this
7995          */
7996         setBounds : function(x, y, width, height, animate){
7997             if(!animate || !A){
7998                 this.setSize(width, height);
7999                 this.setLocation(x, y);
8000             }else{
8001                 width = this.adjustWidth(width); height = this.adjustHeight(height);
8002                 this.anim({points: {to: [x, y]}, width: {to: width}, height: {to: height}},
8003                               this.preanim(arguments, 4), 'motion');
8004             }
8005             return this;
8006         },
8007
8008         /**
8009          * 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.
8010          * @param {Roo.lib.Region} region The region to fill
8011          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8012          * @return {Roo.Element} this
8013          */
8014         setRegion : function(region, animate){
8015             this.setBounds(region.left, region.top, region.right-region.left, region.bottom-region.top, this.preanim(arguments, 1));
8016             return this;
8017         },
8018
8019         /**
8020          * Appends an event handler
8021          *
8022          * @param {String}   eventName     The type of event to append
8023          * @param {Function} fn        The method the event invokes
8024          * @param {Object} scope       (optional) The scope (this object) of the fn
8025          * @param {Object}   options   (optional)An object with standard {@link Roo.EventManager#addListener} options
8026          */
8027         addListener : function(eventName, fn, scope, options){
8028             if (this.dom) {
8029                 Roo.EventManager.on(this.dom,  eventName, fn, scope || this, options);
8030             }
8031         },
8032
8033         /**
8034          * Removes an event handler from this element
8035          * @param {String} eventName the type of event to remove
8036          * @param {Function} fn the method the event invokes
8037          * @return {Roo.Element} this
8038          */
8039         removeListener : function(eventName, fn){
8040             Roo.EventManager.removeListener(this.dom,  eventName, fn);
8041             return this;
8042         },
8043
8044         /**
8045          * Removes all previous added listeners from this element
8046          * @return {Roo.Element} this
8047          */
8048         removeAllListeners : function(){
8049             E.purgeElement(this.dom);
8050             return this;
8051         },
8052
8053         relayEvent : function(eventName, observable){
8054             this.on(eventName, function(e){
8055                 observable.fireEvent(eventName, e);
8056             });
8057         },
8058
8059         /**
8060          * Set the opacity of the element
8061          * @param {Float} opacity The new opacity. 0 = transparent, .5 = 50% visibile, 1 = fully visible, etc
8062          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8063          * @return {Roo.Element} this
8064          */
8065          setOpacity : function(opacity, animate){
8066             if(!animate || !A){
8067                 var s = this.dom.style;
8068                 if(Roo.isIE){
8069                     s.zoom = 1;
8070                     s.filter = (s.filter || '').replace(/alpha\([^\)]*\)/gi,"") +
8071                                (opacity == 1 ? "" : "alpha(opacity=" + opacity * 100 + ")");
8072                 }else{
8073                     s.opacity = opacity;
8074                 }
8075             }else{
8076                 this.anim({opacity: {to: opacity}}, this.preanim(arguments, 1), null, .35, 'easeIn');
8077             }
8078             return this;
8079         },
8080
8081         /**
8082          * Gets the left X coordinate
8083          * @param {Boolean} local True to get the local css position instead of page coordinate
8084          * @return {Number}
8085          */
8086         getLeft : function(local){
8087             if(!local){
8088                 return this.getX();
8089             }else{
8090                 return parseInt(this.getStyle("left"), 10) || 0;
8091             }
8092         },
8093
8094         /**
8095          * Gets the right X coordinate of the element (element X position + element width)
8096          * @param {Boolean} local True to get the local css position instead of page coordinate
8097          * @return {Number}
8098          */
8099         getRight : function(local){
8100             if(!local){
8101                 return this.getX() + this.getWidth();
8102             }else{
8103                 return (this.getLeft(true) + this.getWidth()) || 0;
8104             }
8105         },
8106
8107         /**
8108          * Gets the top Y coordinate
8109          * @param {Boolean} local True to get the local css position instead of page coordinate
8110          * @return {Number}
8111          */
8112         getTop : function(local) {
8113             if(!local){
8114                 return this.getY();
8115             }else{
8116                 return parseInt(this.getStyle("top"), 10) || 0;
8117             }
8118         },
8119
8120         /**
8121          * Gets the bottom Y coordinate of the element (element Y position + element height)
8122          * @param {Boolean} local True to get the local css position instead of page coordinate
8123          * @return {Number}
8124          */
8125         getBottom : function(local){
8126             if(!local){
8127                 return this.getY() + this.getHeight();
8128             }else{
8129                 return (this.getTop(true) + this.getHeight()) || 0;
8130             }
8131         },
8132
8133         /**
8134         * Initializes positioning on this element. If a desired position is not passed, it will make the
8135         * the element positioned relative IF it is not already positioned.
8136         * @param {String} pos (optional) Positioning to use "relative", "absolute" or "fixed"
8137         * @param {Number} zIndex (optional) The zIndex to apply
8138         * @param {Number} x (optional) Set the page X position
8139         * @param {Number} y (optional) Set the page Y position
8140         */
8141         position : function(pos, zIndex, x, y){
8142             if(!pos){
8143                if(this.getStyle('position') == 'static'){
8144                    this.setStyle('position', 'relative');
8145                }
8146             }else{
8147                 this.setStyle("position", pos);
8148             }
8149             if(zIndex){
8150                 this.setStyle("z-index", zIndex);
8151             }
8152             if(x !== undefined && y !== undefined){
8153                 this.setXY([x, y]);
8154             }else if(x !== undefined){
8155                 this.setX(x);
8156             }else if(y !== undefined){
8157                 this.setY(y);
8158             }
8159         },
8160
8161         /**
8162         * Clear positioning back to the default when the document was loaded
8163         * @param {String} value (optional) The value to use for the left,right,top,bottom, defaults to '' (empty string). You could use 'auto'.
8164         * @return {Roo.Element} this
8165          */
8166         clearPositioning : function(value){
8167             value = value ||'';
8168             this.setStyle({
8169                 "left": value,
8170                 "right": value,
8171                 "top": value,
8172                 "bottom": value,
8173                 "z-index": "",
8174                 "position" : "static"
8175             });
8176             return this;
8177         },
8178
8179         /**
8180         * Gets an object with all CSS positioning properties. Useful along with setPostioning to get
8181         * snapshot before performing an update and then restoring the element.
8182         * @return {Object}
8183         */
8184         getPositioning : function(){
8185             var l = this.getStyle("left");
8186             var t = this.getStyle("top");
8187             return {
8188                 "position" : this.getStyle("position"),
8189                 "left" : l,
8190                 "right" : l ? "" : this.getStyle("right"),
8191                 "top" : t,
8192                 "bottom" : t ? "" : this.getStyle("bottom"),
8193                 "z-index" : this.getStyle("z-index")
8194             };
8195         },
8196
8197         /**
8198          * Gets the width of the border(s) for the specified side(s)
8199          * @param {String} side Can be t, l, r, b or any combination of those to add multiple values. For example,
8200          * passing lr would get the border (l)eft width + the border (r)ight width.
8201          * @return {Number} The width of the sides passed added together
8202          */
8203         getBorderWidth : function(side){
8204             return this.addStyles(side, El.borders);
8205         },
8206
8207         /**
8208          * Gets the width of the padding(s) for the specified side(s)
8209          * @param {String} side Can be t, l, r, b or any combination of those to add multiple values. For example,
8210          * passing lr would get the padding (l)eft + the padding (r)ight.
8211          * @return {Number} The padding of the sides passed added together
8212          */
8213         getPadding : function(side){
8214             return this.addStyles(side, El.paddings);
8215         },
8216
8217         /**
8218         * Set positioning with an object returned by getPositioning().
8219         * @param {Object} posCfg
8220         * @return {Roo.Element} this
8221          */
8222         setPositioning : function(pc){
8223             this.applyStyles(pc);
8224             if(pc.right == "auto"){
8225                 this.dom.style.right = "";
8226             }
8227             if(pc.bottom == "auto"){
8228                 this.dom.style.bottom = "";
8229             }
8230             return this;
8231         },
8232
8233         // private
8234         fixDisplay : function(){
8235             if(this.getStyle("display") == "none"){
8236                 this.setStyle("visibility", "hidden");
8237                 this.setStyle("display", this.originalDisplay); // first try reverting to default
8238                 if(this.getStyle("display") == "none"){ // if that fails, default to block
8239                     this.setStyle("display", "block");
8240                 }
8241             }
8242         },
8243
8244         /**
8245          * Quick set left and top adding default units
8246          * @param {String} left The left CSS property value
8247          * @param {String} top The top CSS property value
8248          * @return {Roo.Element} this
8249          */
8250          setLeftTop : function(left, top){
8251             this.dom.style.left = this.addUnits(left);
8252             this.dom.style.top = this.addUnits(top);
8253             return this;
8254         },
8255
8256         /**
8257          * Move this element relative to its current position.
8258          * @param {String} direction Possible values are: "l","left" - "r","right" - "t","top","up" - "b","bottom","down".
8259          * @param {Number} distance How far to move the element in pixels
8260          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8261          * @return {Roo.Element} this
8262          */
8263          move : function(direction, distance, animate){
8264             var xy = this.getXY();
8265             direction = direction.toLowerCase();
8266             switch(direction){
8267                 case "l":
8268                 case "left":
8269                     this.moveTo(xy[0]-distance, xy[1], this.preanim(arguments, 2));
8270                     break;
8271                case "r":
8272                case "right":
8273                     this.moveTo(xy[0]+distance, xy[1], this.preanim(arguments, 2));
8274                     break;
8275                case "t":
8276                case "top":
8277                case "up":
8278                     this.moveTo(xy[0], xy[1]-distance, this.preanim(arguments, 2));
8279                     break;
8280                case "b":
8281                case "bottom":
8282                case "down":
8283                     this.moveTo(xy[0], xy[1]+distance, this.preanim(arguments, 2));
8284                     break;
8285             }
8286             return this;
8287         },
8288
8289         /**
8290          *  Store the current overflow setting and clip overflow on the element - use {@link #unclip} to remove
8291          * @return {Roo.Element} this
8292          */
8293         clip : function(){
8294             if(!this.isClipped){
8295                this.isClipped = true;
8296                this.originalClip = {
8297                    "o": this.getStyle("overflow"),
8298                    "x": this.getStyle("overflow-x"),
8299                    "y": this.getStyle("overflow-y")
8300                };
8301                this.setStyle("overflow", "hidden");
8302                this.setStyle("overflow-x", "hidden");
8303                this.setStyle("overflow-y", "hidden");
8304             }
8305             return this;
8306         },
8307
8308         /**
8309          *  Return clipping (overflow) to original clipping before clip() was called
8310          * @return {Roo.Element} this
8311          */
8312         unclip : function(){
8313             if(this.isClipped){
8314                 this.isClipped = false;
8315                 var o = this.originalClip;
8316                 if(o.o){this.setStyle("overflow", o.o);}
8317                 if(o.x){this.setStyle("overflow-x", o.x);}
8318                 if(o.y){this.setStyle("overflow-y", o.y);}
8319             }
8320             return this;
8321         },
8322
8323
8324         /**
8325          * Gets the x,y coordinates specified by the anchor position on the element.
8326          * @param {String} anchor (optional) The specified anchor position (defaults to "c").  See {@link #alignTo} for details on supported anchor positions.
8327          * @param {Object} size (optional) An object containing the size to use for calculating anchor position
8328          *                       {width: (target width), height: (target height)} (defaults to the element's current size)
8329          * @param {Boolean} local (optional) True to get the local (element top/left-relative) anchor position instead of page coordinates
8330          * @return {Array} [x, y] An array containing the element's x and y coordinates
8331          */
8332         getAnchorXY : function(anchor, local, s){
8333             //Passing a different size is useful for pre-calculating anchors,
8334             //especially for anchored animations that change the el size.
8335
8336             var w, h, vp = false;
8337             if(!s){
8338                 var d = this.dom;
8339                 if(d == document.body || d == document){
8340                     vp = true;
8341                     w = D.getViewWidth(); h = D.getViewHeight();
8342                 }else{
8343                     w = this.getWidth(); h = this.getHeight();
8344                 }
8345             }else{
8346                 w = s.width;  h = s.height;
8347             }
8348             var x = 0, y = 0, r = Math.round;
8349             switch((anchor || "tl").toLowerCase()){
8350                 case "c":
8351                     x = r(w*.5);
8352                     y = r(h*.5);
8353                 break;
8354                 case "t":
8355                     x = r(w*.5);
8356                     y = 0;
8357                 break;
8358                 case "l":
8359                     x = 0;
8360                     y = r(h*.5);
8361                 break;
8362                 case "r":
8363                     x = w;
8364                     y = r(h*.5);
8365                 break;
8366                 case "b":
8367                     x = r(w*.5);
8368                     y = h;
8369                 break;
8370                 case "tl":
8371                     x = 0;
8372                     y = 0;
8373                 break;
8374                 case "bl":
8375                     x = 0;
8376                     y = h;
8377                 break;
8378                 case "br":
8379                     x = w;
8380                     y = h;
8381                 break;
8382                 case "tr":
8383                     x = w;
8384                     y = 0;
8385                 break;
8386             }
8387             if(local === true){
8388                 return [x, y];
8389             }
8390             if(vp){
8391                 var sc = this.getScroll();
8392                 return [x + sc.left, y + sc.top];
8393             }
8394             //Add the element's offset xy
8395             var o = this.getXY();
8396             return [x+o[0], y+o[1]];
8397         },
8398
8399         /**
8400          * Gets the x,y coordinates to align this element with another element. See {@link #alignTo} for more info on the
8401          * supported position values.
8402          * @param {String/HTMLElement/Roo.Element} element The element to align to.
8403          * @param {String} position The position to align to.
8404          * @param {Array} offsets (optional) Offset the positioning by [x, y]
8405          * @return {Array} [x, y]
8406          */
8407         getAlignToXY : function(el, p, o){
8408             el = Roo.get(el);
8409             var d = this.dom;
8410             if(!el.dom){
8411                 throw "Element.alignTo with an element that doesn't exist";
8412             }
8413             var c = false; //constrain to viewport
8414             var p1 = "", p2 = "";
8415             o = o || [0,0];
8416
8417             if(!p){
8418                 p = "tl-bl";
8419             }else if(p == "?"){
8420                 p = "tl-bl?";
8421             }else if(p.indexOf("-") == -1){
8422                 p = "tl-" + p;
8423             }
8424             p = p.toLowerCase();
8425             var m = p.match(/^([a-z]+)-([a-z]+)(\?)?$/);
8426             if(!m){
8427                throw "Element.alignTo with an invalid alignment " + p;
8428             }
8429             p1 = m[1]; p2 = m[2]; c = !!m[3];
8430
8431             //Subtract the aligned el's internal xy from the target's offset xy
8432             //plus custom offset to get the aligned el's new offset xy
8433             var a1 = this.getAnchorXY(p1, true);
8434             var a2 = el.getAnchorXY(p2, false);
8435             var x = a2[0] - a1[0] + o[0];
8436             var y = a2[1] - a1[1] + o[1];
8437             if(c){
8438                 //constrain the aligned el to viewport if necessary
8439                 var w = this.getWidth(), h = this.getHeight(), r = el.getRegion();
8440                 // 5px of margin for ie
8441                 var dw = D.getViewWidth()-5, dh = D.getViewHeight()-5;
8442
8443                 //If we are at a viewport boundary and the aligned el is anchored on a target border that is
8444                 //perpendicular to the vp border, allow the aligned el to slide on that border,
8445                 //otherwise swap the aligned el to the opposite border of the target.
8446                 var p1y = p1.charAt(0), p1x = p1.charAt(p1.length-1);
8447                var p2y = p2.charAt(0), p2x = p2.charAt(p2.length-1);
8448                var swapY = ((p1y=="t" && p2y=="b") || (p1y=="b" && p2y=="t"));
8449                var swapX = ((p1x=="r" && p2x=="l") || (p1x=="l" && p2x=="r"));
8450
8451                var doc = document;
8452                var scrollX = (doc.documentElement.scrollLeft || doc.body.scrollLeft || 0)+5;
8453                var scrollY = (doc.documentElement.scrollTop || doc.body.scrollTop || 0)+5;
8454
8455                if((x+w) > dw + scrollX){
8456                     x = swapX ? r.left-w : dw+scrollX-w;
8457                 }
8458                if(x < scrollX){
8459                    x = swapX ? r.right : scrollX;
8460                }
8461                if((y+h) > dh + scrollY){
8462                     y = swapY ? r.top-h : dh+scrollY-h;
8463                 }
8464                if (y < scrollY){
8465                    y = swapY ? r.bottom : scrollY;
8466                }
8467             }
8468             return [x,y];
8469         },
8470
8471         // private
8472         getConstrainToXY : function(){
8473             var os = {top:0, left:0, bottom:0, right: 0};
8474
8475             return function(el, local, offsets, proposedXY){
8476                 el = Roo.get(el);
8477                 offsets = offsets ? Roo.applyIf(offsets, os) : os;
8478
8479                 var vw, vh, vx = 0, vy = 0;
8480                 if(el.dom == document.body || el.dom == document){
8481                     vw = Roo.lib.Dom.getViewWidth();
8482                     vh = Roo.lib.Dom.getViewHeight();
8483                 }else{
8484                     vw = el.dom.clientWidth;
8485                     vh = el.dom.clientHeight;
8486                     if(!local){
8487                         var vxy = el.getXY();
8488                         vx = vxy[0];
8489                         vy = vxy[1];
8490                     }
8491                 }
8492
8493                 var s = el.getScroll();
8494
8495                 vx += offsets.left + s.left;
8496                 vy += offsets.top + s.top;
8497
8498                 vw -= offsets.right;
8499                 vh -= offsets.bottom;
8500
8501                 var vr = vx+vw;
8502                 var vb = vy+vh;
8503
8504                 var xy = proposedXY || (!local ? this.getXY() : [this.getLeft(true), this.getTop(true)]);
8505                 var x = xy[0], y = xy[1];
8506                 var w = this.dom.offsetWidth, h = this.dom.offsetHeight;
8507
8508                 // only move it if it needs it
8509                 var moved = false;
8510
8511                 // first validate right/bottom
8512                 if((x + w) > vr){
8513                     x = vr - w;
8514                     moved = true;
8515                 }
8516                 if((y + h) > vb){
8517                     y = vb - h;
8518                     moved = true;
8519                 }
8520                 // then make sure top/left isn't negative
8521                 if(x < vx){
8522                     x = vx;
8523                     moved = true;
8524                 }
8525                 if(y < vy){
8526                     y = vy;
8527                     moved = true;
8528                 }
8529                 return moved ? [x, y] : false;
8530             };
8531         }(),
8532
8533         // private
8534         adjustForConstraints : function(xy, parent, offsets){
8535             return this.getConstrainToXY(parent || document, false, offsets, xy) ||  xy;
8536         },
8537
8538         /**
8539          * Aligns this element with another element relative to the specified anchor points. If the other element is the
8540          * document it aligns it to the viewport.
8541          * The position parameter is optional, and can be specified in any one of the following formats:
8542          * <ul>
8543          *   <li><b>Blank</b>: Defaults to aligning the element's top-left corner to the target's bottom-left corner ("tl-bl").</li>
8544          *   <li><b>One anchor (deprecated)</b>: The passed anchor position is used as the target element's anchor point.
8545          *       The element being aligned will position its top-left corner (tl) to that point.  <i>This method has been
8546          *       deprecated in favor of the newer two anchor syntax below</i>.</li>
8547          *   <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
8548          *       element's anchor point, and the second value is used as the target's anchor point.</li>
8549          * </ul>
8550          * In addition to the anchor points, the position parameter also supports the "?" character.  If "?" is passed at the end of
8551          * the position string, the element will attempt to align as specified, but the position will be adjusted to constrain to
8552          * the viewport if necessary.  Note that the element being aligned might be swapped to align to a different position than
8553          * that specified in order to enforce the viewport constraints.
8554          * Following are all of the supported anchor positions:
8555     <pre>
8556     Value  Description
8557     -----  -----------------------------
8558     tl     The top left corner (default)
8559     t      The center of the top edge
8560     tr     The top right corner
8561     l      The center of the left edge
8562     c      In the center of the element
8563     r      The center of the right edge
8564     bl     The bottom left corner
8565     b      The center of the bottom edge
8566     br     The bottom right corner
8567     </pre>
8568     Example Usage:
8569     <pre><code>
8570     // align el to other-el using the default positioning ("tl-bl", non-constrained)
8571     el.alignTo("other-el");
8572
8573     // align the top left corner of el with the top right corner of other-el (constrained to viewport)
8574     el.alignTo("other-el", "tr?");
8575
8576     // align the bottom right corner of el with the center left edge of other-el
8577     el.alignTo("other-el", "br-l?");
8578
8579     // align the center of el with the bottom left corner of other-el and
8580     // adjust the x position by -6 pixels (and the y position by 0)
8581     el.alignTo("other-el", "c-bl", [-6, 0]);
8582     </code></pre>
8583          * @param {String/HTMLElement/Roo.Element} element The element to align to.
8584          * @param {String} position The position to align to.
8585          * @param {Array} offsets (optional) Offset the positioning by [x, y]
8586          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8587          * @return {Roo.Element} this
8588          */
8589         alignTo : function(element, position, offsets, animate){
8590             var xy = this.getAlignToXY(element, position, offsets);
8591             this.setXY(xy, this.preanim(arguments, 3));
8592             return this;
8593         },
8594
8595         /**
8596          * Anchors an element to another element and realigns it when the window is resized.
8597          * @param {String/HTMLElement/Roo.Element} element The element to align to.
8598          * @param {String} position The position to align to.
8599          * @param {Array} offsets (optional) Offset the positioning by [x, y]
8600          * @param {Boolean/Object} animate (optional) True for the default animation or a standard Element animation config object
8601          * @param {Boolean/Number} monitorScroll (optional) True to monitor body scroll and reposition. If this parameter
8602          * is a number, it is used as the buffer delay (defaults to 50ms).
8603          * @param {Function} callback The function to call after the animation finishes
8604          * @return {Roo.Element} this
8605          */
8606         anchorTo : function(el, alignment, offsets, animate, monitorScroll, callback){
8607             var action = function(){
8608                 this.alignTo(el, alignment, offsets, animate);
8609                 Roo.callback(callback, this);
8610             };
8611             Roo.EventManager.onWindowResize(action, this);
8612             var tm = typeof monitorScroll;
8613             if(tm != 'undefined'){
8614                 Roo.EventManager.on(window, 'scroll', action, this,
8615                     {buffer: tm == 'number' ? monitorScroll : 50});
8616             }
8617             action.call(this); // align immediately
8618             return this;
8619         },
8620         /**
8621          * Clears any opacity settings from this element. Required in some cases for IE.
8622          * @return {Roo.Element} this
8623          */
8624         clearOpacity : function(){
8625             if (window.ActiveXObject) {
8626                 if(typeof this.dom.style.filter == 'string' && (/alpha/i).test(this.dom.style.filter)){
8627                     this.dom.style.filter = "";
8628                 }
8629             } else {
8630                 this.dom.style.opacity = "";
8631                 this.dom.style["-moz-opacity"] = "";
8632                 this.dom.style["-khtml-opacity"] = "";
8633             }
8634             return this;
8635         },
8636
8637         /**
8638          * Hide this element - Uses display mode to determine whether to use "display" or "visibility". See {@link #setVisible}.
8639          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8640          * @return {Roo.Element} this
8641          */
8642         hide : function(animate){
8643             this.setVisible(false, this.preanim(arguments, 0));
8644             return this;
8645         },
8646
8647         /**
8648         * Show this element - Uses display mode to determine whether to use "display" or "visibility". See {@link #setVisible}.
8649         * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8650          * @return {Roo.Element} this
8651          */
8652         show : function(animate){
8653             this.setVisible(true, this.preanim(arguments, 0));
8654             return this;
8655         },
8656
8657         /**
8658          * @private Test if size has a unit, otherwise appends the default
8659          */
8660         addUnits : function(size){
8661             return Roo.Element.addUnits(size, this.defaultUnit);
8662         },
8663
8664         /**
8665          * Temporarily enables offsets (width,height,x,y) for an element with display:none, use endMeasure() when done.
8666          * @return {Roo.Element} this
8667          */
8668         beginMeasure : function(){
8669             var el = this.dom;
8670             if(el.offsetWidth || el.offsetHeight){
8671                 return this; // offsets work already
8672             }
8673             var changed = [];
8674             var p = this.dom, b = document.body; // start with this element
8675             while((!el.offsetWidth && !el.offsetHeight) && p && p.tagName && p != b){
8676                 var pe = Roo.get(p);
8677                 if(pe.getStyle('display') == 'none'){
8678                     changed.push({el: p, visibility: pe.getStyle("visibility")});
8679                     p.style.visibility = "hidden";
8680                     p.style.display = "block";
8681                 }
8682                 p = p.parentNode;
8683             }
8684             this._measureChanged = changed;
8685             return this;
8686
8687         },
8688
8689         /**
8690          * Restores displays to before beginMeasure was called
8691          * @return {Roo.Element} this
8692          */
8693         endMeasure : function(){
8694             var changed = this._measureChanged;
8695             if(changed){
8696                 for(var i = 0, len = changed.length; i < len; i++) {
8697                     var r = changed[i];
8698                     r.el.style.visibility = r.visibility;
8699                     r.el.style.display = "none";
8700                 }
8701                 this._measureChanged = null;
8702             }
8703             return this;
8704         },
8705
8706         /**
8707         * Update the innerHTML of this element, optionally searching for and processing scripts
8708         * @param {String} html The new HTML
8709         * @param {Boolean} loadScripts (optional) true to look for and process scripts
8710         * @param {Function} callback For async script loading you can be noticed when the update completes
8711         * @return {Roo.Element} this
8712          */
8713         update : function(html, loadScripts, callback){
8714             if(typeof html == "undefined"){
8715                 html = "";
8716             }
8717             if(loadScripts !== true){
8718                 this.dom.innerHTML = html;
8719                 if(typeof callback == "function"){
8720                     callback();
8721                 }
8722                 return this;
8723             }
8724             var id = Roo.id();
8725             var dom = this.dom;
8726
8727             html += '<span id="' + id + '"></span>';
8728
8729             E.onAvailable(id, function(){
8730                 var hd = document.getElementsByTagName("head")[0];
8731                 var re = /(?:<script([^>]*)?>)((\n|\r|.)*?)(?:<\/script>)/ig;
8732                 var srcRe = /\ssrc=([\'\"])(.*?)\1/i;
8733                 var typeRe = /\stype=([\'\"])(.*?)\1/i;
8734
8735                 var match;
8736                 while(match = re.exec(html)){
8737                     var attrs = match[1];
8738                     var srcMatch = attrs ? attrs.match(srcRe) : false;
8739                     if(srcMatch && srcMatch[2]){
8740                        var s = document.createElement("script");
8741                        s.src = srcMatch[2];
8742                        var typeMatch = attrs.match(typeRe);
8743                        if(typeMatch && typeMatch[2]){
8744                            s.type = typeMatch[2];
8745                        }
8746                        hd.appendChild(s);
8747                     }else if(match[2] && match[2].length > 0){
8748                         if(window.execScript) {
8749                            window.execScript(match[2]);
8750                         } else {
8751                             /**
8752                              * eval:var:id
8753                              * eval:var:dom
8754                              * eval:var:html
8755                              * 
8756                              */
8757                            window.eval(match[2]);
8758                         }
8759                     }
8760                 }
8761                 var el = document.getElementById(id);
8762                 if(el){el.parentNode.removeChild(el);}
8763                 if(typeof callback == "function"){
8764                     callback();
8765                 }
8766             });
8767             dom.innerHTML = html.replace(/(?:<script.*?>)((\n|\r|.)*?)(?:<\/script>)/ig, "");
8768             return this;
8769         },
8770
8771         /**
8772          * Direct access to the UpdateManager update() method (takes the same parameters).
8773          * @param {String/Function} url The url for this request or a function to call to get the url
8774          * @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}
8775          * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
8776          * @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.
8777          * @return {Roo.Element} this
8778          */
8779         load : function(){
8780             var um = this.getUpdateManager();
8781             um.update.apply(um, arguments);
8782             return this;
8783         },
8784
8785         /**
8786         * Gets this element's UpdateManager
8787         * @return {Roo.UpdateManager} The UpdateManager
8788         */
8789         getUpdateManager : function(){
8790             if(!this.updateManager){
8791                 this.updateManager = new Roo.UpdateManager(this);
8792             }
8793             return this.updateManager;
8794         },
8795
8796         /**
8797          * Disables text selection for this element (normalized across browsers)
8798          * @return {Roo.Element} this
8799          */
8800         unselectable : function(){
8801             this.dom.unselectable = "on";
8802             this.swallowEvent("selectstart", true);
8803             this.applyStyles("-moz-user-select:none;-khtml-user-select:none;");
8804             this.addClass("x-unselectable");
8805             return this;
8806         },
8807
8808         /**
8809         * Calculates the x, y to center this element on the screen
8810         * @return {Array} The x, y values [x, y]
8811         */
8812         getCenterXY : function(){
8813             return this.getAlignToXY(document, 'c-c');
8814         },
8815
8816         /**
8817         * Centers the Element in either the viewport, or another Element.
8818         * @param {String/HTMLElement/Roo.Element} centerIn (optional) The element in which to center the element.
8819         */
8820         center : function(centerIn){
8821             this.alignTo(centerIn || document, 'c-c');
8822             return this;
8823         },
8824
8825         /**
8826          * Tests various css rules/browsers to determine if this element uses a border box
8827          * @return {Boolean}
8828          */
8829         isBorderBox : function(){
8830             return noBoxAdjust[this.dom.tagName.toLowerCase()] || Roo.isBorderBox;
8831         },
8832
8833         /**
8834          * Return a box {x, y, width, height} that can be used to set another elements
8835          * size/location to match this element.
8836          * @param {Boolean} contentBox (optional) If true a box for the content of the element is returned.
8837          * @param {Boolean} local (optional) If true the element's left and top are returned instead of page x/y.
8838          * @return {Object} box An object in the format {x, y, width, height}
8839          */
8840         getBox : function(contentBox, local){
8841             var xy;
8842             if(!local){
8843                 xy = this.getXY();
8844             }else{
8845                 var left = parseInt(this.getStyle("left"), 10) || 0;
8846                 var top = parseInt(this.getStyle("top"), 10) || 0;
8847                 xy = [left, top];
8848             }
8849             var el = this.dom, w = el.offsetWidth, h = el.offsetHeight, bx;
8850             if(!contentBox){
8851                 bx = {x: xy[0], y: xy[1], 0: xy[0], 1: xy[1], width: w, height: h};
8852             }else{
8853                 var l = this.getBorderWidth("l")+this.getPadding("l");
8854                 var r = this.getBorderWidth("r")+this.getPadding("r");
8855                 var t = this.getBorderWidth("t")+this.getPadding("t");
8856                 var b = this.getBorderWidth("b")+this.getPadding("b");
8857                 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)};
8858             }
8859             bx.right = bx.x + bx.width;
8860             bx.bottom = bx.y + bx.height;
8861             return bx;
8862         },
8863
8864         /**
8865          * Returns the sum width of the padding and borders for the passed "sides". See getBorderWidth()
8866          for more information about the sides.
8867          * @param {String} sides
8868          * @return {Number}
8869          */
8870         getFrameWidth : function(sides, onlyContentBox){
8871             return onlyContentBox && Roo.isBorderBox ? 0 : (this.getPadding(sides) + this.getBorderWidth(sides));
8872         },
8873
8874         /**
8875          * 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.
8876          * @param {Object} box The box to fill {x, y, width, height}
8877          * @param {Boolean} adjust (optional) Whether to adjust for box-model issues automatically
8878          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8879          * @return {Roo.Element} this
8880          */
8881         setBox : function(box, adjust, animate){
8882             var w = box.width, h = box.height;
8883             if((adjust && !this.autoBoxAdjust) && !this.isBorderBox()){
8884                w -= (this.getBorderWidth("lr") + this.getPadding("lr"));
8885                h -= (this.getBorderWidth("tb") + this.getPadding("tb"));
8886             }
8887             this.setBounds(box.x, box.y, w, h, this.preanim(arguments, 2));
8888             return this;
8889         },
8890
8891         /**
8892          * Forces the browser to repaint this element
8893          * @return {Roo.Element} this
8894          */
8895          repaint : function(){
8896             var dom = this.dom;
8897             this.addClass("x-repaint");
8898             setTimeout(function(){
8899                 Roo.get(dom).removeClass("x-repaint");
8900             }, 1);
8901             return this;
8902         },
8903
8904         /**
8905          * Returns an object with properties top, left, right and bottom representing the margins of this element unless sides is passed,
8906          * then it returns the calculated width of the sides (see getPadding)
8907          * @param {String} sides (optional) Any combination of l, r, t, b to get the sum of those sides
8908          * @return {Object/Number}
8909          */
8910         getMargins : function(side){
8911             if(!side){
8912                 return {
8913                     top: parseInt(this.getStyle("margin-top"), 10) || 0,
8914                     left: parseInt(this.getStyle("margin-left"), 10) || 0,
8915                     bottom: parseInt(this.getStyle("margin-bottom"), 10) || 0,
8916                     right: parseInt(this.getStyle("margin-right"), 10) || 0
8917                 };
8918             }else{
8919                 return this.addStyles(side, El.margins);
8920              }
8921         },
8922
8923         // private
8924         addStyles : function(sides, styles){
8925             var val = 0, v, w;
8926             for(var i = 0, len = sides.length; i < len; i++){
8927                 v = this.getStyle(styles[sides.charAt(i)]);
8928                 if(v){
8929                      w = parseInt(v, 10);
8930                      if(w){ val += w; }
8931                 }
8932             }
8933             return val;
8934         },
8935
8936         /**
8937          * Creates a proxy element of this element
8938          * @param {String/Object} config The class name of the proxy element or a DomHelper config object
8939          * @param {String/HTMLElement} renderTo (optional) The element or element id to render the proxy to (defaults to document.body)
8940          * @param {Boolean} matchBox (optional) True to align and size the proxy to this element now (defaults to false)
8941          * @return {Roo.Element} The new proxy element
8942          */
8943         createProxy : function(config, renderTo, matchBox){
8944             if(renderTo){
8945                 renderTo = Roo.getDom(renderTo);
8946             }else{
8947                 renderTo = document.body;
8948             }
8949             config = typeof config == "object" ?
8950                 config : {tag : "div", cls: config};
8951             var proxy = Roo.DomHelper.append(renderTo, config, true);
8952             if(matchBox){
8953                proxy.setBox(this.getBox());
8954             }
8955             return proxy;
8956         },
8957
8958         /**
8959          * Puts a mask over this element to disable user interaction. Requires core.css.
8960          * This method can only be applied to elements which accept child nodes.
8961          * @param {String} msg (optional) A message to display in the mask
8962          * @param {String} msgCls (optional) A css class to apply to the msg element
8963          * @return {Element} The mask  element
8964          */
8965         mask : function(msg, msgCls)
8966         {
8967             if(this.getStyle("position") == "static"){
8968                 this.setStyle("position", "relative");
8969             }
8970             if(!this._mask){
8971                 this._mask = Roo.DomHelper.append(this.dom, {cls:"roo-el-mask"}, true);
8972             }
8973             this.addClass("x-masked");
8974             this._mask.setDisplayed(true);
8975             
8976             // we wander
8977             var z = 0;
8978             var dom = this.dom
8979             while (dom && dom.style) {
8980                 if (!isNaN(parseInt(dom.style.zIndex))) {
8981                     z = Math.max(z, parseInt(dom.style.zIndex));
8982                 }
8983                 dom = dom.parentNode;
8984             }
8985             // if we are masking the body - then it hides everything..
8986             if (this.dom == document.body) {
8987                 z = 1000000;
8988                 this._mask.setWidth(Roo.lib.Dom.getDocumentWidth());
8989                 this._mask.setHeight(Roo.lib.Dom.getDocumentHeight());
8990             }
8991            
8992             if(typeof msg == 'string'){
8993                 if(!this._maskMsg){
8994                     this._maskMsg = Roo.DomHelper.append(this.dom, {cls:"roo-el-mask-msg", cn:{tag:'div'}}, true);
8995                 }
8996                 var mm = this._maskMsg;
8997                 mm.dom.className = msgCls ? "roo-el-mask-msg " + msgCls : "roo-el-mask-msg";
8998                 mm.dom.firstChild.innerHTML = msg;
8999                 mm.setDisplayed(true);
9000                 mm.center(this);
9001                 mm.setStyle('z-index', z + 102);
9002             }
9003             if(Roo.isIE && !(Roo.isIE7 && Roo.isStrict) && this.getStyle('height') == 'auto'){ // ie will not expand full height automatically
9004                 this._mask.setHeight(this.getHeight());
9005             }
9006             this._mask.setStyle('z-index', z + 100);
9007             
9008             return this._mask;
9009         },
9010
9011         /**
9012          * Removes a previously applied mask. If removeEl is true the mask overlay is destroyed, otherwise
9013          * it is cached for reuse.
9014          */
9015         unmask : function(removeEl){
9016             if(this._mask){
9017                 if(removeEl === true){
9018                     this._mask.remove();
9019                     delete this._mask;
9020                     if(this._maskMsg){
9021                         this._maskMsg.remove();
9022                         delete this._maskMsg;
9023                     }
9024                 }else{
9025                     this._mask.setDisplayed(false);
9026                     if(this._maskMsg){
9027                         this._maskMsg.setDisplayed(false);
9028                     }
9029                 }
9030             }
9031             this.removeClass("x-masked");
9032         },
9033
9034         /**
9035          * Returns true if this element is masked
9036          * @return {Boolean}
9037          */
9038         isMasked : function(){
9039             return this._mask && this._mask.isVisible();
9040         },
9041
9042         /**
9043          * Creates an iframe shim for this element to keep selects and other windowed objects from
9044          * showing through.
9045          * @return {Roo.Element} The new shim element
9046          */
9047         createShim : function(){
9048             var el = document.createElement('iframe');
9049             el.frameBorder = 'no';
9050             el.className = 'roo-shim';
9051             if(Roo.isIE && Roo.isSecure){
9052                 el.src = Roo.SSL_SECURE_URL;
9053             }
9054             var shim = Roo.get(this.dom.parentNode.insertBefore(el, this.dom));
9055             shim.autoBoxAdjust = false;
9056             return shim;
9057         },
9058
9059         /**
9060          * Removes this element from the DOM and deletes it from the cache
9061          */
9062         remove : function(){
9063             if(this.dom.parentNode){
9064                 this.dom.parentNode.removeChild(this.dom);
9065             }
9066             delete El.cache[this.dom.id];
9067         },
9068
9069         /**
9070          * Sets up event handlers to add and remove a css class when the mouse is over this element
9071          * @param {String} className
9072          * @param {Boolean} preventFlicker (optional) If set to true, it prevents flickering by filtering
9073          * mouseout events for children elements
9074          * @return {Roo.Element} this
9075          */
9076         addClassOnOver : function(className, preventFlicker){
9077             this.on("mouseover", function(){
9078                 Roo.fly(this, '_internal').addClass(className);
9079             }, this.dom);
9080             var removeFn = function(e){
9081                 if(preventFlicker !== true || !e.within(this, true)){
9082                     Roo.fly(this, '_internal').removeClass(className);
9083                 }
9084             };
9085             this.on("mouseout", removeFn, this.dom);
9086             return this;
9087         },
9088
9089         /**
9090          * Sets up event handlers to add and remove a css class when this element has the focus
9091          * @param {String} className
9092          * @return {Roo.Element} this
9093          */
9094         addClassOnFocus : function(className){
9095             this.on("focus", function(){
9096                 Roo.fly(this, '_internal').addClass(className);
9097             }, this.dom);
9098             this.on("blur", function(){
9099                 Roo.fly(this, '_internal').removeClass(className);
9100             }, this.dom);
9101             return this;
9102         },
9103         /**
9104          * 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)
9105          * @param {String} className
9106          * @return {Roo.Element} this
9107          */
9108         addClassOnClick : function(className){
9109             var dom = this.dom;
9110             this.on("mousedown", function(){
9111                 Roo.fly(dom, '_internal').addClass(className);
9112                 var d = Roo.get(document);
9113                 var fn = function(){
9114                     Roo.fly(dom, '_internal').removeClass(className);
9115                     d.removeListener("mouseup", fn);
9116                 };
9117                 d.on("mouseup", fn);
9118             });
9119             return this;
9120         },
9121
9122         /**
9123          * Stops the specified event from bubbling and optionally prevents the default action
9124          * @param {String} eventName
9125          * @param {Boolean} preventDefault (optional) true to prevent the default action too
9126          * @return {Roo.Element} this
9127          */
9128         swallowEvent : function(eventName, preventDefault){
9129             var fn = function(e){
9130                 e.stopPropagation();
9131                 if(preventDefault){
9132                     e.preventDefault();
9133                 }
9134             };
9135             if(eventName instanceof Array){
9136                 for(var i = 0, len = eventName.length; i < len; i++){
9137                      this.on(eventName[i], fn);
9138                 }
9139                 return this;
9140             }
9141             this.on(eventName, fn);
9142             return this;
9143         },
9144
9145         /**
9146          * @private
9147          */
9148       fitToParentDelegate : Roo.emptyFn, // keep a reference to the fitToParent delegate
9149
9150         /**
9151          * Sizes this element to its parent element's dimensions performing
9152          * neccessary box adjustments.
9153          * @param {Boolean} monitorResize (optional) If true maintains the fit when the browser window is resized.
9154          * @param {String/HTMLElment/Element} targetParent (optional) The target parent, default to the parentNode.
9155          * @return {Roo.Element} this
9156          */
9157         fitToParent : function(monitorResize, targetParent) {
9158           Roo.EventManager.removeResizeListener(this.fitToParentDelegate); // always remove previous fitToParent delegate from onWindowResize
9159           this.fitToParentDelegate = Roo.emptyFn; // remove reference to previous delegate
9160           if (monitorResize === true && !this.dom.parentNode) { // check if this Element still exists
9161             return;
9162           }
9163           var p = Roo.get(targetParent || this.dom.parentNode);
9164           this.setSize(p.getComputedWidth() - p.getFrameWidth('lr'), p.getComputedHeight() - p.getFrameWidth('tb'));
9165           if (monitorResize === true) {
9166             this.fitToParentDelegate = this.fitToParent.createDelegate(this, [true, targetParent]);
9167             Roo.EventManager.onWindowResize(this.fitToParentDelegate);
9168           }
9169           return this;
9170         },
9171
9172         /**
9173          * Gets the next sibling, skipping text nodes
9174          * @return {HTMLElement} The next sibling or null
9175          */
9176         getNextSibling : function(){
9177             var n = this.dom.nextSibling;
9178             while(n && n.nodeType != 1){
9179                 n = n.nextSibling;
9180             }
9181             return n;
9182         },
9183
9184         /**
9185          * Gets the previous sibling, skipping text nodes
9186          * @return {HTMLElement} The previous sibling or null
9187          */
9188         getPrevSibling : function(){
9189             var n = this.dom.previousSibling;
9190             while(n && n.nodeType != 1){
9191                 n = n.previousSibling;
9192             }
9193             return n;
9194         },
9195
9196
9197         /**
9198          * Appends the passed element(s) to this element
9199          * @param {String/HTMLElement/Array/Element/CompositeElement} el
9200          * @return {Roo.Element} this
9201          */
9202         appendChild: function(el){
9203             el = Roo.get(el);
9204             el.appendTo(this);
9205             return this;
9206         },
9207
9208         /**
9209          * Creates the passed DomHelper config and appends it to this element or optionally inserts it before the passed child element.
9210          * @param {Object} config DomHelper element config object.  If no tag is specified (e.g., {tag:'input'}) then a div will be
9211          * automatically generated with the specified attributes.
9212          * @param {HTMLElement} insertBefore (optional) a child element of this element
9213          * @param {Boolean} returnDom (optional) true to return the dom node instead of creating an Element
9214          * @return {Roo.Element} The new child element
9215          */
9216         createChild: function(config, insertBefore, returnDom){
9217             config = config || {tag:'div'};
9218             if(insertBefore){
9219                 return Roo.DomHelper.insertBefore(insertBefore, config, returnDom !== true);
9220             }
9221             return Roo.DomHelper[!this.dom.firstChild ? 'overwrite' : 'append'](this.dom, config,  returnDom !== true);
9222         },
9223
9224         /**
9225          * Appends this element to the passed element
9226          * @param {String/HTMLElement/Element} el The new parent element
9227          * @return {Roo.Element} this
9228          */
9229         appendTo: function(el){
9230             el = Roo.getDom(el);
9231             el.appendChild(this.dom);
9232             return this;
9233         },
9234
9235         /**
9236          * Inserts this element before the passed element in the DOM
9237          * @param {String/HTMLElement/Element} el The element to insert before
9238          * @return {Roo.Element} this
9239          */
9240         insertBefore: function(el){
9241             el = Roo.getDom(el);
9242             el.parentNode.insertBefore(this.dom, el);
9243             return this;
9244         },
9245
9246         /**
9247          * Inserts this element after the passed element in the DOM
9248          * @param {String/HTMLElement/Element} el The element to insert after
9249          * @return {Roo.Element} this
9250          */
9251         insertAfter: function(el){
9252             el = Roo.getDom(el);
9253             el.parentNode.insertBefore(this.dom, el.nextSibling);
9254             return this;
9255         },
9256
9257         /**
9258          * Inserts (or creates) an element (or DomHelper config) as the first child of the this element
9259          * @param {String/HTMLElement/Element/Object} el The id or element to insert or a DomHelper config to create and insert
9260          * @return {Roo.Element} The new child
9261          */
9262         insertFirst: function(el, returnDom){
9263             el = el || {};
9264             if(typeof el == 'object' && !el.nodeType){ // dh config
9265                 return this.createChild(el, this.dom.firstChild, returnDom);
9266             }else{
9267                 el = Roo.getDom(el);
9268                 this.dom.insertBefore(el, this.dom.firstChild);
9269                 return !returnDom ? Roo.get(el) : el;
9270             }
9271         },
9272
9273         /**
9274          * Inserts (or creates) the passed element (or DomHelper config) as a sibling of this element
9275          * @param {String/HTMLElement/Element/Object} el The id or element to insert or a DomHelper config to create and insert
9276          * @param {String} where (optional) 'before' or 'after' defaults to before
9277          * @param {Boolean} returnDom (optional) True to return the raw DOM element instead of Roo.Element
9278          * @return {Roo.Element} the inserted Element
9279          */
9280         insertSibling: function(el, where, returnDom){
9281             where = where ? where.toLowerCase() : 'before';
9282             el = el || {};
9283             var rt, refNode = where == 'before' ? this.dom : this.dom.nextSibling;
9284
9285             if(typeof el == 'object' && !el.nodeType){ // dh config
9286                 if(where == 'after' && !this.dom.nextSibling){
9287                     rt = Roo.DomHelper.append(this.dom.parentNode, el, !returnDom);
9288                 }else{
9289                     rt = Roo.DomHelper[where == 'after' ? 'insertAfter' : 'insertBefore'](this.dom, el, !returnDom);
9290                 }
9291
9292             }else{
9293                 rt = this.dom.parentNode.insertBefore(Roo.getDom(el),
9294                             where == 'before' ? this.dom : this.dom.nextSibling);
9295                 if(!returnDom){
9296                     rt = Roo.get(rt);
9297                 }
9298             }
9299             return rt;
9300         },
9301
9302         /**
9303          * Creates and wraps this element with another element
9304          * @param {Object} config (optional) DomHelper element config object for the wrapper element or null for an empty div
9305          * @param {Boolean} returnDom (optional) True to return the raw DOM element instead of Roo.Element
9306          * @return {HTMLElement/Element} The newly created wrapper element
9307          */
9308         wrap: function(config, returnDom){
9309             if(!config){
9310                 config = {tag: "div"};
9311             }
9312             var newEl = Roo.DomHelper.insertBefore(this.dom, config, !returnDom);
9313             newEl.dom ? newEl.dom.appendChild(this.dom) : newEl.appendChild(this.dom);
9314             return newEl;
9315         },
9316
9317         /**
9318          * Replaces the passed element with this element
9319          * @param {String/HTMLElement/Element} el The element to replace
9320          * @return {Roo.Element} this
9321          */
9322         replace: function(el){
9323             el = Roo.get(el);
9324             this.insertBefore(el);
9325             el.remove();
9326             return this;
9327         },
9328
9329         /**
9330          * Inserts an html fragment into this element
9331          * @param {String} where Where to insert the html in relation to the this element - beforeBegin, afterBegin, beforeEnd, afterEnd.
9332          * @param {String} html The HTML fragment
9333          * @param {Boolean} returnEl True to return an Roo.Element
9334          * @return {HTMLElement/Roo.Element} The inserted node (or nearest related if more than 1 inserted)
9335          */
9336         insertHtml : function(where, html, returnEl){
9337             var el = Roo.DomHelper.insertHtml(where, this.dom, html);
9338             return returnEl ? Roo.get(el) : el;
9339         },
9340
9341         /**
9342          * Sets the passed attributes as attributes of this element (a style attribute can be a string, object or function)
9343          * @param {Object} o The object with the attributes
9344          * @param {Boolean} useSet (optional) false to override the default setAttribute to use expandos.
9345          * @return {Roo.Element} this
9346          */
9347         set : function(o, useSet){
9348             var el = this.dom;
9349             useSet = typeof useSet == 'undefined' ? (el.setAttribute ? true : false) : useSet;
9350             for(var attr in o){
9351                 if(attr == "style" || typeof o[attr] == "function") continue;
9352                 if(attr=="cls"){
9353                     el.className = o["cls"];
9354                 }else{
9355                     if(useSet) el.setAttribute(attr, o[attr]);
9356                     else el[attr] = o[attr];
9357                 }
9358             }
9359             if(o.style){
9360                 Roo.DomHelper.applyStyles(el, o.style);
9361             }
9362             return this;
9363         },
9364
9365         /**
9366          * Convenience method for constructing a KeyMap
9367          * @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:
9368          *                                  {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
9369          * @param {Function} fn The function to call
9370          * @param {Object} scope (optional) The scope of the function
9371          * @return {Roo.KeyMap} The KeyMap created
9372          */
9373         addKeyListener : function(key, fn, scope){
9374             var config;
9375             if(typeof key != "object" || key instanceof Array){
9376                 config = {
9377                     key: key,
9378                     fn: fn,
9379                     scope: scope
9380                 };
9381             }else{
9382                 config = {
9383                     key : key.key,
9384                     shift : key.shift,
9385                     ctrl : key.ctrl,
9386                     alt : key.alt,
9387                     fn: fn,
9388                     scope: scope
9389                 };
9390             }
9391             return new Roo.KeyMap(this, config);
9392         },
9393
9394         /**
9395          * Creates a KeyMap for this element
9396          * @param {Object} config The KeyMap config. See {@link Roo.KeyMap} for more details
9397          * @return {Roo.KeyMap} The KeyMap created
9398          */
9399         addKeyMap : function(config){
9400             return new Roo.KeyMap(this, config);
9401         },
9402
9403         /**
9404          * Returns true if this element is scrollable.
9405          * @return {Boolean}
9406          */
9407          isScrollable : function(){
9408             var dom = this.dom;
9409             return dom.scrollHeight > dom.clientHeight || dom.scrollWidth > dom.clientWidth;
9410         },
9411
9412         /**
9413          * 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().
9414          * @param {String} side Either "left" for scrollLeft values or "top" for scrollTop values.
9415          * @param {Number} value The new scroll value
9416          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9417          * @return {Element} this
9418          */
9419
9420         scrollTo : function(side, value, animate){
9421             var prop = side.toLowerCase() == "left" ? "scrollLeft" : "scrollTop";
9422             if(!animate || !A){
9423                 this.dom[prop] = value;
9424             }else{
9425                 var to = prop == "scrollLeft" ? [value, this.dom.scrollTop] : [this.dom.scrollLeft, value];
9426                 this.anim({scroll: {"to": to}}, this.preanim(arguments, 2), 'scroll');
9427             }
9428             return this;
9429         },
9430
9431         /**
9432          * Scrolls this element the specified direction. Does bounds checking to make sure the scroll is
9433          * within this element's scrollable range.
9434          * @param {String} direction Possible values are: "l","left" - "r","right" - "t","top","up" - "b","bottom","down".
9435          * @param {Number} distance How far to scroll the element in pixels
9436          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9437          * @return {Boolean} Returns true if a scroll was triggered or false if the element
9438          * was scrolled as far as it could go.
9439          */
9440          scroll : function(direction, distance, animate){
9441              if(!this.isScrollable()){
9442                  return;
9443              }
9444              var el = this.dom;
9445              var l = el.scrollLeft, t = el.scrollTop;
9446              var w = el.scrollWidth, h = el.scrollHeight;
9447              var cw = el.clientWidth, ch = el.clientHeight;
9448              direction = direction.toLowerCase();
9449              var scrolled = false;
9450              var a = this.preanim(arguments, 2);
9451              switch(direction){
9452                  case "l":
9453                  case "left":
9454                      if(w - l > cw){
9455                          var v = Math.min(l + distance, w-cw);
9456                          this.scrollTo("left", v, a);
9457                          scrolled = true;
9458                      }
9459                      break;
9460                 case "r":
9461                 case "right":
9462                      if(l > 0){
9463                          var v = Math.max(l - distance, 0);
9464                          this.scrollTo("left", v, a);
9465                          scrolled = true;
9466                      }
9467                      break;
9468                 case "t":
9469                 case "top":
9470                 case "up":
9471                      if(t > 0){
9472                          var v = Math.max(t - distance, 0);
9473                          this.scrollTo("top", v, a);
9474                          scrolled = true;
9475                      }
9476                      break;
9477                 case "b":
9478                 case "bottom":
9479                 case "down":
9480                      if(h - t > ch){
9481                          var v = Math.min(t + distance, h-ch);
9482                          this.scrollTo("top", v, a);
9483                          scrolled = true;
9484                      }
9485                      break;
9486              }
9487              return scrolled;
9488         },
9489
9490         /**
9491          * Translates the passed page coordinates into left/top css values for this element
9492          * @param {Number/Array} x The page x or an array containing [x, y]
9493          * @param {Number} y The page y
9494          * @return {Object} An object with left and top properties. e.g. {left: (value), top: (value)}
9495          */
9496         translatePoints : function(x, y){
9497             if(typeof x == 'object' || x instanceof Array){
9498                 y = x[1]; x = x[0];
9499             }
9500             var p = this.getStyle('position');
9501             var o = this.getXY();
9502
9503             var l = parseInt(this.getStyle('left'), 10);
9504             var t = parseInt(this.getStyle('top'), 10);
9505
9506             if(isNaN(l)){
9507                 l = (p == "relative") ? 0 : this.dom.offsetLeft;
9508             }
9509             if(isNaN(t)){
9510                 t = (p == "relative") ? 0 : this.dom.offsetTop;
9511             }
9512
9513             return {left: (x - o[0] + l), top: (y - o[1] + t)};
9514         },
9515
9516         /**
9517          * Returns the current scroll position of the element.
9518          * @return {Object} An object containing the scroll position in the format {left: (scrollLeft), top: (scrollTop)}
9519          */
9520         getScroll : function(){
9521             var d = this.dom, doc = document;
9522             if(d == doc || d == doc.body){
9523                 var l = window.pageXOffset || doc.documentElement.scrollLeft || doc.body.scrollLeft || 0;
9524                 var t = window.pageYOffset || doc.documentElement.scrollTop || doc.body.scrollTop || 0;
9525                 return {left: l, top: t};
9526             }else{
9527                 return {left: d.scrollLeft, top: d.scrollTop};
9528             }
9529         },
9530
9531         /**
9532          * Return the CSS color for the specified CSS attribute. rgb, 3 digit (like #fff) and valid values
9533          * are convert to standard 6 digit hex color.
9534          * @param {String} attr The css attribute
9535          * @param {String} defaultValue The default value to use when a valid color isn't found
9536          * @param {String} prefix (optional) defaults to #. Use an empty string when working with
9537          * YUI color anims.
9538          */
9539         getColor : function(attr, defaultValue, prefix){
9540             var v = this.getStyle(attr);
9541             if(!v || v == "transparent" || v == "inherit") {
9542                 return defaultValue;
9543             }
9544             var color = typeof prefix == "undefined" ? "#" : prefix;
9545             if(v.substr(0, 4) == "rgb("){
9546                 var rvs = v.slice(4, v.length -1).split(",");
9547                 for(var i = 0; i < 3; i++){
9548                     var h = parseInt(rvs[i]).toString(16);
9549                     if(h < 16){
9550                         h = "0" + h;
9551                     }
9552                     color += h;
9553                 }
9554             } else {
9555                 if(v.substr(0, 1) == "#"){
9556                     if(v.length == 4) {
9557                         for(var i = 1; i < 4; i++){
9558                             var c = v.charAt(i);
9559                             color +=  c + c;
9560                         }
9561                     }else if(v.length == 7){
9562                         color += v.substr(1);
9563                     }
9564                 }
9565             }
9566             return(color.length > 5 ? color.toLowerCase() : defaultValue);
9567         },
9568
9569         /**
9570          * Wraps the specified element with a special markup/CSS block that renders by default as a gray container with a
9571          * gradient background, rounded corners and a 4-way shadow.
9572          * @param {String} class (optional) A base CSS class to apply to the containing wrapper element (defaults to 'x-box').
9573          * Note that there are a number of CSS rules that are dependent on this name to make the overall effect work,
9574          * so if you supply an alternate base class, make sure you also supply all of the necessary rules.
9575          * @return {Roo.Element} this
9576          */
9577         boxWrap : function(cls){
9578             cls = cls || 'x-box';
9579             var el = Roo.get(this.insertHtml('beforeBegin', String.format('<div class="{0}">'+El.boxMarkup+'</div>', cls)));
9580             el.child('.'+cls+'-mc').dom.appendChild(this.dom);
9581             return el;
9582         },
9583
9584         /**
9585          * Returns the value of a namespaced attribute from the element's underlying DOM node.
9586          * @param {String} namespace The namespace in which to look for the attribute
9587          * @param {String} name The attribute name
9588          * @return {String} The attribute value
9589          */
9590         getAttributeNS : Roo.isIE ? function(ns, name){
9591             var d = this.dom;
9592             var type = typeof d[ns+":"+name];
9593             if(type != 'undefined' && type != 'unknown'){
9594                 return d[ns+":"+name];
9595             }
9596             return d[name];
9597         } : function(ns, name){
9598             var d = this.dom;
9599             return d.getAttributeNS(ns, name) || d.getAttribute(ns+":"+name) || d.getAttribute(name) || d[name];
9600         },
9601         
9602         
9603         /**
9604          * Sets or Returns the value the dom attribute value
9605          * @param {String|Object} name The attribute name (or object to set multiple attributes)
9606          * @param {String} value (optional) The value to set the attribute to
9607          * @return {String} The attribute value
9608          */
9609         attr : function(name){
9610             if (arguments.length > 1) {
9611                 this.dom.setAttribute(name, arguments[1]);
9612                 return arguments[1];
9613             }
9614             if (typeof(name) == 'object') {
9615                 for(var i in name) {
9616                     this.attr(i, name[i]);
9617                 }
9618                 return name;
9619             }
9620             
9621             
9622             if (!this.dom.hasAttribute(name)) {
9623                 return undefined;
9624             }
9625             return this.dom.getAttribute(name);
9626         }
9627         
9628         
9629         
9630     };
9631
9632     var ep = El.prototype;
9633
9634     /**
9635      * Appends an event handler (Shorthand for addListener)
9636      * @param {String}   eventName     The type of event to append
9637      * @param {Function} fn        The method the event invokes
9638      * @param {Object} scope       (optional) The scope (this object) of the fn
9639      * @param {Object}   options   (optional)An object with standard {@link Roo.EventManager#addListener} options
9640      * @method
9641      */
9642     ep.on = ep.addListener;
9643         // backwards compat
9644     ep.mon = ep.addListener;
9645
9646     /**
9647      * Removes an event handler from this element (shorthand for removeListener)
9648      * @param {String} eventName the type of event to remove
9649      * @param {Function} fn the method the event invokes
9650      * @return {Roo.Element} this
9651      * @method
9652      */
9653     ep.un = ep.removeListener;
9654
9655     /**
9656      * true to automatically adjust width and height settings for box-model issues (default to true)
9657      */
9658     ep.autoBoxAdjust = true;
9659
9660     // private
9661     El.unitPattern = /\d+(px|em|%|en|ex|pt|in|cm|mm|pc)$/i;
9662
9663     // private
9664     El.addUnits = function(v, defaultUnit){
9665         if(v === "" || v == "auto"){
9666             return v;
9667         }
9668         if(v === undefined){
9669             return '';
9670         }
9671         if(typeof v == "number" || !El.unitPattern.test(v)){
9672             return v + (defaultUnit || 'px');
9673         }
9674         return v;
9675     };
9676
9677     // special markup used throughout Roo when box wrapping elements
9678     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>';
9679     /**
9680      * Visibility mode constant - Use visibility to hide element
9681      * @static
9682      * @type Number
9683      */
9684     El.VISIBILITY = 1;
9685     /**
9686      * Visibility mode constant - Use display to hide element
9687      * @static
9688      * @type Number
9689      */
9690     El.DISPLAY = 2;
9691
9692     El.borders = {l: "border-left-width", r: "border-right-width", t: "border-top-width", b: "border-bottom-width"};
9693     El.paddings = {l: "padding-left", r: "padding-right", t: "padding-top", b: "padding-bottom"};
9694     El.margins = {l: "margin-left", r: "margin-right", t: "margin-top", b: "margin-bottom"};
9695
9696
9697
9698     /**
9699      * @private
9700      */
9701     El.cache = {};
9702
9703     var docEl;
9704
9705     /**
9706      * Static method to retrieve Element objects. Uses simple caching to consistently return the same object.
9707      * Automatically fixes if an object was recreated with the same id via AJAX or DOM.
9708      * @param {String/HTMLElement/Element} el The id of the node, a DOM Node or an existing Element.
9709      * @return {Element} The Element object
9710      * @static
9711      */
9712     El.get = function(el){
9713         var ex, elm, id;
9714         if(!el){ return null; }
9715         if(typeof el == "string"){ // element id
9716             if(!(elm = document.getElementById(el))){
9717                 return null;
9718             }
9719             if(ex = El.cache[el]){
9720                 ex.dom = elm;
9721             }else{
9722                 ex = El.cache[el] = new El(elm);
9723             }
9724             return ex;
9725         }else if(el.tagName){ // dom element
9726             if(!(id = el.id)){
9727                 id = Roo.id(el);
9728             }
9729             if(ex = El.cache[id]){
9730                 ex.dom = el;
9731             }else{
9732                 ex = El.cache[id] = new El(el);
9733             }
9734             return ex;
9735         }else if(el instanceof El){
9736             if(el != docEl){
9737                 el.dom = document.getElementById(el.id) || el.dom; // refresh dom element in case no longer valid,
9738                                                               // catch case where it hasn't been appended
9739                 El.cache[el.id] = el; // in case it was created directly with Element(), let's cache it
9740             }
9741             return el;
9742         }else if(el.isComposite){
9743             return el;
9744         }else if(el instanceof Array){
9745             return El.select(el);
9746         }else if(el == document){
9747             // create a bogus element object representing the document object
9748             if(!docEl){
9749                 var f = function(){};
9750                 f.prototype = El.prototype;
9751                 docEl = new f();
9752                 docEl.dom = document;
9753             }
9754             return docEl;
9755         }
9756         return null;
9757     };
9758
9759     // private
9760     El.uncache = function(el){
9761         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
9762             if(a[i]){
9763                 delete El.cache[a[i].id || a[i]];
9764             }
9765         }
9766     };
9767
9768     // private
9769     // Garbage collection - uncache elements/purge listeners on orphaned elements
9770     // so we don't hold a reference and cause the browser to retain them
9771     El.garbageCollect = function(){
9772         if(!Roo.enableGarbageCollector){
9773             clearInterval(El.collectorThread);
9774             return;
9775         }
9776         for(var eid in El.cache){
9777             var el = El.cache[eid], d = el.dom;
9778             // -------------------------------------------------------
9779             // Determining what is garbage:
9780             // -------------------------------------------------------
9781             // !d
9782             // dom node is null, definitely garbage
9783             // -------------------------------------------------------
9784             // !d.parentNode
9785             // no parentNode == direct orphan, definitely garbage
9786             // -------------------------------------------------------
9787             // !d.offsetParent && !document.getElementById(eid)
9788             // display none elements have no offsetParent so we will
9789             // also try to look it up by it's id. However, check
9790             // offsetParent first so we don't do unneeded lookups.
9791             // This enables collection of elements that are not orphans
9792             // directly, but somewhere up the line they have an orphan
9793             // parent.
9794             // -------------------------------------------------------
9795             if(!d || !d.parentNode || (!d.offsetParent && !document.getElementById(eid))){
9796                 delete El.cache[eid];
9797                 if(d && Roo.enableListenerCollection){
9798                     E.purgeElement(d);
9799                 }
9800             }
9801         }
9802     }
9803     El.collectorThreadId = setInterval(El.garbageCollect, 30000);
9804
9805
9806     // dom is optional
9807     El.Flyweight = function(dom){
9808         this.dom = dom;
9809     };
9810     El.Flyweight.prototype = El.prototype;
9811
9812     El._flyweights = {};
9813     /**
9814      * Gets the globally shared flyweight Element, with the passed node as the active element. Do not store a reference to this element -
9815      * the dom node can be overwritten by other code.
9816      * @param {String/HTMLElement} el The dom node or id
9817      * @param {String} named (optional) Allows for creation of named reusable flyweights to
9818      *                                  prevent conflicts (e.g. internally Roo uses "_internal")
9819      * @static
9820      * @return {Element} The shared Element object
9821      */
9822     El.fly = function(el, named){
9823         named = named || '_global';
9824         el = Roo.getDom(el);
9825         if(!el){
9826             return null;
9827         }
9828         if(!El._flyweights[named]){
9829             El._flyweights[named] = new El.Flyweight();
9830         }
9831         El._flyweights[named].dom = el;
9832         return El._flyweights[named];
9833     };
9834
9835     /**
9836      * Static method to retrieve Element objects. Uses simple caching to consistently return the same object.
9837      * Automatically fixes if an object was recreated with the same id via AJAX or DOM.
9838      * Shorthand of {@link Roo.Element#get}
9839      * @param {String/HTMLElement/Element} el The id of the node, a DOM Node or an existing Element.
9840      * @return {Element} The Element object
9841      * @member Roo
9842      * @method get
9843      */
9844     Roo.get = El.get;
9845     /**
9846      * Gets the globally shared flyweight Element, with the passed node as the active element. Do not store a reference to this element -
9847      * the dom node can be overwritten by other code.
9848      * Shorthand of {@link Roo.Element#fly}
9849      * @param {String/HTMLElement} el The dom node or id
9850      * @param {String} named (optional) Allows for creation of named reusable flyweights to
9851      *                                  prevent conflicts (e.g. internally Roo uses "_internal")
9852      * @static
9853      * @return {Element} The shared Element object
9854      * @member Roo
9855      * @method fly
9856      */
9857     Roo.fly = El.fly;
9858
9859     // speedy lookup for elements never to box adjust
9860     var noBoxAdjust = Roo.isStrict ? {
9861         select:1
9862     } : {
9863         input:1, select:1, textarea:1
9864     };
9865     if(Roo.isIE || Roo.isGecko){
9866         noBoxAdjust['button'] = 1;
9867     }
9868
9869
9870     Roo.EventManager.on(window, 'unload', function(){
9871         delete El.cache;
9872         delete El._flyweights;
9873     });
9874 })();
9875
9876
9877
9878
9879 if(Roo.DomQuery){
9880     Roo.Element.selectorFunction = Roo.DomQuery.select;
9881 }
9882
9883 Roo.Element.select = function(selector, unique, root){
9884     var els;
9885     if(typeof selector == "string"){
9886         els = Roo.Element.selectorFunction(selector, root);
9887     }else if(selector.length !== undefined){
9888         els = selector;
9889     }else{
9890         throw "Invalid selector";
9891     }
9892     if(unique === true){
9893         return new Roo.CompositeElement(els);
9894     }else{
9895         return new Roo.CompositeElementLite(els);
9896     }
9897 };
9898 /**
9899  * Selects elements based on the passed CSS selector to enable working on them as 1.
9900  * @param {String/Array} selector The CSS selector or an array of elements
9901  * @param {Boolean} unique (optional) true to create a unique Roo.Element for each element (defaults to a shared flyweight object)
9902  * @param {HTMLElement/String} root (optional) The root element of the query or id of the root
9903  * @return {CompositeElementLite/CompositeElement}
9904  * @member Roo
9905  * @method select
9906  */
9907 Roo.select = Roo.Element.select;
9908
9909
9910
9911
9912
9913
9914
9915
9916
9917
9918
9919
9920
9921
9922 /*
9923  * Based on:
9924  * Ext JS Library 1.1.1
9925  * Copyright(c) 2006-2007, Ext JS, LLC.
9926  *
9927  * Originally Released Under LGPL - original licence link has changed is not relivant.
9928  *
9929  * Fork - LGPL
9930  * <script type="text/javascript">
9931  */
9932
9933
9934
9935 //Notifies Element that fx methods are available
9936 Roo.enableFx = true;
9937
9938 /**
9939  * @class Roo.Fx
9940  * <p>A class to provide basic animation and visual effects support.  <b>Note:</b> This class is automatically applied
9941  * to the {@link Roo.Element} interface when included, so all effects calls should be performed via Element.
9942  * Conversely, since the effects are not actually defined in Element, Roo.Fx <b>must</b> be included in order for the 
9943  * Element effects to work.</p><br/>
9944  *
9945  * <p>It is important to note that although the Fx methods and many non-Fx Element methods support "method chaining" in that
9946  * they return the Element object itself as the method return value, it is not always possible to mix the two in a single
9947  * method chain.  The Fx methods use an internal effects queue so that each effect can be properly timed and sequenced.
9948  * Non-Fx methods, on the other hand, have no such internal queueing and will always execute immediately.  For this reason,
9949  * while it may be possible to mix certain Fx and non-Fx method calls in a single chain, it may not always provide the
9950  * expected results and should be done with care.</p><br/>
9951  *
9952  * <p>Motion effects support 8-way anchoring, meaning that you can choose one of 8 different anchor points on the Element
9953  * that will serve as either the start or end point of the animation.  Following are all of the supported anchor positions:</p>
9954 <pre>
9955 Value  Description
9956 -----  -----------------------------
9957 tl     The top left corner
9958 t      The center of the top edge
9959 tr     The top right corner
9960 l      The center of the left edge
9961 r      The center of the right edge
9962 bl     The bottom left corner
9963 b      The center of the bottom edge
9964 br     The bottom right corner
9965 </pre>
9966  * <b>Although some Fx methods accept specific custom config parameters, the ones shown in the Config Options section
9967  * below are common options that can be passed to any Fx method.</b>
9968  * @cfg {Function} callback A function called when the effect is finished
9969  * @cfg {Object} scope The scope of the effect function
9970  * @cfg {String} easing A valid Easing value for the effect
9971  * @cfg {String} afterCls A css class to apply after the effect
9972  * @cfg {Number} duration The length of time (in seconds) that the effect should last
9973  * @cfg {Boolean} remove Whether the Element should be removed from the DOM and destroyed after the effect finishes
9974  * @cfg {Boolean} useDisplay Whether to use the <i>display</i> CSS property instead of <i>visibility</i> when hiding Elements (only applies to 
9975  * effects that end with the element being visually hidden, ignored otherwise)
9976  * @cfg {String/Object/Function} afterStyle A style specification string, e.g. "width:100px", or an object in the form {width:"100px"}, or
9977  * a function which returns such a specification that will be applied to the Element after the effect finishes
9978  * @cfg {Boolean} block Whether the effect should block other effects from queueing while it runs
9979  * @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
9980  * @cfg {Boolean} stopFx Whether subsequent effects should be stopped and removed after the current effect finishes
9981  */
9982 Roo.Fx = {
9983         /**
9984          * Slides the element into view.  An anchor point can be optionally passed to set the point of
9985          * origin for the slide effect.  This function automatically handles wrapping the element with
9986          * a fixed-size container if needed.  See the Fx class overview for valid anchor point options.
9987          * Usage:
9988          *<pre><code>
9989 // default: slide the element in from the top
9990 el.slideIn();
9991
9992 // custom: slide the element in from the right with a 2-second duration
9993 el.slideIn('r', { duration: 2 });
9994
9995 // common config options shown with default values
9996 el.slideIn('t', {
9997     easing: 'easeOut',
9998     duration: .5
9999 });
10000 </code></pre>
10001          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to top: 't')
10002          * @param {Object} options (optional) Object literal with any of the Fx config options
10003          * @return {Roo.Element} The Element
10004          */
10005     slideIn : function(anchor, o){
10006         var el = this.getFxEl();
10007         o = o || {};
10008
10009         el.queueFx(o, function(){
10010
10011             anchor = anchor || "t";
10012
10013             // fix display to visibility
10014             this.fixDisplay();
10015
10016             // restore values after effect
10017             var r = this.getFxRestore();
10018             var b = this.getBox();
10019             // fixed size for slide
10020             this.setSize(b);
10021
10022             // wrap if needed
10023             var wrap = this.fxWrap(r.pos, o, "hidden");
10024
10025             var st = this.dom.style;
10026             st.visibility = "visible";
10027             st.position = "absolute";
10028
10029             // clear out temp styles after slide and unwrap
10030             var after = function(){
10031                 el.fxUnwrap(wrap, r.pos, o);
10032                 st.width = r.width;
10033                 st.height = r.height;
10034                 el.afterFx(o);
10035             };
10036             // time to calc the positions
10037             var a, pt = {to: [b.x, b.y]}, bw = {to: b.width}, bh = {to: b.height};
10038
10039             switch(anchor.toLowerCase()){
10040                 case "t":
10041                     wrap.setSize(b.width, 0);
10042                     st.left = st.bottom = "0";
10043                     a = {height: bh};
10044                 break;
10045                 case "l":
10046                     wrap.setSize(0, b.height);
10047                     st.right = st.top = "0";
10048                     a = {width: bw};
10049                 break;
10050                 case "r":
10051                     wrap.setSize(0, b.height);
10052                     wrap.setX(b.right);
10053                     st.left = st.top = "0";
10054                     a = {width: bw, points: pt};
10055                 break;
10056                 case "b":
10057                     wrap.setSize(b.width, 0);
10058                     wrap.setY(b.bottom);
10059                     st.left = st.top = "0";
10060                     a = {height: bh, points: pt};
10061                 break;
10062                 case "tl":
10063                     wrap.setSize(0, 0);
10064                     st.right = st.bottom = "0";
10065                     a = {width: bw, height: bh};
10066                 break;
10067                 case "bl":
10068                     wrap.setSize(0, 0);
10069                     wrap.setY(b.y+b.height);
10070                     st.right = st.top = "0";
10071                     a = {width: bw, height: bh, points: pt};
10072                 break;
10073                 case "br":
10074                     wrap.setSize(0, 0);
10075                     wrap.setXY([b.right, b.bottom]);
10076                     st.left = st.top = "0";
10077                     a = {width: bw, height: bh, points: pt};
10078                 break;
10079                 case "tr":
10080                     wrap.setSize(0, 0);
10081                     wrap.setX(b.x+b.width);
10082                     st.left = st.bottom = "0";
10083                     a = {width: bw, height: bh, points: pt};
10084                 break;
10085             }
10086             this.dom.style.visibility = "visible";
10087             wrap.show();
10088
10089             arguments.callee.anim = wrap.fxanim(a,
10090                 o,
10091                 'motion',
10092                 .5,
10093                 'easeOut', after);
10094         });
10095         return this;
10096     },
10097     
10098         /**
10099          * Slides the element out of view.  An anchor point can be optionally passed to set the end point
10100          * for the slide effect.  When the effect is completed, the element will be hidden (visibility = 
10101          * 'hidden') but block elements will still take up space in the document.  The element must be removed
10102          * from the DOM using the 'remove' config option if desired.  This function automatically handles 
10103          * wrapping the element with a fixed-size container if needed.  See the Fx class overview for valid anchor point options.
10104          * Usage:
10105          *<pre><code>
10106 // default: slide the element out to the top
10107 el.slideOut();
10108
10109 // custom: slide the element out to the right with a 2-second duration
10110 el.slideOut('r', { duration: 2 });
10111
10112 // common config options shown with default values
10113 el.slideOut('t', {
10114     easing: 'easeOut',
10115     duration: .5,
10116     remove: false,
10117     useDisplay: false
10118 });
10119 </code></pre>
10120          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to top: 't')
10121          * @param {Object} options (optional) Object literal with any of the Fx config options
10122          * @return {Roo.Element} The Element
10123          */
10124     slideOut : function(anchor, o){
10125         var el = this.getFxEl();
10126         o = o || {};
10127
10128         el.queueFx(o, function(){
10129
10130             anchor = anchor || "t";
10131
10132             // restore values after effect
10133             var r = this.getFxRestore();
10134             
10135             var b = this.getBox();
10136             // fixed size for slide
10137             this.setSize(b);
10138
10139             // wrap if needed
10140             var wrap = this.fxWrap(r.pos, o, "visible");
10141
10142             var st = this.dom.style;
10143             st.visibility = "visible";
10144             st.position = "absolute";
10145
10146             wrap.setSize(b);
10147
10148             var after = function(){
10149                 if(o.useDisplay){
10150                     el.setDisplayed(false);
10151                 }else{
10152                     el.hide();
10153                 }
10154
10155                 el.fxUnwrap(wrap, r.pos, o);
10156
10157                 st.width = r.width;
10158                 st.height = r.height;
10159
10160                 el.afterFx(o);
10161             };
10162
10163             var a, zero = {to: 0};
10164             switch(anchor.toLowerCase()){
10165                 case "t":
10166                     st.left = st.bottom = "0";
10167                     a = {height: zero};
10168                 break;
10169                 case "l":
10170                     st.right = st.top = "0";
10171                     a = {width: zero};
10172                 break;
10173                 case "r":
10174                     st.left = st.top = "0";
10175                     a = {width: zero, points: {to:[b.right, b.y]}};
10176                 break;
10177                 case "b":
10178                     st.left = st.top = "0";
10179                     a = {height: zero, points: {to:[b.x, b.bottom]}};
10180                 break;
10181                 case "tl":
10182                     st.right = st.bottom = "0";
10183                     a = {width: zero, height: zero};
10184                 break;
10185                 case "bl":
10186                     st.right = st.top = "0";
10187                     a = {width: zero, height: zero, points: {to:[b.x, b.bottom]}};
10188                 break;
10189                 case "br":
10190                     st.left = st.top = "0";
10191                     a = {width: zero, height: zero, points: {to:[b.x+b.width, b.bottom]}};
10192                 break;
10193                 case "tr":
10194                     st.left = st.bottom = "0";
10195                     a = {width: zero, height: zero, points: {to:[b.right, b.y]}};
10196                 break;
10197             }
10198
10199             arguments.callee.anim = wrap.fxanim(a,
10200                 o,
10201                 'motion',
10202                 .5,
10203                 "easeOut", after);
10204         });
10205         return this;
10206     },
10207
10208         /**
10209          * Fades the element out while slowly expanding it in all directions.  When the effect is completed, the 
10210          * element will be hidden (visibility = 'hidden') but block elements will still take up space in the document. 
10211          * The element must be removed from the DOM using the 'remove' config option if desired.
10212          * Usage:
10213          *<pre><code>
10214 // default
10215 el.puff();
10216
10217 // common config options shown with default values
10218 el.puff({
10219     easing: 'easeOut',
10220     duration: .5,
10221     remove: false,
10222     useDisplay: false
10223 });
10224 </code></pre>
10225          * @param {Object} options (optional) Object literal with any of the Fx config options
10226          * @return {Roo.Element} The Element
10227          */
10228     puff : function(o){
10229         var el = this.getFxEl();
10230         o = o || {};
10231
10232         el.queueFx(o, function(){
10233             this.clearOpacity();
10234             this.show();
10235
10236             // restore values after effect
10237             var r = this.getFxRestore();
10238             var st = this.dom.style;
10239
10240             var after = function(){
10241                 if(o.useDisplay){
10242                     el.setDisplayed(false);
10243                 }else{
10244                     el.hide();
10245                 }
10246
10247                 el.clearOpacity();
10248
10249                 el.setPositioning(r.pos);
10250                 st.width = r.width;
10251                 st.height = r.height;
10252                 st.fontSize = '';
10253                 el.afterFx(o);
10254             };
10255
10256             var width = this.getWidth();
10257             var height = this.getHeight();
10258
10259             arguments.callee.anim = this.fxanim({
10260                     width : {to: this.adjustWidth(width * 2)},
10261                     height : {to: this.adjustHeight(height * 2)},
10262                     points : {by: [-(width * .5), -(height * .5)]},
10263                     opacity : {to: 0},
10264                     fontSize: {to:200, unit: "%"}
10265                 },
10266                 o,
10267                 'motion',
10268                 .5,
10269                 "easeOut", after);
10270         });
10271         return this;
10272     },
10273
10274         /**
10275          * Blinks the element as if it was clicked and then collapses on its center (similar to switching off a television).
10276          * When the effect is completed, the element will be hidden (visibility = 'hidden') but block elements will still 
10277          * take up space in the document. The element must be removed from the DOM using the 'remove' config option if desired.
10278          * Usage:
10279          *<pre><code>
10280 // default
10281 el.switchOff();
10282
10283 // all config options shown with default values
10284 el.switchOff({
10285     easing: 'easeIn',
10286     duration: .3,
10287     remove: false,
10288     useDisplay: false
10289 });
10290 </code></pre>
10291          * @param {Object} options (optional) Object literal with any of the Fx config options
10292          * @return {Roo.Element} The Element
10293          */
10294     switchOff : function(o){
10295         var el = this.getFxEl();
10296         o = o || {};
10297
10298         el.queueFx(o, function(){
10299             this.clearOpacity();
10300             this.clip();
10301
10302             // restore values after effect
10303             var r = this.getFxRestore();
10304             var st = this.dom.style;
10305
10306             var after = function(){
10307                 if(o.useDisplay){
10308                     el.setDisplayed(false);
10309                 }else{
10310                     el.hide();
10311                 }
10312
10313                 el.clearOpacity();
10314                 el.setPositioning(r.pos);
10315                 st.width = r.width;
10316                 st.height = r.height;
10317
10318                 el.afterFx(o);
10319             };
10320
10321             this.fxanim({opacity:{to:0.3}}, null, null, .1, null, function(){
10322                 this.clearOpacity();
10323                 (function(){
10324                     this.fxanim({
10325                         height:{to:1},
10326                         points:{by:[0, this.getHeight() * .5]}
10327                     }, o, 'motion', 0.3, 'easeIn', after);
10328                 }).defer(100, this);
10329             });
10330         });
10331         return this;
10332     },
10333
10334     /**
10335      * Highlights the Element by setting a color (applies to the background-color by default, but can be
10336      * changed using the "attr" config option) and then fading back to the original color. If no original
10337      * color is available, you should provide the "endColor" config option which will be cleared after the animation.
10338      * Usage:
10339 <pre><code>
10340 // default: highlight background to yellow
10341 el.highlight();
10342
10343 // custom: highlight foreground text to blue for 2 seconds
10344 el.highlight("0000ff", { attr: 'color', duration: 2 });
10345
10346 // common config options shown with default values
10347 el.highlight("ffff9c", {
10348     attr: "background-color", //can be any valid CSS property (attribute) that supports a color value
10349     endColor: (current color) or "ffffff",
10350     easing: 'easeIn',
10351     duration: 1
10352 });
10353 </code></pre>
10354      * @param {String} color (optional) The highlight color. Should be a 6 char hex color without the leading # (defaults to yellow: 'ffff9c')
10355      * @param {Object} options (optional) Object literal with any of the Fx config options
10356      * @return {Roo.Element} The Element
10357      */ 
10358     highlight : function(color, o){
10359         var el = this.getFxEl();
10360         o = o || {};
10361
10362         el.queueFx(o, function(){
10363             color = color || "ffff9c";
10364             attr = o.attr || "backgroundColor";
10365
10366             this.clearOpacity();
10367             this.show();
10368
10369             var origColor = this.getColor(attr);
10370             var restoreColor = this.dom.style[attr];
10371             endColor = (o.endColor || origColor) || "ffffff";
10372
10373             var after = function(){
10374                 el.dom.style[attr] = restoreColor;
10375                 el.afterFx(o);
10376             };
10377
10378             var a = {};
10379             a[attr] = {from: color, to: endColor};
10380             arguments.callee.anim = this.fxanim(a,
10381                 o,
10382                 'color',
10383                 1,
10384                 'easeIn', after);
10385         });
10386         return this;
10387     },
10388
10389    /**
10390     * Shows a ripple of exploding, attenuating borders to draw attention to an Element.
10391     * Usage:
10392 <pre><code>
10393 // default: a single light blue ripple
10394 el.frame();
10395
10396 // custom: 3 red ripples lasting 3 seconds total
10397 el.frame("ff0000", 3, { duration: 3 });
10398
10399 // common config options shown with default values
10400 el.frame("C3DAF9", 1, {
10401     duration: 1 //duration of entire animation (not each individual ripple)
10402     // Note: Easing is not configurable and will be ignored if included
10403 });
10404 </code></pre>
10405     * @param {String} color (optional) The color of the border.  Should be a 6 char hex color without the leading # (defaults to light blue: 'C3DAF9').
10406     * @param {Number} count (optional) The number of ripples to display (defaults to 1)
10407     * @param {Object} options (optional) Object literal with any of the Fx config options
10408     * @return {Roo.Element} The Element
10409     */
10410     frame : function(color, count, o){
10411         var el = this.getFxEl();
10412         o = o || {};
10413
10414         el.queueFx(o, function(){
10415             color = color || "#C3DAF9";
10416             if(color.length == 6){
10417                 color = "#" + color;
10418             }
10419             count = count || 1;
10420             duration = o.duration || 1;
10421             this.show();
10422
10423             var b = this.getBox();
10424             var animFn = function(){
10425                 var proxy = this.createProxy({
10426
10427                      style:{
10428                         visbility:"hidden",
10429                         position:"absolute",
10430                         "z-index":"35000", // yee haw
10431                         border:"0px solid " + color
10432                      }
10433                   });
10434                 var scale = Roo.isBorderBox ? 2 : 1;
10435                 proxy.animate({
10436                     top:{from:b.y, to:b.y - 20},
10437                     left:{from:b.x, to:b.x - 20},
10438                     borderWidth:{from:0, to:10},
10439                     opacity:{from:1, to:0},
10440                     height:{from:b.height, to:(b.height + (20*scale))},
10441                     width:{from:b.width, to:(b.width + (20*scale))}
10442                 }, duration, function(){
10443                     proxy.remove();
10444                 });
10445                 if(--count > 0){
10446                      animFn.defer((duration/2)*1000, this);
10447                 }else{
10448                     el.afterFx(o);
10449                 }
10450             };
10451             animFn.call(this);
10452         });
10453         return this;
10454     },
10455
10456    /**
10457     * Creates a pause before any subsequent queued effects begin.  If there are
10458     * no effects queued after the pause it will have no effect.
10459     * Usage:
10460 <pre><code>
10461 el.pause(1);
10462 </code></pre>
10463     * @param {Number} seconds The length of time to pause (in seconds)
10464     * @return {Roo.Element} The Element
10465     */
10466     pause : function(seconds){
10467         var el = this.getFxEl();
10468         var o = {};
10469
10470         el.queueFx(o, function(){
10471             setTimeout(function(){
10472                 el.afterFx(o);
10473             }, seconds * 1000);
10474         });
10475         return this;
10476     },
10477
10478    /**
10479     * Fade an element in (from transparent to opaque).  The ending opacity can be specified
10480     * using the "endOpacity" config option.
10481     * Usage:
10482 <pre><code>
10483 // default: fade in from opacity 0 to 100%
10484 el.fadeIn();
10485
10486 // custom: fade in from opacity 0 to 75% over 2 seconds
10487 el.fadeIn({ endOpacity: .75, duration: 2});
10488
10489 // common config options shown with default values
10490 el.fadeIn({
10491     endOpacity: 1, //can be any value between 0 and 1 (e.g. .5)
10492     easing: 'easeOut',
10493     duration: .5
10494 });
10495 </code></pre>
10496     * @param {Object} options (optional) Object literal with any of the Fx config options
10497     * @return {Roo.Element} The Element
10498     */
10499     fadeIn : function(o){
10500         var el = this.getFxEl();
10501         o = o || {};
10502         el.queueFx(o, function(){
10503             this.setOpacity(0);
10504             this.fixDisplay();
10505             this.dom.style.visibility = 'visible';
10506             var to = o.endOpacity || 1;
10507             arguments.callee.anim = this.fxanim({opacity:{to:to}},
10508                 o, null, .5, "easeOut", function(){
10509                 if(to == 1){
10510                     this.clearOpacity();
10511                 }
10512                 el.afterFx(o);
10513             });
10514         });
10515         return this;
10516     },
10517
10518    /**
10519     * Fade an element out (from opaque to transparent).  The ending opacity can be specified
10520     * using the "endOpacity" config option.
10521     * Usage:
10522 <pre><code>
10523 // default: fade out from the element's current opacity to 0
10524 el.fadeOut();
10525
10526 // custom: fade out from the element's current opacity to 25% over 2 seconds
10527 el.fadeOut({ endOpacity: .25, duration: 2});
10528
10529 // common config options shown with default values
10530 el.fadeOut({
10531     endOpacity: 0, //can be any value between 0 and 1 (e.g. .5)
10532     easing: 'easeOut',
10533     duration: .5
10534     remove: false,
10535     useDisplay: false
10536 });
10537 </code></pre>
10538     * @param {Object} options (optional) Object literal with any of the Fx config options
10539     * @return {Roo.Element} The Element
10540     */
10541     fadeOut : function(o){
10542         var el = this.getFxEl();
10543         o = o || {};
10544         el.queueFx(o, function(){
10545             arguments.callee.anim = this.fxanim({opacity:{to:o.endOpacity || 0}},
10546                 o, null, .5, "easeOut", function(){
10547                 if(this.visibilityMode == Roo.Element.DISPLAY || o.useDisplay){
10548                      this.dom.style.display = "none";
10549                 }else{
10550                      this.dom.style.visibility = "hidden";
10551                 }
10552                 this.clearOpacity();
10553                 el.afterFx(o);
10554             });
10555         });
10556         return this;
10557     },
10558
10559    /**
10560     * Animates the transition of an element's dimensions from a starting height/width
10561     * to an ending height/width.
10562     * Usage:
10563 <pre><code>
10564 // change height and width to 100x100 pixels
10565 el.scale(100, 100);
10566
10567 // common config options shown with default values.  The height and width will default to
10568 // the element's existing values if passed as null.
10569 el.scale(
10570     [element's width],
10571     [element's height], {
10572     easing: 'easeOut',
10573     duration: .35
10574 });
10575 </code></pre>
10576     * @param {Number} width  The new width (pass undefined to keep the original width)
10577     * @param {Number} height  The new height (pass undefined to keep the original height)
10578     * @param {Object} options (optional) Object literal with any of the Fx config options
10579     * @return {Roo.Element} The Element
10580     */
10581     scale : function(w, h, o){
10582         this.shift(Roo.apply({}, o, {
10583             width: w,
10584             height: h
10585         }));
10586         return this;
10587     },
10588
10589    /**
10590     * Animates the transition of any combination of an element's dimensions, xy position and/or opacity.
10591     * Any of these properties not specified in the config object will not be changed.  This effect 
10592     * requires that at least one new dimension, position or opacity setting must be passed in on
10593     * the config object in order for the function to have any effect.
10594     * Usage:
10595 <pre><code>
10596 // slide the element horizontally to x position 200 while changing the height and opacity
10597 el.shift({ x: 200, height: 50, opacity: .8 });
10598
10599 // common config options shown with default values.
10600 el.shift({
10601     width: [element's width],
10602     height: [element's height],
10603     x: [element's x position],
10604     y: [element's y position],
10605     opacity: [element's opacity],
10606     easing: 'easeOut',
10607     duration: .35
10608 });
10609 </code></pre>
10610     * @param {Object} options  Object literal with any of the Fx config options
10611     * @return {Roo.Element} The Element
10612     */
10613     shift : function(o){
10614         var el = this.getFxEl();
10615         o = o || {};
10616         el.queueFx(o, function(){
10617             var a = {}, w = o.width, h = o.height, x = o.x, y = o.y,  op = o.opacity;
10618             if(w !== undefined){
10619                 a.width = {to: this.adjustWidth(w)};
10620             }
10621             if(h !== undefined){
10622                 a.height = {to: this.adjustHeight(h)};
10623             }
10624             if(x !== undefined || y !== undefined){
10625                 a.points = {to: [
10626                     x !== undefined ? x : this.getX(),
10627                     y !== undefined ? y : this.getY()
10628                 ]};
10629             }
10630             if(op !== undefined){
10631                 a.opacity = {to: op};
10632             }
10633             if(o.xy !== undefined){
10634                 a.points = {to: o.xy};
10635             }
10636             arguments.callee.anim = this.fxanim(a,
10637                 o, 'motion', .35, "easeOut", function(){
10638                 el.afterFx(o);
10639             });
10640         });
10641         return this;
10642     },
10643
10644         /**
10645          * Slides the element while fading it out of view.  An anchor point can be optionally passed to set the 
10646          * ending point of the effect.
10647          * Usage:
10648          *<pre><code>
10649 // default: slide the element downward while fading out
10650 el.ghost();
10651
10652 // custom: slide the element out to the right with a 2-second duration
10653 el.ghost('r', { duration: 2 });
10654
10655 // common config options shown with default values
10656 el.ghost('b', {
10657     easing: 'easeOut',
10658     duration: .5
10659     remove: false,
10660     useDisplay: false
10661 });
10662 </code></pre>
10663          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to bottom: 'b')
10664          * @param {Object} options (optional) Object literal with any of the Fx config options
10665          * @return {Roo.Element} The Element
10666          */
10667     ghost : function(anchor, o){
10668         var el = this.getFxEl();
10669         o = o || {};
10670
10671         el.queueFx(o, function(){
10672             anchor = anchor || "b";
10673
10674             // restore values after effect
10675             var r = this.getFxRestore();
10676             var w = this.getWidth(),
10677                 h = this.getHeight();
10678
10679             var st = this.dom.style;
10680
10681             var after = function(){
10682                 if(o.useDisplay){
10683                     el.setDisplayed(false);
10684                 }else{
10685                     el.hide();
10686                 }
10687
10688                 el.clearOpacity();
10689                 el.setPositioning(r.pos);
10690                 st.width = r.width;
10691                 st.height = r.height;
10692
10693                 el.afterFx(o);
10694             };
10695
10696             var a = {opacity: {to: 0}, points: {}}, pt = a.points;
10697             switch(anchor.toLowerCase()){
10698                 case "t":
10699                     pt.by = [0, -h];
10700                 break;
10701                 case "l":
10702                     pt.by = [-w, 0];
10703                 break;
10704                 case "r":
10705                     pt.by = [w, 0];
10706                 break;
10707                 case "b":
10708                     pt.by = [0, h];
10709                 break;
10710                 case "tl":
10711                     pt.by = [-w, -h];
10712                 break;
10713                 case "bl":
10714                     pt.by = [-w, h];
10715                 break;
10716                 case "br":
10717                     pt.by = [w, h];
10718                 break;
10719                 case "tr":
10720                     pt.by = [w, -h];
10721                 break;
10722             }
10723
10724             arguments.callee.anim = this.fxanim(a,
10725                 o,
10726                 'motion',
10727                 .5,
10728                 "easeOut", after);
10729         });
10730         return this;
10731     },
10732
10733         /**
10734          * Ensures that all effects queued after syncFx is called on the element are
10735          * run concurrently.  This is the opposite of {@link #sequenceFx}.
10736          * @return {Roo.Element} The Element
10737          */
10738     syncFx : function(){
10739         this.fxDefaults = Roo.apply(this.fxDefaults || {}, {
10740             block : false,
10741             concurrent : true,
10742             stopFx : false
10743         });
10744         return this;
10745     },
10746
10747         /**
10748          * Ensures that all effects queued after sequenceFx is called on the element are
10749          * run in sequence.  This is the opposite of {@link #syncFx}.
10750          * @return {Roo.Element} The Element
10751          */
10752     sequenceFx : function(){
10753         this.fxDefaults = Roo.apply(this.fxDefaults || {}, {
10754             block : false,
10755             concurrent : false,
10756             stopFx : false
10757         });
10758         return this;
10759     },
10760
10761         /* @private */
10762     nextFx : function(){
10763         var ef = this.fxQueue[0];
10764         if(ef){
10765             ef.call(this);
10766         }
10767     },
10768
10769         /**
10770          * Returns true if the element has any effects actively running or queued, else returns false.
10771          * @return {Boolean} True if element has active effects, else false
10772          */
10773     hasActiveFx : function(){
10774         return this.fxQueue && this.fxQueue[0];
10775     },
10776
10777         /**
10778          * Stops any running effects and clears the element's internal effects queue if it contains
10779          * any additional effects that haven't started yet.
10780          * @return {Roo.Element} The Element
10781          */
10782     stopFx : function(){
10783         if(this.hasActiveFx()){
10784             var cur = this.fxQueue[0];
10785             if(cur && cur.anim && cur.anim.isAnimated()){
10786                 this.fxQueue = [cur]; // clear out others
10787                 cur.anim.stop(true);
10788             }
10789         }
10790         return this;
10791     },
10792
10793         /* @private */
10794     beforeFx : function(o){
10795         if(this.hasActiveFx() && !o.concurrent){
10796            if(o.stopFx){
10797                this.stopFx();
10798                return true;
10799            }
10800            return false;
10801         }
10802         return true;
10803     },
10804
10805         /**
10806          * Returns true if the element is currently blocking so that no other effect can be queued
10807          * until this effect is finished, else returns false if blocking is not set.  This is commonly
10808          * used to ensure that an effect initiated by a user action runs to completion prior to the
10809          * same effect being restarted (e.g., firing only one effect even if the user clicks several times).
10810          * @return {Boolean} True if blocking, else false
10811          */
10812     hasFxBlock : function(){
10813         var q = this.fxQueue;
10814         return q && q[0] && q[0].block;
10815     },
10816
10817         /* @private */
10818     queueFx : function(o, fn){
10819         if(!this.fxQueue){
10820             this.fxQueue = [];
10821         }
10822         if(!this.hasFxBlock()){
10823             Roo.applyIf(o, this.fxDefaults);
10824             if(!o.concurrent){
10825                 var run = this.beforeFx(o);
10826                 fn.block = o.block;
10827                 this.fxQueue.push(fn);
10828                 if(run){
10829                     this.nextFx();
10830                 }
10831             }else{
10832                 fn.call(this);
10833             }
10834         }
10835         return this;
10836     },
10837
10838         /* @private */
10839     fxWrap : function(pos, o, vis){
10840         var wrap;
10841         if(!o.wrap || !(wrap = Roo.get(o.wrap))){
10842             var wrapXY;
10843             if(o.fixPosition){
10844                 wrapXY = this.getXY();
10845             }
10846             var div = document.createElement("div");
10847             div.style.visibility = vis;
10848             wrap = Roo.get(this.dom.parentNode.insertBefore(div, this.dom));
10849             wrap.setPositioning(pos);
10850             if(wrap.getStyle("position") == "static"){
10851                 wrap.position("relative");
10852             }
10853             this.clearPositioning('auto');
10854             wrap.clip();
10855             wrap.dom.appendChild(this.dom);
10856             if(wrapXY){
10857                 wrap.setXY(wrapXY);
10858             }
10859         }
10860         return wrap;
10861     },
10862
10863         /* @private */
10864     fxUnwrap : function(wrap, pos, o){
10865         this.clearPositioning();
10866         this.setPositioning(pos);
10867         if(!o.wrap){
10868             wrap.dom.parentNode.insertBefore(this.dom, wrap.dom);
10869             wrap.remove();
10870         }
10871     },
10872
10873         /* @private */
10874     getFxRestore : function(){
10875         var st = this.dom.style;
10876         return {pos: this.getPositioning(), width: st.width, height : st.height};
10877     },
10878
10879         /* @private */
10880     afterFx : function(o){
10881         if(o.afterStyle){
10882             this.applyStyles(o.afterStyle);
10883         }
10884         if(o.afterCls){
10885             this.addClass(o.afterCls);
10886         }
10887         if(o.remove === true){
10888             this.remove();
10889         }
10890         Roo.callback(o.callback, o.scope, [this]);
10891         if(!o.concurrent){
10892             this.fxQueue.shift();
10893             this.nextFx();
10894         }
10895     },
10896
10897         /* @private */
10898     getFxEl : function(){ // support for composite element fx
10899         return Roo.get(this.dom);
10900     },
10901
10902         /* @private */
10903     fxanim : function(args, opt, animType, defaultDur, defaultEase, cb){
10904         animType = animType || 'run';
10905         opt = opt || {};
10906         var anim = Roo.lib.Anim[animType](
10907             this.dom, args,
10908             (opt.duration || defaultDur) || .35,
10909             (opt.easing || defaultEase) || 'easeOut',
10910             function(){
10911                 Roo.callback(cb, this);
10912             },
10913             this
10914         );
10915         opt.anim = anim;
10916         return anim;
10917     }
10918 };
10919
10920 // backwords compat
10921 Roo.Fx.resize = Roo.Fx.scale;
10922
10923 //When included, Roo.Fx is automatically applied to Element so that all basic
10924 //effects are available directly via the Element API
10925 Roo.apply(Roo.Element.prototype, Roo.Fx);/*
10926  * Based on:
10927  * Ext JS Library 1.1.1
10928  * Copyright(c) 2006-2007, Ext JS, LLC.
10929  *
10930  * Originally Released Under LGPL - original licence link has changed is not relivant.
10931  *
10932  * Fork - LGPL
10933  * <script type="text/javascript">
10934  */
10935
10936
10937 /**
10938  * @class Roo.CompositeElement
10939  * Standard composite class. Creates a Roo.Element for every element in the collection.
10940  * <br><br>
10941  * <b>NOTE: Although they are not listed, this class supports all of the set/update methods of Roo.Element. All Roo.Element
10942  * actions will be performed on all the elements in this collection.</b>
10943  * <br><br>
10944  * All methods return <i>this</i> and can be chained.
10945  <pre><code>
10946  var els = Roo.select("#some-el div.some-class", true);
10947  // or select directly from an existing element
10948  var el = Roo.get('some-el');
10949  el.select('div.some-class', true);
10950
10951  els.setWidth(100); // all elements become 100 width
10952  els.hide(true); // all elements fade out and hide
10953  // or
10954  els.setWidth(100).hide(true);
10955  </code></pre>
10956  */
10957 Roo.CompositeElement = function(els){
10958     this.elements = [];
10959     this.addElements(els);
10960 };
10961 Roo.CompositeElement.prototype = {
10962     isComposite: true,
10963     addElements : function(els){
10964         if(!els) return this;
10965         if(typeof els == "string"){
10966             els = Roo.Element.selectorFunction(els);
10967         }
10968         var yels = this.elements;
10969         var index = yels.length-1;
10970         for(var i = 0, len = els.length; i < len; i++) {
10971                 yels[++index] = Roo.get(els[i]);
10972         }
10973         return this;
10974     },
10975
10976     /**
10977     * Clears this composite and adds the elements returned by the passed selector.
10978     * @param {String/Array} els A string CSS selector, an array of elements or an element
10979     * @return {CompositeElement} this
10980     */
10981     fill : function(els){
10982         this.elements = [];
10983         this.add(els);
10984         return this;
10985     },
10986
10987     /**
10988     * Filters this composite to only elements that match the passed selector.
10989     * @param {String} selector A string CSS selector
10990     * @param {Boolean} inverse return inverse filter (not matches)
10991     * @return {CompositeElement} this
10992     */
10993     filter : function(selector, inverse){
10994         var els = [];
10995         inverse = inverse || false;
10996         this.each(function(el){
10997             var match = inverse ? !el.is(selector) : el.is(selector);
10998             if(match){
10999                 els[els.length] = el.dom;
11000             }
11001         });
11002         this.fill(els);
11003         return this;
11004     },
11005
11006     invoke : function(fn, args){
11007         var els = this.elements;
11008         for(var i = 0, len = els.length; i < len; i++) {
11009                 Roo.Element.prototype[fn].apply(els[i], args);
11010         }
11011         return this;
11012     },
11013     /**
11014     * Adds elements to this composite.
11015     * @param {String/Array} els A string CSS selector, an array of elements or an element
11016     * @return {CompositeElement} this
11017     */
11018     add : function(els){
11019         if(typeof els == "string"){
11020             this.addElements(Roo.Element.selectorFunction(els));
11021         }else if(els.length !== undefined){
11022             this.addElements(els);
11023         }else{
11024             this.addElements([els]);
11025         }
11026         return this;
11027     },
11028     /**
11029     * Calls the passed function passing (el, this, index) for each element in this composite.
11030     * @param {Function} fn The function to call
11031     * @param {Object} scope (optional) The <i>this</i> object (defaults to the element)
11032     * @return {CompositeElement} this
11033     */
11034     each : function(fn, scope){
11035         var els = this.elements;
11036         for(var i = 0, len = els.length; i < len; i++){
11037             if(fn.call(scope || els[i], els[i], this, i) === false) {
11038                 break;
11039             }
11040         }
11041         return this;
11042     },
11043
11044     /**
11045      * Returns the Element object at the specified index
11046      * @param {Number} index
11047      * @return {Roo.Element}
11048      */
11049     item : function(index){
11050         return this.elements[index] || null;
11051     },
11052
11053     /**
11054      * Returns the first Element
11055      * @return {Roo.Element}
11056      */
11057     first : function(){
11058         return this.item(0);
11059     },
11060
11061     /**
11062      * Returns the last Element
11063      * @return {Roo.Element}
11064      */
11065     last : function(){
11066         return this.item(this.elements.length-1);
11067     },
11068
11069     /**
11070      * Returns the number of elements in this composite
11071      * @return Number
11072      */
11073     getCount : function(){
11074         return this.elements.length;
11075     },
11076
11077     /**
11078      * Returns true if this composite contains the passed element
11079      * @return Boolean
11080      */
11081     contains : function(el){
11082         return this.indexOf(el) !== -1;
11083     },
11084
11085     /**
11086      * Returns true if this composite contains the passed element
11087      * @return Boolean
11088      */
11089     indexOf : function(el){
11090         return this.elements.indexOf(Roo.get(el));
11091     },
11092
11093
11094     /**
11095     * Removes the specified element(s).
11096     * @param {Mixed} el The id of an element, the Element itself, the index of the element in this composite
11097     * or an array of any of those.
11098     * @param {Boolean} removeDom (optional) True to also remove the element from the document
11099     * @return {CompositeElement} this
11100     */
11101     removeElement : function(el, removeDom){
11102         if(el instanceof Array){
11103             for(var i = 0, len = el.length; i < len; i++){
11104                 this.removeElement(el[i]);
11105             }
11106             return this;
11107         }
11108         var index = typeof el == 'number' ? el : this.indexOf(el);
11109         if(index !== -1){
11110             if(removeDom){
11111                 var d = this.elements[index];
11112                 if(d.dom){
11113                     d.remove();
11114                 }else{
11115                     d.parentNode.removeChild(d);
11116                 }
11117             }
11118             this.elements.splice(index, 1);
11119         }
11120         return this;
11121     },
11122
11123     /**
11124     * Replaces the specified element with the passed element.
11125     * @param {String/HTMLElement/Element/Number} el The id of an element, the Element itself, the index of the element in this composite
11126     * to replace.
11127     * @param {String/HTMLElement/Element} replacement The id of an element or the Element itself.
11128     * @param {Boolean} domReplace (Optional) True to remove and replace the element in the document too.
11129     * @return {CompositeElement} this
11130     */
11131     replaceElement : function(el, replacement, domReplace){
11132         var index = typeof el == 'number' ? el : this.indexOf(el);
11133         if(index !== -1){
11134             if(domReplace){
11135                 this.elements[index].replaceWith(replacement);
11136             }else{
11137                 this.elements.splice(index, 1, Roo.get(replacement))
11138             }
11139         }
11140         return this;
11141     },
11142
11143     /**
11144      * Removes all elements.
11145      */
11146     clear : function(){
11147         this.elements = [];
11148     }
11149 };
11150 (function(){
11151     Roo.CompositeElement.createCall = function(proto, fnName){
11152         if(!proto[fnName]){
11153             proto[fnName] = function(){
11154                 return this.invoke(fnName, arguments);
11155             };
11156         }
11157     };
11158     for(var fnName in Roo.Element.prototype){
11159         if(typeof Roo.Element.prototype[fnName] == "function"){
11160             Roo.CompositeElement.createCall(Roo.CompositeElement.prototype, fnName);
11161         }
11162     };
11163 })();
11164 /*
11165  * Based on:
11166  * Ext JS Library 1.1.1
11167  * Copyright(c) 2006-2007, Ext JS, LLC.
11168  *
11169  * Originally Released Under LGPL - original licence link has changed is not relivant.
11170  *
11171  * Fork - LGPL
11172  * <script type="text/javascript">
11173  */
11174
11175 /**
11176  * @class Roo.CompositeElementLite
11177  * @extends Roo.CompositeElement
11178  * Flyweight composite class. Reuses the same Roo.Element for element operations.
11179  <pre><code>
11180  var els = Roo.select("#some-el div.some-class");
11181  // or select directly from an existing element
11182  var el = Roo.get('some-el');
11183  el.select('div.some-class');
11184
11185  els.setWidth(100); // all elements become 100 width
11186  els.hide(true); // all elements fade out and hide
11187  // or
11188  els.setWidth(100).hide(true);
11189  </code></pre><br><br>
11190  * <b>NOTE: Although they are not listed, this class supports all of the set/update methods of Roo.Element. All Roo.Element
11191  * actions will be performed on all the elements in this collection.</b>
11192  */
11193 Roo.CompositeElementLite = function(els){
11194     Roo.CompositeElementLite.superclass.constructor.call(this, els);
11195     this.el = new Roo.Element.Flyweight();
11196 };
11197 Roo.extend(Roo.CompositeElementLite, Roo.CompositeElement, {
11198     addElements : function(els){
11199         if(els){
11200             if(els instanceof Array){
11201                 this.elements = this.elements.concat(els);
11202             }else{
11203                 var yels = this.elements;
11204                 var index = yels.length-1;
11205                 for(var i = 0, len = els.length; i < len; i++) {
11206                     yels[++index] = els[i];
11207                 }
11208             }
11209         }
11210         return this;
11211     },
11212     invoke : function(fn, args){
11213         var els = this.elements;
11214         var el = this.el;
11215         for(var i = 0, len = els.length; i < len; i++) {
11216             el.dom = els[i];
11217                 Roo.Element.prototype[fn].apply(el, args);
11218         }
11219         return this;
11220     },
11221     /**
11222      * Returns a flyweight Element of the dom element object at the specified index
11223      * @param {Number} index
11224      * @return {Roo.Element}
11225      */
11226     item : function(index){
11227         if(!this.elements[index]){
11228             return null;
11229         }
11230         this.el.dom = this.elements[index];
11231         return this.el;
11232     },
11233
11234     // fixes scope with flyweight
11235     addListener : function(eventName, handler, scope, opt){
11236         var els = this.elements;
11237         for(var i = 0, len = els.length; i < len; i++) {
11238             Roo.EventManager.on(els[i], eventName, handler, scope || els[i], opt);
11239         }
11240         return this;
11241     },
11242
11243     /**
11244     * Calls the passed function passing (el, this, index) for each element in this composite. <b>The element
11245     * passed is the flyweight (shared) Roo.Element instance, so if you require a
11246     * a reference to the dom node, use el.dom.</b>
11247     * @param {Function} fn The function to call
11248     * @param {Object} scope (optional) The <i>this</i> object (defaults to the element)
11249     * @return {CompositeElement} this
11250     */
11251     each : function(fn, scope){
11252         var els = this.elements;
11253         var el = this.el;
11254         for(var i = 0, len = els.length; i < len; i++){
11255             el.dom = els[i];
11256                 if(fn.call(scope || el, el, this, i) === false){
11257                 break;
11258             }
11259         }
11260         return this;
11261     },
11262
11263     indexOf : function(el){
11264         return this.elements.indexOf(Roo.getDom(el));
11265     },
11266
11267     replaceElement : function(el, replacement, domReplace){
11268         var index = typeof el == 'number' ? el : this.indexOf(el);
11269         if(index !== -1){
11270             replacement = Roo.getDom(replacement);
11271             if(domReplace){
11272                 var d = this.elements[index];
11273                 d.parentNode.insertBefore(replacement, d);
11274                 d.parentNode.removeChild(d);
11275             }
11276             this.elements.splice(index, 1, replacement);
11277         }
11278         return this;
11279     }
11280 });
11281 Roo.CompositeElementLite.prototype.on = Roo.CompositeElementLite.prototype.addListener;
11282
11283 /*
11284  * Based on:
11285  * Ext JS Library 1.1.1
11286  * Copyright(c) 2006-2007, Ext JS, LLC.
11287  *
11288  * Originally Released Under LGPL - original licence link has changed is not relivant.
11289  *
11290  * Fork - LGPL
11291  * <script type="text/javascript">
11292  */
11293
11294  
11295
11296 /**
11297  * @class Roo.data.Connection
11298  * @extends Roo.util.Observable
11299  * The class encapsulates a connection to the page's originating domain, allowing requests to be made
11300  * either to a configured URL, or to a URL specified at request time.<br><br>
11301  * <p>
11302  * Requests made by this class are asynchronous, and will return immediately. No data from
11303  * the server will be available to the statement immediately following the {@link #request} call.
11304  * To process returned data, use a callback in the request options object, or an event listener.</p><br>
11305  * <p>
11306  * Note: If you are doing a file upload, you will not get a normal response object sent back to
11307  * your callback or event handler.  Since the upload is handled via in IFRAME, there is no XMLHttpRequest.
11308  * The response object is created using the innerHTML of the IFRAME's document as the responseText
11309  * property and, if present, the IFRAME's XML document as the responseXML property.</p><br>
11310  * This means that a valid XML or HTML document must be returned. If JSON data is required, it is suggested
11311  * that it be placed either inside a &lt;textarea> in an HTML document and retrieved from the responseText
11312  * using a regex, or inside a CDATA section in an XML document and retrieved from the responseXML using
11313  * standard DOM methods.
11314  * @constructor
11315  * @param {Object} config a configuration object.
11316  */
11317 Roo.data.Connection = function(config){
11318     Roo.apply(this, config);
11319     this.addEvents({
11320         /**
11321          * @event beforerequest
11322          * Fires before a network request is made to retrieve a data object.
11323          * @param {Connection} conn This Connection object.
11324          * @param {Object} options The options config object passed to the {@link #request} method.
11325          */
11326         "beforerequest" : true,
11327         /**
11328          * @event requestcomplete
11329          * Fires if the request was successfully completed.
11330          * @param {Connection} conn This Connection object.
11331          * @param {Object} response The XHR object containing the response data.
11332          * See {@link http://www.w3.org/TR/XMLHttpRequest/} for details.
11333          * @param {Object} options The options config object passed to the {@link #request} method.
11334          */
11335         "requestcomplete" : true,
11336         /**
11337          * @event requestexception
11338          * Fires if an error HTTP status was returned from the server.
11339          * See {@link http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html} for details of HTTP status codes.
11340          * @param {Connection} conn This Connection object.
11341          * @param {Object} response The XHR object containing the response data.
11342          * See {@link http://www.w3.org/TR/XMLHttpRequest/} for details.
11343          * @param {Object} options The options config object passed to the {@link #request} method.
11344          */
11345         "requestexception" : true
11346     });
11347     Roo.data.Connection.superclass.constructor.call(this);
11348 };
11349
11350 Roo.extend(Roo.data.Connection, Roo.util.Observable, {
11351     /**
11352      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
11353      */
11354     /**
11355      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
11356      * extra parameters to each request made by this object. (defaults to undefined)
11357      */
11358     /**
11359      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
11360      *  to each request made by this object. (defaults to undefined)
11361      */
11362     /**
11363      * @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)
11364      */
11365     /**
11366      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
11367      */
11368     timeout : 30000,
11369     /**
11370      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
11371      * @type Boolean
11372      */
11373     autoAbort:false,
11374
11375     /**
11376      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
11377      * @type Boolean
11378      */
11379     disableCaching: true,
11380
11381     /**
11382      * Sends an HTTP request to a remote server.
11383      * @param {Object} options An object which may contain the following properties:<ul>
11384      * <li><b>url</b> {String} (Optional) The URL to which to send the request. Defaults to configured URL</li>
11385      * <li><b>params</b> {Object/String/Function} (Optional) An object containing properties which are used as parameters to the
11386      * request, a url encoded string or a function to call to get either.</li>
11387      * <li><b>method</b> {String} (Optional) The HTTP method to use for the request. Defaults to the configured method, or
11388      * if no method was configured, "GET" if no parameters are being sent, and "POST" if parameters are being sent.</li>
11389      * <li><b>callback</b> {Function} (Optional) The function to be called upon receipt of the HTTP response.
11390      * The callback is called regardless of success or failure and is passed the following parameters:<ul>
11391      * <li>options {Object} The parameter to the request call.</li>
11392      * <li>success {Boolean} True if the request succeeded.</li>
11393      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
11394      * </ul></li>
11395      * <li><b>success</b> {Function} (Optional) The function to be called upon success of the request.
11396      * The callback is passed the following parameters:<ul>
11397      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
11398      * <li>options {Object} The parameter to the request call.</li>
11399      * </ul></li>
11400      * <li><b>failure</b> {Function} (Optional) The function to be called upon failure of the request.
11401      * The callback is passed the following parameters:<ul>
11402      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
11403      * <li>options {Object} The parameter to the request call.</li>
11404      * </ul></li>
11405      * <li><b>scope</b> {Object} (Optional) The scope in which to execute the callbacks: The "this" object
11406      * for the callback function. Defaults to the browser window.</li>
11407      * <li><b>form</b> {Object/String} (Optional) A form object or id to pull parameters from.</li>
11408      * <li><b>isUpload</b> {Boolean} (Optional) True if the form object is a file upload (will usually be automatically detected).</li>
11409      * <li><b>headers</b> {Object} (Optional) Request headers to set for the request.</li>
11410      * <li><b>xmlData</b> {Object} (Optional) XML document to use for the post. Note: This will be used instead of
11411      * params for the post data. Any params will be appended to the URL.</li>
11412      * <li><b>disableCaching</b> {Boolean} (Optional) True to add a unique cache-buster param to GET requests.</li>
11413      * </ul>
11414      * @return {Number} transactionId
11415      */
11416     request : function(o){
11417         if(this.fireEvent("beforerequest", this, o) !== false){
11418             var p = o.params;
11419
11420             if(typeof p == "function"){
11421                 p = p.call(o.scope||window, o);
11422             }
11423             if(typeof p == "object"){
11424                 p = Roo.urlEncode(o.params);
11425             }
11426             if(this.extraParams){
11427                 var extras = Roo.urlEncode(this.extraParams);
11428                 p = p ? (p + '&' + extras) : extras;
11429             }
11430
11431             var url = o.url || this.url;
11432             if(typeof url == 'function'){
11433                 url = url.call(o.scope||window, o);
11434             }
11435
11436             if(o.form){
11437                 var form = Roo.getDom(o.form);
11438                 url = url || form.action;
11439
11440                 var enctype = form.getAttribute("enctype");
11441                 if(o.isUpload || (enctype && enctype.toLowerCase() == 'multipart/form-data')){
11442                     return this.doFormUpload(o, p, url);
11443                 }
11444                 var f = Roo.lib.Ajax.serializeForm(form);
11445                 p = p ? (p + '&' + f) : f;
11446             }
11447
11448             var hs = o.headers;
11449             if(this.defaultHeaders){
11450                 hs = Roo.apply(hs || {}, this.defaultHeaders);
11451                 if(!o.headers){
11452                     o.headers = hs;
11453                 }
11454             }
11455
11456             var cb = {
11457                 success: this.handleResponse,
11458                 failure: this.handleFailure,
11459                 scope: this,
11460                 argument: {options: o},
11461                 timeout : o.timeout || this.timeout
11462             };
11463
11464             var method = o.method||this.method||(p ? "POST" : "GET");
11465
11466             if(method == 'GET' && (this.disableCaching && o.disableCaching !== false) || o.disableCaching === true){
11467                 url += (url.indexOf('?') != -1 ? '&' : '?') + '_dc=' + (new Date().getTime());
11468             }
11469
11470             if(typeof o.autoAbort == 'boolean'){ // options gets top priority
11471                 if(o.autoAbort){
11472                     this.abort();
11473                 }
11474             }else if(this.autoAbort !== false){
11475                 this.abort();
11476             }
11477
11478             if((method == 'GET' && p) || o.xmlData){
11479                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
11480                 p = '';
11481             }
11482             this.transId = Roo.lib.Ajax.request(method, url, cb, p, o);
11483             return this.transId;
11484         }else{
11485             Roo.callback(o.callback, o.scope, [o, null, null]);
11486             return null;
11487         }
11488     },
11489
11490     /**
11491      * Determine whether this object has a request outstanding.
11492      * @param {Number} transactionId (Optional) defaults to the last transaction
11493      * @return {Boolean} True if there is an outstanding request.
11494      */
11495     isLoading : function(transId){
11496         if(transId){
11497             return Roo.lib.Ajax.isCallInProgress(transId);
11498         }else{
11499             return this.transId ? true : false;
11500         }
11501     },
11502
11503     /**
11504      * Aborts any outstanding request.
11505      * @param {Number} transactionId (Optional) defaults to the last transaction
11506      */
11507     abort : function(transId){
11508         if(transId || this.isLoading()){
11509             Roo.lib.Ajax.abort(transId || this.transId);
11510         }
11511     },
11512
11513     // private
11514     handleResponse : function(response){
11515         this.transId = false;
11516         var options = response.argument.options;
11517         response.argument = options ? options.argument : null;
11518         this.fireEvent("requestcomplete", this, response, options);
11519         Roo.callback(options.success, options.scope, [response, options]);
11520         Roo.callback(options.callback, options.scope, [options, true, response]);
11521     },
11522
11523     // private
11524     handleFailure : function(response, e){
11525         this.transId = false;
11526         var options = response.argument.options;
11527         response.argument = options ? options.argument : null;
11528         this.fireEvent("requestexception", this, response, options, e);
11529         Roo.callback(options.failure, options.scope, [response, options]);
11530         Roo.callback(options.callback, options.scope, [options, false, response]);
11531     },
11532
11533     // private
11534     doFormUpload : function(o, ps, url){
11535         var id = Roo.id();
11536         var frame = document.createElement('iframe');
11537         frame.id = id;
11538         frame.name = id;
11539         frame.className = 'x-hidden';
11540         if(Roo.isIE){
11541             frame.src = Roo.SSL_SECURE_URL;
11542         }
11543         document.body.appendChild(frame);
11544
11545         if(Roo.isIE){
11546            document.frames[id].name = id;
11547         }
11548
11549         var form = Roo.getDom(o.form);
11550         form.target = id;
11551         form.method = 'POST';
11552         form.enctype = form.encoding = 'multipart/form-data';
11553         if(url){
11554             form.action = url;
11555         }
11556
11557         var hiddens, hd;
11558         if(ps){ // add dynamic params
11559             hiddens = [];
11560             ps = Roo.urlDecode(ps, false);
11561             for(var k in ps){
11562                 if(ps.hasOwnProperty(k)){
11563                     hd = document.createElement('input');
11564                     hd.type = 'hidden';
11565                     hd.name = k;
11566                     hd.value = ps[k];
11567                     form.appendChild(hd);
11568                     hiddens.push(hd);
11569                 }
11570             }
11571         }
11572
11573         function cb(){
11574             var r = {  // bogus response object
11575                 responseText : '',
11576                 responseXML : null
11577             };
11578
11579             r.argument = o ? o.argument : null;
11580
11581             try { //
11582                 var doc;
11583                 if(Roo.isIE){
11584                     doc = frame.contentWindow.document;
11585                 }else {
11586                     doc = (frame.contentDocument || window.frames[id].document);
11587                 }
11588                 if(doc && doc.body){
11589                     r.responseText = doc.body.innerHTML;
11590                 }
11591                 if(doc && doc.XMLDocument){
11592                     r.responseXML = doc.XMLDocument;
11593                 }else {
11594                     r.responseXML = doc;
11595                 }
11596             }
11597             catch(e) {
11598                 // ignore
11599             }
11600
11601             Roo.EventManager.removeListener(frame, 'load', cb, this);
11602
11603             this.fireEvent("requestcomplete", this, r, o);
11604             Roo.callback(o.success, o.scope, [r, o]);
11605             Roo.callback(o.callback, o.scope, [o, true, r]);
11606
11607             setTimeout(function(){document.body.removeChild(frame);}, 100);
11608         }
11609
11610         Roo.EventManager.on(frame, 'load', cb, this);
11611         form.submit();
11612
11613         if(hiddens){ // remove dynamic params
11614             for(var i = 0, len = hiddens.length; i < len; i++){
11615                 form.removeChild(hiddens[i]);
11616             }
11617         }
11618     }
11619 });
11620 /*
11621  * Based on:
11622  * Ext JS Library 1.1.1
11623  * Copyright(c) 2006-2007, Ext JS, LLC.
11624  *
11625  * Originally Released Under LGPL - original licence link has changed is not relivant.
11626  *
11627  * Fork - LGPL
11628  * <script type="text/javascript">
11629  */
11630  
11631 /**
11632  * Global Ajax request class.
11633  * 
11634  * @class Roo.Ajax
11635  * @extends Roo.data.Connection
11636  * @static
11637  * 
11638  * @cfg {String} url  The default URL to be used for requests to the server. (defaults to undefined)
11639  * @cfg {Object} extraParams  An object containing properties which are used as extra parameters to each request made by this object. (defaults to undefined)
11640  * @cfg {Object} defaultHeaders  An object containing request headers which are added to each request made by this object. (defaults to undefined)
11641  * @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)
11642  * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
11643  * @cfg {Boolean} autoAbort (Optional) Whether a new request should abort any pending requests. (defaults to false)
11644  * @cfg {Boolean} disableCaching (Optional)   True to add a unique cache-buster param to GET requests. (defaults to true)
11645  */
11646 Roo.Ajax = new Roo.data.Connection({
11647     // fix up the docs
11648     /**
11649      * @scope Roo.Ajax
11650      * @type {Boolear} 
11651      */
11652     autoAbort : false,
11653
11654     /**
11655      * Serialize the passed form into a url encoded string
11656      * @scope Roo.Ajax
11657      * @param {String/HTMLElement} form
11658      * @return {String}
11659      */
11660     serializeForm : function(form){
11661         return Roo.lib.Ajax.serializeForm(form);
11662     }
11663 });/*
11664  * Based on:
11665  * Ext JS Library 1.1.1
11666  * Copyright(c) 2006-2007, Ext JS, LLC.
11667  *
11668  * Originally Released Under LGPL - original licence link has changed is not relivant.
11669  *
11670  * Fork - LGPL
11671  * <script type="text/javascript">
11672  */
11673
11674  
11675 /**
11676  * @class Roo.UpdateManager
11677  * @extends Roo.util.Observable
11678  * Provides AJAX-style update for Element object.<br><br>
11679  * Usage:<br>
11680  * <pre><code>
11681  * // Get it from a Roo.Element object
11682  * var el = Roo.get("foo");
11683  * var mgr = el.getUpdateManager();
11684  * mgr.update("http://myserver.com/index.php", "param1=1&amp;param2=2");
11685  * ...
11686  * mgr.formUpdate("myFormId", "http://myserver.com/index.php");
11687  * <br>
11688  * // or directly (returns the same UpdateManager instance)
11689  * var mgr = new Roo.UpdateManager("myElementId");
11690  * mgr.startAutoRefresh(60, "http://myserver.com/index.php");
11691  * mgr.on("update", myFcnNeedsToKnow);
11692  * <br>
11693    // short handed call directly from the element object
11694    Roo.get("foo").load({
11695         url: "bar.php",
11696         scripts:true,
11697         params: "for=bar",
11698         text: "Loading Foo..."
11699    });
11700  * </code></pre>
11701  * @constructor
11702  * Create new UpdateManager directly.
11703  * @param {String/HTMLElement/Roo.Element} el The element to update
11704  * @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).
11705  */
11706 Roo.UpdateManager = function(el, forceNew){
11707     el = Roo.get(el);
11708     if(!forceNew && el.updateManager){
11709         return el.updateManager;
11710     }
11711     /**
11712      * The Element object
11713      * @type Roo.Element
11714      */
11715     this.el = el;
11716     /**
11717      * Cached url to use for refreshes. Overwritten every time update() is called unless "discardUrl" param is set to true.
11718      * @type String
11719      */
11720     this.defaultUrl = null;
11721
11722     this.addEvents({
11723         /**
11724          * @event beforeupdate
11725          * Fired before an update is made, return false from your handler and the update is cancelled.
11726          * @param {Roo.Element} el
11727          * @param {String/Object/Function} url
11728          * @param {String/Object} params
11729          */
11730         "beforeupdate": true,
11731         /**
11732          * @event update
11733          * Fired after successful update is made.
11734          * @param {Roo.Element} el
11735          * @param {Object} oResponseObject The response Object
11736          */
11737         "update": true,
11738         /**
11739          * @event failure
11740          * Fired on update failure.
11741          * @param {Roo.Element} el
11742          * @param {Object} oResponseObject The response Object
11743          */
11744         "failure": true
11745     });
11746     var d = Roo.UpdateManager.defaults;
11747     /**
11748      * Blank page URL to use with SSL file uploads (Defaults to Roo.UpdateManager.defaults.sslBlankUrl or "about:blank").
11749      * @type String
11750      */
11751     this.sslBlankUrl = d.sslBlankUrl;
11752     /**
11753      * Whether to append unique parameter on get request to disable caching (Defaults to Roo.UpdateManager.defaults.disableCaching or false).
11754      * @type Boolean
11755      */
11756     this.disableCaching = d.disableCaching;
11757     /**
11758      * Text for loading indicator (Defaults to Roo.UpdateManager.defaults.indicatorText or '&lt;div class="loading-indicator"&gt;Loading...&lt;/div&gt;').
11759      * @type String
11760      */
11761     this.indicatorText = d.indicatorText;
11762     /**
11763      * Whether to show indicatorText when loading (Defaults to Roo.UpdateManager.defaults.showLoadIndicator or true).
11764      * @type String
11765      */
11766     this.showLoadIndicator = d.showLoadIndicator;
11767     /**
11768      * Timeout for requests or form posts in seconds (Defaults to Roo.UpdateManager.defaults.timeout or 30 seconds).
11769      * @type Number
11770      */
11771     this.timeout = d.timeout;
11772
11773     /**
11774      * True to process scripts in the output (Defaults to Roo.UpdateManager.defaults.loadScripts (false)).
11775      * @type Boolean
11776      */
11777     this.loadScripts = d.loadScripts;
11778
11779     /**
11780      * Transaction object of current executing transaction
11781      */
11782     this.transaction = null;
11783
11784     /**
11785      * @private
11786      */
11787     this.autoRefreshProcId = null;
11788     /**
11789      * Delegate for refresh() prebound to "this", use myUpdater.refreshDelegate.createCallback(arg1, arg2) to bind arguments
11790      * @type Function
11791      */
11792     this.refreshDelegate = this.refresh.createDelegate(this);
11793     /**
11794      * Delegate for update() prebound to "this", use myUpdater.updateDelegate.createCallback(arg1, arg2) to bind arguments
11795      * @type Function
11796      */
11797     this.updateDelegate = this.update.createDelegate(this);
11798     /**
11799      * Delegate for formUpdate() prebound to "this", use myUpdater.formUpdateDelegate.createCallback(arg1, arg2) to bind arguments
11800      * @type Function
11801      */
11802     this.formUpdateDelegate = this.formUpdate.createDelegate(this);
11803     /**
11804      * @private
11805      */
11806     this.successDelegate = this.processSuccess.createDelegate(this);
11807     /**
11808      * @private
11809      */
11810     this.failureDelegate = this.processFailure.createDelegate(this);
11811
11812     if(!this.renderer){
11813      /**
11814       * The renderer for this UpdateManager. Defaults to {@link Roo.UpdateManager.BasicRenderer}.
11815       */
11816     this.renderer = new Roo.UpdateManager.BasicRenderer();
11817     }
11818     
11819     Roo.UpdateManager.superclass.constructor.call(this);
11820 };
11821
11822 Roo.extend(Roo.UpdateManager, Roo.util.Observable, {
11823     /**
11824      * Get the Element this UpdateManager is bound to
11825      * @return {Roo.Element} The element
11826      */
11827     getEl : function(){
11828         return this.el;
11829     },
11830     /**
11831      * Performs an async request, updating this element with the response. If params are specified it uses POST, otherwise it uses GET.
11832      * @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:
11833 <pre><code>
11834 um.update({<br/>
11835     url: "your-url.php",<br/>
11836     params: {param1: "foo", param2: "bar"}, // or a URL encoded string<br/>
11837     callback: yourFunction,<br/>
11838     scope: yourObject, //(optional scope)  <br/>
11839     discardUrl: false, <br/>
11840     nocache: false,<br/>
11841     text: "Loading...",<br/>
11842     timeout: 30,<br/>
11843     scripts: false<br/>
11844 });
11845 </code></pre>
11846      * The only required property is url. The optional properties nocache, text and scripts
11847      * are shorthand for disableCaching, indicatorText and loadScripts and are used to set their associated property on this UpdateManager instance.
11848      * @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}
11849      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess, oResponse)
11850      * @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.
11851      */
11852     update : function(url, params, callback, discardUrl){
11853         if(this.fireEvent("beforeupdate", this.el, url, params) !== false){
11854             var method = this.method,
11855                 cfg;
11856             if(typeof url == "object"){ // must be config object
11857                 cfg = url;
11858                 url = cfg.url;
11859                 params = params || cfg.params;
11860                 callback = callback || cfg.callback;
11861                 discardUrl = discardUrl || cfg.discardUrl;
11862                 if(callback && cfg.scope){
11863                     callback = callback.createDelegate(cfg.scope);
11864                 }
11865                 if(typeof cfg.method != "undefined"){method = cfg.method;};
11866                 if(typeof cfg.nocache != "undefined"){this.disableCaching = cfg.nocache;};
11867                 if(typeof cfg.text != "undefined"){this.indicatorText = '<div class="loading-indicator">'+cfg.text+"</div>";};
11868                 if(typeof cfg.scripts != "undefined"){this.loadScripts = cfg.scripts;};
11869                 if(typeof cfg.timeout != "undefined"){this.timeout = cfg.timeout;};
11870             }
11871             this.showLoading();
11872             if(!discardUrl){
11873                 this.defaultUrl = url;
11874             }
11875             if(typeof url == "function"){
11876                 url = url.call(this);
11877             }
11878
11879             method = method || (params ? "POST" : "GET");
11880             if(method == "GET"){
11881                 url = this.prepareUrl(url);
11882             }
11883
11884             var o = Roo.apply(cfg ||{}, {
11885                 url : url,
11886                 params: params,
11887                 success: this.successDelegate,
11888                 failure: this.failureDelegate,
11889                 callback: undefined,
11890                 timeout: (this.timeout*1000),
11891                 argument: {"url": url, "form": null, "callback": callback, "params": params}
11892             });
11893             Roo.log("updated manager called with timeout of " + o.timeout);
11894             this.transaction = Roo.Ajax.request(o);
11895         }
11896     },
11897
11898     /**
11899      * 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.
11900      * Uses this.sslBlankUrl for SSL file uploads to prevent IE security warning.
11901      * @param {String/HTMLElement} form The form Id or form element
11902      * @param {String} url (optional) The url to pass the form to. If omitted the action attribute on the form will be used.
11903      * @param {Boolean} reset (optional) Whether to try to reset the form after the update
11904      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess, oResponse)
11905      */
11906     formUpdate : function(form, url, reset, callback){
11907         if(this.fireEvent("beforeupdate", this.el, form, url) !== false){
11908             if(typeof url == "function"){
11909                 url = url.call(this);
11910             }
11911             form = Roo.getDom(form);
11912             this.transaction = Roo.Ajax.request({
11913                 form: form,
11914                 url:url,
11915                 success: this.successDelegate,
11916                 failure: this.failureDelegate,
11917                 timeout: (this.timeout*1000),
11918                 argument: {"url": url, "form": form, "callback": callback, "reset": reset}
11919             });
11920             this.showLoading.defer(1, this);
11921         }
11922     },
11923
11924     /**
11925      * Refresh the element with the last used url or defaultUrl. If there is no url, it returns immediately
11926      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
11927      */
11928     refresh : function(callback){
11929         if(this.defaultUrl == null){
11930             return;
11931         }
11932         this.update(this.defaultUrl, null, callback, true);
11933     },
11934
11935     /**
11936      * Set this element to auto refresh.
11937      * @param {Number} interval How often to update (in seconds).
11938      * @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)
11939      * @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}
11940      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
11941      * @param {Boolean} refreshNow (optional) Whether to execute the refresh now, or wait the interval
11942      */
11943     startAutoRefresh : function(interval, url, params, callback, refreshNow){
11944         if(refreshNow){
11945             this.update(url || this.defaultUrl, params, callback, true);
11946         }
11947         if(this.autoRefreshProcId){
11948             clearInterval(this.autoRefreshProcId);
11949         }
11950         this.autoRefreshProcId = setInterval(this.update.createDelegate(this, [url || this.defaultUrl, params, callback, true]), interval*1000);
11951     },
11952
11953     /**
11954      * Stop auto refresh on this element.
11955      */
11956      stopAutoRefresh : function(){
11957         if(this.autoRefreshProcId){
11958             clearInterval(this.autoRefreshProcId);
11959             delete this.autoRefreshProcId;
11960         }
11961     },
11962
11963     isAutoRefreshing : function(){
11964        return this.autoRefreshProcId ? true : false;
11965     },
11966     /**
11967      * Called to update the element to "Loading" state. Override to perform custom action.
11968      */
11969     showLoading : function(){
11970         if(this.showLoadIndicator){
11971             this.el.update(this.indicatorText);
11972         }
11973     },
11974
11975     /**
11976      * Adds unique parameter to query string if disableCaching = true
11977      * @private
11978      */
11979     prepareUrl : function(url){
11980         if(this.disableCaching){
11981             var append = "_dc=" + (new Date().getTime());
11982             if(url.indexOf("?") !== -1){
11983                 url += "&" + append;
11984             }else{
11985                 url += "?" + append;
11986             }
11987         }
11988         return url;
11989     },
11990
11991     /**
11992      * @private
11993      */
11994     processSuccess : function(response){
11995         this.transaction = null;
11996         if(response.argument.form && response.argument.reset){
11997             try{ // put in try/catch since some older FF releases had problems with this
11998                 response.argument.form.reset();
11999             }catch(e){}
12000         }
12001         if(this.loadScripts){
12002             this.renderer.render(this.el, response, this,
12003                 this.updateComplete.createDelegate(this, [response]));
12004         }else{
12005             this.renderer.render(this.el, response, this);
12006             this.updateComplete(response);
12007         }
12008     },
12009
12010     updateComplete : function(response){
12011         this.fireEvent("update", this.el, response);
12012         if(typeof response.argument.callback == "function"){
12013             response.argument.callback(this.el, true, response);
12014         }
12015     },
12016
12017     /**
12018      * @private
12019      */
12020     processFailure : function(response){
12021         this.transaction = null;
12022         this.fireEvent("failure", this.el, response);
12023         if(typeof response.argument.callback == "function"){
12024             response.argument.callback(this.el, false, response);
12025         }
12026     },
12027
12028     /**
12029      * Set the content renderer for this UpdateManager. See {@link Roo.UpdateManager.BasicRenderer#render} for more details.
12030      * @param {Object} renderer The object implementing the render() method
12031      */
12032     setRenderer : function(renderer){
12033         this.renderer = renderer;
12034     },
12035
12036     getRenderer : function(){
12037        return this.renderer;
12038     },
12039
12040     /**
12041      * Set the defaultUrl used for updates
12042      * @param {String/Function} defaultUrl The url or a function to call to get the url
12043      */
12044     setDefaultUrl : function(defaultUrl){
12045         this.defaultUrl = defaultUrl;
12046     },
12047
12048     /**
12049      * Aborts the executing transaction
12050      */
12051     abort : function(){
12052         if(this.transaction){
12053             Roo.Ajax.abort(this.transaction);
12054         }
12055     },
12056
12057     /**
12058      * Returns true if an update is in progress
12059      * @return {Boolean}
12060      */
12061     isUpdating : function(){
12062         if(this.transaction){
12063             return Roo.Ajax.isLoading(this.transaction);
12064         }
12065         return false;
12066     }
12067 });
12068
12069 /**
12070  * @class Roo.UpdateManager.defaults
12071  * @static (not really - but it helps the doc tool)
12072  * The defaults collection enables customizing the default properties of UpdateManager
12073  */
12074    Roo.UpdateManager.defaults = {
12075        /**
12076          * Timeout for requests or form posts in seconds (Defaults 30 seconds).
12077          * @type Number
12078          */
12079          timeout : 30,
12080
12081          /**
12082          * True to process scripts by default (Defaults to false).
12083          * @type Boolean
12084          */
12085         loadScripts : false,
12086
12087         /**
12088         * Blank page URL to use with SSL file uploads (Defaults to "javascript:false").
12089         * @type String
12090         */
12091         sslBlankUrl : (Roo.SSL_SECURE_URL || "javascript:false"),
12092         /**
12093          * Whether to append unique parameter on get request to disable caching (Defaults to false).
12094          * @type Boolean
12095          */
12096         disableCaching : false,
12097         /**
12098          * Whether to show indicatorText when loading (Defaults to true).
12099          * @type Boolean
12100          */
12101         showLoadIndicator : true,
12102         /**
12103          * Text for loading indicator (Defaults to '&lt;div class="loading-indicator"&gt;Loading...&lt;/div&gt;').
12104          * @type String
12105          */
12106         indicatorText : '<div class="loading-indicator">Loading...</div>'
12107    };
12108
12109 /**
12110  * Static convenience method. This method is deprecated in favor of el.load({url:'foo.php', ...}).
12111  *Usage:
12112  * <pre><code>Roo.UpdateManager.updateElement("my-div", "stuff.php");</code></pre>
12113  * @param {String/HTMLElement/Roo.Element} el The element to update
12114  * @param {String} url The url
12115  * @param {String/Object} params (optional) Url encoded param string or an object of name/value pairs
12116  * @param {Object} options (optional) A config object with any of the UpdateManager properties you want to set - for example: {disableCaching:true, indicatorText: "Loading data..."}
12117  * @static
12118  * @deprecated
12119  * @member Roo.UpdateManager
12120  */
12121 Roo.UpdateManager.updateElement = function(el, url, params, options){
12122     var um = Roo.get(el, true).getUpdateManager();
12123     Roo.apply(um, options);
12124     um.update(url, params, options ? options.callback : null);
12125 };
12126 // alias for backwards compat
12127 Roo.UpdateManager.update = Roo.UpdateManager.updateElement;
12128 /**
12129  * @class Roo.UpdateManager.BasicRenderer
12130  * Default Content renderer. Updates the elements innerHTML with the responseText.
12131  */
12132 Roo.UpdateManager.BasicRenderer = function(){};
12133
12134 Roo.UpdateManager.BasicRenderer.prototype = {
12135     /**
12136      * This is called when the transaction is completed and it's time to update the element - The BasicRenderer
12137      * updates the elements innerHTML with the responseText - To perform a custom render (i.e. XML or JSON processing),
12138      * create an object with a "render(el, response)" method and pass it to setRenderer on the UpdateManager.
12139      * @param {Roo.Element} el The element being rendered
12140      * @param {Object} response The YUI Connect response object
12141      * @param {UpdateManager} updateManager The calling update manager
12142      * @param {Function} callback A callback that will need to be called if loadScripts is true on the UpdateManager
12143      */
12144      render : function(el, response, updateManager, callback){
12145         el.update(response.responseText, updateManager.loadScripts, callback);
12146     }
12147 };
12148 /*
12149  * Based on:
12150  * Roo JS
12151  * (c)) Alan Knowles
12152  * Licence : LGPL
12153  */
12154
12155
12156 /**
12157  * @class Roo.DomTemplate
12158  * @extends Roo.Template
12159  * An effort at a dom based template engine..
12160  *
12161  * Similar to XTemplate, except it uses dom parsing to create the template..
12162  *
12163  * Supported features:
12164  *
12165  *  Tags:
12166
12167 <pre><code>
12168       {a_variable} - output encoded.
12169       {a_variable.format:("Y-m-d")} - call a method on the variable
12170       {a_variable:raw} - unencoded output
12171       {a_variable:toFixed(1,2)} - Roo.util.Format."toFixed"
12172       {a_variable:this.method_on_template(...)} - call a method on the template object.
12173  
12174 </code></pre>
12175  *  The tpl tag:
12176 <pre><code>
12177         &lt;div roo-for="a_variable or condition.."&gt;&lt;/div&gt;
12178         &lt;div roo-if="a_variable or condition"&gt;&lt;/div&gt;
12179         &lt;div roo-exec="some javascript"&gt;&lt;/div&gt;
12180         &lt;div roo-name="named_template"&gt;&lt;/div&gt; 
12181   
12182 </code></pre>
12183  *      
12184  */
12185 Roo.DomTemplate = function()
12186 {
12187      Roo.DomTemplate.superclass.constructor.apply(this, arguments);
12188      if (this.html) {
12189         this.compile();
12190      }
12191 };
12192
12193
12194 Roo.extend(Roo.DomTemplate, Roo.Template, {
12195     /**
12196      * id counter for sub templates.
12197      */
12198     id : 0,
12199     /**
12200      * flag to indicate if dom parser is inside a pre,
12201      * it will strip whitespace if not.
12202      */
12203     inPre : false,
12204     
12205     /**
12206      * The various sub templates
12207      */
12208     tpls : false,
12209     
12210     
12211     
12212     /**
12213      *
12214      * basic tag replacing syntax
12215      * WORD:WORD()
12216      *
12217      * // you can fake an object call by doing this
12218      *  x.t:(test,tesT) 
12219      * 
12220      */
12221     re : /(\{|\%7B)([\w-\.]+)(?:\:([\w\.]*)(?:\(([^)]*?)?\))?)?(\}|\%7D)/g,
12222     //re : /\{([\w-\.]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
12223     
12224     iterChild : function (node, method) {
12225         
12226         var oldPre = this.inPre;
12227         if (node.tagName == 'PRE') {
12228             this.inPre = true;
12229         }
12230         for( var i = 0; i < node.childNodes.length; i++) {
12231             method.call(this, node.childNodes[i]);
12232         }
12233         this.inPre = oldPre;
12234     },
12235     
12236     
12237     
12238     /**
12239      * compile the template
12240      *
12241      * This is not recursive, so I'm not sure how nested templates are really going to be handled..
12242      *
12243      */
12244     compile: function()
12245     {
12246         var s = this.html;
12247         
12248         // covert the html into DOM...
12249         var doc = false;
12250         var div =false;
12251         try {
12252             doc = document.implementation.createHTMLDocument("");
12253             doc.documentElement.innerHTML =   this.html  ;
12254             div = doc.documentElement;
12255         } catch (e) {
12256             // old IE... - nasty -- it causes all sorts of issues.. with
12257             // images getting pulled from server..
12258             div = document.createElement('div');
12259             div.innerHTML = this.html;
12260         }
12261         //doc.documentElement.innerHTML = htmlBody
12262          
12263         
12264         
12265         this.tpls = [];
12266         var _t = this;
12267         this.iterChild(div, function(n) {_t.compileNode(n, true); });
12268         
12269         var tpls = this.tpls;
12270         
12271         // create a top level template from the snippet..
12272         
12273         //Roo.log(div.innerHTML);
12274         
12275         var tpl = {
12276             uid : 'master',
12277             id : this.id++,
12278             attr : false,
12279             value : false,
12280             body : div.innerHTML,
12281             
12282             forCall : false,
12283             execCall : false,
12284             dom : div,
12285             isTop : true
12286             
12287         };
12288         tpls.unshift(tpl);
12289         
12290         
12291         // compile them...
12292         this.tpls = [];
12293         Roo.each(tpls, function(tp){
12294             this.compileTpl(tp);
12295             this.tpls[tp.id] = tp;
12296         }, this);
12297         
12298         this.master = tpls[0];
12299         return this;
12300         
12301         
12302     },
12303     
12304     compileNode : function(node, istop) {
12305         // test for
12306         //Roo.log(node);
12307         
12308         
12309         // skip anything not a tag..
12310         if (node.nodeType != 1) {
12311             if (node.nodeType == 3 && !this.inPre) {
12312                 // reduce white space..
12313                 node.nodeValue = node.nodeValue.replace(/\s+/g, ' '); 
12314                 
12315             }
12316             return;
12317         }
12318         
12319         var tpl = {
12320             uid : false,
12321             id : false,
12322             attr : false,
12323             value : false,
12324             body : '',
12325             
12326             forCall : false,
12327             execCall : false,
12328             dom : false,
12329             isTop : istop
12330             
12331             
12332         };
12333         
12334         
12335         switch(true) {
12336             case (node.hasAttribute('roo-for')): tpl.attr = 'for'; break;
12337             case (node.hasAttribute('roo-if')): tpl.attr = 'if'; break;
12338             case (node.hasAttribute('roo-name')): tpl.attr = 'name'; break;
12339             case (node.hasAttribute('roo-exec')): tpl.attr = 'exec'; break;
12340             // no default..
12341         }
12342         
12343         
12344         if (!tpl.attr) {
12345             // just itterate children..
12346             this.iterChild(node,this.compileNode);
12347             return;
12348         }
12349         tpl.uid = this.id++;
12350         tpl.value = node.getAttribute('roo-' +  tpl.attr);
12351         node.removeAttribute('roo-'+ tpl.attr);
12352         if (tpl.attr != 'name') {
12353             var placeholder = document.createTextNode('{domtpl' + tpl.uid + '}');
12354             node.parentNode.replaceChild(placeholder,  node);
12355         } else {
12356             
12357             var placeholder =  document.createElement('span');
12358             placeholder.className = 'roo-tpl-' + tpl.value;
12359             node.parentNode.replaceChild(placeholder,  node);
12360         }
12361         
12362         // parent now sees '{domtplXXXX}
12363         this.iterChild(node,this.compileNode);
12364         
12365         // we should now have node body...
12366         var div = document.createElement('div');
12367         div.appendChild(node);
12368         tpl.dom = node;
12369         // this has the unfortunate side effect of converting tagged attributes
12370         // eg. href="{...}" into %7C...%7D
12371         // this has been fixed by searching for those combo's although it's a bit hacky..
12372         
12373         
12374         tpl.body = div.innerHTML;
12375         
12376         
12377          
12378         tpl.id = tpl.uid;
12379         switch(tpl.attr) {
12380             case 'for' :
12381                 switch (tpl.value) {
12382                     case '.':  tpl.forCall = new Function('values', 'parent', 'with(values){ return values; }'); break;
12383                     case '..': tpl.forCall= new Function('values', 'parent', 'with(values){ return parent; }'); break;
12384                     default:   tpl.forCall= new Function('values', 'parent', 'with(values){ return '+tpl.value+'; }');
12385                 }
12386                 break;
12387             
12388             case 'exec':
12389                 tpl.execCall = new Function('values', 'parent', 'with(values){ '+(Roo.util.Format.htmlDecode(tpl.value))+'; }');
12390                 break;
12391             
12392             case 'if':     
12393                 tpl.ifCall = new Function('values', 'parent', 'with(values){ return '+(Roo.util.Format.htmlDecode(tpl.value))+'; }');
12394                 break;
12395             
12396             case 'name':
12397                 tpl.id  = tpl.value; // replace non characters???
12398                 break;
12399             
12400         }
12401         
12402         
12403         this.tpls.push(tpl);
12404         
12405         
12406         
12407     },
12408     
12409     
12410     
12411     
12412     /**
12413      * Compile a segment of the template into a 'sub-template'
12414      *
12415      * 
12416      * 
12417      *
12418      */
12419     compileTpl : function(tpl)
12420     {
12421         var fm = Roo.util.Format;
12422         var useF = this.disableFormats !== true;
12423         
12424         var sep = Roo.isGecko ? "+\n" : ",\n";
12425         
12426         var undef = function(str) {
12427             Roo.debug && Roo.log("Property not found :"  + str);
12428             return '';
12429         };
12430           
12431         //Roo.log(tpl.body);
12432         
12433         
12434         
12435         var fn = function(m, lbrace, name, format, args)
12436         {
12437             //Roo.log("ARGS");
12438             //Roo.log(arguments);
12439             args = args ? args.replace(/\\'/g,"'") : args;
12440             //["{TEST:(a,b,c)}", "TEST", "", "a,b,c", 0, "{TEST:(a,b,c)}"]
12441             if (typeof(format) == 'undefined') {
12442                 format =  'htmlEncode'; 
12443             }
12444             if (format == 'raw' ) {
12445                 format = false;
12446             }
12447             
12448             if(name.substr(0, 6) == 'domtpl'){
12449                 return "'"+ sep +'this.applySubTemplate('+name.substr(6)+', values, parent)'+sep+"'";
12450             }
12451             
12452             // build an array of options to determine if value is undefined..
12453             
12454             // basically get 'xxxx.yyyy' then do
12455             // (typeof(xxxx) == 'undefined' || typeof(xxx.yyyy) == 'undefined') ?
12456             //    (function () { Roo.log("Property not found"); return ''; })() :
12457             //    ......
12458             
12459             var udef_ar = [];
12460             var lookfor = '';
12461             Roo.each(name.split('.'), function(st) {
12462                 lookfor += (lookfor.length ? '.': '') + st;
12463                 udef_ar.push(  "(typeof(" + lookfor + ") == 'undefined')"  );
12464             });
12465             
12466             var udef_st = '((' + udef_ar.join(" || ") +") ? undef('" + name + "') : "; // .. needs )
12467             
12468             
12469             if(format && useF){
12470                 
12471                 args = args ? ',' + args : "";
12472                  
12473                 if(format.substr(0, 5) != "this."){
12474                     format = "fm." + format + '(';
12475                 }else{
12476                     format = 'this.call("'+ format.substr(5) + '", ';
12477                     args = ", values";
12478                 }
12479                 
12480                 return "'"+ sep +   udef_st   +    format + name + args + "))"+sep+"'";
12481             }
12482              
12483             if (args && args.length) {
12484                 // called with xxyx.yuu:(test,test)
12485                 // change to ()
12486                 return "'"+ sep + udef_st  + name + '(' +  args + "))"+sep+"'";
12487             }
12488             // raw.. - :raw modifier..
12489             return "'"+ sep + udef_st  + name + ")"+sep+"'";
12490             
12491         };
12492         var body;
12493         // branched to use + in gecko and [].join() in others
12494         if(Roo.isGecko){
12495             body = "tpl.compiled = function(values, parent){  with(values) { return '" +
12496                    tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
12497                     "';};};";
12498         }else{
12499             body = ["tpl.compiled = function(values, parent){  with (values) { return ['"];
12500             body.push(tpl.body.replace(/(\r\n|\n)/g,
12501                             '\\n').replace(/'/g, "\\'").replace(this.re, fn));
12502             body.push("'].join('');};};");
12503             body = body.join('');
12504         }
12505         
12506         Roo.debug && Roo.log(body.replace(/\\n/,'\n'));
12507        
12508         /** eval:var:tpl eval:var:fm eval:var:useF eval:var:undef  */
12509         eval(body);
12510         
12511         return this;
12512     },
12513      
12514     /**
12515      * same as applyTemplate, except it's done to one of the subTemplates
12516      * when using named templates, you can do:
12517      *
12518      * var str = pl.applySubTemplate('your-name', values);
12519      *
12520      * 
12521      * @param {Number} id of the template
12522      * @param {Object} values to apply to template
12523      * @param {Object} parent (normaly the instance of this object)
12524      */
12525     applySubTemplate : function(id, values, parent)
12526     {
12527         
12528         
12529         var t = this.tpls[id];
12530         
12531         
12532         try { 
12533             if(t.ifCall && !t.ifCall.call(this, values, parent)){
12534                 Roo.debug && Roo.log('if call on ' + t.value + ' return false');
12535                 return '';
12536             }
12537         } catch(e) {
12538             Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on roo-if="' + t.value + '" - ' + e.toString());
12539             Roo.log(values);
12540           
12541             return '';
12542         }
12543         try { 
12544             
12545             if(t.execCall && t.execCall.call(this, values, parent)){
12546                 return '';
12547             }
12548         } catch(e) {
12549             Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on roo-for="' + t.value + '" - ' + e.toString());
12550             Roo.log(values);
12551             return '';
12552         }
12553         
12554         try {
12555             var vs = t.forCall ? t.forCall.call(this, values, parent) : values;
12556             parent = t.target ? values : parent;
12557             if(t.forCall && vs instanceof Array){
12558                 var buf = [];
12559                 for(var i = 0, len = vs.length; i < len; i++){
12560                     try {
12561                         buf[buf.length] = t.compiled.call(this, vs[i], parent);
12562                     } catch (e) {
12563                         Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on body="' + t.value + '" - ' + e.toString());
12564                         Roo.log(e.body);
12565                         //Roo.log(t.compiled);
12566                         Roo.log(vs[i]);
12567                     }   
12568                 }
12569                 return buf.join('');
12570             }
12571         } catch (e) {
12572             Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on roo-for="' + t.value + '" - ' + e.toString());
12573             Roo.log(values);
12574             return '';
12575         }
12576         try {
12577             return t.compiled.call(this, vs, parent);
12578         } catch (e) {
12579             Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on body="' + t.value + '" - ' + e.toString());
12580             Roo.log(e.body);
12581             //Roo.log(t.compiled);
12582             Roo.log(values);
12583             return '';
12584         }
12585     },
12586
12587    
12588
12589     applyTemplate : function(values){
12590         return this.master.compiled.call(this, values, {});
12591         //var s = this.subs;
12592     },
12593
12594     apply : function(){
12595         return this.applyTemplate.apply(this, arguments);
12596     }
12597
12598  });
12599
12600 Roo.DomTemplate.from = function(el){
12601     el = Roo.getDom(el);
12602     return new Roo.Domtemplate(el.value || el.innerHTML);
12603 };/*
12604  * Based on:
12605  * Ext JS Library 1.1.1
12606  * Copyright(c) 2006-2007, Ext JS, LLC.
12607  *
12608  * Originally Released Under LGPL - original licence link has changed is not relivant.
12609  *
12610  * Fork - LGPL
12611  * <script type="text/javascript">
12612  */
12613
12614 /**
12615  * @class Roo.util.DelayedTask
12616  * Provides a convenient method of performing setTimeout where a new
12617  * timeout cancels the old timeout. An example would be performing validation on a keypress.
12618  * You can use this class to buffer
12619  * the keypress events for a certain number of milliseconds, and perform only if they stop
12620  * for that amount of time.
12621  * @constructor The parameters to this constructor serve as defaults and are not required.
12622  * @param {Function} fn (optional) The default function to timeout
12623  * @param {Object} scope (optional) The default scope of that timeout
12624  * @param {Array} args (optional) The default Array of arguments
12625  */
12626 Roo.util.DelayedTask = function(fn, scope, args){
12627     var id = null, d, t;
12628
12629     var call = function(){
12630         var now = new Date().getTime();
12631         if(now - t >= d){
12632             clearInterval(id);
12633             id = null;
12634             fn.apply(scope, args || []);
12635         }
12636     };
12637     /**
12638      * Cancels any pending timeout and queues a new one
12639      * @param {Number} delay The milliseconds to delay
12640      * @param {Function} newFn (optional) Overrides function passed to constructor
12641      * @param {Object} newScope (optional) Overrides scope passed to constructor
12642      * @param {Array} newArgs (optional) Overrides args passed to constructor
12643      */
12644     this.delay = function(delay, newFn, newScope, newArgs){
12645         if(id && delay != d){
12646             this.cancel();
12647         }
12648         d = delay;
12649         t = new Date().getTime();
12650         fn = newFn || fn;
12651         scope = newScope || scope;
12652         args = newArgs || args;
12653         if(!id){
12654             id = setInterval(call, d);
12655         }
12656     };
12657
12658     /**
12659      * Cancel the last queued timeout
12660      */
12661     this.cancel = function(){
12662         if(id){
12663             clearInterval(id);
12664             id = null;
12665         }
12666     };
12667 };/*
12668  * Based on:
12669  * Ext JS Library 1.1.1
12670  * Copyright(c) 2006-2007, Ext JS, LLC.
12671  *
12672  * Originally Released Under LGPL - original licence link has changed is not relivant.
12673  *
12674  * Fork - LGPL
12675  * <script type="text/javascript">
12676  */
12677  
12678  
12679 Roo.util.TaskRunner = function(interval){
12680     interval = interval || 10;
12681     var tasks = [], removeQueue = [];
12682     var id = 0;
12683     var running = false;
12684
12685     var stopThread = function(){
12686         running = false;
12687         clearInterval(id);
12688         id = 0;
12689     };
12690
12691     var startThread = function(){
12692         if(!running){
12693             running = true;
12694             id = setInterval(runTasks, interval);
12695         }
12696     };
12697
12698     var removeTask = function(task){
12699         removeQueue.push(task);
12700         if(task.onStop){
12701             task.onStop();
12702         }
12703     };
12704
12705     var runTasks = function(){
12706         if(removeQueue.length > 0){
12707             for(var i = 0, len = removeQueue.length; i < len; i++){
12708                 tasks.remove(removeQueue[i]);
12709             }
12710             removeQueue = [];
12711             if(tasks.length < 1){
12712                 stopThread();
12713                 return;
12714             }
12715         }
12716         var now = new Date().getTime();
12717         for(var i = 0, len = tasks.length; i < len; ++i){
12718             var t = tasks[i];
12719             var itime = now - t.taskRunTime;
12720             if(t.interval <= itime){
12721                 var rt = t.run.apply(t.scope || t, t.args || [++t.taskRunCount]);
12722                 t.taskRunTime = now;
12723                 if(rt === false || t.taskRunCount === t.repeat){
12724                     removeTask(t);
12725                     return;
12726                 }
12727             }
12728             if(t.duration && t.duration <= (now - t.taskStartTime)){
12729                 removeTask(t);
12730             }
12731         }
12732     };
12733
12734     /**
12735      * Queues a new task.
12736      * @param {Object} task
12737      */
12738     this.start = function(task){
12739         tasks.push(task);
12740         task.taskStartTime = new Date().getTime();
12741         task.taskRunTime = 0;
12742         task.taskRunCount = 0;
12743         startThread();
12744         return task;
12745     };
12746
12747     this.stop = function(task){
12748         removeTask(task);
12749         return task;
12750     };
12751
12752     this.stopAll = function(){
12753         stopThread();
12754         for(var i = 0, len = tasks.length; i < len; i++){
12755             if(tasks[i].onStop){
12756                 tasks[i].onStop();
12757             }
12758         }
12759         tasks = [];
12760         removeQueue = [];
12761     };
12762 };
12763
12764 Roo.TaskMgr = new Roo.util.TaskRunner();/*
12765  * Based on:
12766  * Ext JS Library 1.1.1
12767  * Copyright(c) 2006-2007, Ext JS, LLC.
12768  *
12769  * Originally Released Under LGPL - original licence link has changed is not relivant.
12770  *
12771  * Fork - LGPL
12772  * <script type="text/javascript">
12773  */
12774
12775  
12776 /**
12777  * @class Roo.util.MixedCollection
12778  * @extends Roo.util.Observable
12779  * A Collection class that maintains both numeric indexes and keys and exposes events.
12780  * @constructor
12781  * @param {Boolean} allowFunctions True if the addAll function should add function references to the
12782  * collection (defaults to false)
12783  * @param {Function} keyFn A function that can accept an item of the type(s) stored in this MixedCollection
12784  * and return the key value for that item.  This is used when available to look up the key on items that
12785  * were passed without an explicit key parameter to a MixedCollection method.  Passing this parameter is
12786  * equivalent to providing an implementation for the {@link #getKey} method.
12787  */
12788 Roo.util.MixedCollection = function(allowFunctions, keyFn){
12789     this.items = [];
12790     this.map = {};
12791     this.keys = [];
12792     this.length = 0;
12793     this.addEvents({
12794         /**
12795          * @event clear
12796          * Fires when the collection is cleared.
12797          */
12798         "clear" : true,
12799         /**
12800          * @event add
12801          * Fires when an item is added to the collection.
12802          * @param {Number} index The index at which the item was added.
12803          * @param {Object} o The item added.
12804          * @param {String} key The key associated with the added item.
12805          */
12806         "add" : true,
12807         /**
12808          * @event replace
12809          * Fires when an item is replaced in the collection.
12810          * @param {String} key he key associated with the new added.
12811          * @param {Object} old The item being replaced.
12812          * @param {Object} new The new item.
12813          */
12814         "replace" : true,
12815         /**
12816          * @event remove
12817          * Fires when an item is removed from the collection.
12818          * @param {Object} o The item being removed.
12819          * @param {String} key (optional) The key associated with the removed item.
12820          */
12821         "remove" : true,
12822         "sort" : true
12823     });
12824     this.allowFunctions = allowFunctions === true;
12825     if(keyFn){
12826         this.getKey = keyFn;
12827     }
12828     Roo.util.MixedCollection.superclass.constructor.call(this);
12829 };
12830
12831 Roo.extend(Roo.util.MixedCollection, Roo.util.Observable, {
12832     allowFunctions : false,
12833     
12834 /**
12835  * Adds an item to the collection.
12836  * @param {String} key The key to associate with the item
12837  * @param {Object} o The item to add.
12838  * @return {Object} The item added.
12839  */
12840     add : function(key, o){
12841         if(arguments.length == 1){
12842             o = arguments[0];
12843             key = this.getKey(o);
12844         }
12845         if(typeof key == "undefined" || key === null){
12846             this.length++;
12847             this.items.push(o);
12848             this.keys.push(null);
12849         }else{
12850             var old = this.map[key];
12851             if(old){
12852                 return this.replace(key, o);
12853             }
12854             this.length++;
12855             this.items.push(o);
12856             this.map[key] = o;
12857             this.keys.push(key);
12858         }
12859         this.fireEvent("add", this.length-1, o, key);
12860         return o;
12861     },
12862        
12863 /**
12864   * MixedCollection has a generic way to fetch keys if you implement getKey.
12865 <pre><code>
12866 // normal way
12867 var mc = new Roo.util.MixedCollection();
12868 mc.add(someEl.dom.id, someEl);
12869 mc.add(otherEl.dom.id, otherEl);
12870 //and so on
12871
12872 // using getKey
12873 var mc = new Roo.util.MixedCollection();
12874 mc.getKey = function(el){
12875    return el.dom.id;
12876 };
12877 mc.add(someEl);
12878 mc.add(otherEl);
12879
12880 // or via the constructor
12881 var mc = new Roo.util.MixedCollection(false, function(el){
12882    return el.dom.id;
12883 });
12884 mc.add(someEl);
12885 mc.add(otherEl);
12886 </code></pre>
12887  * @param o {Object} The item for which to find the key.
12888  * @return {Object} The key for the passed item.
12889  */
12890     getKey : function(o){
12891          return o.id; 
12892     },
12893    
12894 /**
12895  * Replaces an item in the collection.
12896  * @param {String} key The key associated with the item to replace, or the item to replace.
12897  * @param o {Object} o (optional) If the first parameter passed was a key, the item to associate with that key.
12898  * @return {Object}  The new item.
12899  */
12900     replace : function(key, o){
12901         if(arguments.length == 1){
12902             o = arguments[0];
12903             key = this.getKey(o);
12904         }
12905         var old = this.item(key);
12906         if(typeof key == "undefined" || key === null || typeof old == "undefined"){
12907              return this.add(key, o);
12908         }
12909         var index = this.indexOfKey(key);
12910         this.items[index] = o;
12911         this.map[key] = o;
12912         this.fireEvent("replace", key, old, o);
12913         return o;
12914     },
12915    
12916 /**
12917  * Adds all elements of an Array or an Object to the collection.
12918  * @param {Object/Array} objs An Object containing properties which will be added to the collection, or
12919  * an Array of values, each of which are added to the collection.
12920  */
12921     addAll : function(objs){
12922         if(arguments.length > 1 || objs instanceof Array){
12923             var args = arguments.length > 1 ? arguments : objs;
12924             for(var i = 0, len = args.length; i < len; i++){
12925                 this.add(args[i]);
12926             }
12927         }else{
12928             for(var key in objs){
12929                 if(this.allowFunctions || typeof objs[key] != "function"){
12930                     this.add(key, objs[key]);
12931                 }
12932             }
12933         }
12934     },
12935    
12936 /**
12937  * Executes the specified function once for every item in the collection, passing each
12938  * item as the first and only parameter. returning false from the function will stop the iteration.
12939  * @param {Function} fn The function to execute for each item.
12940  * @param {Object} scope (optional) The scope in which to execute the function.
12941  */
12942     each : function(fn, scope){
12943         var items = [].concat(this.items); // each safe for removal
12944         for(var i = 0, len = items.length; i < len; i++){
12945             if(fn.call(scope || items[i], items[i], i, len) === false){
12946                 break;
12947             }
12948         }
12949     },
12950    
12951 /**
12952  * Executes the specified function once for every key in the collection, passing each
12953  * key, and its associated item as the first two parameters.
12954  * @param {Function} fn The function to execute for each item.
12955  * @param {Object} scope (optional) The scope in which to execute the function.
12956  */
12957     eachKey : function(fn, scope){
12958         for(var i = 0, len = this.keys.length; i < len; i++){
12959             fn.call(scope || window, this.keys[i], this.items[i], i, len);
12960         }
12961     },
12962    
12963 /**
12964  * Returns the first item in the collection which elicits a true return value from the
12965  * passed selection function.
12966  * @param {Function} fn The selection function to execute for each item.
12967  * @param {Object} scope (optional) The scope in which to execute the function.
12968  * @return {Object} The first item in the collection which returned true from the selection function.
12969  */
12970     find : function(fn, scope){
12971         for(var i = 0, len = this.items.length; i < len; i++){
12972             if(fn.call(scope || window, this.items[i], this.keys[i])){
12973                 return this.items[i];
12974             }
12975         }
12976         return null;
12977     },
12978    
12979 /**
12980  * Inserts an item at the specified index in the collection.
12981  * @param {Number} index The index to insert the item at.
12982  * @param {String} key The key to associate with the new item, or the item itself.
12983  * @param {Object} o  (optional) If the second parameter was a key, the new item.
12984  * @return {Object} The item inserted.
12985  */
12986     insert : function(index, key, o){
12987         if(arguments.length == 2){
12988             o = arguments[1];
12989             key = this.getKey(o);
12990         }
12991         if(index >= this.length){
12992             return this.add(key, o);
12993         }
12994         this.length++;
12995         this.items.splice(index, 0, o);
12996         if(typeof key != "undefined" && key != null){
12997             this.map[key] = o;
12998         }
12999         this.keys.splice(index, 0, key);
13000         this.fireEvent("add", index, o, key);
13001         return o;
13002     },
13003    
13004 /**
13005  * Removed an item from the collection.
13006  * @param {Object} o The item to remove.
13007  * @return {Object} The item removed.
13008  */
13009     remove : function(o){
13010         return this.removeAt(this.indexOf(o));
13011     },
13012    
13013 /**
13014  * Remove an item from a specified index in the collection.
13015  * @param {Number} index The index within the collection of the item to remove.
13016  */
13017     removeAt : function(index){
13018         if(index < this.length && index >= 0){
13019             this.length--;
13020             var o = this.items[index];
13021             this.items.splice(index, 1);
13022             var key = this.keys[index];
13023             if(typeof key != "undefined"){
13024                 delete this.map[key];
13025             }
13026             this.keys.splice(index, 1);
13027             this.fireEvent("remove", o, key);
13028         }
13029     },
13030    
13031 /**
13032  * Removed an item associated with the passed key fom the collection.
13033  * @param {String} key The key of the item to remove.
13034  */
13035     removeKey : function(key){
13036         return this.removeAt(this.indexOfKey(key));
13037     },
13038    
13039 /**
13040  * Returns the number of items in the collection.
13041  * @return {Number} the number of items in the collection.
13042  */
13043     getCount : function(){
13044         return this.length; 
13045     },
13046    
13047 /**
13048  * Returns index within the collection of the passed Object.
13049  * @param {Object} o The item to find the index of.
13050  * @return {Number} index of the item.
13051  */
13052     indexOf : function(o){
13053         if(!this.items.indexOf){
13054             for(var i = 0, len = this.items.length; i < len; i++){
13055                 if(this.items[i] == o) return i;
13056             }
13057             return -1;
13058         }else{
13059             return this.items.indexOf(o);
13060         }
13061     },
13062    
13063 /**
13064  * Returns index within the collection of the passed key.
13065  * @param {String} key The key to find the index of.
13066  * @return {Number} index of the key.
13067  */
13068     indexOfKey : function(key){
13069         if(!this.keys.indexOf){
13070             for(var i = 0, len = this.keys.length; i < len; i++){
13071                 if(this.keys[i] == key) return i;
13072             }
13073             return -1;
13074         }else{
13075             return this.keys.indexOf(key);
13076         }
13077     },
13078    
13079 /**
13080  * Returns the item associated with the passed key OR index. Key has priority over index.
13081  * @param {String/Number} key The key or index of the item.
13082  * @return {Object} The item associated with the passed key.
13083  */
13084     item : function(key){
13085         var item = typeof this.map[key] != "undefined" ? this.map[key] : this.items[key];
13086         return typeof item != 'function' || this.allowFunctions ? item : null; // for prototype!
13087     },
13088     
13089 /**
13090  * Returns the item at the specified index.
13091  * @param {Number} index The index of the item.
13092  * @return {Object}
13093  */
13094     itemAt : function(index){
13095         return this.items[index];
13096     },
13097     
13098 /**
13099  * Returns the item associated with the passed key.
13100  * @param {String/Number} key The key of the item.
13101  * @return {Object} The item associated with the passed key.
13102  */
13103     key : function(key){
13104         return this.map[key];
13105     },
13106    
13107 /**
13108  * Returns true if the collection contains the passed Object as an item.
13109  * @param {Object} o  The Object to look for in the collection.
13110  * @return {Boolean} True if the collection contains the Object as an item.
13111  */
13112     contains : function(o){
13113         return this.indexOf(o) != -1;
13114     },
13115    
13116 /**
13117  * Returns true if the collection contains the passed Object as a key.
13118  * @param {String} key The key to look for in the collection.
13119  * @return {Boolean} True if the collection contains the Object as a key.
13120  */
13121     containsKey : function(key){
13122         return typeof this.map[key] != "undefined";
13123     },
13124    
13125 /**
13126  * Removes all items from the collection.
13127  */
13128     clear : function(){
13129         this.length = 0;
13130         this.items = [];
13131         this.keys = [];
13132         this.map = {};
13133         this.fireEvent("clear");
13134     },
13135    
13136 /**
13137  * Returns the first item in the collection.
13138  * @return {Object} the first item in the collection..
13139  */
13140     first : function(){
13141         return this.items[0]; 
13142     },
13143    
13144 /**
13145  * Returns the last item in the collection.
13146  * @return {Object} the last item in the collection..
13147  */
13148     last : function(){
13149         return this.items[this.length-1];   
13150     },
13151     
13152     _sort : function(property, dir, fn){
13153         var dsc = String(dir).toUpperCase() == "DESC" ? -1 : 1;
13154         fn = fn || function(a, b){
13155             return a-b;
13156         };
13157         var c = [], k = this.keys, items = this.items;
13158         for(var i = 0, len = items.length; i < len; i++){
13159             c[c.length] = {key: k[i], value: items[i], index: i};
13160         }
13161         c.sort(function(a, b){
13162             var v = fn(a[property], b[property]) * dsc;
13163             if(v == 0){
13164                 v = (a.index < b.index ? -1 : 1);
13165             }
13166             return v;
13167         });
13168         for(var i = 0, len = c.length; i < len; i++){
13169             items[i] = c[i].value;
13170             k[i] = c[i].key;
13171         }
13172         this.fireEvent("sort", this);
13173     },
13174     
13175     /**
13176      * Sorts this collection with the passed comparison function
13177      * @param {String} direction (optional) "ASC" or "DESC"
13178      * @param {Function} fn (optional) comparison function
13179      */
13180     sort : function(dir, fn){
13181         this._sort("value", dir, fn);
13182     },
13183     
13184     /**
13185      * Sorts this collection by keys
13186      * @param {String} direction (optional) "ASC" or "DESC"
13187      * @param {Function} fn (optional) a comparison function (defaults to case insensitive string)
13188      */
13189     keySort : function(dir, fn){
13190         this._sort("key", dir, fn || function(a, b){
13191             return String(a).toUpperCase()-String(b).toUpperCase();
13192         });
13193     },
13194     
13195     /**
13196      * Returns a range of items in this collection
13197      * @param {Number} startIndex (optional) defaults to 0
13198      * @param {Number} endIndex (optional) default to the last item
13199      * @return {Array} An array of items
13200      */
13201     getRange : function(start, end){
13202         var items = this.items;
13203         if(items.length < 1){
13204             return [];
13205         }
13206         start = start || 0;
13207         end = Math.min(typeof end == "undefined" ? this.length-1 : end, this.length-1);
13208         var r = [];
13209         if(start <= end){
13210             for(var i = start; i <= end; i++) {
13211                     r[r.length] = items[i];
13212             }
13213         }else{
13214             for(var i = start; i >= end; i--) {
13215                     r[r.length] = items[i];
13216             }
13217         }
13218         return r;
13219     },
13220         
13221     /**
13222      * Filter the <i>objects</i> in this collection by a specific property. 
13223      * Returns a new collection that has been filtered.
13224      * @param {String} property A property on your objects
13225      * @param {String/RegExp} value Either string that the property values 
13226      * should start with or a RegExp to test against the property
13227      * @return {MixedCollection} The new filtered collection
13228      */
13229     filter : function(property, value){
13230         if(!value.exec){ // not a regex
13231             value = String(value);
13232             if(value.length == 0){
13233                 return this.clone();
13234             }
13235             value = new RegExp("^" + Roo.escapeRe(value), "i");
13236         }
13237         return this.filterBy(function(o){
13238             return o && value.test(o[property]);
13239         });
13240         },
13241     
13242     /**
13243      * Filter by a function. * Returns a new collection that has been filtered.
13244      * The passed function will be called with each 
13245      * object in the collection. If the function returns true, the value is included 
13246      * otherwise it is filtered.
13247      * @param {Function} fn The function to be called, it will receive the args o (the object), k (the key)
13248      * @param {Object} scope (optional) The scope of the function (defaults to this) 
13249      * @return {MixedCollection} The new filtered collection
13250      */
13251     filterBy : function(fn, scope){
13252         var r = new Roo.util.MixedCollection();
13253         r.getKey = this.getKey;
13254         var k = this.keys, it = this.items;
13255         for(var i = 0, len = it.length; i < len; i++){
13256             if(fn.call(scope||this, it[i], k[i])){
13257                                 r.add(k[i], it[i]);
13258                         }
13259         }
13260         return r;
13261     },
13262     
13263     /**
13264      * Creates a duplicate of this collection
13265      * @return {MixedCollection}
13266      */
13267     clone : function(){
13268         var r = new Roo.util.MixedCollection();
13269         var k = this.keys, it = this.items;
13270         for(var i = 0, len = it.length; i < len; i++){
13271             r.add(k[i], it[i]);
13272         }
13273         r.getKey = this.getKey;
13274         return r;
13275     }
13276 });
13277 /**
13278  * Returns the item associated with the passed key or index.
13279  * @method
13280  * @param {String/Number} key The key or index of the item.
13281  * @return {Object} The item associated with the passed key.
13282  */
13283 Roo.util.MixedCollection.prototype.get = Roo.util.MixedCollection.prototype.item;/*
13284  * Based on:
13285  * Ext JS Library 1.1.1
13286  * Copyright(c) 2006-2007, Ext JS, LLC.
13287  *
13288  * Originally Released Under LGPL - original licence link has changed is not relivant.
13289  *
13290  * Fork - LGPL
13291  * <script type="text/javascript">
13292  */
13293 /**
13294  * @class Roo.util.JSON
13295  * Modified version of Douglas Crockford"s json.js that doesn"t
13296  * mess with the Object prototype 
13297  * http://www.json.org/js.html
13298  * @singleton
13299  */
13300 Roo.util.JSON = new (function(){
13301     var useHasOwn = {}.hasOwnProperty ? true : false;
13302     
13303     // crashes Safari in some instances
13304     //var validRE = /^("(\\.|[^"\\\n\r])*?"|[,:{}\[\]0-9.\-+Eaeflnr-u \n\r\t])+?$/;
13305     
13306     var pad = function(n) {
13307         return n < 10 ? "0" + n : n;
13308     };
13309     
13310     var m = {
13311         "\b": '\\b',
13312         "\t": '\\t',
13313         "\n": '\\n',
13314         "\f": '\\f',
13315         "\r": '\\r',
13316         '"' : '\\"',
13317         "\\": '\\\\'
13318     };
13319
13320     var encodeString = function(s){
13321         if (/["\\\x00-\x1f]/.test(s)) {
13322             return '"' + s.replace(/([\x00-\x1f\\"])/g, function(a, b) {
13323                 var c = m[b];
13324                 if(c){
13325                     return c;
13326                 }
13327                 c = b.charCodeAt();
13328                 return "\\u00" +
13329                     Math.floor(c / 16).toString(16) +
13330                     (c % 16).toString(16);
13331             }) + '"';
13332         }
13333         return '"' + s + '"';
13334     };
13335     
13336     var encodeArray = function(o){
13337         var a = ["["], b, i, l = o.length, v;
13338             for (i = 0; i < l; i += 1) {
13339                 v = o[i];
13340                 switch (typeof v) {
13341                     case "undefined":
13342                     case "function":
13343                     case "unknown":
13344                         break;
13345                     default:
13346                         if (b) {
13347                             a.push(',');
13348                         }
13349                         a.push(v === null ? "null" : Roo.util.JSON.encode(v));
13350                         b = true;
13351                 }
13352             }
13353             a.push("]");
13354             return a.join("");
13355     };
13356     
13357     var encodeDate = function(o){
13358         return '"' + o.getFullYear() + "-" +
13359                 pad(o.getMonth() + 1) + "-" +
13360                 pad(o.getDate()) + "T" +
13361                 pad(o.getHours()) + ":" +
13362                 pad(o.getMinutes()) + ":" +
13363                 pad(o.getSeconds()) + '"';
13364     };
13365     
13366     /**
13367      * Encodes an Object, Array or other value
13368      * @param {Mixed} o The variable to encode
13369      * @return {String} The JSON string
13370      */
13371     this.encode = function(o)
13372     {
13373         // should this be extended to fully wrap stringify..
13374         
13375         if(typeof o == "undefined" || o === null){
13376             return "null";
13377         }else if(o instanceof Array){
13378             return encodeArray(o);
13379         }else if(o instanceof Date){
13380             return encodeDate(o);
13381         }else if(typeof o == "string"){
13382             return encodeString(o);
13383         }else if(typeof o == "number"){
13384             return isFinite(o) ? String(o) : "null";
13385         }else if(typeof o == "boolean"){
13386             return String(o);
13387         }else {
13388             var a = ["{"], b, i, v;
13389             for (i in o) {
13390                 if(!useHasOwn || o.hasOwnProperty(i)) {
13391                     v = o[i];
13392                     switch (typeof v) {
13393                     case "undefined":
13394                     case "function":
13395                     case "unknown":
13396                         break;
13397                     default:
13398                         if(b){
13399                             a.push(',');
13400                         }
13401                         a.push(this.encode(i), ":",
13402                                 v === null ? "null" : this.encode(v));
13403                         b = true;
13404                     }
13405                 }
13406             }
13407             a.push("}");
13408             return a.join("");
13409         }
13410     };
13411     
13412     /**
13413      * Decodes (parses) a JSON string to an object. If the JSON is invalid, this function throws a SyntaxError.
13414      * @param {String} json The JSON string
13415      * @return {Object} The resulting object
13416      */
13417     this.decode = function(json){
13418         
13419         return  /** eval:var:json */ eval("(" + json + ')');
13420     };
13421 })();
13422 /** 
13423  * Shorthand for {@link Roo.util.JSON#encode}
13424  * @member Roo encode 
13425  * @method */
13426 Roo.encode = typeof(JSON) != 'undefined' && JSON.stringify ? JSON.stringify : Roo.util.JSON.encode;
13427 /** 
13428  * Shorthand for {@link Roo.util.JSON#decode}
13429  * @member Roo decode 
13430  * @method */
13431 Roo.decode = typeof(JSON) != 'undefined' && JSON.parse ? JSON.parse : Roo.util.JSON.decode;
13432 /*
13433  * Based on:
13434  * Ext JS Library 1.1.1
13435  * Copyright(c) 2006-2007, Ext JS, LLC.
13436  *
13437  * Originally Released Under LGPL - original licence link has changed is not relivant.
13438  *
13439  * Fork - LGPL
13440  * <script type="text/javascript">
13441  */
13442  
13443 /**
13444  * @class Roo.util.Format
13445  * Reusable data formatting functions
13446  * @singleton
13447  */
13448 Roo.util.Format = function(){
13449     var trimRe = /^\s+|\s+$/g;
13450     return {
13451         /**
13452          * Truncate a string and add an ellipsis ('...') to the end if it exceeds the specified length
13453          * @param {String} value The string to truncate
13454          * @param {Number} length The maximum length to allow before truncating
13455          * @return {String} The converted text
13456          */
13457         ellipsis : function(value, len){
13458             if(value && value.length > len){
13459                 return value.substr(0, len-3)+"...";
13460             }
13461             return value;
13462         },
13463
13464         /**
13465          * Checks a reference and converts it to empty string if it is undefined
13466          * @param {Mixed} value Reference to check
13467          * @return {Mixed} Empty string if converted, otherwise the original value
13468          */
13469         undef : function(value){
13470             return typeof value != "undefined" ? value : "";
13471         },
13472
13473         /**
13474          * Convert certain characters (&, <, >, and ') to their HTML character equivalents for literal display in web pages.
13475          * @param {String} value The string to encode
13476          * @return {String} The encoded text
13477          */
13478         htmlEncode : function(value){
13479             return !value ? value : String(value).replace(/&/g, "&amp;").replace(/>/g, "&gt;").replace(/</g, "&lt;").replace(/"/g, "&quot;");
13480         },
13481
13482         /**
13483          * Convert certain characters (&, <, >, and ') from their HTML character equivalents.
13484          * @param {String} value The string to decode
13485          * @return {String} The decoded text
13486          */
13487         htmlDecode : function(value){
13488             return !value ? value : String(value).replace(/&amp;/g, "&").replace(/&gt;/g, ">").replace(/&lt;/g, "<").replace(/&quot;/g, '"');
13489         },
13490
13491         /**
13492          * Trims any whitespace from either side of a string
13493          * @param {String} value The text to trim
13494          * @return {String} The trimmed text
13495          */
13496         trim : function(value){
13497             return String(value).replace(trimRe, "");
13498         },
13499
13500         /**
13501          * Returns a substring from within an original string
13502          * @param {String} value The original text
13503          * @param {Number} start The start index of the substring
13504          * @param {Number} length The length of the substring
13505          * @return {String} The substring
13506          */
13507         substr : function(value, start, length){
13508             return String(value).substr(start, length);
13509         },
13510
13511         /**
13512          * Converts a string to all lower case letters
13513          * @param {String} value The text to convert
13514          * @return {String} The converted text
13515          */
13516         lowercase : function(value){
13517             return String(value).toLowerCase();
13518         },
13519
13520         /**
13521          * Converts a string to all upper case letters
13522          * @param {String} value The text to convert
13523          * @return {String} The converted text
13524          */
13525         uppercase : function(value){
13526             return String(value).toUpperCase();
13527         },
13528
13529         /**
13530          * Converts the first character only of a string to upper case
13531          * @param {String} value The text to convert
13532          * @return {String} The converted text
13533          */
13534         capitalize : function(value){
13535             return !value ? value : value.charAt(0).toUpperCase() + value.substr(1).toLowerCase();
13536         },
13537
13538         // private
13539         call : function(value, fn){
13540             if(arguments.length > 2){
13541                 var args = Array.prototype.slice.call(arguments, 2);
13542                 args.unshift(value);
13543                  
13544                 return /** eval:var:value */  eval(fn).apply(window, args);
13545             }else{
13546                 /** eval:var:value */
13547                 return /** eval:var:value */ eval(fn).call(window, value);
13548             }
13549         },
13550
13551        
13552         /**
13553          * safer version of Math.toFixed..??/
13554          * @param {Number/String} value The numeric value to format
13555          * @param {Number/String} value Decimal places 
13556          * @return {String} The formatted currency string
13557          */
13558         toFixed : function(v, n)
13559         {
13560             // why not use to fixed - precision is buggered???
13561             if (!n) {
13562                 return Math.round(v-0);
13563             }
13564             var fact = Math.pow(10,n+1);
13565             v = (Math.round((v-0)*fact))/fact;
13566             var z = (''+fact).substring(2);
13567             if (v == Math.floor(v)) {
13568                 return Math.floor(v) + '.' + z;
13569             }
13570             
13571             // now just padd decimals..
13572             var ps = String(v).split('.');
13573             var fd = (ps[1] + z);
13574             var r = fd.substring(0,n); 
13575             var rm = fd.substring(n); 
13576             if (rm < 5) {
13577                 return ps[0] + '.' + r;
13578             }
13579             r*=1; // turn it into a number;
13580             r++;
13581             if (String(r).length != n) {
13582                 ps[0]*=1;
13583                 ps[0]++;
13584                 r = String(r).substring(1); // chop the end off.
13585             }
13586             
13587             return ps[0] + '.' + r;
13588              
13589         },
13590         
13591         /**
13592          * Format a number as US currency
13593          * @param {Number/String} value The numeric value to format
13594          * @return {String} The formatted currency string
13595          */
13596         usMoney : function(v){
13597             return '$' + Roo.util.Format.number(v);
13598         },
13599         
13600         /**
13601          * Format a number
13602          * eventually this should probably emulate php's number_format
13603          * @param {Number/String} value The numeric value to format
13604          * @param {Number} decimals number of decimal places
13605          * @return {String} The formatted currency string
13606          */
13607         number : function(v,decimals)
13608         {
13609             // multiply and round.
13610             decimals = typeof(decimals) == 'undefined' ? 2 : decimals;
13611             var mul = Math.pow(10, decimals);
13612             var zero = String(mul).substring(1);
13613             v = (Math.round((v-0)*mul))/mul;
13614             
13615             // if it's '0' number.. then
13616             
13617             //v = (v == Math.floor(v)) ? v + "." + zero : ((v*10 == Math.floor(v*10)) ? v + "0" : v);
13618             v = String(v);
13619             var ps = v.split('.');
13620             var whole = ps[0];
13621             
13622             
13623             var r = /(\d+)(\d{3})/;
13624             // add comma's
13625             while (r.test(whole)) {
13626                 whole = whole.replace(r, '$1' + ',' + '$2');
13627             }
13628             
13629             
13630             var sub = ps[1] ?
13631                     // has decimals..
13632                     (decimals ?  ('.'+ ps[1] + zero.substring(ps[1].length)) : '') :
13633                     // does not have decimals
13634                     (decimals ? ('.' + zero) : '');
13635             
13636             
13637             return whole + sub ;
13638         },
13639         
13640         /**
13641          * Parse a value into a formatted date using the specified format pattern.
13642          * @param {Mixed} value The value to format
13643          * @param {String} format (optional) Any valid date format string (defaults to 'm/d/Y')
13644          * @return {String} The formatted date string
13645          */
13646         date : function(v, format){
13647             if(!v){
13648                 return "";
13649             }
13650             if(!(v instanceof Date)){
13651                 v = new Date(Date.parse(v));
13652             }
13653             return v.dateFormat(format || Roo.util.Format.defaults.date);
13654         },
13655
13656         /**
13657          * Returns a date rendering function that can be reused to apply a date format multiple times efficiently
13658          * @param {String} format Any valid date format string
13659          * @return {Function} The date formatting function
13660          */
13661         dateRenderer : function(format){
13662             return function(v){
13663                 return Roo.util.Format.date(v, format);  
13664             };
13665         },
13666
13667         // private
13668         stripTagsRE : /<\/?[^>]+>/gi,
13669         
13670         /**
13671          * Strips all HTML tags
13672          * @param {Mixed} value The text from which to strip tags
13673          * @return {String} The stripped text
13674          */
13675         stripTags : function(v){
13676             return !v ? v : String(v).replace(this.stripTagsRE, "");
13677         }
13678     };
13679 }();
13680 Roo.util.Format.defaults = {
13681     date : 'd/M/Y'
13682 };/*
13683  * Based on:
13684  * Ext JS Library 1.1.1
13685  * Copyright(c) 2006-2007, Ext JS, LLC.
13686  *
13687  * Originally Released Under LGPL - original licence link has changed is not relivant.
13688  *
13689  * Fork - LGPL
13690  * <script type="text/javascript">
13691  */
13692
13693
13694  
13695
13696 /**
13697  * @class Roo.MasterTemplate
13698  * @extends Roo.Template
13699  * Provides a template that can have child templates. The syntax is:
13700 <pre><code>
13701 var t = new Roo.MasterTemplate(
13702         '&lt;select name="{name}"&gt;',
13703                 '&lt;tpl name="options"&gt;&lt;option value="{value:trim}"&gt;{text:ellipsis(10)}&lt;/option&gt;&lt;/tpl&gt;',
13704         '&lt;/select&gt;'
13705 );
13706 t.add('options', {value: 'foo', text: 'bar'});
13707 // or you can add multiple child elements in one shot
13708 t.addAll('options', [
13709     {value: 'foo', text: 'bar'},
13710     {value: 'foo2', text: 'bar2'},
13711     {value: 'foo3', text: 'bar3'}
13712 ]);
13713 // then append, applying the master template values
13714 t.append('my-form', {name: 'my-select'});
13715 </code></pre>
13716 * A name attribute for the child template is not required if you have only one child
13717 * template or you want to refer to them by index.
13718  */
13719 Roo.MasterTemplate = function(){
13720     Roo.MasterTemplate.superclass.constructor.apply(this, arguments);
13721     this.originalHtml = this.html;
13722     var st = {};
13723     var m, re = this.subTemplateRe;
13724     re.lastIndex = 0;
13725     var subIndex = 0;
13726     while(m = re.exec(this.html)){
13727         var name = m[1], content = m[2];
13728         st[subIndex] = {
13729             name: name,
13730             index: subIndex,
13731             buffer: [],
13732             tpl : new Roo.Template(content)
13733         };
13734         if(name){
13735             st[name] = st[subIndex];
13736         }
13737         st[subIndex].tpl.compile();
13738         st[subIndex].tpl.call = this.call.createDelegate(this);
13739         subIndex++;
13740     }
13741     this.subCount = subIndex;
13742     this.subs = st;
13743 };
13744 Roo.extend(Roo.MasterTemplate, Roo.Template, {
13745     /**
13746     * The regular expression used to match sub templates
13747     * @type RegExp
13748     * @property
13749     */
13750     subTemplateRe : /<tpl(?:\sname="([\w-]+)")?>((?:.|\n)*?)<\/tpl>/gi,
13751
13752     /**
13753      * Applies the passed values to a child template.
13754      * @param {String/Number} name (optional) The name or index of the child template
13755      * @param {Array/Object} values The values to be applied to the template
13756      * @return {MasterTemplate} this
13757      */
13758      add : function(name, values){
13759         if(arguments.length == 1){
13760             values = arguments[0];
13761             name = 0;
13762         }
13763         var s = this.subs[name];
13764         s.buffer[s.buffer.length] = s.tpl.apply(values);
13765         return this;
13766     },
13767
13768     /**
13769      * Applies all the passed values to a child template.
13770      * @param {String/Number} name (optional) The name or index of the child template
13771      * @param {Array} values The values to be applied to the template, this should be an array of objects.
13772      * @param {Boolean} reset (optional) True to reset the template first
13773      * @return {MasterTemplate} this
13774      */
13775     fill : function(name, values, reset){
13776         var a = arguments;
13777         if(a.length == 1 || (a.length == 2 && typeof a[1] == "boolean")){
13778             values = a[0];
13779             name = 0;
13780             reset = a[1];
13781         }
13782         if(reset){
13783             this.reset();
13784         }
13785         for(var i = 0, len = values.length; i < len; i++){
13786             this.add(name, values[i]);
13787         }
13788         return this;
13789     },
13790
13791     /**
13792      * Resets the template for reuse
13793      * @return {MasterTemplate} this
13794      */
13795      reset : function(){
13796         var s = this.subs;
13797         for(var i = 0; i < this.subCount; i++){
13798             s[i].buffer = [];
13799         }
13800         return this;
13801     },
13802
13803     applyTemplate : function(values){
13804         var s = this.subs;
13805         var replaceIndex = -1;
13806         this.html = this.originalHtml.replace(this.subTemplateRe, function(m, name){
13807             return s[++replaceIndex].buffer.join("");
13808         });
13809         return Roo.MasterTemplate.superclass.applyTemplate.call(this, values);
13810     },
13811
13812     apply : function(){
13813         return this.applyTemplate.apply(this, arguments);
13814     },
13815
13816     compile : function(){return this;}
13817 });
13818
13819 /**
13820  * Alias for fill().
13821  * @method
13822  */
13823 Roo.MasterTemplate.prototype.addAll = Roo.MasterTemplate.prototype.fill;
13824  /**
13825  * Creates a template from the passed element's value (display:none textarea, preferred) or innerHTML. e.g.
13826  * var tpl = Roo.MasterTemplate.from('element-id');
13827  * @param {String/HTMLElement} el
13828  * @param {Object} config
13829  * @static
13830  */
13831 Roo.MasterTemplate.from = function(el, config){
13832     el = Roo.getDom(el);
13833     return new Roo.MasterTemplate(el.value || el.innerHTML, config || '');
13834 };/*
13835  * Based on:
13836  * Ext JS Library 1.1.1
13837  * Copyright(c) 2006-2007, Ext JS, LLC.
13838  *
13839  * Originally Released Under LGPL - original licence link has changed is not relivant.
13840  *
13841  * Fork - LGPL
13842  * <script type="text/javascript">
13843  */
13844
13845  
13846 /**
13847  * @class Roo.util.CSS
13848  * Utility class for manipulating CSS rules
13849  * @singleton
13850  */
13851 Roo.util.CSS = function(){
13852         var rules = null;
13853         var doc = document;
13854
13855     var camelRe = /(-[a-z])/gi;
13856     var camelFn = function(m, a){ return a.charAt(1).toUpperCase(); };
13857
13858    return {
13859    /**
13860     * Very simple dynamic creation of stylesheets from a text blob of rules.  The text will wrapped in a style
13861     * tag and appended to the HEAD of the document.
13862     * @param {String|Object} cssText The text containing the css rules
13863     * @param {String} id An id to add to the stylesheet for later removal
13864     * @return {StyleSheet}
13865     */
13866     createStyleSheet : function(cssText, id){
13867         var ss;
13868         var head = doc.getElementsByTagName("head")[0];
13869         var nrules = doc.createElement("style");
13870         nrules.setAttribute("type", "text/css");
13871         if(id){
13872             nrules.setAttribute("id", id);
13873         }
13874         if (typeof(cssText) != 'string') {
13875             // support object maps..
13876             // not sure if this a good idea.. 
13877             // perhaps it should be merged with the general css handling
13878             // and handle js style props.
13879             var cssTextNew = [];
13880             for(var n in cssText) {
13881                 var citems = [];
13882                 for(var k in cssText[n]) {
13883                     citems.push( k + ' : ' +cssText[n][k] + ';' );
13884                 }
13885                 cssTextNew.push( n + ' { ' + citems.join(' ') + '} ');
13886                 
13887             }
13888             cssText = cssTextNew.join("\n");
13889             
13890         }
13891        
13892        
13893        if(Roo.isIE){
13894            head.appendChild(nrules);
13895            ss = nrules.styleSheet;
13896            ss.cssText = cssText;
13897        }else{
13898            try{
13899                 nrules.appendChild(doc.createTextNode(cssText));
13900            }catch(e){
13901                nrules.cssText = cssText; 
13902            }
13903            head.appendChild(nrules);
13904            ss = nrules.styleSheet ? nrules.styleSheet : (nrules.sheet || doc.styleSheets[doc.styleSheets.length-1]);
13905        }
13906        this.cacheStyleSheet(ss);
13907        return ss;
13908    },
13909
13910    /**
13911     * Removes a style or link tag by id
13912     * @param {String} id The id of the tag
13913     */
13914    removeStyleSheet : function(id){
13915        var existing = doc.getElementById(id);
13916        if(existing){
13917            existing.parentNode.removeChild(existing);
13918        }
13919    },
13920
13921    /**
13922     * Dynamically swaps an existing stylesheet reference for a new one
13923     * @param {String} id The id of an existing link tag to remove
13924     * @param {String} url The href of the new stylesheet to include
13925     */
13926    swapStyleSheet : function(id, url){
13927        this.removeStyleSheet(id);
13928        var ss = doc.createElement("link");
13929        ss.setAttribute("rel", "stylesheet");
13930        ss.setAttribute("type", "text/css");
13931        ss.setAttribute("id", id);
13932        ss.setAttribute("href", url);
13933        doc.getElementsByTagName("head")[0].appendChild(ss);
13934    },
13935    
13936    /**
13937     * Refresh the rule cache if you have dynamically added stylesheets
13938     * @return {Object} An object (hash) of rules indexed by selector
13939     */
13940    refreshCache : function(){
13941        return this.getRules(true);
13942    },
13943
13944    // private
13945    cacheStyleSheet : function(stylesheet){
13946        if(!rules){
13947            rules = {};
13948        }
13949        try{// try catch for cross domain access issue
13950            var ssRules = stylesheet.cssRules || stylesheet.rules;
13951            for(var j = ssRules.length-1; j >= 0; --j){
13952                rules[ssRules[j].selectorText] = ssRules[j];
13953            }
13954        }catch(e){}
13955    },
13956    
13957    /**
13958     * Gets all css rules for the document
13959     * @param {Boolean} refreshCache true to refresh the internal cache
13960     * @return {Object} An object (hash) of rules indexed by selector
13961     */
13962    getRules : function(refreshCache){
13963                 if(rules == null || refreshCache){
13964                         rules = {};
13965                         var ds = doc.styleSheets;
13966                         for(var i =0, len = ds.length; i < len; i++){
13967                             try{
13968                         this.cacheStyleSheet(ds[i]);
13969                     }catch(e){} 
13970                 }
13971                 }
13972                 return rules;
13973         },
13974         
13975         /**
13976     * Gets an an individual CSS rule by selector(s)
13977     * @param {String/Array} selector The CSS selector or an array of selectors to try. The first selector that is found is returned.
13978     * @param {Boolean} refreshCache true to refresh the internal cache if you have recently updated any rules or added styles dynamically
13979     * @return {CSSRule} The CSS rule or null if one is not found
13980     */
13981    getRule : function(selector, refreshCache){
13982                 var rs = this.getRules(refreshCache);
13983                 if(!(selector instanceof Array)){
13984                     return rs[selector];
13985                 }
13986                 for(var i = 0; i < selector.length; i++){
13987                         if(rs[selector[i]]){
13988                                 return rs[selector[i]];
13989                         }
13990                 }
13991                 return null;
13992         },
13993         
13994         
13995         /**
13996     * Updates a rule property
13997     * @param {String/Array} selector If it's an array it tries each selector until it finds one. Stops immediately once one is found.
13998     * @param {String} property The css property
13999     * @param {String} value The new value for the property
14000     * @return {Boolean} true If a rule was found and updated
14001     */
14002    updateRule : function(selector, property, value){
14003                 if(!(selector instanceof Array)){
14004                         var rule = this.getRule(selector);
14005                         if(rule){
14006                                 rule.style[property.replace(camelRe, camelFn)] = value;
14007                                 return true;
14008                         }
14009                 }else{
14010                         for(var i = 0; i < selector.length; i++){
14011                                 if(this.updateRule(selector[i], property, value)){
14012                                         return true;
14013                                 }
14014                         }
14015                 }
14016                 return false;
14017         }
14018    };   
14019 }();/*
14020  * Based on:
14021  * Ext JS Library 1.1.1
14022  * Copyright(c) 2006-2007, Ext JS, LLC.
14023  *
14024  * Originally Released Under LGPL - original licence link has changed is not relivant.
14025  *
14026  * Fork - LGPL
14027  * <script type="text/javascript">
14028  */
14029
14030  
14031
14032 /**
14033  * @class Roo.util.ClickRepeater
14034  * @extends Roo.util.Observable
14035  * 
14036  * A wrapper class which can be applied to any element. Fires a "click" event while the
14037  * mouse is pressed. The interval between firings may be specified in the config but
14038  * defaults to 10 milliseconds.
14039  * 
14040  * Optionally, a CSS class may be applied to the element during the time it is pressed.
14041  * 
14042  * @cfg {String/HTMLElement/Element} el The element to act as a button.
14043  * @cfg {Number} delay The initial delay before the repeating event begins firing.
14044  * Similar to an autorepeat key delay.
14045  * @cfg {Number} interval The interval between firings of the "click" event. Default 10 ms.
14046  * @cfg {String} pressClass A CSS class name to be applied to the element while pressed.
14047  * @cfg {Boolean} accelerate True if autorepeating should start slowly and accelerate.
14048  *           "interval" and "delay" are ignored. "immediate" is honored.
14049  * @cfg {Boolean} preventDefault True to prevent the default click event
14050  * @cfg {Boolean} stopDefault True to stop the default click event
14051  * 
14052  * @history
14053  *     2007-02-02 jvs Original code contributed by Nige "Animal" White
14054  *     2007-02-02 jvs Renamed to ClickRepeater
14055  *   2007-02-03 jvs Modifications for FF Mac and Safari 
14056  *
14057  *  @constructor
14058  * @param {String/HTMLElement/Element} el The element to listen on
14059  * @param {Object} config
14060  **/
14061 Roo.util.ClickRepeater = function(el, config)
14062 {
14063     this.el = Roo.get(el);
14064     this.el.unselectable();
14065
14066     Roo.apply(this, config);
14067
14068     this.addEvents({
14069     /**
14070      * @event mousedown
14071      * Fires when the mouse button is depressed.
14072      * @param {Roo.util.ClickRepeater} this
14073      */
14074         "mousedown" : true,
14075     /**
14076      * @event click
14077      * Fires on a specified interval during the time the element is pressed.
14078      * @param {Roo.util.ClickRepeater} this
14079      */
14080         "click" : true,
14081     /**
14082      * @event mouseup
14083      * Fires when the mouse key is released.
14084      * @param {Roo.util.ClickRepeater} this
14085      */
14086         "mouseup" : true
14087     });
14088
14089     this.el.on("mousedown", this.handleMouseDown, this);
14090     if(this.preventDefault || this.stopDefault){
14091         this.el.on("click", function(e){
14092             if(this.preventDefault){
14093                 e.preventDefault();
14094             }
14095             if(this.stopDefault){
14096                 e.stopEvent();
14097             }
14098         }, this);
14099     }
14100
14101     // allow inline handler
14102     if(this.handler){
14103         this.on("click", this.handler,  this.scope || this);
14104     }
14105
14106     Roo.util.ClickRepeater.superclass.constructor.call(this);
14107 };
14108
14109 Roo.extend(Roo.util.ClickRepeater, Roo.util.Observable, {
14110     interval : 20,
14111     delay: 250,
14112     preventDefault : true,
14113     stopDefault : false,
14114     timer : 0,
14115
14116     // private
14117     handleMouseDown : function(){
14118         clearTimeout(this.timer);
14119         this.el.blur();
14120         if(this.pressClass){
14121             this.el.addClass(this.pressClass);
14122         }
14123         this.mousedownTime = new Date();
14124
14125         Roo.get(document).on("mouseup", this.handleMouseUp, this);
14126         this.el.on("mouseout", this.handleMouseOut, this);
14127
14128         this.fireEvent("mousedown", this);
14129         this.fireEvent("click", this);
14130         
14131         this.timer = this.click.defer(this.delay || this.interval, this);
14132     },
14133
14134     // private
14135     click : function(){
14136         this.fireEvent("click", this);
14137         this.timer = this.click.defer(this.getInterval(), this);
14138     },
14139
14140     // private
14141     getInterval: function(){
14142         if(!this.accelerate){
14143             return this.interval;
14144         }
14145         var pressTime = this.mousedownTime.getElapsed();
14146         if(pressTime < 500){
14147             return 400;
14148         }else if(pressTime < 1700){
14149             return 320;
14150         }else if(pressTime < 2600){
14151             return 250;
14152         }else if(pressTime < 3500){
14153             return 180;
14154         }else if(pressTime < 4400){
14155             return 140;
14156         }else if(pressTime < 5300){
14157             return 80;
14158         }else if(pressTime < 6200){
14159             return 50;
14160         }else{
14161             return 10;
14162         }
14163     },
14164
14165     // private
14166     handleMouseOut : function(){
14167         clearTimeout(this.timer);
14168         if(this.pressClass){
14169             this.el.removeClass(this.pressClass);
14170         }
14171         this.el.on("mouseover", this.handleMouseReturn, this);
14172     },
14173
14174     // private
14175     handleMouseReturn : function(){
14176         this.el.un("mouseover", this.handleMouseReturn);
14177         if(this.pressClass){
14178             this.el.addClass(this.pressClass);
14179         }
14180         this.click();
14181     },
14182
14183     // private
14184     handleMouseUp : function(){
14185         clearTimeout(this.timer);
14186         this.el.un("mouseover", this.handleMouseReturn);
14187         this.el.un("mouseout", this.handleMouseOut);
14188         Roo.get(document).un("mouseup", this.handleMouseUp);
14189         this.el.removeClass(this.pressClass);
14190         this.fireEvent("mouseup", this);
14191     }
14192 });/*
14193  * Based on:
14194  * Ext JS Library 1.1.1
14195  * Copyright(c) 2006-2007, Ext JS, LLC.
14196  *
14197  * Originally Released Under LGPL - original licence link has changed is not relivant.
14198  *
14199  * Fork - LGPL
14200  * <script type="text/javascript">
14201  */
14202
14203  
14204 /**
14205  * @class Roo.KeyNav
14206  * <p>Provides a convenient wrapper for normalized keyboard navigation.  KeyNav allows you to bind
14207  * navigation keys to function calls that will get called when the keys are pressed, providing an easy
14208  * way to implement custom navigation schemes for any UI component.</p>
14209  * <p>The following are all of the possible keys that can be implemented: enter, left, right, up, down, tab, esc,
14210  * pageUp, pageDown, del, home, end.  Usage:</p>
14211  <pre><code>
14212 var nav = new Roo.KeyNav("my-element", {
14213     "left" : function(e){
14214         this.moveLeft(e.ctrlKey);
14215     },
14216     "right" : function(e){
14217         this.moveRight(e.ctrlKey);
14218     },
14219     "enter" : function(e){
14220         this.save();
14221     },
14222     scope : this
14223 });
14224 </code></pre>
14225  * @constructor
14226  * @param {String/HTMLElement/Roo.Element} el The element to bind to
14227  * @param {Object} config The config
14228  */
14229 Roo.KeyNav = function(el, config){
14230     this.el = Roo.get(el);
14231     Roo.apply(this, config);
14232     if(!this.disabled){
14233         this.disabled = true;
14234         this.enable();
14235     }
14236 };
14237
14238 Roo.KeyNav.prototype = {
14239     /**
14240      * @cfg {Boolean} disabled
14241      * True to disable this KeyNav instance (defaults to false)
14242      */
14243     disabled : false,
14244     /**
14245      * @cfg {String} defaultEventAction
14246      * The method to call on the {@link Roo.EventObject} after this KeyNav intercepts a key.  Valid values are
14247      * {@link Roo.EventObject#stopEvent}, {@link Roo.EventObject#preventDefault} and
14248      * {@link Roo.EventObject#stopPropagation} (defaults to 'stopEvent')
14249      */
14250     defaultEventAction: "stopEvent",
14251     /**
14252      * @cfg {Boolean} forceKeyDown
14253      * Handle the keydown event instead of keypress (defaults to false).  KeyNav automatically does this for IE since
14254      * IE does not propagate special keys on keypress, but setting this to true will force other browsers to also
14255      * handle keydown instead of keypress.
14256      */
14257     forceKeyDown : false,
14258
14259     // private
14260     prepareEvent : function(e){
14261         var k = e.getKey();
14262         var h = this.keyToHandler[k];
14263         //if(h && this[h]){
14264         //    e.stopPropagation();
14265         //}
14266         if(Roo.isSafari && h && k >= 37 && k <= 40){
14267             e.stopEvent();
14268         }
14269     },
14270
14271     // private
14272     relay : function(e){
14273         var k = e.getKey();
14274         var h = this.keyToHandler[k];
14275         if(h && this[h]){
14276             if(this.doRelay(e, this[h], h) !== true){
14277                 e[this.defaultEventAction]();
14278             }
14279         }
14280     },
14281
14282     // private
14283     doRelay : function(e, h, hname){
14284         return h.call(this.scope || this, e);
14285     },
14286
14287     // possible handlers
14288     enter : false,
14289     left : false,
14290     right : false,
14291     up : false,
14292     down : false,
14293     tab : false,
14294     esc : false,
14295     pageUp : false,
14296     pageDown : false,
14297     del : false,
14298     home : false,
14299     end : false,
14300
14301     // quick lookup hash
14302     keyToHandler : {
14303         37 : "left",
14304         39 : "right",
14305         38 : "up",
14306         40 : "down",
14307         33 : "pageUp",
14308         34 : "pageDown",
14309         46 : "del",
14310         36 : "home",
14311         35 : "end",
14312         13 : "enter",
14313         27 : "esc",
14314         9  : "tab"
14315     },
14316
14317         /**
14318          * Enable this KeyNav
14319          */
14320         enable: function(){
14321                 if(this.disabled){
14322             // ie won't do special keys on keypress, no one else will repeat keys with keydown
14323             // the EventObject will normalize Safari automatically
14324             if(this.forceKeyDown || Roo.isIE || Roo.isAir){
14325                 this.el.on("keydown", this.relay,  this);
14326             }else{
14327                 this.el.on("keydown", this.prepareEvent,  this);
14328                 this.el.on("keypress", this.relay,  this);
14329             }
14330                     this.disabled = false;
14331                 }
14332         },
14333
14334         /**
14335          * Disable this KeyNav
14336          */
14337         disable: function(){
14338                 if(!this.disabled){
14339                     if(this.forceKeyDown || Roo.isIE || Roo.isAir){
14340                 this.el.un("keydown", this.relay);
14341             }else{
14342                 this.el.un("keydown", this.prepareEvent);
14343                 this.el.un("keypress", this.relay);
14344             }
14345                     this.disabled = true;
14346                 }
14347         }
14348 };/*
14349  * Based on:
14350  * Ext JS Library 1.1.1
14351  * Copyright(c) 2006-2007, Ext JS, LLC.
14352  *
14353  * Originally Released Under LGPL - original licence link has changed is not relivant.
14354  *
14355  * Fork - LGPL
14356  * <script type="text/javascript">
14357  */
14358
14359  
14360 /**
14361  * @class Roo.KeyMap
14362  * Handles mapping keys to actions for an element. One key map can be used for multiple actions.
14363  * The constructor accepts the same config object as defined by {@link #addBinding}.
14364  * If you bind a callback function to a KeyMap, anytime the KeyMap handles an expected key
14365  * combination it will call the function with this signature (if the match is a multi-key
14366  * combination the callback will still be called only once): (String key, Roo.EventObject e)
14367  * A KeyMap can also handle a string representation of keys.<br />
14368  * Usage:
14369  <pre><code>
14370 // map one key by key code
14371 var map = new Roo.KeyMap("my-element", {
14372     key: 13, // or Roo.EventObject.ENTER
14373     fn: myHandler,
14374     scope: myObject
14375 });
14376
14377 // map multiple keys to one action by string
14378 var map = new Roo.KeyMap("my-element", {
14379     key: "a\r\n\t",
14380     fn: myHandler,
14381     scope: myObject
14382 });
14383
14384 // map multiple keys to multiple actions by strings and array of codes
14385 var map = new Roo.KeyMap("my-element", [
14386     {
14387         key: [10,13],
14388         fn: function(){ alert("Return was pressed"); }
14389     }, {
14390         key: "abc",
14391         fn: function(){ alert('a, b or c was pressed'); }
14392     }, {
14393         key: "\t",
14394         ctrl:true,
14395         shift:true,
14396         fn: function(){ alert('Control + shift + tab was pressed.'); }
14397     }
14398 ]);
14399 </code></pre>
14400  * <b>Note: A KeyMap starts enabled</b>
14401  * @constructor
14402  * @param {String/HTMLElement/Roo.Element} el The element to bind to
14403  * @param {Object} config The config (see {@link #addBinding})
14404  * @param {String} eventName (optional) The event to bind to (defaults to "keydown")
14405  */
14406 Roo.KeyMap = function(el, config, eventName){
14407     this.el  = Roo.get(el);
14408     this.eventName = eventName || "keydown";
14409     this.bindings = [];
14410     if(config){
14411         this.addBinding(config);
14412     }
14413     this.enable();
14414 };
14415
14416 Roo.KeyMap.prototype = {
14417     /**
14418      * True to stop the event from bubbling and prevent the default browser action if the
14419      * key was handled by the KeyMap (defaults to false)
14420      * @type Boolean
14421      */
14422     stopEvent : false,
14423
14424     /**
14425      * Add a new binding to this KeyMap. The following config object properties are supported:
14426      * <pre>
14427 Property    Type             Description
14428 ----------  ---------------  ----------------------------------------------------------------------
14429 key         String/Array     A single keycode or an array of keycodes to handle
14430 shift       Boolean          True to handle key only when shift is pressed (defaults to false)
14431 ctrl        Boolean          True to handle key only when ctrl is pressed (defaults to false)
14432 alt         Boolean          True to handle key only when alt is pressed (defaults to false)
14433 fn          Function         The function to call when KeyMap finds the expected key combination
14434 scope       Object           The scope of the callback function
14435 </pre>
14436      *
14437      * Usage:
14438      * <pre><code>
14439 // Create a KeyMap
14440 var map = new Roo.KeyMap(document, {
14441     key: Roo.EventObject.ENTER,
14442     fn: handleKey,
14443     scope: this
14444 });
14445
14446 //Add a new binding to the existing KeyMap later
14447 map.addBinding({
14448     key: 'abc',
14449     shift: true,
14450     fn: handleKey,
14451     scope: this
14452 });
14453 </code></pre>
14454      * @param {Object/Array} config A single KeyMap config or an array of configs
14455      */
14456         addBinding : function(config){
14457         if(config instanceof Array){
14458             for(var i = 0, len = config.length; i < len; i++){
14459                 this.addBinding(config[i]);
14460             }
14461             return;
14462         }
14463         var keyCode = config.key,
14464             shift = config.shift, 
14465             ctrl = config.ctrl, 
14466             alt = config.alt,
14467             fn = config.fn,
14468             scope = config.scope;
14469         if(typeof keyCode == "string"){
14470             var ks = [];
14471             var keyString = keyCode.toUpperCase();
14472             for(var j = 0, len = keyString.length; j < len; j++){
14473                 ks.push(keyString.charCodeAt(j));
14474             }
14475             keyCode = ks;
14476         }
14477         var keyArray = keyCode instanceof Array;
14478         var handler = function(e){
14479             if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) &&  (!alt || e.altKey)){
14480                 var k = e.getKey();
14481                 if(keyArray){
14482                     for(var i = 0, len = keyCode.length; i < len; i++){
14483                         if(keyCode[i] == k){
14484                           if(this.stopEvent){
14485                               e.stopEvent();
14486                           }
14487                           fn.call(scope || window, k, e);
14488                           return;
14489                         }
14490                     }
14491                 }else{
14492                     if(k == keyCode){
14493                         if(this.stopEvent){
14494                            e.stopEvent();
14495                         }
14496                         fn.call(scope || window, k, e);
14497                     }
14498                 }
14499             }
14500         };
14501         this.bindings.push(handler);  
14502         },
14503
14504     /**
14505      * Shorthand for adding a single key listener
14506      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the
14507      * following options:
14508      * {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
14509      * @param {Function} fn The function to call
14510      * @param {Object} scope (optional) The scope of the function
14511      */
14512     on : function(key, fn, scope){
14513         var keyCode, shift, ctrl, alt;
14514         if(typeof key == "object" && !(key instanceof Array)){
14515             keyCode = key.key;
14516             shift = key.shift;
14517             ctrl = key.ctrl;
14518             alt = key.alt;
14519         }else{
14520             keyCode = key;
14521         }
14522         this.addBinding({
14523             key: keyCode,
14524             shift: shift,
14525             ctrl: ctrl,
14526             alt: alt,
14527             fn: fn,
14528             scope: scope
14529         })
14530     },
14531
14532     // private
14533     handleKeyDown : function(e){
14534             if(this.enabled){ //just in case
14535             var b = this.bindings;
14536             for(var i = 0, len = b.length; i < len; i++){
14537                 b[i].call(this, e);
14538             }
14539             }
14540         },
14541         
14542         /**
14543          * Returns true if this KeyMap is enabled
14544          * @return {Boolean} 
14545          */
14546         isEnabled : function(){
14547             return this.enabled;  
14548         },
14549         
14550         /**
14551          * Enables this KeyMap
14552          */
14553         enable: function(){
14554                 if(!this.enabled){
14555                     this.el.on(this.eventName, this.handleKeyDown, this);
14556                     this.enabled = true;
14557                 }
14558         },
14559
14560         /**
14561          * Disable this KeyMap
14562          */
14563         disable: function(){
14564                 if(this.enabled){
14565                     this.el.removeListener(this.eventName, this.handleKeyDown, this);
14566                     this.enabled = false;
14567                 }
14568         }
14569 };/*
14570  * Based on:
14571  * Ext JS Library 1.1.1
14572  * Copyright(c) 2006-2007, Ext JS, LLC.
14573  *
14574  * Originally Released Under LGPL - original licence link has changed is not relivant.
14575  *
14576  * Fork - LGPL
14577  * <script type="text/javascript">
14578  */
14579
14580  
14581 /**
14582  * @class Roo.util.TextMetrics
14583  * Provides precise pixel measurements for blocks of text so that you can determine exactly how high and
14584  * wide, in pixels, a given block of text will be.
14585  * @singleton
14586  */
14587 Roo.util.TextMetrics = function(){
14588     var shared;
14589     return {
14590         /**
14591          * Measures the size of the specified text
14592          * @param {String/HTMLElement} el The element, dom node or id from which to copy existing CSS styles
14593          * that can affect the size of the rendered text
14594          * @param {String} text The text to measure
14595          * @param {Number} fixedWidth (optional) If the text will be multiline, you have to set a fixed width
14596          * in order to accurately measure the text height
14597          * @return {Object} An object containing the text's size {width: (width), height: (height)}
14598          */
14599         measure : function(el, text, fixedWidth){
14600             if(!shared){
14601                 shared = Roo.util.TextMetrics.Instance(el, fixedWidth);
14602             }
14603             shared.bind(el);
14604             shared.setFixedWidth(fixedWidth || 'auto');
14605             return shared.getSize(text);
14606         },
14607
14608         /**
14609          * Return a unique TextMetrics instance that can be bound directly to an element and reused.  This reduces
14610          * the overhead of multiple calls to initialize the style properties on each measurement.
14611          * @param {String/HTMLElement} el The element, dom node or id that the instance will be bound to
14612          * @param {Number} fixedWidth (optional) If the text will be multiline, you have to set a fixed width
14613          * in order to accurately measure the text height
14614          * @return {Roo.util.TextMetrics.Instance} instance The new instance
14615          */
14616         createInstance : function(el, fixedWidth){
14617             return Roo.util.TextMetrics.Instance(el, fixedWidth);
14618         }
14619     };
14620 }();
14621
14622  
14623
14624 Roo.util.TextMetrics.Instance = function(bindTo, fixedWidth){
14625     var ml = new Roo.Element(document.createElement('div'));
14626     document.body.appendChild(ml.dom);
14627     ml.position('absolute');
14628     ml.setLeftTop(-1000, -1000);
14629     ml.hide();
14630
14631     if(fixedWidth){
14632         ml.setWidth(fixedWidth);
14633     }
14634      
14635     var instance = {
14636         /**
14637          * Returns the size of the specified text based on the internal element's style and width properties
14638          * @memberOf Roo.util.TextMetrics.Instance#
14639          * @param {String} text The text to measure
14640          * @return {Object} An object containing the text's size {width: (width), height: (height)}
14641          */
14642         getSize : function(text){
14643             ml.update(text);
14644             var s = ml.getSize();
14645             ml.update('');
14646             return s;
14647         },
14648
14649         /**
14650          * Binds this TextMetrics instance to an element from which to copy existing CSS styles
14651          * that can affect the size of the rendered text
14652          * @memberOf Roo.util.TextMetrics.Instance#
14653          * @param {String/HTMLElement} el The element, dom node or id
14654          */
14655         bind : function(el){
14656             ml.setStyle(
14657                 Roo.fly(el).getStyles('font-size','font-style', 'font-weight', 'font-family','line-height')
14658             );
14659         },
14660
14661         /**
14662          * Sets a fixed width on the internal measurement element.  If the text will be multiline, you have
14663          * to set a fixed width in order to accurately measure the text height.
14664          * @memberOf Roo.util.TextMetrics.Instance#
14665          * @param {Number} width The width to set on the element
14666          */
14667         setFixedWidth : function(width){
14668             ml.setWidth(width);
14669         },
14670
14671         /**
14672          * Returns the measured width of the specified text
14673          * @memberOf Roo.util.TextMetrics.Instance#
14674          * @param {String} text The text to measure
14675          * @return {Number} width The width in pixels
14676          */
14677         getWidth : function(text){
14678             ml.dom.style.width = 'auto';
14679             return this.getSize(text).width;
14680         },
14681
14682         /**
14683          * Returns the measured height of the specified text.  For multiline text, be sure to call
14684          * {@link #setFixedWidth} if necessary.
14685          * @memberOf Roo.util.TextMetrics.Instance#
14686          * @param {String} text The text to measure
14687          * @return {Number} height The height in pixels
14688          */
14689         getHeight : function(text){
14690             return this.getSize(text).height;
14691         }
14692     };
14693
14694     instance.bind(bindTo);
14695
14696     return instance;
14697 };
14698
14699 // backwards compat
14700 Roo.Element.measureText = Roo.util.TextMetrics.measure;/*
14701  * Based on:
14702  * Ext JS Library 1.1.1
14703  * Copyright(c) 2006-2007, Ext JS, LLC.
14704  *
14705  * Originally Released Under LGPL - original licence link has changed is not relivant.
14706  *
14707  * Fork - LGPL
14708  * <script type="text/javascript">
14709  */
14710
14711 /**
14712  * @class Roo.state.Provider
14713  * Abstract base class for state provider implementations. This class provides methods
14714  * for encoding and decoding <b>typed</b> variables including dates and defines the 
14715  * Provider interface.
14716  */
14717 Roo.state.Provider = function(){
14718     /**
14719      * @event statechange
14720      * Fires when a state change occurs.
14721      * @param {Provider} this This state provider
14722      * @param {String} key The state key which was changed
14723      * @param {String} value The encoded value for the state
14724      */
14725     this.addEvents({
14726         "statechange": true
14727     });
14728     this.state = {};
14729     Roo.state.Provider.superclass.constructor.call(this);
14730 };
14731 Roo.extend(Roo.state.Provider, Roo.util.Observable, {
14732     /**
14733      * Returns the current value for a key
14734      * @param {String} name The key name
14735      * @param {Mixed} defaultValue A default value to return if the key's value is not found
14736      * @return {Mixed} The state data
14737      */
14738     get : function(name, defaultValue){
14739         return typeof this.state[name] == "undefined" ?
14740             defaultValue : this.state[name];
14741     },
14742     
14743     /**
14744      * Clears a value from the state
14745      * @param {String} name The key name
14746      */
14747     clear : function(name){
14748         delete this.state[name];
14749         this.fireEvent("statechange", this, name, null);
14750     },
14751     
14752     /**
14753      * Sets the value for a key
14754      * @param {String} name The key name
14755      * @param {Mixed} value The value to set
14756      */
14757     set : function(name, value){
14758         this.state[name] = value;
14759         this.fireEvent("statechange", this, name, value);
14760     },
14761     
14762     /**
14763      * Decodes a string previously encoded with {@link #encodeValue}.
14764      * @param {String} value The value to decode
14765      * @return {Mixed} The decoded value
14766      */
14767     decodeValue : function(cookie){
14768         var re = /^(a|n|d|b|s|o)\:(.*)$/;
14769         var matches = re.exec(unescape(cookie));
14770         if(!matches || !matches[1]) return; // non state cookie
14771         var type = matches[1];
14772         var v = matches[2];
14773         switch(type){
14774             case "n":
14775                 return parseFloat(v);
14776             case "d":
14777                 return new Date(Date.parse(v));
14778             case "b":
14779                 return (v == "1");
14780             case "a":
14781                 var all = [];
14782                 var values = v.split("^");
14783                 for(var i = 0, len = values.length; i < len; i++){
14784                     all.push(this.decodeValue(values[i]));
14785                 }
14786                 return all;
14787            case "o":
14788                 var all = {};
14789                 var values = v.split("^");
14790                 for(var i = 0, len = values.length; i < len; i++){
14791                     var kv = values[i].split("=");
14792                     all[kv[0]] = this.decodeValue(kv[1]);
14793                 }
14794                 return all;
14795            default:
14796                 return v;
14797         }
14798     },
14799     
14800     /**
14801      * Encodes a value including type information.  Decode with {@link #decodeValue}.
14802      * @param {Mixed} value The value to encode
14803      * @return {String} The encoded value
14804      */
14805     encodeValue : function(v){
14806         var enc;
14807         if(typeof v == "number"){
14808             enc = "n:" + v;
14809         }else if(typeof v == "boolean"){
14810             enc = "b:" + (v ? "1" : "0");
14811         }else if(v instanceof Date){
14812             enc = "d:" + v.toGMTString();
14813         }else if(v instanceof Array){
14814             var flat = "";
14815             for(var i = 0, len = v.length; i < len; i++){
14816                 flat += this.encodeValue(v[i]);
14817                 if(i != len-1) flat += "^";
14818             }
14819             enc = "a:" + flat;
14820         }else if(typeof v == "object"){
14821             var flat = "";
14822             for(var key in v){
14823                 if(typeof v[key] != "function"){
14824                     flat += key + "=" + this.encodeValue(v[key]) + "^";
14825                 }
14826             }
14827             enc = "o:" + flat.substring(0, flat.length-1);
14828         }else{
14829             enc = "s:" + v;
14830         }
14831         return escape(enc);        
14832     }
14833 });
14834
14835 /*
14836  * Based on:
14837  * Ext JS Library 1.1.1
14838  * Copyright(c) 2006-2007, Ext JS, LLC.
14839  *
14840  * Originally Released Under LGPL - original licence link has changed is not relivant.
14841  *
14842  * Fork - LGPL
14843  * <script type="text/javascript">
14844  */
14845 /**
14846  * @class Roo.state.Manager
14847  * This is the global state manager. By default all components that are "state aware" check this class
14848  * for state information if you don't pass them a custom state provider. In order for this class
14849  * to be useful, it must be initialized with a provider when your application initializes.
14850  <pre><code>
14851 // in your initialization function
14852 init : function(){
14853    Roo.state.Manager.setProvider(new Roo.state.CookieProvider());
14854    ...
14855    // supposed you have a {@link Roo.BorderLayout}
14856    var layout = new Roo.BorderLayout(...);
14857    layout.restoreState();
14858    // or a {Roo.BasicDialog}
14859    var dialog = new Roo.BasicDialog(...);
14860    dialog.restoreState();
14861  </code></pre>
14862  * @singleton
14863  */
14864 Roo.state.Manager = function(){
14865     var provider = new Roo.state.Provider();
14866     
14867     return {
14868         /**
14869          * Configures the default state provider for your application
14870          * @param {Provider} stateProvider The state provider to set
14871          */
14872         setProvider : function(stateProvider){
14873             provider = stateProvider;
14874         },
14875         
14876         /**
14877          * Returns the current value for a key
14878          * @param {String} name The key name
14879          * @param {Mixed} defaultValue The default value to return if the key lookup does not match
14880          * @return {Mixed} The state data
14881          */
14882         get : function(key, defaultValue){
14883             return provider.get(key, defaultValue);
14884         },
14885         
14886         /**
14887          * Sets the value for a key
14888          * @param {String} name The key name
14889          * @param {Mixed} value The state data
14890          */
14891          set : function(key, value){
14892             provider.set(key, value);
14893         },
14894         
14895         /**
14896          * Clears a value from the state
14897          * @param {String} name The key name
14898          */
14899         clear : function(key){
14900             provider.clear(key);
14901         },
14902         
14903         /**
14904          * Gets the currently configured state provider
14905          * @return {Provider} The state provider
14906          */
14907         getProvider : function(){
14908             return provider;
14909         }
14910     };
14911 }();
14912 /*
14913  * Based on:
14914  * Ext JS Library 1.1.1
14915  * Copyright(c) 2006-2007, Ext JS, LLC.
14916  *
14917  * Originally Released Under LGPL - original licence link has changed is not relivant.
14918  *
14919  * Fork - LGPL
14920  * <script type="text/javascript">
14921  */
14922 /**
14923  * @class Roo.state.CookieProvider
14924  * @extends Roo.state.Provider
14925  * The default Provider implementation which saves state via cookies.
14926  * <br />Usage:
14927  <pre><code>
14928    var cp = new Roo.state.CookieProvider({
14929        path: "/cgi-bin/",
14930        expires: new Date(new Date().getTime()+(1000*60*60*24*30)); //30 days
14931        domain: "roojs.com"
14932    })
14933    Roo.state.Manager.setProvider(cp);
14934  </code></pre>
14935  * @cfg {String} path The path for which the cookie is active (defaults to root '/' which makes it active for all pages in the site)
14936  * @cfg {Date} expires The cookie expiration date (defaults to 7 days from now)
14937  * @cfg {String} domain The domain to save the cookie for.  Note that you cannot specify a different domain than
14938  * your page is on, but you can specify a sub-domain, or simply the domain itself like 'roojs.com' to include
14939  * all sub-domains if you need to access cookies across different sub-domains (defaults to null which uses the same
14940  * domain the page is running on including the 'www' like 'www.roojs.com')
14941  * @cfg {Boolean} secure True if the site is using SSL (defaults to false)
14942  * @constructor
14943  * Create a new CookieProvider
14944  * @param {Object} config The configuration object
14945  */
14946 Roo.state.CookieProvider = function(config){
14947     Roo.state.CookieProvider.superclass.constructor.call(this);
14948     this.path = "/";
14949     this.expires = new Date(new Date().getTime()+(1000*60*60*24*7)); //7 days
14950     this.domain = null;
14951     this.secure = false;
14952     Roo.apply(this, config);
14953     this.state = this.readCookies();
14954 };
14955
14956 Roo.extend(Roo.state.CookieProvider, Roo.state.Provider, {
14957     // private
14958     set : function(name, value){
14959         if(typeof value == "undefined" || value === null){
14960             this.clear(name);
14961             return;
14962         }
14963         this.setCookie(name, value);
14964         Roo.state.CookieProvider.superclass.set.call(this, name, value);
14965     },
14966
14967     // private
14968     clear : function(name){
14969         this.clearCookie(name);
14970         Roo.state.CookieProvider.superclass.clear.call(this, name);
14971     },
14972
14973     // private
14974     readCookies : function(){
14975         var cookies = {};
14976         var c = document.cookie + ";";
14977         var re = /\s?(.*?)=(.*?);/g;
14978         var matches;
14979         while((matches = re.exec(c)) != null){
14980             var name = matches[1];
14981             var value = matches[2];
14982             if(name && name.substring(0,3) == "ys-"){
14983                 cookies[name.substr(3)] = this.decodeValue(value);
14984             }
14985         }
14986         return cookies;
14987     },
14988
14989     // private
14990     setCookie : function(name, value){
14991         document.cookie = "ys-"+ name + "=" + this.encodeValue(value) +
14992            ((this.expires == null) ? "" : ("; expires=" + this.expires.toGMTString())) +
14993            ((this.path == null) ? "" : ("; path=" + this.path)) +
14994            ((this.domain == null) ? "" : ("; domain=" + this.domain)) +
14995            ((this.secure == true) ? "; secure" : "");
14996     },
14997
14998     // private
14999     clearCookie : function(name){
15000         document.cookie = "ys-" + name + "=null; expires=Thu, 01-Jan-70 00:00:01 GMT" +
15001            ((this.path == null) ? "" : ("; path=" + this.path)) +
15002            ((this.domain == null) ? "" : ("; domain=" + this.domain)) +
15003            ((this.secure == true) ? "; secure" : "");
15004     }
15005 });/*
15006  * Based on:
15007  * Ext JS Library 1.1.1
15008  * Copyright(c) 2006-2007, Ext JS, LLC.
15009  *
15010  * Originally Released Under LGPL - original licence link has changed is not relivant.
15011  *
15012  * Fork - LGPL
15013  * <script type="text/javascript">
15014  */
15015  
15016
15017 /**
15018  * @class Roo.ComponentMgr
15019  * Provides a common registry of all components on a page so that they can be easily accessed by component id (see {@link Roo.getCmp}).
15020  * @singleton
15021  */
15022 Roo.ComponentMgr = function(){
15023     var all = new Roo.util.MixedCollection();
15024
15025     return {
15026         /**
15027          * Registers a component.
15028          * @param {Roo.Component} c The component
15029          */
15030         register : function(c){
15031             all.add(c);
15032         },
15033
15034         /**
15035          * Unregisters a component.
15036          * @param {Roo.Component} c The component
15037          */
15038         unregister : function(c){
15039             all.remove(c);
15040         },
15041
15042         /**
15043          * Returns a component by id
15044          * @param {String} id The component id
15045          */
15046         get : function(id){
15047             return all.get(id);
15048         },
15049
15050         /**
15051          * Registers a function that will be called when a specified component is added to ComponentMgr
15052          * @param {String} id The component id
15053          * @param {Funtction} fn The callback function
15054          * @param {Object} scope The scope of the callback
15055          */
15056         onAvailable : function(id, fn, scope){
15057             all.on("add", function(index, o){
15058                 if(o.id == id){
15059                     fn.call(scope || o, o);
15060                     all.un("add", fn, scope);
15061                 }
15062             });
15063         }
15064     };
15065 }();/*
15066  * Based on:
15067  * Ext JS Library 1.1.1
15068  * Copyright(c) 2006-2007, Ext JS, LLC.
15069  *
15070  * Originally Released Under LGPL - original licence link has changed is not relivant.
15071  *
15072  * Fork - LGPL
15073  * <script type="text/javascript">
15074  */
15075  
15076 /**
15077  * @class Roo.Component
15078  * @extends Roo.util.Observable
15079  * Base class for all major Roo components.  All subclasses of Component can automatically participate in the standard
15080  * Roo component lifecycle of creation, rendering and destruction.  They also have automatic support for basic hide/show
15081  * and enable/disable behavior.  Component allows any subclass to be lazy-rendered into any {@link Roo.Container} and
15082  * to be automatically registered with the {@link Roo.ComponentMgr} so that it can be referenced at any time via {@link Roo.getCmp}.
15083  * All visual components (widgets) that require rendering into a layout should subclass Component.
15084  * @constructor
15085  * @param {Roo.Element/String/Object} config The configuration options.  If an element is passed, it is set as the internal
15086  * 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
15087  * and is used as the component id.  Otherwise, it is assumed to be a standard config object and is applied to the component.
15088  */
15089 Roo.Component = function(config){
15090     config = config || {};
15091     if(config.tagName || config.dom || typeof config == "string"){ // element object
15092         config = {el: config, id: config.id || config};
15093     }
15094     this.initialConfig = config;
15095
15096     Roo.apply(this, config);
15097     this.addEvents({
15098         /**
15099          * @event disable
15100          * Fires after the component is disabled.
15101              * @param {Roo.Component} this
15102              */
15103         disable : true,
15104         /**
15105          * @event enable
15106          * Fires after the component is enabled.
15107              * @param {Roo.Component} this
15108              */
15109         enable : true,
15110         /**
15111          * @event beforeshow
15112          * Fires before the component is shown.  Return false to stop the show.
15113              * @param {Roo.Component} this
15114              */
15115         beforeshow : true,
15116         /**
15117          * @event show
15118          * Fires after the component is shown.
15119              * @param {Roo.Component} this
15120              */
15121         show : true,
15122         /**
15123          * @event beforehide
15124          * Fires before the component is hidden. Return false to stop the hide.
15125              * @param {Roo.Component} this
15126              */
15127         beforehide : true,
15128         /**
15129          * @event hide
15130          * Fires after the component is hidden.
15131              * @param {Roo.Component} this
15132              */
15133         hide : true,
15134         /**
15135          * @event beforerender
15136          * Fires before the component is rendered. Return false to stop the render.
15137              * @param {Roo.Component} this
15138              */
15139         beforerender : true,
15140         /**
15141          * @event render
15142          * Fires after the component is rendered.
15143              * @param {Roo.Component} this
15144              */
15145         render : true,
15146         /**
15147          * @event beforedestroy
15148          * Fires before the component is destroyed. Return false to stop the destroy.
15149              * @param {Roo.Component} this
15150              */
15151         beforedestroy : true,
15152         /**
15153          * @event destroy
15154          * Fires after the component is destroyed.
15155              * @param {Roo.Component} this
15156              */
15157         destroy : true
15158     });
15159     if(!this.id){
15160         this.id = "ext-comp-" + (++Roo.Component.AUTO_ID);
15161     }
15162     Roo.ComponentMgr.register(this);
15163     Roo.Component.superclass.constructor.call(this);
15164     this.initComponent();
15165     if(this.renderTo){ // not supported by all components yet. use at your own risk!
15166         this.render(this.renderTo);
15167         delete this.renderTo;
15168     }
15169 };
15170
15171 /** @private */
15172 Roo.Component.AUTO_ID = 1000;
15173
15174 Roo.extend(Roo.Component, Roo.util.Observable, {
15175     /**
15176      * @scope Roo.Component.prototype
15177      * @type {Boolean}
15178      * true if this component is hidden. Read-only.
15179      */
15180     hidden : false,
15181     /**
15182      * @type {Boolean}
15183      * true if this component is disabled. Read-only.
15184      */
15185     disabled : false,
15186     /**
15187      * @type {Boolean}
15188      * true if this component has been rendered. Read-only.
15189      */
15190     rendered : false,
15191     
15192     /** @cfg {String} disableClass
15193      * CSS class added to the component when it is disabled (defaults to "x-item-disabled").
15194      */
15195     disabledClass : "x-item-disabled",
15196         /** @cfg {Boolean} allowDomMove
15197          * Whether the component can move the Dom node when rendering (defaults to true).
15198          */
15199     allowDomMove : true,
15200     /** @cfg {String} hideMode
15201      * How this component should hidden. Supported values are
15202      * "visibility" (css visibility), "offsets" (negative offset position) and
15203      * "display" (css display) - defaults to "display".
15204      */
15205     hideMode: 'display',
15206
15207     /** @private */
15208     ctype : "Roo.Component",
15209
15210     /**
15211      * @cfg {String} actionMode 
15212      * which property holds the element that used for  hide() / show() / disable() / enable()
15213      * default is 'el' 
15214      */
15215     actionMode : "el",
15216
15217     /** @private */
15218     getActionEl : function(){
15219         return this[this.actionMode];
15220     },
15221
15222     initComponent : Roo.emptyFn,
15223     /**
15224      * If this is a lazy rendering component, render it to its container element.
15225      * @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.
15226      */
15227     render : function(container, position){
15228         if(!this.rendered && this.fireEvent("beforerender", this) !== false){
15229             if(!container && this.el){
15230                 this.el = Roo.get(this.el);
15231                 container = this.el.dom.parentNode;
15232                 this.allowDomMove = false;
15233             }
15234             this.container = Roo.get(container);
15235             this.rendered = true;
15236             if(position !== undefined){
15237                 if(typeof position == 'number'){
15238                     position = this.container.dom.childNodes[position];
15239                 }else{
15240                     position = Roo.getDom(position);
15241                 }
15242             }
15243             this.onRender(this.container, position || null);
15244             if(this.cls){
15245                 this.el.addClass(this.cls);
15246                 delete this.cls;
15247             }
15248             if(this.style){
15249                 this.el.applyStyles(this.style);
15250                 delete this.style;
15251             }
15252             this.fireEvent("render", this);
15253             this.afterRender(this.container);
15254             if(this.hidden){
15255                 this.hide();
15256             }
15257             if(this.disabled){
15258                 this.disable();
15259             }
15260         }
15261         return this;
15262     },
15263
15264     /** @private */
15265     // default function is not really useful
15266     onRender : function(ct, position){
15267         if(this.el){
15268             this.el = Roo.get(this.el);
15269             if(this.allowDomMove !== false){
15270                 ct.dom.insertBefore(this.el.dom, position);
15271             }
15272         }
15273     },
15274
15275     /** @private */
15276     getAutoCreate : function(){
15277         var cfg = typeof this.autoCreate == "object" ?
15278                       this.autoCreate : Roo.apply({}, this.defaultAutoCreate);
15279         if(this.id && !cfg.id){
15280             cfg.id = this.id;
15281         }
15282         return cfg;
15283     },
15284
15285     /** @private */
15286     afterRender : Roo.emptyFn,
15287
15288     /**
15289      * Destroys this component by purging any event listeners, removing the component's element from the DOM,
15290      * removing the component from its {@link Roo.Container} (if applicable) and unregistering it from {@link Roo.ComponentMgr}.
15291      */
15292     destroy : function(){
15293         if(this.fireEvent("beforedestroy", this) !== false){
15294             this.purgeListeners();
15295             this.beforeDestroy();
15296             if(this.rendered){
15297                 this.el.removeAllListeners();
15298                 this.el.remove();
15299                 if(this.actionMode == "container"){
15300                     this.container.remove();
15301                 }
15302             }
15303             this.onDestroy();
15304             Roo.ComponentMgr.unregister(this);
15305             this.fireEvent("destroy", this);
15306         }
15307     },
15308
15309         /** @private */
15310     beforeDestroy : function(){
15311
15312     },
15313
15314         /** @private */
15315         onDestroy : function(){
15316
15317     },
15318
15319     /**
15320      * Returns the underlying {@link Roo.Element}.
15321      * @return {Roo.Element} The element
15322      */
15323     getEl : function(){
15324         return this.el;
15325     },
15326
15327     /**
15328      * Returns the id of this component.
15329      * @return {String}
15330      */
15331     getId : function(){
15332         return this.id;
15333     },
15334
15335     /**
15336      * Try to focus this component.
15337      * @param {Boolean} selectText True to also select the text in this component (if applicable)
15338      * @return {Roo.Component} this
15339      */
15340     focus : function(selectText){
15341         if(this.rendered){
15342             this.el.focus();
15343             if(selectText === true){
15344                 this.el.dom.select();
15345             }
15346         }
15347         return this;
15348     },
15349
15350     /** @private */
15351     blur : function(){
15352         if(this.rendered){
15353             this.el.blur();
15354         }
15355         return this;
15356     },
15357
15358     /**
15359      * Disable this component.
15360      * @return {Roo.Component} this
15361      */
15362     disable : function(){
15363         if(this.rendered){
15364             this.onDisable();
15365         }
15366         this.disabled = true;
15367         this.fireEvent("disable", this);
15368         return this;
15369     },
15370
15371         // private
15372     onDisable : function(){
15373         this.getActionEl().addClass(this.disabledClass);
15374         this.el.dom.disabled = true;
15375     },
15376
15377     /**
15378      * Enable this component.
15379      * @return {Roo.Component} this
15380      */
15381     enable : function(){
15382         if(this.rendered){
15383             this.onEnable();
15384         }
15385         this.disabled = false;
15386         this.fireEvent("enable", this);
15387         return this;
15388     },
15389
15390         // private
15391     onEnable : function(){
15392         this.getActionEl().removeClass(this.disabledClass);
15393         this.el.dom.disabled = false;
15394     },
15395
15396     /**
15397      * Convenience function for setting disabled/enabled by boolean.
15398      * @param {Boolean} disabled
15399      */
15400     setDisabled : function(disabled){
15401         this[disabled ? "disable" : "enable"]();
15402     },
15403
15404     /**
15405      * Show this component.
15406      * @return {Roo.Component} this
15407      */
15408     show: function(){
15409         if(this.fireEvent("beforeshow", this) !== false){
15410             this.hidden = false;
15411             if(this.rendered){
15412                 this.onShow();
15413             }
15414             this.fireEvent("show", this);
15415         }
15416         return this;
15417     },
15418
15419     // private
15420     onShow : function(){
15421         var ae = this.getActionEl();
15422         if(this.hideMode == 'visibility'){
15423             ae.dom.style.visibility = "visible";
15424         }else if(this.hideMode == 'offsets'){
15425             ae.removeClass('x-hidden');
15426         }else{
15427             ae.dom.style.display = "";
15428         }
15429     },
15430
15431     /**
15432      * Hide this component.
15433      * @return {Roo.Component} this
15434      */
15435     hide: function(){
15436         if(this.fireEvent("beforehide", this) !== false){
15437             this.hidden = true;
15438             if(this.rendered){
15439                 this.onHide();
15440             }
15441             this.fireEvent("hide", this);
15442         }
15443         return this;
15444     },
15445
15446     // private
15447     onHide : function(){
15448         var ae = this.getActionEl();
15449         if(this.hideMode == 'visibility'){
15450             ae.dom.style.visibility = "hidden";
15451         }else if(this.hideMode == 'offsets'){
15452             ae.addClass('x-hidden');
15453         }else{
15454             ae.dom.style.display = "none";
15455         }
15456     },
15457
15458     /**
15459      * Convenience function to hide or show this component by boolean.
15460      * @param {Boolean} visible True to show, false to hide
15461      * @return {Roo.Component} this
15462      */
15463     setVisible: function(visible){
15464         if(visible) {
15465             this.show();
15466         }else{
15467             this.hide();
15468         }
15469         return this;
15470     },
15471
15472     /**
15473      * Returns true if this component is visible.
15474      */
15475     isVisible : function(){
15476         return this.getActionEl().isVisible();
15477     },
15478
15479     cloneConfig : function(overrides){
15480         overrides = overrides || {};
15481         var id = overrides.id || Roo.id();
15482         var cfg = Roo.applyIf(overrides, this.initialConfig);
15483         cfg.id = id; // prevent dup id
15484         return new this.constructor(cfg);
15485     }
15486 });/*
15487  * Based on:
15488  * Ext JS Library 1.1.1
15489  * Copyright(c) 2006-2007, Ext JS, LLC.
15490  *
15491  * Originally Released Under LGPL - original licence link has changed is not relivant.
15492  *
15493  * Fork - LGPL
15494  * <script type="text/javascript">
15495  */
15496
15497 /**
15498  * @class Roo.BoxComponent
15499  * @extends Roo.Component
15500  * Base class for any visual {@link Roo.Component} that uses a box container.  BoxComponent provides automatic box
15501  * model adjustments for sizing and positioning and will work correctly withnin the Component rendering model.  All
15502  * container classes should subclass BoxComponent so that they will work consistently when nested within other Ext
15503  * layout containers.
15504  * @constructor
15505  * @param {Roo.Element/String/Object} config The configuration options.
15506  */
15507 Roo.BoxComponent = function(config){
15508     Roo.Component.call(this, config);
15509     this.addEvents({
15510         /**
15511          * @event resize
15512          * Fires after the component is resized.
15513              * @param {Roo.Component} this
15514              * @param {Number} adjWidth The box-adjusted width that was set
15515              * @param {Number} adjHeight The box-adjusted height that was set
15516              * @param {Number} rawWidth The width that was originally specified
15517              * @param {Number} rawHeight The height that was originally specified
15518              */
15519         resize : true,
15520         /**
15521          * @event move
15522          * Fires after the component is moved.
15523              * @param {Roo.Component} this
15524              * @param {Number} x The new x position
15525              * @param {Number} y The new y position
15526              */
15527         move : true
15528     });
15529 };
15530
15531 Roo.extend(Roo.BoxComponent, Roo.Component, {
15532     // private, set in afterRender to signify that the component has been rendered
15533     boxReady : false,
15534     // private, used to defer height settings to subclasses
15535     deferHeight: false,
15536     /** @cfg {Number} width
15537      * width (optional) size of component
15538      */
15539      /** @cfg {Number} height
15540      * height (optional) size of component
15541      */
15542      
15543     /**
15544      * Sets the width and height of the component.  This method fires the resize event.  This method can accept
15545      * either width and height as separate numeric arguments, or you can pass a size object like {width:10, height:20}.
15546      * @param {Number/Object} width The new width to set, or a size object in the format {width, height}
15547      * @param {Number} height The new height to set (not required if a size object is passed as the first arg)
15548      * @return {Roo.BoxComponent} this
15549      */
15550     setSize : function(w, h){
15551         // support for standard size objects
15552         if(typeof w == 'object'){
15553             h = w.height;
15554             w = w.width;
15555         }
15556         // not rendered
15557         if(!this.boxReady){
15558             this.width = w;
15559             this.height = h;
15560             return this;
15561         }
15562
15563         // prevent recalcs when not needed
15564         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
15565             return this;
15566         }
15567         this.lastSize = {width: w, height: h};
15568
15569         var adj = this.adjustSize(w, h);
15570         var aw = adj.width, ah = adj.height;
15571         if(aw !== undefined || ah !== undefined){ // this code is nasty but performs better with floaters
15572             var rz = this.getResizeEl();
15573             if(!this.deferHeight && aw !== undefined && ah !== undefined){
15574                 rz.setSize(aw, ah);
15575             }else if(!this.deferHeight && ah !== undefined){
15576                 rz.setHeight(ah);
15577             }else if(aw !== undefined){
15578                 rz.setWidth(aw);
15579             }
15580             this.onResize(aw, ah, w, h);
15581             this.fireEvent('resize', this, aw, ah, w, h);
15582         }
15583         return this;
15584     },
15585
15586     /**
15587      * Gets the current size of the component's underlying element.
15588      * @return {Object} An object containing the element's size {width: (element width), height: (element height)}
15589      */
15590     getSize : function(){
15591         return this.el.getSize();
15592     },
15593
15594     /**
15595      * Gets the current XY position of the component's underlying element.
15596      * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
15597      * @return {Array} The XY position of the element (e.g., [100, 200])
15598      */
15599     getPosition : function(local){
15600         if(local === true){
15601             return [this.el.getLeft(true), this.el.getTop(true)];
15602         }
15603         return this.xy || this.el.getXY();
15604     },
15605
15606     /**
15607      * Gets the current box measurements of the component's underlying element.
15608      * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
15609      * @returns {Object} box An object in the format {x, y, width, height}
15610      */
15611     getBox : function(local){
15612         var s = this.el.getSize();
15613         if(local){
15614             s.x = this.el.getLeft(true);
15615             s.y = this.el.getTop(true);
15616         }else{
15617             var xy = this.xy || this.el.getXY();
15618             s.x = xy[0];
15619             s.y = xy[1];
15620         }
15621         return s;
15622     },
15623
15624     /**
15625      * Sets the current box measurements of the component's underlying element.
15626      * @param {Object} box An object in the format {x, y, width, height}
15627      * @returns {Roo.BoxComponent} this
15628      */
15629     updateBox : function(box){
15630         this.setSize(box.width, box.height);
15631         this.setPagePosition(box.x, box.y);
15632         return this;
15633     },
15634
15635     // protected
15636     getResizeEl : function(){
15637         return this.resizeEl || this.el;
15638     },
15639
15640     // protected
15641     getPositionEl : function(){
15642         return this.positionEl || this.el;
15643     },
15644
15645     /**
15646      * Sets the left and top of the component.  To set the page XY position instead, use {@link #setPagePosition}.
15647      * This method fires the move event.
15648      * @param {Number} left The new left
15649      * @param {Number} top The new top
15650      * @returns {Roo.BoxComponent} this
15651      */
15652     setPosition : function(x, y){
15653         this.x = x;
15654         this.y = y;
15655         if(!this.boxReady){
15656             return this;
15657         }
15658         var adj = this.adjustPosition(x, y);
15659         var ax = adj.x, ay = adj.y;
15660
15661         var el = this.getPositionEl();
15662         if(ax !== undefined || ay !== undefined){
15663             if(ax !== undefined && ay !== undefined){
15664                 el.setLeftTop(ax, ay);
15665             }else if(ax !== undefined){
15666                 el.setLeft(ax);
15667             }else if(ay !== undefined){
15668                 el.setTop(ay);
15669             }
15670             this.onPosition(ax, ay);
15671             this.fireEvent('move', this, ax, ay);
15672         }
15673         return this;
15674     },
15675
15676     /**
15677      * Sets the page XY position of the component.  To set the left and top instead, use {@link #setPosition}.
15678      * This method fires the move event.
15679      * @param {Number} x The new x position
15680      * @param {Number} y The new y position
15681      * @returns {Roo.BoxComponent} this
15682      */
15683     setPagePosition : function(x, y){
15684         this.pageX = x;
15685         this.pageY = y;
15686         if(!this.boxReady){
15687             return;
15688         }
15689         if(x === undefined || y === undefined){ // cannot translate undefined points
15690             return;
15691         }
15692         var p = this.el.translatePoints(x, y);
15693         this.setPosition(p.left, p.top);
15694         return this;
15695     },
15696
15697     // private
15698     onRender : function(ct, position){
15699         Roo.BoxComponent.superclass.onRender.call(this, ct, position);
15700         if(this.resizeEl){
15701             this.resizeEl = Roo.get(this.resizeEl);
15702         }
15703         if(this.positionEl){
15704             this.positionEl = Roo.get(this.positionEl);
15705         }
15706     },
15707
15708     // private
15709     afterRender : function(){
15710         Roo.BoxComponent.superclass.afterRender.call(this);
15711         this.boxReady = true;
15712         this.setSize(this.width, this.height);
15713         if(this.x || this.y){
15714             this.setPosition(this.x, this.y);
15715         }
15716         if(this.pageX || this.pageY){
15717             this.setPagePosition(this.pageX, this.pageY);
15718         }
15719     },
15720
15721     /**
15722      * Force the component's size to recalculate based on the underlying element's current height and width.
15723      * @returns {Roo.BoxComponent} this
15724      */
15725     syncSize : function(){
15726         delete this.lastSize;
15727         this.setSize(this.el.getWidth(), this.el.getHeight());
15728         return this;
15729     },
15730
15731     /**
15732      * Called after the component is resized, this method is empty by default but can be implemented by any
15733      * subclass that needs to perform custom logic after a resize occurs.
15734      * @param {Number} adjWidth The box-adjusted width that was set
15735      * @param {Number} adjHeight The box-adjusted height that was set
15736      * @param {Number} rawWidth The width that was originally specified
15737      * @param {Number} rawHeight The height that was originally specified
15738      */
15739     onResize : function(adjWidth, adjHeight, rawWidth, rawHeight){
15740
15741     },
15742
15743     /**
15744      * Called after the component is moved, this method is empty by default but can be implemented by any
15745      * subclass that needs to perform custom logic after a move occurs.
15746      * @param {Number} x The new x position
15747      * @param {Number} y The new y position
15748      */
15749     onPosition : function(x, y){
15750
15751     },
15752
15753     // private
15754     adjustSize : function(w, h){
15755         if(this.autoWidth){
15756             w = 'auto';
15757         }
15758         if(this.autoHeight){
15759             h = 'auto';
15760         }
15761         return {width : w, height: h};
15762     },
15763
15764     // private
15765     adjustPosition : function(x, y){
15766         return {x : x, y: y};
15767     }
15768 });/*
15769  * Original code for Roojs - LGPL
15770  * <script type="text/javascript">
15771  */
15772  
15773 /**
15774  * @class Roo.XComponent
15775  * A delayed Element creator...
15776  * Or a way to group chunks of interface together.
15777  * technically this is a wrapper around a tree of Roo elements (which defines a 'module'),
15778  *  used in conjunction with XComponent.build() it will create an instance of each element,
15779  *  then call addxtype() to build the User interface.
15780  * 
15781  * Mypart.xyx = new Roo.XComponent({
15782
15783     parent : 'Mypart.xyz', // empty == document.element.!!
15784     order : '001',
15785     name : 'xxxx'
15786     region : 'xxxx'
15787     disabled : function() {} 
15788      
15789     tree : function() { // return an tree of xtype declared components
15790         var MODULE = this;
15791         return 
15792         {
15793             xtype : 'NestedLayoutPanel',
15794             // technicall
15795         }
15796      ]
15797  *})
15798  *
15799  *
15800  * It can be used to build a big heiracy, with parent etc.
15801  * or you can just use this to render a single compoent to a dom element
15802  * MYPART.render(Roo.Element | String(id) | dom_element )
15803  *
15804  *
15805  * Usage patterns.
15806  *
15807  * Classic Roo
15808  *
15809  * Roo is designed primarily as a single page application, so the UI build for a standard interface will
15810  * expect a single 'TOP' level module normally indicated by the 'parent' of the XComponent definition being defined as false.
15811  *
15812  * Each sub module is expected to have a parent pointing to the class name of it's parent module.
15813  *
15814  * When the top level is false, a 'Roo.BorderLayout' is created and the element is flagged as 'topModule'
15815  * - if mulitple topModules exist, the last one is defined as the top module.
15816  *
15817  * Embeded Roo
15818  * 
15819  * When the top level or multiple modules are to embedded into a existing HTML page,
15820  * the parent element can container '#id' of the element where the module will be drawn.
15821  *
15822  * Bootstrap Roo
15823  *
15824  * Unlike classic Roo, the bootstrap tends not to be used as a single page.
15825  * it relies more on a include mechanism, where sub modules are included into an outer page.
15826  * This is normally managed by the builder tools using Roo.apply( options, Included.Sub.Module )
15827  * 
15828  * Bootstrap Roo Included elements
15829  *
15830  * Our builder application needs the ability to preview these sub compoennts. They will normally have parent=false set,
15831  * hence confusing the component builder as it thinks there are multiple top level elements. 
15832  *
15833  * 
15834  * 
15835  * @extends Roo.util.Observable
15836  * @constructor
15837  * @param cfg {Object} configuration of component
15838  * 
15839  */
15840 Roo.XComponent = function(cfg) {
15841     Roo.apply(this, cfg);
15842     this.addEvents({ 
15843         /**
15844              * @event built
15845              * Fires when this the componnt is built
15846              * @param {Roo.XComponent} c the component
15847              */
15848         'built' : true
15849         
15850     });
15851     this.region = this.region || 'center'; // default..
15852     Roo.XComponent.register(this);
15853     this.modules = false;
15854     this.el = false; // where the layout goes..
15855     
15856     
15857 }
15858 Roo.extend(Roo.XComponent, Roo.util.Observable, {
15859     /**
15860      * @property el
15861      * The created element (with Roo.factory())
15862      * @type {Roo.Layout}
15863      */
15864     el  : false,
15865     
15866     /**
15867      * @property el
15868      * for BC  - use el in new code
15869      * @type {Roo.Layout}
15870      */
15871     panel : false,
15872     
15873     /**
15874      * @property layout
15875      * for BC  - use el in new code
15876      * @type {Roo.Layout}
15877      */
15878     layout : false,
15879     
15880      /**
15881      * @cfg {Function|boolean} disabled
15882      * If this module is disabled by some rule, return true from the funtion
15883      */
15884     disabled : false,
15885     
15886     /**
15887      * @cfg {String} parent 
15888      * Name of parent element which it get xtype added to..
15889      */
15890     parent: false,
15891     
15892     /**
15893      * @cfg {String} order
15894      * Used to set the order in which elements are created (usefull for multiple tabs)
15895      */
15896     
15897     order : false,
15898     /**
15899      * @cfg {String} name
15900      * String to display while loading.
15901      */
15902     name : false,
15903     /**
15904      * @cfg {String} region
15905      * Region to render component to (defaults to center)
15906      */
15907     region : 'center',
15908     
15909     /**
15910      * @cfg {Array} items
15911      * A single item array - the first element is the root of the tree..
15912      * It's done this way to stay compatible with the Xtype system...
15913      */
15914     items : false,
15915     
15916     /**
15917      * @property _tree
15918      * The method that retuns the tree of parts that make up this compoennt 
15919      * @type {function}
15920      */
15921     _tree  : false,
15922     
15923      /**
15924      * render
15925      * render element to dom or tree
15926      * @param {Roo.Element|String|DomElement} optional render to if parent is not set.
15927      */
15928     
15929     render : function(el)
15930     {
15931         
15932         el = el || false;
15933         var hp = this.parent ? 1 : 0;
15934         Roo.log(this);
15935         
15936         if (!el && typeof(this.parent) == 'string' && this.parent.substring(0,1) == '#') {
15937             // if parent is a '#.....' string, then let's use that..
15938             var ename = this.parent.substr(1);
15939             this.parent = false;
15940             Roo.log(ename);
15941             switch (ename) {
15942                 case 'bootstrap-body' :
15943                     if (typeof(Roo.bootstrap.Body) != 'undefined') {
15944                         this.parent = { el :  new  Roo.bootstrap.Body() };
15945                         Roo.log("setting el to doc body");
15946                          
15947                     } else {
15948                         throw "Container is bootstrap body, but Roo.bootstrap.Body is not defined";
15949                     }
15950                     break;
15951                 case 'bootstrap':
15952                     this.parent = { el : true};
15953                     // fall through
15954                 default:
15955                     el = Roo.get(ename);
15956                     break;
15957             }
15958                 
15959             
15960             if (!el && !this.parent) {
15961                 Roo.log("Warning - element can not be found :#" + ename );
15962                 return;
15963             }
15964         }
15965         Roo.log("EL:");Roo.log(el);
15966         Roo.log("this.parent.el:");Roo.log(this.parent.el);
15967         
15968         var tree = this._tree ? this._tree() : this.tree();
15969
15970         // altertive root elements ??? - we need a better way to indicate these.
15971         var is_alt = (typeof(Roo.bootstrap) != 'undefined' && tree.xns == Roo.bootstrap) ||
15972                         (typeof(Roo.mailer) != 'undefined' && tree.xns == Roo.mailer) ;
15973         
15974         if (!this.parent && is_alt) {
15975             //el = Roo.get(document.body);
15976             this.parent = { el : true };
15977         }
15978             
15979             
15980         
15981         if (!this.parent) {
15982             
15983             Roo.log("no parent - creating one");
15984             
15985             el = el ? Roo.get(el) : false;      
15986             
15987             // it's a top level one..
15988             this.parent =  {
15989                 el : new Roo.BorderLayout(el || document.body, {
15990                 
15991                      center: {
15992                          titlebar: false,
15993                          autoScroll:false,
15994                          closeOnTab: true,
15995                          tabPosition: 'top',
15996                           //resizeTabs: true,
15997                          alwaysShowTabs: el && hp? false :  true,
15998                          hideTabs: el || !hp ? true :  false,
15999                          minTabWidth: 140
16000                      }
16001                  })
16002             }
16003         }
16004         
16005                 if (!this.parent.el) {
16006                         // probably an old style ctor, which has been disabled.
16007                         return;
16008                         
16009                 }
16010                 // The 'tree' method is  '_tree now' 
16011             
16012         tree.region = tree.region || this.region;
16013         
16014         if (this.parent.el === true) {
16015             // bootstrap... - body..
16016             this.parent.el = Roo.factory(tree);
16017         }
16018         
16019         this.el = this.parent.el.addxtype(tree);
16020         this.fireEvent('built', this);
16021         
16022         this.panel = this.el;
16023         this.layout = this.panel.layout;
16024                 this.parentLayout = this.parent.layout  || false;  
16025          
16026     }
16027     
16028 });
16029
16030 Roo.apply(Roo.XComponent, {
16031     /**
16032      * @property  hideProgress
16033      * true to disable the building progress bar.. usefull on single page renders.
16034      * @type Boolean
16035      */
16036     hideProgress : false,
16037     /**
16038      * @property  buildCompleted
16039      * True when the builder has completed building the interface.
16040      * @type Boolean
16041      */
16042     buildCompleted : false,
16043      
16044     /**
16045      * @property  topModule
16046      * the upper most module - uses document.element as it's constructor.
16047      * @type Object
16048      */
16049      
16050     topModule  : false,
16051       
16052     /**
16053      * @property  modules
16054      * array of modules to be created by registration system.
16055      * @type {Array} of Roo.XComponent
16056      */
16057     
16058     modules : [],
16059     /**
16060      * @property  elmodules
16061      * array of modules to be created by which use #ID 
16062      * @type {Array} of Roo.XComponent
16063      */
16064      
16065     elmodules : [],
16066
16067      /**
16068      * @property  build_from_html
16069      * Build elements from html - used by bootstrap HTML stuff 
16070      *    - this is cleared after build is completed
16071      * @type {boolean} true  (default false)
16072      */
16073      
16074     build_from_html : false,
16075
16076     /**
16077      * Register components to be built later.
16078      *
16079      * This solves the following issues
16080      * - Building is not done on page load, but after an authentication process has occured.
16081      * - Interface elements are registered on page load
16082      * - Parent Interface elements may not be loaded before child, so this handles that..
16083      * 
16084      *
16085      * example:
16086      * 
16087      * MyApp.register({
16088           order : '000001',
16089           module : 'Pman.Tab.projectMgr',
16090           region : 'center',
16091           parent : 'Pman.layout',
16092           disabled : false,  // or use a function..
16093         })
16094      
16095      * * @param {Object} details about module
16096      */
16097     register : function(obj) {
16098                 
16099         Roo.XComponent.event.fireEvent('register', obj);
16100         switch(typeof(obj.disabled) ) {
16101                 
16102             case 'undefined':
16103                 break;
16104             
16105             case 'function':
16106                 if ( obj.disabled() ) {
16107                         return;
16108                 }
16109                 break;
16110             
16111             default:
16112                 if (obj.disabled) {
16113                         return;
16114                 }
16115                 break;
16116         }
16117                 
16118         this.modules.push(obj);
16119          
16120     },
16121     /**
16122      * convert a string to an object..
16123      * eg. 'AAA.BBB' -> finds AAA.BBB
16124
16125      */
16126     
16127     toObject : function(str)
16128     {
16129         if (!str || typeof(str) == 'object') {
16130             return str;
16131         }
16132         if (str.substring(0,1) == '#') {
16133             return str;
16134         }
16135
16136         var ar = str.split('.');
16137         var rt, o;
16138         rt = ar.shift();
16139             /** eval:var:o */
16140         try {
16141             eval('if (typeof ' + rt + ' == "undefined"){ o = false;} o = ' + rt + ';');
16142         } catch (e) {
16143             throw "Module not found : " + str;
16144         }
16145         
16146         if (o === false) {
16147             throw "Module not found : " + str;
16148         }
16149         Roo.each(ar, function(e) {
16150             if (typeof(o[e]) == 'undefined') {
16151                 throw "Module not found : " + str;
16152             }
16153             o = o[e];
16154         });
16155         
16156         return o;
16157         
16158     },
16159     
16160     
16161     /**
16162      * move modules into their correct place in the tree..
16163      * 
16164      */
16165     preBuild : function ()
16166     {
16167         var _t = this;
16168         Roo.each(this.modules , function (obj)
16169         {
16170             Roo.XComponent.event.fireEvent('beforebuild', obj);
16171             
16172             var opar = obj.parent;
16173             try { 
16174                 obj.parent = this.toObject(opar);
16175             } catch(e) {
16176                 Roo.log("parent:toObject failed: " + e.toString());
16177                 return;
16178             }
16179             
16180             if (!obj.parent) {
16181                 Roo.debug && Roo.log("GOT top level module");
16182                 Roo.debug && Roo.log(obj);
16183                 obj.modules = new Roo.util.MixedCollection(false, 
16184                     function(o) { return o.order + '' }
16185                 );
16186                 this.topModule = obj;
16187                 return;
16188             }
16189                         // parent is a string (usually a dom element name..)
16190             if (typeof(obj.parent) == 'string') {
16191                 this.elmodules.push(obj);
16192                 return;
16193             }
16194             if (obj.parent.constructor != Roo.XComponent) {
16195                 Roo.log("Warning : Object Parent is not instance of XComponent:" + obj.name)
16196             }
16197             if (!obj.parent.modules) {
16198                 obj.parent.modules = new Roo.util.MixedCollection(false, 
16199                     function(o) { return o.order + '' }
16200                 );
16201             }
16202             if (obj.parent.disabled) {
16203                 obj.disabled = true;
16204             }
16205             obj.parent.modules.add(obj);
16206         }, this);
16207     },
16208     
16209      /**
16210      * make a list of modules to build.
16211      * @return {Array} list of modules. 
16212      */ 
16213     
16214     buildOrder : function()
16215     {
16216         var _this = this;
16217         var cmp = function(a,b) {   
16218             return String(a).toUpperCase() > String(b).toUpperCase() ? 1 : -1;
16219         };
16220         if ((!this.topModule || !this.topModule.modules) && !this.elmodules.length) {
16221             throw "No top level modules to build";
16222         }
16223         
16224         // make a flat list in order of modules to build.
16225         var mods = this.topModule ? [ this.topModule ] : [];
16226                 
16227         
16228         // elmodules (is a list of DOM based modules )
16229         Roo.each(this.elmodules, function(e) {
16230             mods.push(e);
16231             if (!this.topModule &&
16232                 typeof(e.parent) == 'string' &&
16233                 e.parent.substring(0,1) == '#' &&
16234                 Roo.get(e.parent.substr(1))
16235                ) {
16236                 
16237                 _this.topModule = e;
16238             }
16239             
16240         });
16241
16242         
16243         // add modules to their parents..
16244         var addMod = function(m) {
16245             Roo.debug && Roo.log("build Order: add: " + m.name);
16246                 
16247             mods.push(m);
16248             if (m.modules && !m.disabled) {
16249                 Roo.debug && Roo.log("build Order: " + m.modules.length + " child modules");
16250                 m.modules.keySort('ASC',  cmp );
16251                 Roo.debug && Roo.log("build Order: " + m.modules.length + " child modules (after sort)");
16252     
16253                 m.modules.each(addMod);
16254             } else {
16255                 Roo.debug && Roo.log("build Order: no child modules");
16256             }
16257             // not sure if this is used any more..
16258             if (m.finalize) {
16259                 m.finalize.name = m.name + " (clean up) ";
16260                 mods.push(m.finalize);
16261             }
16262             
16263         }
16264         if (this.topModule && this.topModule.modules) { 
16265             this.topModule.modules.keySort('ASC',  cmp );
16266             this.topModule.modules.each(addMod);
16267         } 
16268         return mods;
16269     },
16270     
16271      /**
16272      * Build the registered modules.
16273      * @param {Object} parent element.
16274      * @param {Function} optional method to call after module has been added.
16275      * 
16276      */ 
16277    
16278     build : function(opts) 
16279     {
16280         
16281         if (typeof(opts) != 'undefined') {
16282             Roo.apply(this,opts);
16283         }
16284         
16285         this.preBuild();
16286         var mods = this.buildOrder();
16287       
16288         //this.allmods = mods;
16289         //Roo.debug && Roo.log(mods);
16290         //return;
16291         if (!mods.length) { // should not happen
16292             throw "NO modules!!!";
16293         }
16294         
16295         
16296         var msg = "Building Interface...";
16297         // flash it up as modal - so we store the mask!?
16298         if (!this.hideProgress && Roo.MessageBox) {
16299             Roo.MessageBox.show({ title: 'loading' });
16300             Roo.MessageBox.show({
16301                title: "Please wait...",
16302                msg: msg,
16303                width:450,
16304                progress:true,
16305                closable:false,
16306                modal: false
16307               
16308             });
16309         }
16310         var total = mods.length;
16311         
16312         var _this = this;
16313         var progressRun = function() {
16314             if (!mods.length) {
16315                 Roo.debug && Roo.log('hide?');
16316                 if (!this.hideProgress && Roo.MessageBox) {
16317                     Roo.MessageBox.hide();
16318                 }
16319                 Roo.XComponent.build_from_html = false; // reset, so dialogs will be build from javascript
16320                 
16321                 Roo.XComponent.event.fireEvent('buildcomplete', _this.topModule);
16322                 
16323                 // THE END...
16324                 return false;   
16325             }
16326             
16327             var m = mods.shift();
16328             
16329             
16330             Roo.debug && Roo.log(m);
16331             // not sure if this is supported any more.. - modules that are are just function
16332             if (typeof(m) == 'function') { 
16333                 m.call(this);
16334                 return progressRun.defer(10, _this);
16335             } 
16336             
16337             
16338             msg = "Building Interface " + (total  - mods.length) + 
16339                     " of " + total + 
16340                     (m.name ? (' - ' + m.name) : '');
16341                         Roo.debug && Roo.log(msg);
16342             if (!this.hideProgress &&  Roo.MessageBox) { 
16343                 Roo.MessageBox.updateProgress(  (total  - mods.length)/total, msg  );
16344             }
16345             
16346          
16347             // is the module disabled?
16348             var disabled = (typeof(m.disabled) == 'function') ?
16349                 m.disabled.call(m.module.disabled) : m.disabled;    
16350             
16351             
16352             if (disabled) {
16353                 return progressRun(); // we do not update the display!
16354             }
16355             
16356             // now build 
16357             
16358                         
16359                         
16360             m.render();
16361             // it's 10 on top level, and 1 on others??? why...
16362             return progressRun.defer(10, _this);
16363              
16364         }
16365         progressRun.defer(1, _this);
16366      
16367         
16368         
16369     },
16370         
16371         
16372         /**
16373          * Event Object.
16374          *
16375          *
16376          */
16377         event: false, 
16378     /**
16379          * wrapper for event.on - aliased later..  
16380          * Typically use to register a event handler for register:
16381          *
16382          * eg. Roo.XComponent.on('register', function(comp) { comp.disable = true } );
16383          *
16384          */
16385     on : false
16386    
16387     
16388     
16389 });
16390
16391 Roo.XComponent.event = new Roo.util.Observable({
16392                 events : { 
16393                         /**
16394                          * @event register
16395                          * Fires when an Component is registered,
16396                          * set the disable property on the Component to stop registration.
16397                          * @param {Roo.XComponent} c the component being registerd.
16398                          * 
16399                          */
16400                         'register' : true,
16401             /**
16402                          * @event beforebuild
16403                          * Fires before each Component is built
16404                          * can be used to apply permissions.
16405                          * @param {Roo.XComponent} c the component being registerd.
16406                          * 
16407                          */
16408                         'beforebuild' : true,
16409                         /**
16410                          * @event buildcomplete
16411                          * Fires on the top level element when all elements have been built
16412                          * @param {Roo.XComponent} the top level component.
16413                          */
16414                         'buildcomplete' : true
16415                         
16416                 }
16417 });
16418
16419 Roo.XComponent.on = Roo.XComponent.event.on.createDelegate(Roo.XComponent.event); 
16420  /*
16421  * Based on:
16422  * Ext JS Library 1.1.1
16423  * Copyright(c) 2006-2007, Ext JS, LLC.
16424  *
16425  * Originally Released Under LGPL - original licence link has changed is not relivant.
16426  *
16427  * Fork - LGPL
16428  * <script type="text/javascript">
16429  */
16430
16431
16432
16433 /*
16434  * These classes are derivatives of the similarly named classes in the YUI Library.
16435  * The original license:
16436  * Copyright (c) 2006, Yahoo! Inc. All rights reserved.
16437  * Code licensed under the BSD License:
16438  * http://developer.yahoo.net/yui/license.txt
16439  */
16440
16441 (function() {
16442
16443 var Event=Roo.EventManager;
16444 var Dom=Roo.lib.Dom;
16445
16446 /**
16447  * @class Roo.dd.DragDrop
16448  * @extends Roo.util.Observable
16449  * Defines the interface and base operation of items that that can be
16450  * dragged or can be drop targets.  It was designed to be extended, overriding
16451  * the event handlers for startDrag, onDrag, onDragOver and onDragOut.
16452  * Up to three html elements can be associated with a DragDrop instance:
16453  * <ul>
16454  * <li>linked element: the element that is passed into the constructor.
16455  * This is the element which defines the boundaries for interaction with
16456  * other DragDrop objects.</li>
16457  * <li>handle element(s): The drag operation only occurs if the element that
16458  * was clicked matches a handle element.  By default this is the linked
16459  * element, but there are times that you will want only a portion of the
16460  * linked element to initiate the drag operation, and the setHandleElId()
16461  * method provides a way to define this.</li>
16462  * <li>drag element: this represents the element that would be moved along
16463  * with the cursor during a drag operation.  By default, this is the linked
16464  * element itself as in {@link Roo.dd.DD}.  setDragElId() lets you define
16465  * a separate element that would be moved, as in {@link Roo.dd.DDProxy}.
16466  * </li>
16467  * </ul>
16468  * This class should not be instantiated until the onload event to ensure that
16469  * the associated elements are available.
16470  * The following would define a DragDrop obj that would interact with any
16471  * other DragDrop obj in the "group1" group:
16472  * <pre>
16473  *  dd = new Roo.dd.DragDrop("div1", "group1");
16474  * </pre>
16475  * Since none of the event handlers have been implemented, nothing would
16476  * actually happen if you were to run the code above.  Normally you would
16477  * override this class or one of the default implementations, but you can
16478  * also override the methods you want on an instance of the class...
16479  * <pre>
16480  *  dd.onDragDrop = function(e, id) {
16481  *  &nbsp;&nbsp;alert("dd was dropped on " + id);
16482  *  }
16483  * </pre>
16484  * @constructor
16485  * @param {String} id of the element that is linked to this instance
16486  * @param {String} sGroup the group of related DragDrop objects
16487  * @param {object} config an object containing configurable attributes
16488  *                Valid properties for DragDrop:
16489  *                    padding, isTarget, maintainOffset, primaryButtonOnly
16490  */
16491 Roo.dd.DragDrop = function(id, sGroup, config) {
16492     if (id) {
16493         this.init(id, sGroup, config);
16494     }
16495     
16496 };
16497
16498 Roo.extend(Roo.dd.DragDrop, Roo.util.Observable , {
16499
16500     /**
16501      * The id of the element associated with this object.  This is what we
16502      * refer to as the "linked element" because the size and position of
16503      * this element is used to determine when the drag and drop objects have
16504      * interacted.
16505      * @property id
16506      * @type String
16507      */
16508     id: null,
16509
16510     /**
16511      * Configuration attributes passed into the constructor
16512      * @property config
16513      * @type object
16514      */
16515     config: null,
16516
16517     /**
16518      * The id of the element that will be dragged.  By default this is same
16519      * as the linked element , but could be changed to another element. Ex:
16520      * Roo.dd.DDProxy
16521      * @property dragElId
16522      * @type String
16523      * @private
16524      */
16525     dragElId: null,
16526
16527     /**
16528      * the id of the element that initiates the drag operation.  By default
16529      * this is the linked element, but could be changed to be a child of this
16530      * element.  This lets us do things like only starting the drag when the
16531      * header element within the linked html element is clicked.
16532      * @property handleElId
16533      * @type String
16534      * @private
16535      */
16536     handleElId: null,
16537
16538     /**
16539      * An associative array of HTML tags that will be ignored if clicked.
16540      * @property invalidHandleTypes
16541      * @type {string: string}
16542      */
16543     invalidHandleTypes: null,
16544
16545     /**
16546      * An associative array of ids for elements that will be ignored if clicked
16547      * @property invalidHandleIds
16548      * @type {string: string}
16549      */
16550     invalidHandleIds: null,
16551
16552     /**
16553      * An indexted array of css class names for elements that will be ignored
16554      * if clicked.
16555      * @property invalidHandleClasses
16556      * @type string[]
16557      */
16558     invalidHandleClasses: null,
16559
16560     /**
16561      * The linked element's absolute X position at the time the drag was
16562      * started
16563      * @property startPageX
16564      * @type int
16565      * @private
16566      */
16567     startPageX: 0,
16568
16569     /**
16570      * The linked element's absolute X position at the time the drag was
16571      * started
16572      * @property startPageY
16573      * @type int
16574      * @private
16575      */
16576     startPageY: 0,
16577
16578     /**
16579      * The group defines a logical collection of DragDrop objects that are
16580      * related.  Instances only get events when interacting with other
16581      * DragDrop object in the same group.  This lets us define multiple
16582      * groups using a single DragDrop subclass if we want.
16583      * @property groups
16584      * @type {string: string}
16585      */
16586     groups: null,
16587
16588     /**
16589      * Individual drag/drop instances can be locked.  This will prevent
16590      * onmousedown start drag.
16591      * @property locked
16592      * @type boolean
16593      * @private
16594      */
16595     locked: false,
16596
16597     /**
16598      * Lock this instance
16599      * @method lock
16600      */
16601     lock: function() { this.locked = true; },
16602
16603     /**
16604      * Unlock this instace
16605      * @method unlock
16606      */
16607     unlock: function() { this.locked = false; },
16608
16609     /**
16610      * By default, all insances can be a drop target.  This can be disabled by
16611      * setting isTarget to false.
16612      * @method isTarget
16613      * @type boolean
16614      */
16615     isTarget: true,
16616
16617     /**
16618      * The padding configured for this drag and drop object for calculating
16619      * the drop zone intersection with this object.
16620      * @method padding
16621      * @type int[]
16622      */
16623     padding: null,
16624
16625     /**
16626      * Cached reference to the linked element
16627      * @property _domRef
16628      * @private
16629      */
16630     _domRef: null,
16631
16632     /**
16633      * Internal typeof flag
16634      * @property __ygDragDrop
16635      * @private
16636      */
16637     __ygDragDrop: true,
16638
16639     /**
16640      * Set to true when horizontal contraints are applied
16641      * @property constrainX
16642      * @type boolean
16643      * @private
16644      */
16645     constrainX: false,
16646
16647     /**
16648      * Set to true when vertical contraints are applied
16649      * @property constrainY
16650      * @type boolean
16651      * @private
16652      */
16653     constrainY: false,
16654
16655     /**
16656      * The left constraint
16657      * @property minX
16658      * @type int
16659      * @private
16660      */
16661     minX: 0,
16662
16663     /**
16664      * The right constraint
16665      * @property maxX
16666      * @type int
16667      * @private
16668      */
16669     maxX: 0,
16670
16671     /**
16672      * The up constraint
16673      * @property minY
16674      * @type int
16675      * @type int
16676      * @private
16677      */
16678     minY: 0,
16679
16680     /**
16681      * The down constraint
16682      * @property maxY
16683      * @type int
16684      * @private
16685      */
16686     maxY: 0,
16687
16688     /**
16689      * Maintain offsets when we resetconstraints.  Set to true when you want
16690      * the position of the element relative to its parent to stay the same
16691      * when the page changes
16692      *
16693      * @property maintainOffset
16694      * @type boolean
16695      */
16696     maintainOffset: false,
16697
16698     /**
16699      * Array of pixel locations the element will snap to if we specified a
16700      * horizontal graduation/interval.  This array is generated automatically
16701      * when you define a tick interval.
16702      * @property xTicks
16703      * @type int[]
16704      */
16705     xTicks: null,
16706
16707     /**
16708      * Array of pixel locations the element will snap to if we specified a
16709      * vertical graduation/interval.  This array is generated automatically
16710      * when you define a tick interval.
16711      * @property yTicks
16712      * @type int[]
16713      */
16714     yTicks: null,
16715
16716     /**
16717      * By default the drag and drop instance will only respond to the primary
16718      * button click (left button for a right-handed mouse).  Set to true to
16719      * allow drag and drop to start with any mouse click that is propogated
16720      * by the browser
16721      * @property primaryButtonOnly
16722      * @type boolean
16723      */
16724     primaryButtonOnly: true,
16725
16726     /**
16727      * The availabe property is false until the linked dom element is accessible.
16728      * @property available
16729      * @type boolean
16730      */
16731     available: false,
16732
16733     /**
16734      * By default, drags can only be initiated if the mousedown occurs in the
16735      * region the linked element is.  This is done in part to work around a
16736      * bug in some browsers that mis-report the mousedown if the previous
16737      * mouseup happened outside of the window.  This property is set to true
16738      * if outer handles are defined.
16739      *
16740      * @property hasOuterHandles
16741      * @type boolean
16742      * @default false
16743      */
16744     hasOuterHandles: false,
16745
16746     /**
16747      * Code that executes immediately before the startDrag event
16748      * @method b4StartDrag
16749      * @private
16750      */
16751     b4StartDrag: function(x, y) { },
16752
16753     /**
16754      * Abstract method called after a drag/drop object is clicked
16755      * and the drag or mousedown time thresholds have beeen met.
16756      * @method startDrag
16757      * @param {int} X click location
16758      * @param {int} Y click location
16759      */
16760     startDrag: function(x, y) { /* override this */ },
16761
16762     /**
16763      * Code that executes immediately before the onDrag event
16764      * @method b4Drag
16765      * @private
16766      */
16767     b4Drag: function(e) { },
16768
16769     /**
16770      * Abstract method called during the onMouseMove event while dragging an
16771      * object.
16772      * @method onDrag
16773      * @param {Event} e the mousemove event
16774      */
16775     onDrag: function(e) { /* override this */ },
16776
16777     /**
16778      * Abstract method called when this element fist begins hovering over
16779      * another DragDrop obj
16780      * @method onDragEnter
16781      * @param {Event} e the mousemove event
16782      * @param {String|DragDrop[]} id In POINT mode, the element
16783      * id this is hovering over.  In INTERSECT mode, an array of one or more
16784      * dragdrop items being hovered over.
16785      */
16786     onDragEnter: function(e, id) { /* override this */ },
16787
16788     /**
16789      * Code that executes immediately before the onDragOver event
16790      * @method b4DragOver
16791      * @private
16792      */
16793     b4DragOver: function(e) { },
16794
16795     /**
16796      * Abstract method called when this element is hovering over another
16797      * DragDrop obj
16798      * @method onDragOver
16799      * @param {Event} e the mousemove event
16800      * @param {String|DragDrop[]} id In POINT mode, the element
16801      * id this is hovering over.  In INTERSECT mode, an array of dd items
16802      * being hovered over.
16803      */
16804     onDragOver: function(e, id) { /* override this */ },
16805
16806     /**
16807      * Code that executes immediately before the onDragOut event
16808      * @method b4DragOut
16809      * @private
16810      */
16811     b4DragOut: function(e) { },
16812
16813     /**
16814      * Abstract method called when we are no longer hovering over an element
16815      * @method onDragOut
16816      * @param {Event} e the mousemove event
16817      * @param {String|DragDrop[]} id In POINT mode, the element
16818      * id this was hovering over.  In INTERSECT mode, an array of dd items
16819      * that the mouse is no longer over.
16820      */
16821     onDragOut: function(e, id) { /* override this */ },
16822
16823     /**
16824      * Code that executes immediately before the onDragDrop event
16825      * @method b4DragDrop
16826      * @private
16827      */
16828     b4DragDrop: function(e) { },
16829
16830     /**
16831      * Abstract method called when this item is dropped on another DragDrop
16832      * obj
16833      * @method onDragDrop
16834      * @param {Event} e the mouseup event
16835      * @param {String|DragDrop[]} id In POINT mode, the element
16836      * id this was dropped on.  In INTERSECT mode, an array of dd items this
16837      * was dropped on.
16838      */
16839     onDragDrop: function(e, id) { /* override this */ },
16840
16841     /**
16842      * Abstract method called when this item is dropped on an area with no
16843      * drop target
16844      * @method onInvalidDrop
16845      * @param {Event} e the mouseup event
16846      */
16847     onInvalidDrop: function(e) { /* override this */ },
16848
16849     /**
16850      * Code that executes immediately before the endDrag event
16851      * @method b4EndDrag
16852      * @private
16853      */
16854     b4EndDrag: function(e) { },
16855
16856     /**
16857      * Fired when we are done dragging the object
16858      * @method endDrag
16859      * @param {Event} e the mouseup event
16860      */
16861     endDrag: function(e) { /* override this */ },
16862
16863     /**
16864      * Code executed immediately before the onMouseDown event
16865      * @method b4MouseDown
16866      * @param {Event} e the mousedown event
16867      * @private
16868      */
16869     b4MouseDown: function(e) {  },
16870
16871     /**
16872      * Event handler that fires when a drag/drop obj gets a mousedown
16873      * @method onMouseDown
16874      * @param {Event} e the mousedown event
16875      */
16876     onMouseDown: function(e) { /* override this */ },
16877
16878     /**
16879      * Event handler that fires when a drag/drop obj gets a mouseup
16880      * @method onMouseUp
16881      * @param {Event} e the mouseup event
16882      */
16883     onMouseUp: function(e) { /* override this */ },
16884
16885     /**
16886      * Override the onAvailable method to do what is needed after the initial
16887      * position was determined.
16888      * @method onAvailable
16889      */
16890     onAvailable: function () {
16891     },
16892
16893     /*
16894      * Provides default constraint padding to "constrainTo" elements (defaults to {left: 0, right:0, top:0, bottom:0}).
16895      * @type Object
16896      */
16897     defaultPadding : {left:0, right:0, top:0, bottom:0},
16898
16899     /*
16900      * Initializes the drag drop object's constraints to restrict movement to a certain element.
16901  *
16902  * Usage:
16903  <pre><code>
16904  var dd = new Roo.dd.DDProxy("dragDiv1", "proxytest",
16905                 { dragElId: "existingProxyDiv" });
16906  dd.startDrag = function(){
16907      this.constrainTo("parent-id");
16908  };
16909  </code></pre>
16910  * Or you can initalize it using the {@link Roo.Element} object:
16911  <pre><code>
16912  Roo.get("dragDiv1").initDDProxy("proxytest", {dragElId: "existingProxyDiv"}, {
16913      startDrag : function(){
16914          this.constrainTo("parent-id");
16915      }
16916  });
16917  </code></pre>
16918      * @param {String/HTMLElement/Element} constrainTo The element to constrain to.
16919      * @param {Object/Number} pad (optional) Pad provides a way to specify "padding" of the constraints,
16920      * and can be either a number for symmetrical padding (4 would be equal to {left:4, right:4, top:4, bottom:4}) or
16921      * an object containing the sides to pad. For example: {right:10, bottom:10}
16922      * @param {Boolean} inContent (optional) Constrain the draggable in the content box of the element (inside padding and borders)
16923      */
16924     constrainTo : function(constrainTo, pad, inContent){
16925         if(typeof pad == "number"){
16926             pad = {left: pad, right:pad, top:pad, bottom:pad};
16927         }
16928         pad = pad || this.defaultPadding;
16929         var b = Roo.get(this.getEl()).getBox();
16930         var ce = Roo.get(constrainTo);
16931         var s = ce.getScroll();
16932         var c, cd = ce.dom;
16933         if(cd == document.body){
16934             c = { x: s.left, y: s.top, width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
16935         }else{
16936             xy = ce.getXY();
16937             c = {x : xy[0]+s.left, y: xy[1]+s.top, width: cd.clientWidth, height: cd.clientHeight};
16938         }
16939
16940
16941         var topSpace = b.y - c.y;
16942         var leftSpace = b.x - c.x;
16943
16944         this.resetConstraints();
16945         this.setXConstraint(leftSpace - (pad.left||0), // left
16946                 c.width - leftSpace - b.width - (pad.right||0) //right
16947         );
16948         this.setYConstraint(topSpace - (pad.top||0), //top
16949                 c.height - topSpace - b.height - (pad.bottom||0) //bottom
16950         );
16951     },
16952
16953     /**
16954      * Returns a reference to the linked element
16955      * @method getEl
16956      * @return {HTMLElement} the html element
16957      */
16958     getEl: function() {
16959         if (!this._domRef) {
16960             this._domRef = Roo.getDom(this.id);
16961         }
16962
16963         return this._domRef;
16964     },
16965
16966     /**
16967      * Returns a reference to the actual element to drag.  By default this is
16968      * the same as the html element, but it can be assigned to another
16969      * element. An example of this can be found in Roo.dd.DDProxy
16970      * @method getDragEl
16971      * @return {HTMLElement} the html element
16972      */
16973     getDragEl: function() {
16974         return Roo.getDom(this.dragElId);
16975     },
16976
16977     /**
16978      * Sets up the DragDrop object.  Must be called in the constructor of any
16979      * Roo.dd.DragDrop subclass
16980      * @method init
16981      * @param id the id of the linked element
16982      * @param {String} sGroup the group of related items
16983      * @param {object} config configuration attributes
16984      */
16985     init: function(id, sGroup, config) {
16986         this.initTarget(id, sGroup, config);
16987         if (!Roo.isTouch) {
16988             Event.on(this.id, "mousedown", this.handleMouseDown, this);
16989         }
16990         Event.on(this.id, "touchstart", this.handleMouseDown, this);
16991         // Event.on(this.id, "selectstart", Event.preventDefault);
16992     },
16993
16994     /**
16995      * Initializes Targeting functionality only... the object does not
16996      * get a mousedown handler.
16997      * @method initTarget
16998      * @param id the id of the linked element
16999      * @param {String} sGroup the group of related items
17000      * @param {object} config configuration attributes
17001      */
17002     initTarget: function(id, sGroup, config) {
17003
17004         // configuration attributes
17005         this.config = config || {};
17006
17007         // create a local reference to the drag and drop manager
17008         this.DDM = Roo.dd.DDM;
17009         // initialize the groups array
17010         this.groups = {};
17011
17012         // assume that we have an element reference instead of an id if the
17013         // parameter is not a string
17014         if (typeof id !== "string") {
17015             id = Roo.id(id);
17016         }
17017
17018         // set the id
17019         this.id = id;
17020
17021         // add to an interaction group
17022         this.addToGroup((sGroup) ? sGroup : "default");
17023
17024         // We don't want to register this as the handle with the manager
17025         // so we just set the id rather than calling the setter.
17026         this.handleElId = id;
17027
17028         // the linked element is the element that gets dragged by default
17029         this.setDragElId(id);
17030
17031         // by default, clicked anchors will not start drag operations.
17032         this.invalidHandleTypes = { A: "A" };
17033         this.invalidHandleIds = {};
17034         this.invalidHandleClasses = [];
17035
17036         this.applyConfig();
17037
17038         this.handleOnAvailable();
17039     },
17040
17041     /**
17042      * Applies the configuration parameters that were passed into the constructor.
17043      * This is supposed to happen at each level through the inheritance chain.  So
17044      * a DDProxy implentation will execute apply config on DDProxy, DD, and
17045      * DragDrop in order to get all of the parameters that are available in
17046      * each object.
17047      * @method applyConfig
17048      */
17049     applyConfig: function() {
17050
17051         // configurable properties:
17052         //    padding, isTarget, maintainOffset, primaryButtonOnly
17053         this.padding           = this.config.padding || [0, 0, 0, 0];
17054         this.isTarget          = (this.config.isTarget !== false);
17055         this.maintainOffset    = (this.config.maintainOffset);
17056         this.primaryButtonOnly = (this.config.primaryButtonOnly !== false);
17057
17058     },
17059
17060     /**
17061      * Executed when the linked element is available
17062      * @method handleOnAvailable
17063      * @private
17064      */
17065     handleOnAvailable: function() {
17066         this.available = true;
17067         this.resetConstraints();
17068         this.onAvailable();
17069     },
17070
17071      /**
17072      * Configures the padding for the target zone in px.  Effectively expands
17073      * (or reduces) the virtual object size for targeting calculations.
17074      * Supports css-style shorthand; if only one parameter is passed, all sides
17075      * will have that padding, and if only two are passed, the top and bottom
17076      * will have the first param, the left and right the second.
17077      * @method setPadding
17078      * @param {int} iTop    Top pad
17079      * @param {int} iRight  Right pad
17080      * @param {int} iBot    Bot pad
17081      * @param {int} iLeft   Left pad
17082      */
17083     setPadding: function(iTop, iRight, iBot, iLeft) {
17084         // this.padding = [iLeft, iRight, iTop, iBot];
17085         if (!iRight && 0 !== iRight) {
17086             this.padding = [iTop, iTop, iTop, iTop];
17087         } else if (!iBot && 0 !== iBot) {
17088             this.padding = [iTop, iRight, iTop, iRight];
17089         } else {
17090             this.padding = [iTop, iRight, iBot, iLeft];
17091         }
17092     },
17093
17094     /**
17095      * Stores the initial placement of the linked element.
17096      * @method setInitialPosition
17097      * @param {int} diffX   the X offset, default 0
17098      * @param {int} diffY   the Y offset, default 0
17099      */
17100     setInitPosition: function(diffX, diffY) {
17101         var el = this.getEl();
17102
17103         if (!this.DDM.verifyEl(el)) {
17104             return;
17105         }
17106
17107         var dx = diffX || 0;
17108         var dy = diffY || 0;
17109
17110         var p = Dom.getXY( el );
17111
17112         this.initPageX = p[0] - dx;
17113         this.initPageY = p[1] - dy;
17114
17115         this.lastPageX = p[0];
17116         this.lastPageY = p[1];
17117
17118
17119         this.setStartPosition(p);
17120     },
17121
17122     /**
17123      * Sets the start position of the element.  This is set when the obj
17124      * is initialized, the reset when a drag is started.
17125      * @method setStartPosition
17126      * @param pos current position (from previous lookup)
17127      * @private
17128      */
17129     setStartPosition: function(pos) {
17130         var p = pos || Dom.getXY( this.getEl() );
17131         this.deltaSetXY = null;
17132
17133         this.startPageX = p[0];
17134         this.startPageY = p[1];
17135     },
17136
17137     /**
17138      * Add this instance to a group of related drag/drop objects.  All
17139      * instances belong to at least one group, and can belong to as many
17140      * groups as needed.
17141      * @method addToGroup
17142      * @param sGroup {string} the name of the group
17143      */
17144     addToGroup: function(sGroup) {
17145         this.groups[sGroup] = true;
17146         this.DDM.regDragDrop(this, sGroup);
17147     },
17148
17149     /**
17150      * Remove's this instance from the supplied interaction group
17151      * @method removeFromGroup
17152      * @param {string}  sGroup  The group to drop
17153      */
17154     removeFromGroup: function(sGroup) {
17155         if (this.groups[sGroup]) {
17156             delete this.groups[sGroup];
17157         }
17158
17159         this.DDM.removeDDFromGroup(this, sGroup);
17160     },
17161
17162     /**
17163      * Allows you to specify that an element other than the linked element
17164      * will be moved with the cursor during a drag
17165      * @method setDragElId
17166      * @param id {string} the id of the element that will be used to initiate the drag
17167      */
17168     setDragElId: function(id) {
17169         this.dragElId = id;
17170     },
17171
17172     /**
17173      * Allows you to specify a child of the linked element that should be
17174      * used to initiate the drag operation.  An example of this would be if
17175      * you have a content div with text and links.  Clicking anywhere in the
17176      * content area would normally start the drag operation.  Use this method
17177      * to specify that an element inside of the content div is the element
17178      * that starts the drag operation.
17179      * @method setHandleElId
17180      * @param id {string} the id of the element that will be used to
17181      * initiate the drag.
17182      */
17183     setHandleElId: function(id) {
17184         if (typeof id !== "string") {
17185             id = Roo.id(id);
17186         }
17187         this.handleElId = id;
17188         this.DDM.regHandle(this.id, id);
17189     },
17190
17191     /**
17192      * Allows you to set an element outside of the linked element as a drag
17193      * handle
17194      * @method setOuterHandleElId
17195      * @param id the id of the element that will be used to initiate the drag
17196      */
17197     setOuterHandleElId: function(id) {
17198         if (typeof id !== "string") {
17199             id = Roo.id(id);
17200         }
17201         Event.on(id, "mousedown",
17202                 this.handleMouseDown, this);
17203         this.setHandleElId(id);
17204
17205         this.hasOuterHandles = true;
17206     },
17207
17208     /**
17209      * Remove all drag and drop hooks for this element
17210      * @method unreg
17211      */
17212     unreg: function() {
17213         Event.un(this.id, "mousedown",
17214                 this.handleMouseDown);
17215         Event.un(this.id, "touchstart",
17216                 this.handleMouseDown);
17217         this._domRef = null;
17218         this.DDM._remove(this);
17219     },
17220
17221     destroy : function(){
17222         this.unreg();
17223     },
17224
17225     /**
17226      * Returns true if this instance is locked, or the drag drop mgr is locked
17227      * (meaning that all drag/drop is disabled on the page.)
17228      * @method isLocked
17229      * @return {boolean} true if this obj or all drag/drop is locked, else
17230      * false
17231      */
17232     isLocked: function() {
17233         return (this.DDM.isLocked() || this.locked);
17234     },
17235
17236     /**
17237      * Fired when this object is clicked
17238      * @method handleMouseDown
17239      * @param {Event} e
17240      * @param {Roo.dd.DragDrop} oDD the clicked dd object (this dd obj)
17241      * @private
17242      */
17243     handleMouseDown: function(e, oDD){
17244      
17245         if (!Roo.isTouch && this.primaryButtonOnly && e.button != 0) {
17246             //Roo.log('not touch/ button !=0');
17247             return;
17248         }
17249         if (e.browserEvent.touches && e.browserEvent.touches.length != 1) {
17250             return; // double touch..
17251         }
17252         
17253
17254         if (this.isLocked()) {
17255             //Roo.log('locked');
17256             return;
17257         }
17258
17259         this.DDM.refreshCache(this.groups);
17260 //        Roo.log([Roo.lib.Event.getPageX(e), Roo.lib.Event.getPageY(e)]);
17261         var pt = new Roo.lib.Point(Roo.lib.Event.getPageX(e), Roo.lib.Event.getPageY(e));
17262         if (!this.hasOuterHandles && !this.DDM.isOverTarget(pt, this) )  {
17263             //Roo.log('no outer handes or not over target');
17264                 // do nothing.
17265         } else {
17266 //            Roo.log('check validator');
17267             if (this.clickValidator(e)) {
17268 //                Roo.log('validate success');
17269                 // set the initial element position
17270                 this.setStartPosition();
17271
17272
17273                 this.b4MouseDown(e);
17274                 this.onMouseDown(e);
17275
17276                 this.DDM.handleMouseDown(e, this);
17277
17278                 this.DDM.stopEvent(e);
17279             } else {
17280
17281
17282             }
17283         }
17284     },
17285
17286     clickValidator: function(e) {
17287         var target = e.getTarget();
17288         return ( this.isValidHandleChild(target) &&
17289                     (this.id == this.handleElId ||
17290                         this.DDM.handleWasClicked(target, this.id)) );
17291     },
17292
17293     /**
17294      * Allows you to specify a tag name that should not start a drag operation
17295      * when clicked.  This is designed to facilitate embedding links within a
17296      * drag handle that do something other than start the drag.
17297      * @method addInvalidHandleType
17298      * @param {string} tagName the type of element to exclude
17299      */
17300     addInvalidHandleType: function(tagName) {
17301         var type = tagName.toUpperCase();
17302         this.invalidHandleTypes[type] = type;
17303     },
17304
17305     /**
17306      * Lets you to specify an element id for a child of a drag handle
17307      * that should not initiate a drag
17308      * @method addInvalidHandleId
17309      * @param {string} id the element id of the element you wish to ignore
17310      */
17311     addInvalidHandleId: function(id) {
17312         if (typeof id !== "string") {
17313             id = Roo.id(id);
17314         }
17315         this.invalidHandleIds[id] = id;
17316     },
17317
17318     /**
17319      * Lets you specify a css class of elements that will not initiate a drag
17320      * @method addInvalidHandleClass
17321      * @param {string} cssClass the class of the elements you wish to ignore
17322      */
17323     addInvalidHandleClass: function(cssClass) {
17324         this.invalidHandleClasses.push(cssClass);
17325     },
17326
17327     /**
17328      * Unsets an excluded tag name set by addInvalidHandleType
17329      * @method removeInvalidHandleType
17330      * @param {string} tagName the type of element to unexclude
17331      */
17332     removeInvalidHandleType: function(tagName) {
17333         var type = tagName.toUpperCase();
17334         // this.invalidHandleTypes[type] = null;
17335         delete this.invalidHandleTypes[type];
17336     },
17337
17338     /**
17339      * Unsets an invalid handle id
17340      * @method removeInvalidHandleId
17341      * @param {string} id the id of the element to re-enable
17342      */
17343     removeInvalidHandleId: function(id) {
17344         if (typeof id !== "string") {
17345             id = Roo.id(id);
17346         }
17347         delete this.invalidHandleIds[id];
17348     },
17349
17350     /**
17351      * Unsets an invalid css class
17352      * @method removeInvalidHandleClass
17353      * @param {string} cssClass the class of the element(s) you wish to
17354      * re-enable
17355      */
17356     removeInvalidHandleClass: function(cssClass) {
17357         for (var i=0, len=this.invalidHandleClasses.length; i<len; ++i) {
17358             if (this.invalidHandleClasses[i] == cssClass) {
17359                 delete this.invalidHandleClasses[i];
17360             }
17361         }
17362     },
17363
17364     /**
17365      * Checks the tag exclusion list to see if this click should be ignored
17366      * @method isValidHandleChild
17367      * @param {HTMLElement} node the HTMLElement to evaluate
17368      * @return {boolean} true if this is a valid tag type, false if not
17369      */
17370     isValidHandleChild: function(node) {
17371
17372         var valid = true;
17373         // var n = (node.nodeName == "#text") ? node.parentNode : node;
17374         var nodeName;
17375         try {
17376             nodeName = node.nodeName.toUpperCase();
17377         } catch(e) {
17378             nodeName = node.nodeName;
17379         }
17380         valid = valid && !this.invalidHandleTypes[nodeName];
17381         valid = valid && !this.invalidHandleIds[node.id];
17382
17383         for (var i=0, len=this.invalidHandleClasses.length; valid && i<len; ++i) {
17384             valid = !Dom.hasClass(node, this.invalidHandleClasses[i]);
17385         }
17386
17387
17388         return valid;
17389
17390     },
17391
17392     /**
17393      * Create the array of horizontal tick marks if an interval was specified
17394      * in setXConstraint().
17395      * @method setXTicks
17396      * @private
17397      */
17398     setXTicks: function(iStartX, iTickSize) {
17399         this.xTicks = [];
17400         this.xTickSize = iTickSize;
17401
17402         var tickMap = {};
17403
17404         for (var i = this.initPageX; i >= this.minX; i = i - iTickSize) {
17405             if (!tickMap[i]) {
17406                 this.xTicks[this.xTicks.length] = i;
17407                 tickMap[i] = true;
17408             }
17409         }
17410
17411         for (i = this.initPageX; i <= this.maxX; i = i + iTickSize) {
17412             if (!tickMap[i]) {
17413                 this.xTicks[this.xTicks.length] = i;
17414                 tickMap[i] = true;
17415             }
17416         }
17417
17418         this.xTicks.sort(this.DDM.numericSort) ;
17419     },
17420
17421     /**
17422      * Create the array of vertical tick marks if an interval was specified in
17423      * setYConstraint().
17424      * @method setYTicks
17425      * @private
17426      */
17427     setYTicks: function(iStartY, iTickSize) {
17428         this.yTicks = [];
17429         this.yTickSize = iTickSize;
17430
17431         var tickMap = {};
17432
17433         for (var i = this.initPageY; i >= this.minY; i = i - iTickSize) {
17434             if (!tickMap[i]) {
17435                 this.yTicks[this.yTicks.length] = i;
17436                 tickMap[i] = true;
17437             }
17438         }
17439
17440         for (i = this.initPageY; i <= this.maxY; i = i + iTickSize) {
17441             if (!tickMap[i]) {
17442                 this.yTicks[this.yTicks.length] = i;
17443                 tickMap[i] = true;
17444             }
17445         }
17446
17447         this.yTicks.sort(this.DDM.numericSort) ;
17448     },
17449
17450     /**
17451      * By default, the element can be dragged any place on the screen.  Use
17452      * this method to limit the horizontal travel of the element.  Pass in
17453      * 0,0 for the parameters if you want to lock the drag to the y axis.
17454      * @method setXConstraint
17455      * @param {int} iLeft the number of pixels the element can move to the left
17456      * @param {int} iRight the number of pixels the element can move to the
17457      * right
17458      * @param {int} iTickSize optional parameter for specifying that the
17459      * element
17460      * should move iTickSize pixels at a time.
17461      */
17462     setXConstraint: function(iLeft, iRight, iTickSize) {
17463         this.leftConstraint = iLeft;
17464         this.rightConstraint = iRight;
17465
17466         this.minX = this.initPageX - iLeft;
17467         this.maxX = this.initPageX + iRight;
17468         if (iTickSize) { this.setXTicks(this.initPageX, iTickSize); }
17469
17470         this.constrainX = true;
17471     },
17472
17473     /**
17474      * Clears any constraints applied to this instance.  Also clears ticks
17475      * since they can't exist independent of a constraint at this time.
17476      * @method clearConstraints
17477      */
17478     clearConstraints: function() {
17479         this.constrainX = false;
17480         this.constrainY = false;
17481         this.clearTicks();
17482     },
17483
17484     /**
17485      * Clears any tick interval defined for this instance
17486      * @method clearTicks
17487      */
17488     clearTicks: function() {
17489         this.xTicks = null;
17490         this.yTicks = null;
17491         this.xTickSize = 0;
17492         this.yTickSize = 0;
17493     },
17494
17495     /**
17496      * By default, the element can be dragged any place on the screen.  Set
17497      * this to limit the vertical travel of the element.  Pass in 0,0 for the
17498      * parameters if you want to lock the drag to the x axis.
17499      * @method setYConstraint
17500      * @param {int} iUp the number of pixels the element can move up
17501      * @param {int} iDown the number of pixels the element can move down
17502      * @param {int} iTickSize optional parameter for specifying that the
17503      * element should move iTickSize pixels at a time.
17504      */
17505     setYConstraint: function(iUp, iDown, iTickSize) {
17506         this.topConstraint = iUp;
17507         this.bottomConstraint = iDown;
17508
17509         this.minY = this.initPageY - iUp;
17510         this.maxY = this.initPageY + iDown;
17511         if (iTickSize) { this.setYTicks(this.initPageY, iTickSize); }
17512
17513         this.constrainY = true;
17514
17515     },
17516
17517     /**
17518      * resetConstraints must be called if you manually reposition a dd element.
17519      * @method resetConstraints
17520      * @param {boolean} maintainOffset
17521      */
17522     resetConstraints: function() {
17523
17524
17525         // Maintain offsets if necessary
17526         if (this.initPageX || this.initPageX === 0) {
17527             // figure out how much this thing has moved
17528             var dx = (this.maintainOffset) ? this.lastPageX - this.initPageX : 0;
17529             var dy = (this.maintainOffset) ? this.lastPageY - this.initPageY : 0;
17530
17531             this.setInitPosition(dx, dy);
17532
17533         // This is the first time we have detected the element's position
17534         } else {
17535             this.setInitPosition();
17536         }
17537
17538         if (this.constrainX) {
17539             this.setXConstraint( this.leftConstraint,
17540                                  this.rightConstraint,
17541                                  this.xTickSize        );
17542         }
17543
17544         if (this.constrainY) {
17545             this.setYConstraint( this.topConstraint,
17546                                  this.bottomConstraint,
17547                                  this.yTickSize         );
17548         }
17549     },
17550
17551     /**
17552      * Normally the drag element is moved pixel by pixel, but we can specify
17553      * that it move a number of pixels at a time.  This method resolves the
17554      * location when we have it set up like this.
17555      * @method getTick
17556      * @param {int} val where we want to place the object
17557      * @param {int[]} tickArray sorted array of valid points
17558      * @return {int} the closest tick
17559      * @private
17560      */
17561     getTick: function(val, tickArray) {
17562
17563         if (!tickArray) {
17564             // If tick interval is not defined, it is effectively 1 pixel,
17565             // so we return the value passed to us.
17566             return val;
17567         } else if (tickArray[0] >= val) {
17568             // The value is lower than the first tick, so we return the first
17569             // tick.
17570             return tickArray[0];
17571         } else {
17572             for (var i=0, len=tickArray.length; i<len; ++i) {
17573                 var next = i + 1;
17574                 if (tickArray[next] && tickArray[next] >= val) {
17575                     var diff1 = val - tickArray[i];
17576                     var diff2 = tickArray[next] - val;
17577                     return (diff2 > diff1) ? tickArray[i] : tickArray[next];
17578                 }
17579             }
17580
17581             // The value is larger than the last tick, so we return the last
17582             // tick.
17583             return tickArray[tickArray.length - 1];
17584         }
17585     },
17586
17587     /**
17588      * toString method
17589      * @method toString
17590      * @return {string} string representation of the dd obj
17591      */
17592     toString: function() {
17593         return ("DragDrop " + this.id);
17594     }
17595
17596 });
17597
17598 })();
17599 /*
17600  * Based on:
17601  * Ext JS Library 1.1.1
17602  * Copyright(c) 2006-2007, Ext JS, LLC.
17603  *
17604  * Originally Released Under LGPL - original licence link has changed is not relivant.
17605  *
17606  * Fork - LGPL
17607  * <script type="text/javascript">
17608  */
17609
17610
17611 /**
17612  * The drag and drop utility provides a framework for building drag and drop
17613  * applications.  In addition to enabling drag and drop for specific elements,
17614  * the drag and drop elements are tracked by the manager class, and the
17615  * interactions between the various elements are tracked during the drag and
17616  * the implementing code is notified about these important moments.
17617  */
17618
17619 // Only load the library once.  Rewriting the manager class would orphan
17620 // existing drag and drop instances.
17621 if (!Roo.dd.DragDropMgr) {
17622
17623 /**
17624  * @class Roo.dd.DragDropMgr
17625  * DragDropMgr is a singleton that tracks the element interaction for
17626  * all DragDrop items in the window.  Generally, you will not call
17627  * this class directly, but it does have helper methods that could
17628  * be useful in your DragDrop implementations.
17629  * @singleton
17630  */
17631 Roo.dd.DragDropMgr = function() {
17632
17633     var Event = Roo.EventManager;
17634
17635     return {
17636
17637         /**
17638          * Two dimensional Array of registered DragDrop objects.  The first
17639          * dimension is the DragDrop item group, the second the DragDrop
17640          * object.
17641          * @property ids
17642          * @type {string: string}
17643          * @private
17644          * @static
17645          */
17646         ids: {},
17647
17648         /**
17649          * Array of element ids defined as drag handles.  Used to determine
17650          * if the element that generated the mousedown event is actually the
17651          * handle and not the html element itself.
17652          * @property handleIds
17653          * @type {string: string}
17654          * @private
17655          * @static
17656          */
17657         handleIds: {},
17658
17659         /**
17660          * the DragDrop object that is currently being dragged
17661          * @property dragCurrent
17662          * @type DragDrop
17663          * @private
17664          * @static
17665          **/
17666         dragCurrent: null,
17667
17668         /**
17669          * the DragDrop object(s) that are being hovered over
17670          * @property dragOvers
17671          * @type Array
17672          * @private
17673          * @static
17674          */
17675         dragOvers: {},
17676
17677         /**
17678          * the X distance between the cursor and the object being dragged
17679          * @property deltaX
17680          * @type int
17681          * @private
17682          * @static
17683          */
17684         deltaX: 0,
17685
17686         /**
17687          * the Y distance between the cursor and the object being dragged
17688          * @property deltaY
17689          * @type int
17690          * @private
17691          * @static
17692          */
17693         deltaY: 0,
17694
17695         /**
17696          * Flag to determine if we should prevent the default behavior of the
17697          * events we define. By default this is true, but this can be set to
17698          * false if you need the default behavior (not recommended)
17699          * @property preventDefault
17700          * @type boolean
17701          * @static
17702          */
17703         preventDefault: true,
17704
17705         /**
17706          * Flag to determine if we should stop the propagation of the events
17707          * we generate. This is true by default but you may want to set it to
17708          * false if the html element contains other features that require the
17709          * mouse click.
17710          * @property stopPropagation
17711          * @type boolean
17712          * @static
17713          */
17714         stopPropagation: true,
17715
17716         /**
17717          * Internal flag that is set to true when drag and drop has been
17718          * intialized
17719          * @property initialized
17720          * @private
17721          * @static
17722          */
17723         initalized: false,
17724
17725         /**
17726          * All drag and drop can be disabled.
17727          * @property locked
17728          * @private
17729          * @static
17730          */
17731         locked: false,
17732
17733         /**
17734          * Called the first time an element is registered.
17735          * @method init
17736          * @private
17737          * @static
17738          */
17739         init: function() {
17740             this.initialized = true;
17741         },
17742
17743         /**
17744          * In point mode, drag and drop interaction is defined by the
17745          * location of the cursor during the drag/drop
17746          * @property POINT
17747          * @type int
17748          * @static
17749          */
17750         POINT: 0,
17751
17752         /**
17753          * In intersect mode, drag and drop interactio nis defined by the
17754          * overlap of two or more drag and drop objects.
17755          * @property INTERSECT
17756          * @type int
17757          * @static
17758          */
17759         INTERSECT: 1,
17760
17761         /**
17762          * The current drag and drop mode.  Default: POINT
17763          * @property mode
17764          * @type int
17765          * @static
17766          */
17767         mode: 0,
17768
17769         /**
17770          * Runs method on all drag and drop objects
17771          * @method _execOnAll
17772          * @private
17773          * @static
17774          */
17775         _execOnAll: function(sMethod, args) {
17776             for (var i in this.ids) {
17777                 for (var j in this.ids[i]) {
17778                     var oDD = this.ids[i][j];
17779                     if (! this.isTypeOfDD(oDD)) {
17780                         continue;
17781                     }
17782                     oDD[sMethod].apply(oDD, args);
17783                 }
17784             }
17785         },
17786
17787         /**
17788          * Drag and drop initialization.  Sets up the global event handlers
17789          * @method _onLoad
17790          * @private
17791          * @static
17792          */
17793         _onLoad: function() {
17794
17795             this.init();
17796
17797             if (!Roo.isTouch) {
17798                 Event.on(document, "mouseup",   this.handleMouseUp, this, true);
17799                 Event.on(document, "mousemove", this.handleMouseMove, this, true);
17800             }
17801             Event.on(document, "touchend",   this.handleMouseUp, this, true);
17802             Event.on(document, "touchmove", this.handleMouseMove, this, true);
17803             
17804             Event.on(window,   "unload",    this._onUnload, this, true);
17805             Event.on(window,   "resize",    this._onResize, this, true);
17806             // Event.on(window,   "mouseout",    this._test);
17807
17808         },
17809
17810         /**
17811          * Reset constraints on all drag and drop objs
17812          * @method _onResize
17813          * @private
17814          * @static
17815          */
17816         _onResize: function(e) {
17817             this._execOnAll("resetConstraints", []);
17818         },
17819
17820         /**
17821          * Lock all drag and drop functionality
17822          * @method lock
17823          * @static
17824          */
17825         lock: function() { this.locked = true; },
17826
17827         /**
17828          * Unlock all drag and drop functionality
17829          * @method unlock
17830          * @static
17831          */
17832         unlock: function() { this.locked = false; },
17833
17834         /**
17835          * Is drag and drop locked?
17836          * @method isLocked
17837          * @return {boolean} True if drag and drop is locked, false otherwise.
17838          * @static
17839          */
17840         isLocked: function() { return this.locked; },
17841
17842         /**
17843          * Location cache that is set for all drag drop objects when a drag is
17844          * initiated, cleared when the drag is finished.
17845          * @property locationCache
17846          * @private
17847          * @static
17848          */
17849         locationCache: {},
17850
17851         /**
17852          * Set useCache to false if you want to force object the lookup of each
17853          * drag and drop linked element constantly during a drag.
17854          * @property useCache
17855          * @type boolean
17856          * @static
17857          */
17858         useCache: true,
17859
17860         /**
17861          * The number of pixels that the mouse needs to move after the
17862          * mousedown before the drag is initiated.  Default=3;
17863          * @property clickPixelThresh
17864          * @type int
17865          * @static
17866          */
17867         clickPixelThresh: 3,
17868
17869         /**
17870          * The number of milliseconds after the mousedown event to initiate the
17871          * drag if we don't get a mouseup event. Default=1000
17872          * @property clickTimeThresh
17873          * @type int
17874          * @static
17875          */
17876         clickTimeThresh: 350,
17877
17878         /**
17879          * Flag that indicates that either the drag pixel threshold or the
17880          * mousdown time threshold has been met
17881          * @property dragThreshMet
17882          * @type boolean
17883          * @private
17884          * @static
17885          */
17886         dragThreshMet: false,
17887
17888         /**
17889          * Timeout used for the click time threshold
17890          * @property clickTimeout
17891          * @type Object
17892          * @private
17893          * @static
17894          */
17895         clickTimeout: null,
17896
17897         /**
17898          * The X position of the mousedown event stored for later use when a
17899          * drag threshold is met.
17900          * @property startX
17901          * @type int
17902          * @private
17903          * @static
17904          */
17905         startX: 0,
17906
17907         /**
17908          * The Y position of the mousedown event stored for later use when a
17909          * drag threshold is met.
17910          * @property startY
17911          * @type int
17912          * @private
17913          * @static
17914          */
17915         startY: 0,
17916
17917         /**
17918          * Each DragDrop instance must be registered with the DragDropMgr.
17919          * This is executed in DragDrop.init()
17920          * @method regDragDrop
17921          * @param {DragDrop} oDD the DragDrop object to register
17922          * @param {String} sGroup the name of the group this element belongs to
17923          * @static
17924          */
17925         regDragDrop: function(oDD, sGroup) {
17926             if (!this.initialized) { this.init(); }
17927
17928             if (!this.ids[sGroup]) {
17929                 this.ids[sGroup] = {};
17930             }
17931             this.ids[sGroup][oDD.id] = oDD;
17932         },
17933
17934         /**
17935          * Removes the supplied dd instance from the supplied group. Executed
17936          * by DragDrop.removeFromGroup, so don't call this function directly.
17937          * @method removeDDFromGroup
17938          * @private
17939          * @static
17940          */
17941         removeDDFromGroup: function(oDD, sGroup) {
17942             if (!this.ids[sGroup]) {
17943                 this.ids[sGroup] = {};
17944             }
17945
17946             var obj = this.ids[sGroup];
17947             if (obj && obj[oDD.id]) {
17948                 delete obj[oDD.id];
17949             }
17950         },
17951
17952         /**
17953          * Unregisters a drag and drop item.  This is executed in
17954          * DragDrop.unreg, use that method instead of calling this directly.
17955          * @method _remove
17956          * @private
17957          * @static
17958          */
17959         _remove: function(oDD) {
17960             for (var g in oDD.groups) {
17961                 if (g && this.ids[g][oDD.id]) {
17962                     delete this.ids[g][oDD.id];
17963                 }
17964             }
17965             delete this.handleIds[oDD.id];
17966         },
17967
17968         /**
17969          * Each DragDrop handle element must be registered.  This is done
17970          * automatically when executing DragDrop.setHandleElId()
17971          * @method regHandle
17972          * @param {String} sDDId the DragDrop id this element is a handle for
17973          * @param {String} sHandleId the id of the element that is the drag
17974          * handle
17975          * @static
17976          */
17977         regHandle: function(sDDId, sHandleId) {
17978             if (!this.handleIds[sDDId]) {
17979                 this.handleIds[sDDId] = {};
17980             }
17981             this.handleIds[sDDId][sHandleId] = sHandleId;
17982         },
17983
17984         /**
17985          * Utility function to determine if a given element has been
17986          * registered as a drag drop item.
17987          * @method isDragDrop
17988          * @param {String} id the element id to check
17989          * @return {boolean} true if this element is a DragDrop item,
17990          * false otherwise
17991          * @static
17992          */
17993         isDragDrop: function(id) {
17994             return ( this.getDDById(id) ) ? true : false;
17995         },
17996
17997         /**
17998          * Returns the drag and drop instances that are in all groups the
17999          * passed in instance belongs to.
18000          * @method getRelated
18001          * @param {DragDrop} p_oDD the obj to get related data for
18002          * @param {boolean} bTargetsOnly if true, only return targetable objs
18003          * @return {DragDrop[]} the related instances
18004          * @static
18005          */
18006         getRelated: function(p_oDD, bTargetsOnly) {
18007             var oDDs = [];
18008             for (var i in p_oDD.groups) {
18009                 for (j in this.ids[i]) {
18010                     var dd = this.ids[i][j];
18011                     if (! this.isTypeOfDD(dd)) {
18012                         continue;
18013                     }
18014                     if (!bTargetsOnly || dd.isTarget) {
18015                         oDDs[oDDs.length] = dd;
18016                     }
18017                 }
18018             }
18019
18020             return oDDs;
18021         },
18022
18023         /**
18024          * Returns true if the specified dd target is a legal target for
18025          * the specifice drag obj
18026          * @method isLegalTarget
18027          * @param {DragDrop} the drag obj
18028          * @param {DragDrop} the target
18029          * @return {boolean} true if the target is a legal target for the
18030          * dd obj
18031          * @static
18032          */
18033         isLegalTarget: function (oDD, oTargetDD) {
18034             var targets = this.getRelated(oDD, true);
18035             for (var i=0, len=targets.length;i<len;++i) {
18036                 if (targets[i].id == oTargetDD.id) {
18037                     return true;
18038                 }
18039             }
18040
18041             return false;
18042         },
18043
18044         /**
18045          * My goal is to be able to transparently determine if an object is
18046          * typeof DragDrop, and the exact subclass of DragDrop.  typeof
18047          * returns "object", oDD.constructor.toString() always returns
18048          * "DragDrop" and not the name of the subclass.  So for now it just
18049          * evaluates a well-known variable in DragDrop.
18050          * @method isTypeOfDD
18051          * @param {Object} the object to evaluate
18052          * @return {boolean} true if typeof oDD = DragDrop
18053          * @static
18054          */
18055         isTypeOfDD: function (oDD) {
18056             return (oDD && oDD.__ygDragDrop);
18057         },
18058
18059         /**
18060          * Utility function to determine if a given element has been
18061          * registered as a drag drop handle for the given Drag Drop object.
18062          * @method isHandle
18063          * @param {String} id the element id to check
18064          * @return {boolean} true if this element is a DragDrop handle, false
18065          * otherwise
18066          * @static
18067          */
18068         isHandle: function(sDDId, sHandleId) {
18069             return ( this.handleIds[sDDId] &&
18070                             this.handleIds[sDDId][sHandleId] );
18071         },
18072
18073         /**
18074          * Returns the DragDrop instance for a given id
18075          * @method getDDById
18076          * @param {String} id the id of the DragDrop object
18077          * @return {DragDrop} the drag drop object, null if it is not found
18078          * @static
18079          */
18080         getDDById: function(id) {
18081             for (var i in this.ids) {
18082                 if (this.ids[i][id]) {
18083                     return this.ids[i][id];
18084                 }
18085             }
18086             return null;
18087         },
18088
18089         /**
18090          * Fired after a registered DragDrop object gets the mousedown event.
18091          * Sets up the events required to track the object being dragged
18092          * @method handleMouseDown
18093          * @param {Event} e the event
18094          * @param oDD the DragDrop object being dragged
18095          * @private
18096          * @static
18097          */
18098         handleMouseDown: function(e, oDD) {
18099             if(Roo.QuickTips){
18100                 Roo.QuickTips.disable();
18101             }
18102             this.currentTarget = e.getTarget();
18103
18104             this.dragCurrent = oDD;
18105
18106             var el = oDD.getEl();
18107
18108             // track start position
18109             this.startX = e.getPageX();
18110             this.startY = e.getPageY();
18111
18112             this.deltaX = this.startX - el.offsetLeft;
18113             this.deltaY = this.startY - el.offsetTop;
18114
18115             this.dragThreshMet = false;
18116
18117             this.clickTimeout = setTimeout(
18118                     function() {
18119                         var DDM = Roo.dd.DDM;
18120                         DDM.startDrag(DDM.startX, DDM.startY);
18121                     },
18122                     this.clickTimeThresh );
18123         },
18124
18125         /**
18126          * Fired when either the drag pixel threshol or the mousedown hold
18127          * time threshold has been met.
18128          * @method startDrag
18129          * @param x {int} the X position of the original mousedown
18130          * @param y {int} the Y position of the original mousedown
18131          * @static
18132          */
18133         startDrag: function(x, y) {
18134             clearTimeout(this.clickTimeout);
18135             if (this.dragCurrent) {
18136                 this.dragCurrent.b4StartDrag(x, y);
18137                 this.dragCurrent.startDrag(x, y);
18138             }
18139             this.dragThreshMet = true;
18140         },
18141
18142         /**
18143          * Internal function to handle the mouseup event.  Will be invoked
18144          * from the context of the document.
18145          * @method handleMouseUp
18146          * @param {Event} e the event
18147          * @private
18148          * @static
18149          */
18150         handleMouseUp: function(e) {
18151
18152             if(Roo.QuickTips){
18153                 Roo.QuickTips.enable();
18154             }
18155             if (! this.dragCurrent) {
18156                 return;
18157             }
18158
18159             clearTimeout(this.clickTimeout);
18160
18161             if (this.dragThreshMet) {
18162                 this.fireEvents(e, true);
18163             } else {
18164             }
18165
18166             this.stopDrag(e);
18167
18168             this.stopEvent(e);
18169         },
18170
18171         /**
18172          * Utility to stop event propagation and event default, if these
18173          * features are turned on.
18174          * @method stopEvent
18175          * @param {Event} e the event as returned by this.getEvent()
18176          * @static
18177          */
18178         stopEvent: function(e){
18179             if(this.stopPropagation) {
18180                 e.stopPropagation();
18181             }
18182
18183             if (this.preventDefault) {
18184                 e.preventDefault();
18185             }
18186         },
18187
18188         /**
18189          * Internal function to clean up event handlers after the drag
18190          * operation is complete
18191          * @method stopDrag
18192          * @param {Event} e the event
18193          * @private
18194          * @static
18195          */
18196         stopDrag: function(e) {
18197             // Fire the drag end event for the item that was dragged
18198             if (this.dragCurrent) {
18199                 if (this.dragThreshMet) {
18200                     this.dragCurrent.b4EndDrag(e);
18201                     this.dragCurrent.endDrag(e);
18202                 }
18203
18204                 this.dragCurrent.onMouseUp(e);
18205             }
18206
18207             this.dragCurrent = null;
18208             this.dragOvers = {};
18209         },
18210
18211         /**
18212          * Internal function to handle the mousemove event.  Will be invoked
18213          * from the context of the html element.
18214          *
18215          * @TODO figure out what we can do about mouse events lost when the
18216          * user drags objects beyond the window boundary.  Currently we can
18217          * detect this in internet explorer by verifying that the mouse is
18218          * down during the mousemove event.  Firefox doesn't give us the
18219          * button state on the mousemove event.
18220          * @method handleMouseMove
18221          * @param {Event} e the event
18222          * @private
18223          * @static
18224          */
18225         handleMouseMove: function(e) {
18226             if (! this.dragCurrent) {
18227                 return true;
18228             }
18229
18230             // var button = e.which || e.button;
18231
18232             // check for IE mouseup outside of page boundary
18233             if (Roo.isIE && (e.button !== 0 && e.button !== 1 && e.button !== 2)) {
18234                 this.stopEvent(e);
18235                 return this.handleMouseUp(e);
18236             }
18237
18238             if (!this.dragThreshMet) {
18239                 var diffX = Math.abs(this.startX - e.getPageX());
18240                 var diffY = Math.abs(this.startY - e.getPageY());
18241                 if (diffX > this.clickPixelThresh ||
18242                             diffY > this.clickPixelThresh) {
18243                     this.startDrag(this.startX, this.startY);
18244                 }
18245             }
18246
18247             if (this.dragThreshMet) {
18248                 this.dragCurrent.b4Drag(e);
18249                 this.dragCurrent.onDrag(e);
18250                 if(!this.dragCurrent.moveOnly){
18251                     this.fireEvents(e, false);
18252                 }
18253             }
18254
18255             this.stopEvent(e);
18256
18257             return true;
18258         },
18259
18260         /**
18261          * Iterates over all of the DragDrop elements to find ones we are
18262          * hovering over or dropping on
18263          * @method fireEvents
18264          * @param {Event} e the event
18265          * @param {boolean} isDrop is this a drop op or a mouseover op?
18266          * @private
18267          * @static
18268          */
18269         fireEvents: function(e, isDrop) {
18270             var dc = this.dragCurrent;
18271
18272             // If the user did the mouse up outside of the window, we could
18273             // get here even though we have ended the drag.
18274             if (!dc || dc.isLocked()) {
18275                 return;
18276             }
18277
18278             var pt = e.getPoint();
18279
18280             // cache the previous dragOver array
18281             var oldOvers = [];
18282
18283             var outEvts   = [];
18284             var overEvts  = [];
18285             var dropEvts  = [];
18286             var enterEvts = [];
18287
18288             // Check to see if the object(s) we were hovering over is no longer
18289             // being hovered over so we can fire the onDragOut event
18290             for (var i in this.dragOvers) {
18291
18292                 var ddo = this.dragOvers[i];
18293
18294                 if (! this.isTypeOfDD(ddo)) {
18295                     continue;
18296                 }
18297
18298                 if (! this.isOverTarget(pt, ddo, this.mode)) {
18299                     outEvts.push( ddo );
18300                 }
18301
18302                 oldOvers[i] = true;
18303                 delete this.dragOvers[i];
18304             }
18305
18306             for (var sGroup in dc.groups) {
18307
18308                 if ("string" != typeof sGroup) {
18309                     continue;
18310                 }
18311
18312                 for (i in this.ids[sGroup]) {
18313                     var oDD = this.ids[sGroup][i];
18314                     if (! this.isTypeOfDD(oDD)) {
18315                         continue;
18316                     }
18317
18318                     if (oDD.isTarget && !oDD.isLocked() && oDD != dc) {
18319                         if (this.isOverTarget(pt, oDD, this.mode)) {
18320                             // look for drop interactions
18321                             if (isDrop) {
18322                                 dropEvts.push( oDD );
18323                             // look for drag enter and drag over interactions
18324                             } else {
18325
18326                                 // initial drag over: dragEnter fires
18327                                 if (!oldOvers[oDD.id]) {
18328                                     enterEvts.push( oDD );
18329                                 // subsequent drag overs: dragOver fires
18330                                 } else {
18331                                     overEvts.push( oDD );
18332                                 }
18333
18334                                 this.dragOvers[oDD.id] = oDD;
18335                             }
18336                         }
18337                     }
18338                 }
18339             }
18340
18341             if (this.mode) {
18342                 if (outEvts.length) {
18343                     dc.b4DragOut(e, outEvts);
18344                     dc.onDragOut(e, outEvts);
18345                 }
18346
18347                 if (enterEvts.length) {
18348                     dc.onDragEnter(e, enterEvts);
18349                 }
18350
18351                 if (overEvts.length) {
18352                     dc.b4DragOver(e, overEvts);
18353                     dc.onDragOver(e, overEvts);
18354                 }
18355
18356                 if (dropEvts.length) {
18357                     dc.b4DragDrop(e, dropEvts);
18358                     dc.onDragDrop(e, dropEvts);
18359                 }
18360
18361             } else {
18362                 // fire dragout events
18363                 var len = 0;
18364                 for (i=0, len=outEvts.length; i<len; ++i) {
18365                     dc.b4DragOut(e, outEvts[i].id);
18366                     dc.onDragOut(e, outEvts[i].id);
18367                 }
18368
18369                 // fire enter events
18370                 for (i=0,len=enterEvts.length; i<len; ++i) {
18371                     // dc.b4DragEnter(e, oDD.id);
18372                     dc.onDragEnter(e, enterEvts[i].id);
18373                 }
18374
18375                 // fire over events
18376                 for (i=0,len=overEvts.length; i<len; ++i) {
18377                     dc.b4DragOver(e, overEvts[i].id);
18378                     dc.onDragOver(e, overEvts[i].id);
18379                 }
18380
18381                 // fire drop events
18382                 for (i=0, len=dropEvts.length; i<len; ++i) {
18383                     dc.b4DragDrop(e, dropEvts[i].id);
18384                     dc.onDragDrop(e, dropEvts[i].id);
18385                 }
18386
18387             }
18388
18389             // notify about a drop that did not find a target
18390             if (isDrop && !dropEvts.length) {
18391                 dc.onInvalidDrop(e);
18392             }
18393
18394         },
18395
18396         /**
18397          * Helper function for getting the best match from the list of drag
18398          * and drop objects returned by the drag and drop events when we are
18399          * in INTERSECT mode.  It returns either the first object that the
18400          * cursor is over, or the object that has the greatest overlap with
18401          * the dragged element.
18402          * @method getBestMatch
18403          * @param  {DragDrop[]} dds The array of drag and drop objects
18404          * targeted
18405          * @return {DragDrop}       The best single match
18406          * @static
18407          */
18408         getBestMatch: function(dds) {
18409             var winner = null;
18410             // Return null if the input is not what we expect
18411             //if (!dds || !dds.length || dds.length == 0) {
18412                // winner = null;
18413             // If there is only one item, it wins
18414             //} else if (dds.length == 1) {
18415
18416             var len = dds.length;
18417
18418             if (len == 1) {
18419                 winner = dds[0];
18420             } else {
18421                 // Loop through the targeted items
18422                 for (var i=0; i<len; ++i) {
18423                     var dd = dds[i];
18424                     // If the cursor is over the object, it wins.  If the
18425                     // cursor is over multiple matches, the first one we come
18426                     // to wins.
18427                     if (dd.cursorIsOver) {
18428                         winner = dd;
18429                         break;
18430                     // Otherwise the object with the most overlap wins
18431                     } else {
18432                         if (!winner ||
18433                             winner.overlap.getArea() < dd.overlap.getArea()) {
18434                             winner = dd;
18435                         }
18436                     }
18437                 }
18438             }
18439
18440             return winner;
18441         },
18442
18443         /**
18444          * Refreshes the cache of the top-left and bottom-right points of the
18445          * drag and drop objects in the specified group(s).  This is in the
18446          * format that is stored in the drag and drop instance, so typical
18447          * usage is:
18448          * <code>
18449          * Roo.dd.DragDropMgr.refreshCache(ddinstance.groups);
18450          * </code>
18451          * Alternatively:
18452          * <code>
18453          * Roo.dd.DragDropMgr.refreshCache({group1:true, group2:true});
18454          * </code>
18455          * @TODO this really should be an indexed array.  Alternatively this
18456          * method could accept both.
18457          * @method refreshCache
18458          * @param {Object} groups an associative array of groups to refresh
18459          * @static
18460          */
18461         refreshCache: function(groups) {
18462             for (var sGroup in groups) {
18463                 if ("string" != typeof sGroup) {
18464                     continue;
18465                 }
18466                 for (var i in this.ids[sGroup]) {
18467                     var oDD = this.ids[sGroup][i];
18468
18469                     if (this.isTypeOfDD(oDD)) {
18470                     // if (this.isTypeOfDD(oDD) && oDD.isTarget) {
18471                         var loc = this.getLocation(oDD);
18472                         if (loc) {
18473                             this.locationCache[oDD.id] = loc;
18474                         } else {
18475                             delete this.locationCache[oDD.id];
18476                             // this will unregister the drag and drop object if
18477                             // the element is not in a usable state
18478                             // oDD.unreg();
18479                         }
18480                     }
18481                 }
18482             }
18483         },
18484
18485         /**
18486          * This checks to make sure an element exists and is in the DOM.  The
18487          * main purpose is to handle cases where innerHTML is used to remove
18488          * drag and drop objects from the DOM.  IE provides an 'unspecified
18489          * error' when trying to access the offsetParent of such an element
18490          * @method verifyEl
18491          * @param {HTMLElement} el the element to check
18492          * @return {boolean} true if the element looks usable
18493          * @static
18494          */
18495         verifyEl: function(el) {
18496             if (el) {
18497                 var parent;
18498                 if(Roo.isIE){
18499                     try{
18500                         parent = el.offsetParent;
18501                     }catch(e){}
18502                 }else{
18503                     parent = el.offsetParent;
18504                 }
18505                 if (parent) {
18506                     return true;
18507                 }
18508             }
18509
18510             return false;
18511         },
18512
18513         /**
18514          * Returns a Region object containing the drag and drop element's position
18515          * and size, including the padding configured for it
18516          * @method getLocation
18517          * @param {DragDrop} oDD the drag and drop object to get the
18518          *                       location for
18519          * @return {Roo.lib.Region} a Region object representing the total area
18520          *                             the element occupies, including any padding
18521          *                             the instance is configured for.
18522          * @static
18523          */
18524         getLocation: function(oDD) {
18525             if (! this.isTypeOfDD(oDD)) {
18526                 return null;
18527             }
18528
18529             var el = oDD.getEl(), pos, x1, x2, y1, y2, t, r, b, l;
18530
18531             try {
18532                 pos= Roo.lib.Dom.getXY(el);
18533             } catch (e) { }
18534
18535             if (!pos) {
18536                 return null;
18537             }
18538
18539             x1 = pos[0];
18540             x2 = x1 + el.offsetWidth;
18541             y1 = pos[1];
18542             y2 = y1 + el.offsetHeight;
18543
18544             t = y1 - oDD.padding[0];
18545             r = x2 + oDD.padding[1];
18546             b = y2 + oDD.padding[2];
18547             l = x1 - oDD.padding[3];
18548
18549             return new Roo.lib.Region( t, r, b, l );
18550         },
18551
18552         /**
18553          * Checks the cursor location to see if it over the target
18554          * @method isOverTarget
18555          * @param {Roo.lib.Point} pt The point to evaluate
18556          * @param {DragDrop} oTarget the DragDrop object we are inspecting
18557          * @return {boolean} true if the mouse is over the target
18558          * @private
18559          * @static
18560          */
18561         isOverTarget: function(pt, oTarget, intersect) {
18562             // use cache if available
18563             var loc = this.locationCache[oTarget.id];
18564             if (!loc || !this.useCache) {
18565                 loc = this.getLocation(oTarget);
18566                 this.locationCache[oTarget.id] = loc;
18567
18568             }
18569
18570             if (!loc) {
18571                 return false;
18572             }
18573
18574             oTarget.cursorIsOver = loc.contains( pt );
18575
18576             // DragDrop is using this as a sanity check for the initial mousedown
18577             // in this case we are done.  In POINT mode, if the drag obj has no
18578             // contraints, we are also done. Otherwise we need to evaluate the
18579             // location of the target as related to the actual location of the
18580             // dragged element.
18581             var dc = this.dragCurrent;
18582             if (!dc || !dc.getTargetCoord ||
18583                     (!intersect && !dc.constrainX && !dc.constrainY)) {
18584                 return oTarget.cursorIsOver;
18585             }
18586
18587             oTarget.overlap = null;
18588
18589             // Get the current location of the drag element, this is the
18590             // location of the mouse event less the delta that represents
18591             // where the original mousedown happened on the element.  We
18592             // need to consider constraints and ticks as well.
18593             var pos = dc.getTargetCoord(pt.x, pt.y);
18594
18595             var el = dc.getDragEl();
18596             var curRegion = new Roo.lib.Region( pos.y,
18597                                                    pos.x + el.offsetWidth,
18598                                                    pos.y + el.offsetHeight,
18599                                                    pos.x );
18600
18601             var overlap = curRegion.intersect(loc);
18602
18603             if (overlap) {
18604                 oTarget.overlap = overlap;
18605                 return (intersect) ? true : oTarget.cursorIsOver;
18606             } else {
18607                 return false;
18608             }
18609         },
18610
18611         /**
18612          * unload event handler
18613          * @method _onUnload
18614          * @private
18615          * @static
18616          */
18617         _onUnload: function(e, me) {
18618             Roo.dd.DragDropMgr.unregAll();
18619         },
18620
18621         /**
18622          * Cleans up the drag and drop events and objects.
18623          * @method unregAll
18624          * @private
18625          * @static
18626          */
18627         unregAll: function() {
18628
18629             if (this.dragCurrent) {
18630                 this.stopDrag();
18631                 this.dragCurrent = null;
18632             }
18633
18634             this._execOnAll("unreg", []);
18635
18636             for (i in this.elementCache) {
18637                 delete this.elementCache[i];
18638             }
18639
18640             this.elementCache = {};
18641             this.ids = {};
18642         },
18643
18644         /**
18645          * A cache of DOM elements
18646          * @property elementCache
18647          * @private
18648          * @static
18649          */
18650         elementCache: {},
18651
18652         /**
18653          * Get the wrapper for the DOM element specified
18654          * @method getElWrapper
18655          * @param {String} id the id of the element to get
18656          * @return {Roo.dd.DDM.ElementWrapper} the wrapped element
18657          * @private
18658          * @deprecated This wrapper isn't that useful
18659          * @static
18660          */
18661         getElWrapper: function(id) {
18662             var oWrapper = this.elementCache[id];
18663             if (!oWrapper || !oWrapper.el) {
18664                 oWrapper = this.elementCache[id] =
18665                     new this.ElementWrapper(Roo.getDom(id));
18666             }
18667             return oWrapper;
18668         },
18669
18670         /**
18671          * Returns the actual DOM element
18672          * @method getElement
18673          * @param {String} id the id of the elment to get
18674          * @return {Object} The element
18675          * @deprecated use Roo.getDom instead
18676          * @static
18677          */
18678         getElement: function(id) {
18679             return Roo.getDom(id);
18680         },
18681
18682         /**
18683          * Returns the style property for the DOM element (i.e.,
18684          * document.getElById(id).style)
18685          * @method getCss
18686          * @param {String} id the id of the elment to get
18687          * @return {Object} The style property of the element
18688          * @deprecated use Roo.getDom instead
18689          * @static
18690          */
18691         getCss: function(id) {
18692             var el = Roo.getDom(id);
18693             return (el) ? el.style : null;
18694         },
18695
18696         /**
18697          * Inner class for cached elements
18698          * @class DragDropMgr.ElementWrapper
18699          * @for DragDropMgr
18700          * @private
18701          * @deprecated
18702          */
18703         ElementWrapper: function(el) {
18704                 /**
18705                  * The element
18706                  * @property el
18707                  */
18708                 this.el = el || null;
18709                 /**
18710                  * The element id
18711                  * @property id
18712                  */
18713                 this.id = this.el && el.id;
18714                 /**
18715                  * A reference to the style property
18716                  * @property css
18717                  */
18718                 this.css = this.el && el.style;
18719             },
18720
18721         /**
18722          * Returns the X position of an html element
18723          * @method getPosX
18724          * @param el the element for which to get the position
18725          * @return {int} the X coordinate
18726          * @for DragDropMgr
18727          * @deprecated use Roo.lib.Dom.getX instead
18728          * @static
18729          */
18730         getPosX: function(el) {
18731             return Roo.lib.Dom.getX(el);
18732         },
18733
18734         /**
18735          * Returns the Y position of an html element
18736          * @method getPosY
18737          * @param el the element for which to get the position
18738          * @return {int} the Y coordinate
18739          * @deprecated use Roo.lib.Dom.getY instead
18740          * @static
18741          */
18742         getPosY: function(el) {
18743             return Roo.lib.Dom.getY(el);
18744         },
18745
18746         /**
18747          * Swap two nodes.  In IE, we use the native method, for others we
18748          * emulate the IE behavior
18749          * @method swapNode
18750          * @param n1 the first node to swap
18751          * @param n2 the other node to swap
18752          * @static
18753          */
18754         swapNode: function(n1, n2) {
18755             if (n1.swapNode) {
18756                 n1.swapNode(n2);
18757             } else {
18758                 var p = n2.parentNode;
18759                 var s = n2.nextSibling;
18760
18761                 if (s == n1) {
18762                     p.insertBefore(n1, n2);
18763                 } else if (n2 == n1.nextSibling) {
18764                     p.insertBefore(n2, n1);
18765                 } else {
18766                     n1.parentNode.replaceChild(n2, n1);
18767                     p.insertBefore(n1, s);
18768                 }
18769             }
18770         },
18771
18772         /**
18773          * Returns the current scroll position
18774          * @method getScroll
18775          * @private
18776          * @static
18777          */
18778         getScroll: function () {
18779             var t, l, dde=document.documentElement, db=document.body;
18780             if (dde && (dde.scrollTop || dde.scrollLeft)) {
18781                 t = dde.scrollTop;
18782                 l = dde.scrollLeft;
18783             } else if (db) {
18784                 t = db.scrollTop;
18785                 l = db.scrollLeft;
18786             } else {
18787
18788             }
18789             return { top: t, left: l };
18790         },
18791
18792         /**
18793          * Returns the specified element style property
18794          * @method getStyle
18795          * @param {HTMLElement} el          the element
18796          * @param {string}      styleProp   the style property
18797          * @return {string} The value of the style property
18798          * @deprecated use Roo.lib.Dom.getStyle
18799          * @static
18800          */
18801         getStyle: function(el, styleProp) {
18802             return Roo.fly(el).getStyle(styleProp);
18803         },
18804
18805         /**
18806          * Gets the scrollTop
18807          * @method getScrollTop
18808          * @return {int} the document's scrollTop
18809          * @static
18810          */
18811         getScrollTop: function () { return this.getScroll().top; },
18812
18813         /**
18814          * Gets the scrollLeft
18815          * @method getScrollLeft
18816          * @return {int} the document's scrollTop
18817          * @static
18818          */
18819         getScrollLeft: function () { return this.getScroll().left; },
18820
18821         /**
18822          * Sets the x/y position of an element to the location of the
18823          * target element.
18824          * @method moveToEl
18825          * @param {HTMLElement} moveEl      The element to move
18826          * @param {HTMLElement} targetEl    The position reference element
18827          * @static
18828          */
18829         moveToEl: function (moveEl, targetEl) {
18830             var aCoord = Roo.lib.Dom.getXY(targetEl);
18831             Roo.lib.Dom.setXY(moveEl, aCoord);
18832         },
18833
18834         /**
18835          * Numeric array sort function
18836          * @method numericSort
18837          * @static
18838          */
18839         numericSort: function(a, b) { return (a - b); },
18840
18841         /**
18842          * Internal counter
18843          * @property _timeoutCount
18844          * @private
18845          * @static
18846          */
18847         _timeoutCount: 0,
18848
18849         /**
18850          * Trying to make the load order less important.  Without this we get
18851          * an error if this file is loaded before the Event Utility.
18852          * @method _addListeners
18853          * @private
18854          * @static
18855          */
18856         _addListeners: function() {
18857             var DDM = Roo.dd.DDM;
18858             if ( Roo.lib.Event && document ) {
18859                 DDM._onLoad();
18860             } else {
18861                 if (DDM._timeoutCount > 2000) {
18862                 } else {
18863                     setTimeout(DDM._addListeners, 10);
18864                     if (document && document.body) {
18865                         DDM._timeoutCount += 1;
18866                     }
18867                 }
18868             }
18869         },
18870
18871         /**
18872          * Recursively searches the immediate parent and all child nodes for
18873          * the handle element in order to determine wheter or not it was
18874          * clicked.
18875          * @method handleWasClicked
18876          * @param node the html element to inspect
18877          * @static
18878          */
18879         handleWasClicked: function(node, id) {
18880             if (this.isHandle(id, node.id)) {
18881                 return true;
18882             } else {
18883                 // check to see if this is a text node child of the one we want
18884                 var p = node.parentNode;
18885
18886                 while (p) {
18887                     if (this.isHandle(id, p.id)) {
18888                         return true;
18889                     } else {
18890                         p = p.parentNode;
18891                     }
18892                 }
18893             }
18894
18895             return false;
18896         }
18897
18898     };
18899
18900 }();
18901
18902 // shorter alias, save a few bytes
18903 Roo.dd.DDM = Roo.dd.DragDropMgr;
18904 Roo.dd.DDM._addListeners();
18905
18906 }/*
18907  * Based on:
18908  * Ext JS Library 1.1.1
18909  * Copyright(c) 2006-2007, Ext JS, LLC.
18910  *
18911  * Originally Released Under LGPL - original licence link has changed is not relivant.
18912  *
18913  * Fork - LGPL
18914  * <script type="text/javascript">
18915  */
18916
18917 /**
18918  * @class Roo.dd.DD
18919  * A DragDrop implementation where the linked element follows the
18920  * mouse cursor during a drag.
18921  * @extends Roo.dd.DragDrop
18922  * @constructor
18923  * @param {String} id the id of the linked element
18924  * @param {String} sGroup the group of related DragDrop items
18925  * @param {object} config an object containing configurable attributes
18926  *                Valid properties for DD:
18927  *                    scroll
18928  */
18929 Roo.dd.DD = function(id, sGroup, config) {
18930     if (id) {
18931         this.init(id, sGroup, config);
18932     }
18933 };
18934
18935 Roo.extend(Roo.dd.DD, Roo.dd.DragDrop, {
18936
18937     /**
18938      * When set to true, the utility automatically tries to scroll the browser
18939      * window wehn a drag and drop element is dragged near the viewport boundary.
18940      * Defaults to true.
18941      * @property scroll
18942      * @type boolean
18943      */
18944     scroll: true,
18945
18946     /**
18947      * Sets the pointer offset to the distance between the linked element's top
18948      * left corner and the location the element was clicked
18949      * @method autoOffset
18950      * @param {int} iPageX the X coordinate of the click
18951      * @param {int} iPageY the Y coordinate of the click
18952      */
18953     autoOffset: function(iPageX, iPageY) {
18954         var x = iPageX - this.startPageX;
18955         var y = iPageY - this.startPageY;
18956         this.setDelta(x, y);
18957     },
18958
18959     /**
18960      * Sets the pointer offset.  You can call this directly to force the
18961      * offset to be in a particular location (e.g., pass in 0,0 to set it
18962      * to the center of the object)
18963      * @method setDelta
18964      * @param {int} iDeltaX the distance from the left
18965      * @param {int} iDeltaY the distance from the top
18966      */
18967     setDelta: function(iDeltaX, iDeltaY) {
18968         this.deltaX = iDeltaX;
18969         this.deltaY = iDeltaY;
18970     },
18971
18972     /**
18973      * Sets the drag element to the location of the mousedown or click event,
18974      * maintaining the cursor location relative to the location on the element
18975      * that was clicked.  Override this if you want to place the element in a
18976      * location other than where the cursor is.
18977      * @method setDragElPos
18978      * @param {int} iPageX the X coordinate of the mousedown or drag event
18979      * @param {int} iPageY the Y coordinate of the mousedown or drag event
18980      */
18981     setDragElPos: function(iPageX, iPageY) {
18982         // the first time we do this, we are going to check to make sure
18983         // the element has css positioning
18984
18985         var el = this.getDragEl();
18986         this.alignElWithMouse(el, iPageX, iPageY);
18987     },
18988
18989     /**
18990      * Sets the element to the location of the mousedown or click event,
18991      * maintaining the cursor location relative to the location on the element
18992      * that was clicked.  Override this if you want to place the element in a
18993      * location other than where the cursor is.
18994      * @method alignElWithMouse
18995      * @param {HTMLElement} el the element to move
18996      * @param {int} iPageX the X coordinate of the mousedown or drag event
18997      * @param {int} iPageY the Y coordinate of the mousedown or drag event
18998      */
18999     alignElWithMouse: function(el, iPageX, iPageY) {
19000         var oCoord = this.getTargetCoord(iPageX, iPageY);
19001         var fly = el.dom ? el : Roo.fly(el);
19002         if (!this.deltaSetXY) {
19003             var aCoord = [oCoord.x, oCoord.y];
19004             fly.setXY(aCoord);
19005             var newLeft = fly.getLeft(true);
19006             var newTop  = fly.getTop(true);
19007             this.deltaSetXY = [ newLeft - oCoord.x, newTop - oCoord.y ];
19008         } else {
19009             fly.setLeftTop(oCoord.x + this.deltaSetXY[0], oCoord.y + this.deltaSetXY[1]);
19010         }
19011
19012         this.cachePosition(oCoord.x, oCoord.y);
19013         this.autoScroll(oCoord.x, oCoord.y, el.offsetHeight, el.offsetWidth);
19014         return oCoord;
19015     },
19016
19017     /**
19018      * Saves the most recent position so that we can reset the constraints and
19019      * tick marks on-demand.  We need to know this so that we can calculate the
19020      * number of pixels the element is offset from its original position.
19021      * @method cachePosition
19022      * @param iPageX the current x position (optional, this just makes it so we
19023      * don't have to look it up again)
19024      * @param iPageY the current y position (optional, this just makes it so we
19025      * don't have to look it up again)
19026      */
19027     cachePosition: function(iPageX, iPageY) {
19028         if (iPageX) {
19029             this.lastPageX = iPageX;
19030             this.lastPageY = iPageY;
19031         } else {
19032             var aCoord = Roo.lib.Dom.getXY(this.getEl());
19033             this.lastPageX = aCoord[0];
19034             this.lastPageY = aCoord[1];
19035         }
19036     },
19037
19038     /**
19039      * Auto-scroll the window if the dragged object has been moved beyond the
19040      * visible window boundary.
19041      * @method autoScroll
19042      * @param {int} x the drag element's x position
19043      * @param {int} y the drag element's y position
19044      * @param {int} h the height of the drag element
19045      * @param {int} w the width of the drag element
19046      * @private
19047      */
19048     autoScroll: function(x, y, h, w) {
19049
19050         if (this.scroll) {
19051             // The client height
19052             var clientH = Roo.lib.Dom.getViewWidth();
19053
19054             // The client width
19055             var clientW = Roo.lib.Dom.getViewHeight();
19056
19057             // The amt scrolled down
19058             var st = this.DDM.getScrollTop();
19059
19060             // The amt scrolled right
19061             var sl = this.DDM.getScrollLeft();
19062
19063             // Location of the bottom of the element
19064             var bot = h + y;
19065
19066             // Location of the right of the element
19067             var right = w + x;
19068
19069             // The distance from the cursor to the bottom of the visible area,
19070             // adjusted so that we don't scroll if the cursor is beyond the
19071             // element drag constraints
19072             var toBot = (clientH + st - y - this.deltaY);
19073
19074             // The distance from the cursor to the right of the visible area
19075             var toRight = (clientW + sl - x - this.deltaX);
19076
19077
19078             // How close to the edge the cursor must be before we scroll
19079             // var thresh = (document.all) ? 100 : 40;
19080             var thresh = 40;
19081
19082             // How many pixels to scroll per autoscroll op.  This helps to reduce
19083             // clunky scrolling. IE is more sensitive about this ... it needs this
19084             // value to be higher.
19085             var scrAmt = (document.all) ? 80 : 30;
19086
19087             // Scroll down if we are near the bottom of the visible page and the
19088             // obj extends below the crease
19089             if ( bot > clientH && toBot < thresh ) {
19090                 window.scrollTo(sl, st + scrAmt);
19091             }
19092
19093             // Scroll up if the window is scrolled down and the top of the object
19094             // goes above the top border
19095             if ( y < st && st > 0 && y - st < thresh ) {
19096                 window.scrollTo(sl, st - scrAmt);
19097             }
19098
19099             // Scroll right if the obj is beyond the right border and the cursor is
19100             // near the border.
19101             if ( right > clientW && toRight < thresh ) {
19102                 window.scrollTo(sl + scrAmt, st);
19103             }
19104
19105             // Scroll left if the window has been scrolled to the right and the obj
19106             // extends past the left border
19107             if ( x < sl && sl > 0 && x - sl < thresh ) {
19108                 window.scrollTo(sl - scrAmt, st);
19109             }
19110         }
19111     },
19112
19113     /**
19114      * Finds the location the element should be placed if we want to move
19115      * it to where the mouse location less the click offset would place us.
19116      * @method getTargetCoord
19117      * @param {int} iPageX the X coordinate of the click
19118      * @param {int} iPageY the Y coordinate of the click
19119      * @return an object that contains the coordinates (Object.x and Object.y)
19120      * @private
19121      */
19122     getTargetCoord: function(iPageX, iPageY) {
19123
19124
19125         var x = iPageX - this.deltaX;
19126         var y = iPageY - this.deltaY;
19127
19128         if (this.constrainX) {
19129             if (x < this.minX) { x = this.minX; }
19130             if (x > this.maxX) { x = this.maxX; }
19131         }
19132
19133         if (this.constrainY) {
19134             if (y < this.minY) { y = this.minY; }
19135             if (y > this.maxY) { y = this.maxY; }
19136         }
19137
19138         x = this.getTick(x, this.xTicks);
19139         y = this.getTick(y, this.yTicks);
19140
19141
19142         return {x:x, y:y};
19143     },
19144
19145     /*
19146      * Sets up config options specific to this class. Overrides
19147      * Roo.dd.DragDrop, but all versions of this method through the
19148      * inheritance chain are called
19149      */
19150     applyConfig: function() {
19151         Roo.dd.DD.superclass.applyConfig.call(this);
19152         this.scroll = (this.config.scroll !== false);
19153     },
19154
19155     /*
19156      * Event that fires prior to the onMouseDown event.  Overrides
19157      * Roo.dd.DragDrop.
19158      */
19159     b4MouseDown: function(e) {
19160         // this.resetConstraints();
19161         this.autoOffset(e.getPageX(),
19162                             e.getPageY());
19163     },
19164
19165     /*
19166      * Event that fires prior to the onDrag event.  Overrides
19167      * Roo.dd.DragDrop.
19168      */
19169     b4Drag: function(e) {
19170         this.setDragElPos(e.getPageX(),
19171                             e.getPageY());
19172     },
19173
19174     toString: function() {
19175         return ("DD " + this.id);
19176     }
19177
19178     //////////////////////////////////////////////////////////////////////////
19179     // Debugging ygDragDrop events that can be overridden
19180     //////////////////////////////////////////////////////////////////////////
19181     /*
19182     startDrag: function(x, y) {
19183     },
19184
19185     onDrag: function(e) {
19186     },
19187
19188     onDragEnter: function(e, id) {
19189     },
19190
19191     onDragOver: function(e, id) {
19192     },
19193
19194     onDragOut: function(e, id) {
19195     },
19196
19197     onDragDrop: function(e, id) {
19198     },
19199
19200     endDrag: function(e) {
19201     }
19202
19203     */
19204
19205 });/*
19206  * Based on:
19207  * Ext JS Library 1.1.1
19208  * Copyright(c) 2006-2007, Ext JS, LLC.
19209  *
19210  * Originally Released Under LGPL - original licence link has changed is not relivant.
19211  *
19212  * Fork - LGPL
19213  * <script type="text/javascript">
19214  */
19215
19216 /**
19217  * @class Roo.dd.DDProxy
19218  * A DragDrop implementation that inserts an empty, bordered div into
19219  * the document that follows the cursor during drag operations.  At the time of
19220  * the click, the frame div is resized to the dimensions of the linked html
19221  * element, and moved to the exact location of the linked element.
19222  *
19223  * References to the "frame" element refer to the single proxy element that
19224  * was created to be dragged in place of all DDProxy elements on the
19225  * page.
19226  *
19227  * @extends Roo.dd.DD
19228  * @constructor
19229  * @param {String} id the id of the linked html element
19230  * @param {String} sGroup the group of related DragDrop objects
19231  * @param {object} config an object containing configurable attributes
19232  *                Valid properties for DDProxy in addition to those in DragDrop:
19233  *                   resizeFrame, centerFrame, dragElId
19234  */
19235 Roo.dd.DDProxy = function(id, sGroup, config) {
19236     if (id) {
19237         this.init(id, sGroup, config);
19238         this.initFrame();
19239     }
19240 };
19241
19242 /**
19243  * The default drag frame div id
19244  * @property Roo.dd.DDProxy.dragElId
19245  * @type String
19246  * @static
19247  */
19248 Roo.dd.DDProxy.dragElId = "ygddfdiv";
19249
19250 Roo.extend(Roo.dd.DDProxy, Roo.dd.DD, {
19251
19252     /**
19253      * By default we resize the drag frame to be the same size as the element
19254      * we want to drag (this is to get the frame effect).  We can turn it off
19255      * if we want a different behavior.
19256      * @property resizeFrame
19257      * @type boolean
19258      */
19259     resizeFrame: true,
19260
19261     /**
19262      * By default the frame is positioned exactly where the drag element is, so
19263      * we use the cursor offset provided by Roo.dd.DD.  Another option that works only if
19264      * you do not have constraints on the obj is to have the drag frame centered
19265      * around the cursor.  Set centerFrame to true for this effect.
19266      * @property centerFrame
19267      * @type boolean
19268      */
19269     centerFrame: false,
19270
19271     /**
19272      * Creates the proxy element if it does not yet exist
19273      * @method createFrame
19274      */
19275     createFrame: function() {
19276         var self = this;
19277         var body = document.body;
19278
19279         if (!body || !body.firstChild) {
19280             setTimeout( function() { self.createFrame(); }, 50 );
19281             return;
19282         }
19283
19284         var div = this.getDragEl();
19285
19286         if (!div) {
19287             div    = document.createElement("div");
19288             div.id = this.dragElId;
19289             var s  = div.style;
19290
19291             s.position   = "absolute";
19292             s.visibility = "hidden";
19293             s.cursor     = "move";
19294             s.border     = "2px solid #aaa";
19295             s.zIndex     = 999;
19296
19297             // appendChild can blow up IE if invoked prior to the window load event
19298             // while rendering a table.  It is possible there are other scenarios
19299             // that would cause this to happen as well.
19300             body.insertBefore(div, body.firstChild);
19301         }
19302     },
19303
19304     /**
19305      * Initialization for the drag frame element.  Must be called in the
19306      * constructor of all subclasses
19307      * @method initFrame
19308      */
19309     initFrame: function() {
19310         this.createFrame();
19311     },
19312
19313     applyConfig: function() {
19314         Roo.dd.DDProxy.superclass.applyConfig.call(this);
19315
19316         this.resizeFrame = (this.config.resizeFrame !== false);
19317         this.centerFrame = (this.config.centerFrame);
19318         this.setDragElId(this.config.dragElId || Roo.dd.DDProxy.dragElId);
19319     },
19320
19321     /**
19322      * Resizes the drag frame to the dimensions of the clicked object, positions
19323      * it over the object, and finally displays it
19324      * @method showFrame
19325      * @param {int} iPageX X click position
19326      * @param {int} iPageY Y click position
19327      * @private
19328      */
19329     showFrame: function(iPageX, iPageY) {
19330         var el = this.getEl();
19331         var dragEl = this.getDragEl();
19332         var s = dragEl.style;
19333
19334         this._resizeProxy();
19335
19336         if (this.centerFrame) {
19337             this.setDelta( Math.round(parseInt(s.width,  10)/2),
19338                            Math.round(parseInt(s.height, 10)/2) );
19339         }
19340
19341         this.setDragElPos(iPageX, iPageY);
19342
19343         Roo.fly(dragEl).show();
19344     },
19345
19346     /**
19347      * The proxy is automatically resized to the dimensions of the linked
19348      * element when a drag is initiated, unless resizeFrame is set to false
19349      * @method _resizeProxy
19350      * @private
19351      */
19352     _resizeProxy: function() {
19353         if (this.resizeFrame) {
19354             var el = this.getEl();
19355             Roo.fly(this.getDragEl()).setSize(el.offsetWidth, el.offsetHeight);
19356         }
19357     },
19358
19359     // overrides Roo.dd.DragDrop
19360     b4MouseDown: function(e) {
19361         var x = e.getPageX();
19362         var y = e.getPageY();
19363         this.autoOffset(x, y);
19364         this.setDragElPos(x, y);
19365     },
19366
19367     // overrides Roo.dd.DragDrop
19368     b4StartDrag: function(x, y) {
19369         // show the drag frame
19370         this.showFrame(x, y);
19371     },
19372
19373     // overrides Roo.dd.DragDrop
19374     b4EndDrag: function(e) {
19375         Roo.fly(this.getDragEl()).hide();
19376     },
19377
19378     // overrides Roo.dd.DragDrop
19379     // By default we try to move the element to the last location of the frame.
19380     // This is so that the default behavior mirrors that of Roo.dd.DD.
19381     endDrag: function(e) {
19382
19383         var lel = this.getEl();
19384         var del = this.getDragEl();
19385
19386         // Show the drag frame briefly so we can get its position
19387         del.style.visibility = "";
19388
19389         this.beforeMove();
19390         // Hide the linked element before the move to get around a Safari
19391         // rendering bug.
19392         lel.style.visibility = "hidden";
19393         Roo.dd.DDM.moveToEl(lel, del);
19394         del.style.visibility = "hidden";
19395         lel.style.visibility = "";
19396
19397         this.afterDrag();
19398     },
19399
19400     beforeMove : function(){
19401
19402     },
19403
19404     afterDrag : function(){
19405
19406     },
19407
19408     toString: function() {
19409         return ("DDProxy " + this.id);
19410     }
19411
19412 });
19413 /*
19414  * Based on:
19415  * Ext JS Library 1.1.1
19416  * Copyright(c) 2006-2007, Ext JS, LLC.
19417  *
19418  * Originally Released Under LGPL - original licence link has changed is not relivant.
19419  *
19420  * Fork - LGPL
19421  * <script type="text/javascript">
19422  */
19423
19424  /**
19425  * @class Roo.dd.DDTarget
19426  * A DragDrop implementation that does not move, but can be a drop
19427  * target.  You would get the same result by simply omitting implementation
19428  * for the event callbacks, but this way we reduce the processing cost of the
19429  * event listener and the callbacks.
19430  * @extends Roo.dd.DragDrop
19431  * @constructor
19432  * @param {String} id the id of the element that is a drop target
19433  * @param {String} sGroup the group of related DragDrop objects
19434  * @param {object} config an object containing configurable attributes
19435  *                 Valid properties for DDTarget in addition to those in
19436  *                 DragDrop:
19437  *                    none
19438  */
19439 Roo.dd.DDTarget = function(id, sGroup, config) {
19440     if (id) {
19441         this.initTarget(id, sGroup, config);
19442     }
19443     if (config.listeners || config.events) { 
19444        Roo.dd.DragDrop.superclass.constructor.call(this,  { 
19445             listeners : config.listeners || {}, 
19446             events : config.events || {} 
19447         });    
19448     }
19449 };
19450
19451 // Roo.dd.DDTarget.prototype = new Roo.dd.DragDrop();
19452 Roo.extend(Roo.dd.DDTarget, Roo.dd.DragDrop, {
19453     toString: function() {
19454         return ("DDTarget " + this.id);
19455     }
19456 });
19457 /*
19458  * Based on:
19459  * Ext JS Library 1.1.1
19460  * Copyright(c) 2006-2007, Ext JS, LLC.
19461  *
19462  * Originally Released Under LGPL - original licence link has changed is not relivant.
19463  *
19464  * Fork - LGPL
19465  * <script type="text/javascript">
19466  */
19467  
19468
19469 /**
19470  * @class Roo.dd.ScrollManager
19471  * Provides automatic scrolling of overflow regions in the page during drag operations.<br><br>
19472  * <b>Note: This class uses "Point Mode" and is untested in "Intersect Mode".</b>
19473  * @singleton
19474  */
19475 Roo.dd.ScrollManager = function(){
19476     var ddm = Roo.dd.DragDropMgr;
19477     var els = {};
19478     var dragEl = null;
19479     var proc = {};
19480     
19481     
19482     
19483     var onStop = function(e){
19484         dragEl = null;
19485         clearProc();
19486     };
19487     
19488     var triggerRefresh = function(){
19489         if(ddm.dragCurrent){
19490              ddm.refreshCache(ddm.dragCurrent.groups);
19491         }
19492     };
19493     
19494     var doScroll = function(){
19495         if(ddm.dragCurrent){
19496             var dds = Roo.dd.ScrollManager;
19497             if(!dds.animate){
19498                 if(proc.el.scroll(proc.dir, dds.increment)){
19499                     triggerRefresh();
19500                 }
19501             }else{
19502                 proc.el.scroll(proc.dir, dds.increment, true, dds.animDuration, triggerRefresh);
19503             }
19504         }
19505     };
19506     
19507     var clearProc = function(){
19508         if(proc.id){
19509             clearInterval(proc.id);
19510         }
19511         proc.id = 0;
19512         proc.el = null;
19513         proc.dir = "";
19514     };
19515     
19516     var startProc = function(el, dir){
19517          Roo.log('scroll startproc');
19518         clearProc();
19519         proc.el = el;
19520         proc.dir = dir;
19521         proc.id = setInterval(doScroll, Roo.dd.ScrollManager.frequency);
19522     };
19523     
19524     var onFire = function(e, isDrop){
19525        
19526         if(isDrop || !ddm.dragCurrent){ return; }
19527         var dds = Roo.dd.ScrollManager;
19528         if(!dragEl || dragEl != ddm.dragCurrent){
19529             dragEl = ddm.dragCurrent;
19530             // refresh regions on drag start
19531             dds.refreshCache();
19532         }
19533         
19534         var xy = Roo.lib.Event.getXY(e);
19535         var pt = new Roo.lib.Point(xy[0], xy[1]);
19536         for(var id in els){
19537             var el = els[id], r = el._region;
19538             if(r && r.contains(pt) && el.isScrollable()){
19539                 if(r.bottom - pt.y <= dds.thresh){
19540                     if(proc.el != el){
19541                         startProc(el, "down");
19542                     }
19543                     return;
19544                 }else if(r.right - pt.x <= dds.thresh){
19545                     if(proc.el != el){
19546                         startProc(el, "left");
19547                     }
19548                     return;
19549                 }else if(pt.y - r.top <= dds.thresh){
19550                     if(proc.el != el){
19551                         startProc(el, "up");
19552                     }
19553                     return;
19554                 }else if(pt.x - r.left <= dds.thresh){
19555                     if(proc.el != el){
19556                         startProc(el, "right");
19557                     }
19558                     return;
19559                 }
19560             }
19561         }
19562         clearProc();
19563     };
19564     
19565     ddm.fireEvents = ddm.fireEvents.createSequence(onFire, ddm);
19566     ddm.stopDrag = ddm.stopDrag.createSequence(onStop, ddm);
19567     
19568     return {
19569         /**
19570          * Registers new overflow element(s) to auto scroll
19571          * @param {String/HTMLElement/Element/Array} el The id of or the element to be scrolled or an array of either
19572          */
19573         register : function(el){
19574             if(el instanceof Array){
19575                 for(var i = 0, len = el.length; i < len; i++) {
19576                         this.register(el[i]);
19577                 }
19578             }else{
19579                 el = Roo.get(el);
19580                 els[el.id] = el;
19581             }
19582             Roo.dd.ScrollManager.els = els;
19583         },
19584         
19585         /**
19586          * Unregisters overflow element(s) so they are no longer scrolled
19587          * @param {String/HTMLElement/Element/Array} el The id of or the element to be removed or an array of either
19588          */
19589         unregister : function(el){
19590             if(el instanceof Array){
19591                 for(var i = 0, len = el.length; i < len; i++) {
19592                         this.unregister(el[i]);
19593                 }
19594             }else{
19595                 el = Roo.get(el);
19596                 delete els[el.id];
19597             }
19598         },
19599         
19600         /**
19601          * The number of pixels from the edge of a container the pointer needs to be to 
19602          * trigger scrolling (defaults to 25)
19603          * @type Number
19604          */
19605         thresh : 25,
19606         
19607         /**
19608          * The number of pixels to scroll in each scroll increment (defaults to 50)
19609          * @type Number
19610          */
19611         increment : 100,
19612         
19613         /**
19614          * The frequency of scrolls in milliseconds (defaults to 500)
19615          * @type Number
19616          */
19617         frequency : 500,
19618         
19619         /**
19620          * True to animate the scroll (defaults to true)
19621          * @type Boolean
19622          */
19623         animate: true,
19624         
19625         /**
19626          * The animation duration in seconds - 
19627          * MUST BE less than Roo.dd.ScrollManager.frequency! (defaults to .4)
19628          * @type Number
19629          */
19630         animDuration: .4,
19631         
19632         /**
19633          * Manually trigger a cache refresh.
19634          */
19635         refreshCache : function(){
19636             for(var id in els){
19637                 if(typeof els[id] == 'object'){ // for people extending the object prototype
19638                     els[id]._region = els[id].getRegion();
19639                 }
19640             }
19641         }
19642     };
19643 }();/*
19644  * Based on:
19645  * Ext JS Library 1.1.1
19646  * Copyright(c) 2006-2007, Ext JS, LLC.
19647  *
19648  * Originally Released Under LGPL - original licence link has changed is not relivant.
19649  *
19650  * Fork - LGPL
19651  * <script type="text/javascript">
19652  */
19653  
19654
19655 /**
19656  * @class Roo.dd.Registry
19657  * Provides easy access to all drag drop components that are registered on a page.  Items can be retrieved either
19658  * directly by DOM node id, or by passing in the drag drop event that occurred and looking up the event target.
19659  * @singleton
19660  */
19661 Roo.dd.Registry = function(){
19662     var elements = {}; 
19663     var handles = {}; 
19664     var autoIdSeed = 0;
19665
19666     var getId = function(el, autogen){
19667         if(typeof el == "string"){
19668             return el;
19669         }
19670         var id = el.id;
19671         if(!id && autogen !== false){
19672             id = "roodd-" + (++autoIdSeed);
19673             el.id = id;
19674         }
19675         return id;
19676     };
19677     
19678     return {
19679     /**
19680      * Register a drag drop element
19681      * @param {String|HTMLElement} element The id or DOM node to register
19682      * @param {Object} data (optional) A custom data object that will be passed between the elements that are involved
19683      * in drag drop operations.  You can populate this object with any arbitrary properties that your own code
19684      * knows how to interpret, plus there are some specific properties known to the Registry that should be
19685      * populated in the data object (if applicable):
19686      * <pre>
19687 Value      Description<br />
19688 ---------  ------------------------------------------<br />
19689 handles    Array of DOM nodes that trigger dragging<br />
19690            for the element being registered<br />
19691 isHandle   True if the element passed in triggers<br />
19692            dragging itself, else false
19693 </pre>
19694      */
19695         register : function(el, data){
19696             data = data || {};
19697             if(typeof el == "string"){
19698                 el = document.getElementById(el);
19699             }
19700             data.ddel = el;
19701             elements[getId(el)] = data;
19702             if(data.isHandle !== false){
19703                 handles[data.ddel.id] = data;
19704             }
19705             if(data.handles){
19706                 var hs = data.handles;
19707                 for(var i = 0, len = hs.length; i < len; i++){
19708                         handles[getId(hs[i])] = data;
19709                 }
19710             }
19711         },
19712
19713     /**
19714      * Unregister a drag drop element
19715      * @param {String|HTMLElement}  element The id or DOM node to unregister
19716      */
19717         unregister : function(el){
19718             var id = getId(el, false);
19719             var data = elements[id];
19720             if(data){
19721                 delete elements[id];
19722                 if(data.handles){
19723                     var hs = data.handles;
19724                     for(var i = 0, len = hs.length; i < len; i++){
19725                         delete handles[getId(hs[i], false)];
19726                     }
19727                 }
19728             }
19729         },
19730
19731     /**
19732      * Returns the handle registered for a DOM Node by id
19733      * @param {String|HTMLElement} id The DOM node or id to look up
19734      * @return {Object} handle The custom handle data
19735      */
19736         getHandle : function(id){
19737             if(typeof id != "string"){ // must be element?
19738                 id = id.id;
19739             }
19740             return handles[id];
19741         },
19742
19743     /**
19744      * Returns the handle that is registered for the DOM node that is the target of the event
19745      * @param {Event} e The event
19746      * @return {Object} handle The custom handle data
19747      */
19748         getHandleFromEvent : function(e){
19749             var t = Roo.lib.Event.getTarget(e);
19750             return t ? handles[t.id] : null;
19751         },
19752
19753     /**
19754      * Returns a custom data object that is registered for a DOM node by id
19755      * @param {String|HTMLElement} id The DOM node or id to look up
19756      * @return {Object} data The custom data
19757      */
19758         getTarget : function(id){
19759             if(typeof id != "string"){ // must be element?
19760                 id = id.id;
19761             }
19762             return elements[id];
19763         },
19764
19765     /**
19766      * Returns a custom data object that is registered for the DOM node that is the target of the event
19767      * @param {Event} e The event
19768      * @return {Object} data The custom data
19769      */
19770         getTargetFromEvent : function(e){
19771             var t = Roo.lib.Event.getTarget(e);
19772             return t ? elements[t.id] || handles[t.id] : null;
19773         }
19774     };
19775 }();/*
19776  * Based on:
19777  * Ext JS Library 1.1.1
19778  * Copyright(c) 2006-2007, Ext JS, LLC.
19779  *
19780  * Originally Released Under LGPL - original licence link has changed is not relivant.
19781  *
19782  * Fork - LGPL
19783  * <script type="text/javascript">
19784  */
19785  
19786
19787 /**
19788  * @class Roo.dd.StatusProxy
19789  * A specialized drag proxy that supports a drop status icon, {@link Roo.Layer} styles and auto-repair.  This is the
19790  * default drag proxy used by all Roo.dd components.
19791  * @constructor
19792  * @param {Object} config
19793  */
19794 Roo.dd.StatusProxy = function(config){
19795     Roo.apply(this, config);
19796     this.id = this.id || Roo.id();
19797     this.el = new Roo.Layer({
19798         dh: {
19799             id: this.id, tag: "div", cls: "x-dd-drag-proxy "+this.dropNotAllowed, children: [
19800                 {tag: "div", cls: "x-dd-drop-icon"},
19801                 {tag: "div", cls: "x-dd-drag-ghost"}
19802             ]
19803         }, 
19804         shadow: !config || config.shadow !== false
19805     });
19806     this.ghost = Roo.get(this.el.dom.childNodes[1]);
19807     this.dropStatus = this.dropNotAllowed;
19808 };
19809
19810 Roo.dd.StatusProxy.prototype = {
19811     /**
19812      * @cfg {String} dropAllowed
19813      * The CSS class to apply to the status element when drop is allowed (defaults to "x-dd-drop-ok").
19814      */
19815     dropAllowed : "x-dd-drop-ok",
19816     /**
19817      * @cfg {String} dropNotAllowed
19818      * The CSS class to apply to the status element when drop is not allowed (defaults to "x-dd-drop-nodrop").
19819      */
19820     dropNotAllowed : "x-dd-drop-nodrop",
19821
19822     /**
19823      * Updates the proxy's visual element to indicate the status of whether or not drop is allowed
19824      * over the current target element.
19825      * @param {String} cssClass The css class for the new drop status indicator image
19826      */
19827     setStatus : function(cssClass){
19828         cssClass = cssClass || this.dropNotAllowed;
19829         if(this.dropStatus != cssClass){
19830             this.el.replaceClass(this.dropStatus, cssClass);
19831             this.dropStatus = cssClass;
19832         }
19833     },
19834
19835     /**
19836      * Resets the status indicator to the default dropNotAllowed value
19837      * @param {Boolean} clearGhost True to also remove all content from the ghost, false to preserve it
19838      */
19839     reset : function(clearGhost){
19840         this.el.dom.className = "x-dd-drag-proxy " + this.dropNotAllowed;
19841         this.dropStatus = this.dropNotAllowed;
19842         if(clearGhost){
19843             this.ghost.update("");
19844         }
19845     },
19846
19847     /**
19848      * Updates the contents of the ghost element
19849      * @param {String} html The html that will replace the current innerHTML of the ghost element
19850      */
19851     update : function(html){
19852         if(typeof html == "string"){
19853             this.ghost.update(html);
19854         }else{
19855             this.ghost.update("");
19856             html.style.margin = "0";
19857             this.ghost.dom.appendChild(html);
19858         }
19859         // ensure float = none set?? cant remember why though.
19860         var el = this.ghost.dom.firstChild;
19861                 if(el){
19862                         Roo.fly(el).setStyle('float', 'none');
19863                 }
19864     },
19865     
19866     /**
19867      * Returns the underlying proxy {@link Roo.Layer}
19868      * @return {Roo.Layer} el
19869     */
19870     getEl : function(){
19871         return this.el;
19872     },
19873
19874     /**
19875      * Returns the ghost element
19876      * @return {Roo.Element} el
19877      */
19878     getGhost : function(){
19879         return this.ghost;
19880     },
19881
19882     /**
19883      * Hides the proxy
19884      * @param {Boolean} clear True to reset the status and clear the ghost contents, false to preserve them
19885      */
19886     hide : function(clear){
19887         this.el.hide();
19888         if(clear){
19889             this.reset(true);
19890         }
19891     },
19892
19893     /**
19894      * Stops the repair animation if it's currently running
19895      */
19896     stop : function(){
19897         if(this.anim && this.anim.isAnimated && this.anim.isAnimated()){
19898             this.anim.stop();
19899         }
19900     },
19901
19902     /**
19903      * Displays this proxy
19904      */
19905     show : function(){
19906         this.el.show();
19907     },
19908
19909     /**
19910      * Force the Layer to sync its shadow and shim positions to the element
19911      */
19912     sync : function(){
19913         this.el.sync();
19914     },
19915
19916     /**
19917      * Causes the proxy to return to its position of origin via an animation.  Should be called after an
19918      * invalid drop operation by the item being dragged.
19919      * @param {Array} xy The XY position of the element ([x, y])
19920      * @param {Function} callback The function to call after the repair is complete
19921      * @param {Object} scope The scope in which to execute the callback
19922      */
19923     repair : function(xy, callback, scope){
19924         this.callback = callback;
19925         this.scope = scope;
19926         if(xy && this.animRepair !== false){
19927             this.el.addClass("x-dd-drag-repair");
19928             this.el.hideUnders(true);
19929             this.anim = this.el.shift({
19930                 duration: this.repairDuration || .5,
19931                 easing: 'easeOut',
19932                 xy: xy,
19933                 stopFx: true,
19934                 callback: this.afterRepair,
19935                 scope: this
19936             });
19937         }else{
19938             this.afterRepair();
19939         }
19940     },
19941
19942     // private
19943     afterRepair : function(){
19944         this.hide(true);
19945         if(typeof this.callback == "function"){
19946             this.callback.call(this.scope || this);
19947         }
19948         this.callback = null;
19949         this.scope = null;
19950     }
19951 };/*
19952  * Based on:
19953  * Ext JS Library 1.1.1
19954  * Copyright(c) 2006-2007, Ext JS, LLC.
19955  *
19956  * Originally Released Under LGPL - original licence link has changed is not relivant.
19957  *
19958  * Fork - LGPL
19959  * <script type="text/javascript">
19960  */
19961
19962 /**
19963  * @class Roo.dd.DragSource
19964  * @extends Roo.dd.DDProxy
19965  * A simple class that provides the basic implementation needed to make any element draggable.
19966  * @constructor
19967  * @param {String/HTMLElement/Element} el The container element
19968  * @param {Object} config
19969  */
19970 Roo.dd.DragSource = function(el, config){
19971     this.el = Roo.get(el);
19972     this.dragData = {};
19973     
19974     Roo.apply(this, config);
19975     
19976     if(!this.proxy){
19977         this.proxy = new Roo.dd.StatusProxy();
19978     }
19979
19980     Roo.dd.DragSource.superclass.constructor.call(this, this.el.dom, this.ddGroup || this.group,
19981           {dragElId : this.proxy.id, resizeFrame: false, isTarget: false, scroll: this.scroll === true});
19982     
19983     this.dragging = false;
19984 };
19985
19986 Roo.extend(Roo.dd.DragSource, Roo.dd.DDProxy, {
19987     /**
19988      * @cfg {String} dropAllowed
19989      * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
19990      */
19991     dropAllowed : "x-dd-drop-ok",
19992     /**
19993      * @cfg {String} dropNotAllowed
19994      * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
19995      */
19996     dropNotAllowed : "x-dd-drop-nodrop",
19997
19998     /**
19999      * Returns the data object associated with this drag source
20000      * @return {Object} data An object containing arbitrary data
20001      */
20002     getDragData : function(e){
20003         return this.dragData;
20004     },
20005
20006     // private
20007     onDragEnter : function(e, id){
20008         var target = Roo.dd.DragDropMgr.getDDById(id);
20009         this.cachedTarget = target;
20010         if(this.beforeDragEnter(target, e, id) !== false){
20011             if(target.isNotifyTarget){
20012                 var status = target.notifyEnter(this, e, this.dragData);
20013                 this.proxy.setStatus(status);
20014             }else{
20015                 this.proxy.setStatus(this.dropAllowed);
20016             }
20017             
20018             if(this.afterDragEnter){
20019                 /**
20020                  * An empty function by default, but provided so that you can perform a custom action
20021                  * when the dragged item enters the drop target by providing an implementation.
20022                  * @param {Roo.dd.DragDrop} target The drop target
20023                  * @param {Event} e The event object
20024                  * @param {String} id The id of the dragged element
20025                  * @method afterDragEnter
20026                  */
20027                 this.afterDragEnter(target, e, id);
20028             }
20029         }
20030     },
20031
20032     /**
20033      * An empty function by default, but provided so that you can perform a custom action
20034      * before the dragged item enters the drop target and optionally cancel the onDragEnter.
20035      * @param {Roo.dd.DragDrop} target The drop target
20036      * @param {Event} e The event object
20037      * @param {String} id The id of the dragged element
20038      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
20039      */
20040     beforeDragEnter : function(target, e, id){
20041         return true;
20042     },
20043
20044     // private
20045     alignElWithMouse: function() {
20046         Roo.dd.DragSource.superclass.alignElWithMouse.apply(this, arguments);
20047         this.proxy.sync();
20048     },
20049
20050     // private
20051     onDragOver : function(e, id){
20052         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
20053         if(this.beforeDragOver(target, e, id) !== false){
20054             if(target.isNotifyTarget){
20055                 var status = target.notifyOver(this, e, this.dragData);
20056                 this.proxy.setStatus(status);
20057             }
20058
20059             if(this.afterDragOver){
20060                 /**
20061                  * An empty function by default, but provided so that you can perform a custom action
20062                  * while the dragged item is over the drop target by providing an implementation.
20063                  * @param {Roo.dd.DragDrop} target The drop target
20064                  * @param {Event} e The event object
20065                  * @param {String} id The id of the dragged element
20066                  * @method afterDragOver
20067                  */
20068                 this.afterDragOver(target, e, id);
20069             }
20070         }
20071     },
20072
20073     /**
20074      * An empty function by default, but provided so that you can perform a custom action
20075      * while the dragged item is over the drop target and optionally cancel the onDragOver.
20076      * @param {Roo.dd.DragDrop} target The drop target
20077      * @param {Event} e The event object
20078      * @param {String} id The id of the dragged element
20079      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
20080      */
20081     beforeDragOver : function(target, e, id){
20082         return true;
20083     },
20084
20085     // private
20086     onDragOut : function(e, id){
20087         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
20088         if(this.beforeDragOut(target, e, id) !== false){
20089             if(target.isNotifyTarget){
20090                 target.notifyOut(this, e, this.dragData);
20091             }
20092             this.proxy.reset();
20093             if(this.afterDragOut){
20094                 /**
20095                  * An empty function by default, but provided so that you can perform a custom action
20096                  * after the dragged item is dragged out of the target without dropping.
20097                  * @param {Roo.dd.DragDrop} target The drop target
20098                  * @param {Event} e The event object
20099                  * @param {String} id The id of the dragged element
20100                  * @method afterDragOut
20101                  */
20102                 this.afterDragOut(target, e, id);
20103             }
20104         }
20105         this.cachedTarget = null;
20106     },
20107
20108     /**
20109      * An empty function by default, but provided so that you can perform a custom action before the dragged
20110      * item is dragged out of the target without dropping, and optionally cancel the onDragOut.
20111      * @param {Roo.dd.DragDrop} target The drop target
20112      * @param {Event} e The event object
20113      * @param {String} id The id of the dragged element
20114      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
20115      */
20116     beforeDragOut : function(target, e, id){
20117         return true;
20118     },
20119     
20120     // private
20121     onDragDrop : function(e, id){
20122         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
20123         if(this.beforeDragDrop(target, e, id) !== false){
20124             if(target.isNotifyTarget){
20125                 if(target.notifyDrop(this, e, this.dragData)){ // valid drop?
20126                     this.onValidDrop(target, e, id);
20127                 }else{
20128                     this.onInvalidDrop(target, e, id);
20129                 }
20130             }else{
20131                 this.onValidDrop(target, e, id);
20132             }
20133             
20134             if(this.afterDragDrop){
20135                 /**
20136                  * An empty function by default, but provided so that you can perform a custom action
20137                  * after a valid drag drop has occurred by providing an implementation.
20138                  * @param {Roo.dd.DragDrop} target The drop target
20139                  * @param {Event} e The event object
20140                  * @param {String} id The id of the dropped element
20141                  * @method afterDragDrop
20142                  */
20143                 this.afterDragDrop(target, e, id);
20144             }
20145         }
20146         delete this.cachedTarget;
20147     },
20148
20149     /**
20150      * An empty function by default, but provided so that you can perform a custom action before the dragged
20151      * item is dropped onto the target and optionally cancel the onDragDrop.
20152      * @param {Roo.dd.DragDrop} target The drop target
20153      * @param {Event} e The event object
20154      * @param {String} id The id of the dragged element
20155      * @return {Boolean} isValid True if the drag drop event is valid, else false to cancel
20156      */
20157     beforeDragDrop : function(target, e, id){
20158         return true;
20159     },
20160
20161     // private
20162     onValidDrop : function(target, e, id){
20163         this.hideProxy();
20164         if(this.afterValidDrop){
20165             /**
20166              * An empty function by default, but provided so that you can perform a custom action
20167              * after a valid drop has occurred by providing an implementation.
20168              * @param {Object} target The target DD 
20169              * @param {Event} e The event object
20170              * @param {String} id The id of the dropped element
20171              * @method afterInvalidDrop
20172              */
20173             this.afterValidDrop(target, e, id);
20174         }
20175     },
20176
20177     // private
20178     getRepairXY : function(e, data){
20179         return this.el.getXY();  
20180     },
20181
20182     // private
20183     onInvalidDrop : function(target, e, id){
20184         this.beforeInvalidDrop(target, e, id);
20185         if(this.cachedTarget){
20186             if(this.cachedTarget.isNotifyTarget){
20187                 this.cachedTarget.notifyOut(this, e, this.dragData);
20188             }
20189             this.cacheTarget = null;
20190         }
20191         this.proxy.repair(this.getRepairXY(e, this.dragData), this.afterRepair, this);
20192
20193         if(this.afterInvalidDrop){
20194             /**
20195              * An empty function by default, but provided so that you can perform a custom action
20196              * after an invalid drop has occurred by providing an implementation.
20197              * @param {Event} e The event object
20198              * @param {String} id The id of the dropped element
20199              * @method afterInvalidDrop
20200              */
20201             this.afterInvalidDrop(e, id);
20202         }
20203     },
20204
20205     // private
20206     afterRepair : function(){
20207         if(Roo.enableFx){
20208             this.el.highlight(this.hlColor || "c3daf9");
20209         }
20210         this.dragging = false;
20211     },
20212
20213     /**
20214      * An empty function by default, but provided so that you can perform a custom action after an invalid
20215      * drop has occurred.
20216      * @param {Roo.dd.DragDrop} target The drop target
20217      * @param {Event} e The event object
20218      * @param {String} id The id of the dragged element
20219      * @return {Boolean} isValid True if the invalid drop should proceed, else false to cancel
20220      */
20221     beforeInvalidDrop : function(target, e, id){
20222         return true;
20223     },
20224
20225     // private
20226     handleMouseDown : function(e){
20227         if(this.dragging) {
20228             return;
20229         }
20230         var data = this.getDragData(e);
20231         if(data && this.onBeforeDrag(data, e) !== false){
20232             this.dragData = data;
20233             this.proxy.stop();
20234             Roo.dd.DragSource.superclass.handleMouseDown.apply(this, arguments);
20235         } 
20236     },
20237
20238     /**
20239      * An empty function by default, but provided so that you can perform a custom action before the initial
20240      * drag event begins and optionally cancel it.
20241      * @param {Object} data An object containing arbitrary data to be shared with drop targets
20242      * @param {Event} e The event object
20243      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
20244      */
20245     onBeforeDrag : function(data, e){
20246         return true;
20247     },
20248
20249     /**
20250      * An empty function by default, but provided so that you can perform a custom action once the initial
20251      * drag event has begun.  The drag cannot be canceled from this function.
20252      * @param {Number} x The x position of the click on the dragged object
20253      * @param {Number} y The y position of the click on the dragged object
20254      */
20255     onStartDrag : Roo.emptyFn,
20256
20257     // private - YUI override
20258     startDrag : function(x, y){
20259         this.proxy.reset();
20260         this.dragging = true;
20261         this.proxy.update("");
20262         this.onInitDrag(x, y);
20263         this.proxy.show();
20264     },
20265
20266     // private
20267     onInitDrag : function(x, y){
20268         var clone = this.el.dom.cloneNode(true);
20269         clone.id = Roo.id(); // prevent duplicate ids
20270         this.proxy.update(clone);
20271         this.onStartDrag(x, y);
20272         return true;
20273     },
20274
20275     /**
20276      * Returns the drag source's underlying {@link Roo.dd.StatusProxy}
20277      * @return {Roo.dd.StatusProxy} proxy The StatusProxy
20278      */
20279     getProxy : function(){
20280         return this.proxy;  
20281     },
20282
20283     /**
20284      * Hides the drag source's {@link Roo.dd.StatusProxy}
20285      */
20286     hideProxy : function(){
20287         this.proxy.hide();  
20288         this.proxy.reset(true);
20289         this.dragging = false;
20290     },
20291
20292     // private
20293     triggerCacheRefresh : function(){
20294         Roo.dd.DDM.refreshCache(this.groups);
20295     },
20296
20297     // private - override to prevent hiding
20298     b4EndDrag: function(e) {
20299     },
20300
20301     // private - override to prevent moving
20302     endDrag : function(e){
20303         this.onEndDrag(this.dragData, e);
20304     },
20305
20306     // private
20307     onEndDrag : function(data, e){
20308     },
20309     
20310     // private - pin to cursor
20311     autoOffset : function(x, y) {
20312         this.setDelta(-12, -20);
20313     }    
20314 });/*
20315  * Based on:
20316  * Ext JS Library 1.1.1
20317  * Copyright(c) 2006-2007, Ext JS, LLC.
20318  *
20319  * Originally Released Under LGPL - original licence link has changed is not relivant.
20320  *
20321  * Fork - LGPL
20322  * <script type="text/javascript">
20323  */
20324
20325
20326 /**
20327  * @class Roo.dd.DropTarget
20328  * @extends Roo.dd.DDTarget
20329  * A simple class that provides the basic implementation needed to make any element a drop target that can have
20330  * draggable items dropped onto it.  The drop has no effect until an implementation of notifyDrop is provided.
20331  * @constructor
20332  * @param {String/HTMLElement/Element} el The container element
20333  * @param {Object} config
20334  */
20335 Roo.dd.DropTarget = function(el, config){
20336     this.el = Roo.get(el);
20337     
20338     var listeners = false; ;
20339     if (config && config.listeners) {
20340         listeners= config.listeners;
20341         delete config.listeners;
20342     }
20343     Roo.apply(this, config);
20344     
20345     if(this.containerScroll){
20346         Roo.dd.ScrollManager.register(this.el);
20347     }
20348     this.addEvents( {
20349          /**
20350          * @scope Roo.dd.DropTarget
20351          */
20352          
20353          /**
20354          * @event enter
20355          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source is now over the
20356          * target.  This default implementation adds the CSS class specified by overClass (if any) to the drop element
20357          * and returns the dropAllowed config value.  This method should be overridden if drop validation is required.
20358          * 
20359          * IMPORTANT : it should set this.overClass and this.dropAllowed
20360          * 
20361          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
20362          * @param {Event} e The event
20363          * @param {Object} data An object containing arbitrary data supplied by the drag source
20364          */
20365         "enter" : true,
20366         
20367          /**
20368          * @event over
20369          * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the target.
20370          * This method will be called on every mouse movement while the drag source is over the drop target.
20371          * This default implementation simply returns the dropAllowed config value.
20372          * 
20373          * IMPORTANT : it should set this.dropAllowed
20374          * 
20375          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
20376          * @param {Event} e The event
20377          * @param {Object} data An object containing arbitrary data supplied by the drag source
20378          
20379          */
20380         "over" : true,
20381         /**
20382          * @event out
20383          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source has been dragged
20384          * out of the target without dropping.  This default implementation simply removes the CSS class specified by
20385          * overClass (if any) from the drop element.
20386          * 
20387          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
20388          * @param {Event} e The event
20389          * @param {Object} data An object containing arbitrary data supplied by the drag source
20390          */
20391          "out" : true,
20392          
20393         /**
20394          * @event drop
20395          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the dragged item has
20396          * been dropped on it.  This method has no default implementation and returns false, so you must provide an
20397          * implementation that does something to process the drop event and returns true so that the drag source's
20398          * repair action does not run.
20399          * 
20400          * IMPORTANT : it should set this.success
20401          * 
20402          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
20403          * @param {Event} e The event
20404          * @param {Object} data An object containing arbitrary data supplied by the drag source
20405         */
20406          "drop" : true
20407     });
20408             
20409      
20410     Roo.dd.DropTarget.superclass.constructor.call(  this, 
20411         this.el.dom, 
20412         this.ddGroup || this.group,
20413         {
20414             isTarget: true,
20415             listeners : listeners || {} 
20416            
20417         
20418         }
20419     );
20420
20421 };
20422
20423 Roo.extend(Roo.dd.DropTarget, Roo.dd.DDTarget, {
20424     /**
20425      * @cfg {String} overClass
20426      * The CSS class applied to the drop target element while the drag source is over it (defaults to "").
20427      */
20428      /**
20429      * @cfg {String} ddGroup
20430      * The drag drop group to handle drop events for
20431      */
20432      
20433     /**
20434      * @cfg {String} dropAllowed
20435      * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
20436      */
20437     dropAllowed : "x-dd-drop-ok",
20438     /**
20439      * @cfg {String} dropNotAllowed
20440      * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
20441      */
20442     dropNotAllowed : "x-dd-drop-nodrop",
20443     /**
20444      * @cfg {boolean} success
20445      * set this after drop listener.. 
20446      */
20447     success : false,
20448     /**
20449      * @cfg {boolean|String} valid true/false or string (ok-add/ok-sub/ok/nodrop)
20450      * if the drop point is valid for over/enter..
20451      */
20452     valid : false,
20453     // private
20454     isTarget : true,
20455
20456     // private
20457     isNotifyTarget : true,
20458     
20459     /**
20460      * @hide
20461      */
20462     notifyEnter : function(dd, e, data)
20463     {
20464         this.valid = true;
20465         this.fireEvent('enter', dd, e, data);
20466         if(this.overClass){
20467             this.el.addClass(this.overClass);
20468         }
20469         return typeof(this.valid) == 'string' ? 'x-dd-drop-' + this.valid : (
20470             this.valid ? this.dropAllowed : this.dropNotAllowed
20471         );
20472     },
20473
20474     /**
20475      * @hide
20476      */
20477     notifyOver : function(dd, e, data)
20478     {
20479         this.valid = true;
20480         this.fireEvent('over', dd, e, data);
20481         return typeof(this.valid) == 'string' ? 'x-dd-drop-' + this.valid : (
20482             this.valid ? this.dropAllowed : this.dropNotAllowed
20483         );
20484     },
20485
20486     /**
20487      * @hide
20488      */
20489     notifyOut : function(dd, e, data)
20490     {
20491         this.fireEvent('out', dd, e, data);
20492         if(this.overClass){
20493             this.el.removeClass(this.overClass);
20494         }
20495     },
20496
20497     /**
20498      * @hide
20499      */
20500     notifyDrop : function(dd, e, data)
20501     {
20502         this.success = false;
20503         this.fireEvent('drop', dd, e, data);
20504         return this.success;
20505     }
20506 });/*
20507  * Based on:
20508  * Ext JS Library 1.1.1
20509  * Copyright(c) 2006-2007, Ext JS, LLC.
20510  *
20511  * Originally Released Under LGPL - original licence link has changed is not relivant.
20512  *
20513  * Fork - LGPL
20514  * <script type="text/javascript">
20515  */
20516
20517
20518 /**
20519  * @class Roo.dd.DragZone
20520  * @extends Roo.dd.DragSource
20521  * This class provides a container DD instance that proxies for multiple child node sources.<br />
20522  * By default, this class requires that draggable child nodes are registered with {@link Roo.dd.Registry}.
20523  * @constructor
20524  * @param {String/HTMLElement/Element} el The container element
20525  * @param {Object} config
20526  */
20527 Roo.dd.DragZone = function(el, config){
20528     Roo.dd.DragZone.superclass.constructor.call(this, el, config);
20529     if(this.containerScroll){
20530         Roo.dd.ScrollManager.register(this.el);
20531     }
20532 };
20533
20534 Roo.extend(Roo.dd.DragZone, Roo.dd.DragSource, {
20535     /**
20536      * @cfg {Boolean} containerScroll True to register this container with the Scrollmanager
20537      * for auto scrolling during drag operations.
20538      */
20539     /**
20540      * @cfg {String} hlColor The color to use when visually highlighting the drag source in the afterRepair
20541      * method after a failed drop (defaults to "c3daf9" - light blue)
20542      */
20543
20544     /**
20545      * Called when a mousedown occurs in this container. Looks in {@link Roo.dd.Registry}
20546      * for a valid target to drag based on the mouse down. Override this method
20547      * to provide your own lookup logic (e.g. finding a child by class name). Make sure your returned
20548      * object has a "ddel" attribute (with an HTML Element) for other functions to work.
20549      * @param {EventObject} e The mouse down event
20550      * @return {Object} The dragData
20551      */
20552     getDragData : function(e){
20553         return Roo.dd.Registry.getHandleFromEvent(e);
20554     },
20555     
20556     /**
20557      * Called once drag threshold has been reached to initialize the proxy element. By default, it clones the
20558      * this.dragData.ddel
20559      * @param {Number} x The x position of the click on the dragged object
20560      * @param {Number} y The y position of the click on the dragged object
20561      * @return {Boolean} true to continue the drag, false to cancel
20562      */
20563     onInitDrag : function(x, y){
20564         this.proxy.update(this.dragData.ddel.cloneNode(true));
20565         this.onStartDrag(x, y);
20566         return true;
20567     },
20568     
20569     /**
20570      * Called after a repair of an invalid drop. By default, highlights this.dragData.ddel 
20571      */
20572     afterRepair : function(){
20573         if(Roo.enableFx){
20574             Roo.Element.fly(this.dragData.ddel).highlight(this.hlColor || "c3daf9");
20575         }
20576         this.dragging = false;
20577     },
20578
20579     /**
20580      * Called before a repair of an invalid drop to get the XY to animate to. By default returns
20581      * the XY of this.dragData.ddel
20582      * @param {EventObject} e The mouse up event
20583      * @return {Array} The xy location (e.g. [100, 200])
20584      */
20585     getRepairXY : function(e){
20586         return Roo.Element.fly(this.dragData.ddel).getXY();  
20587     }
20588 });/*
20589  * Based on:
20590  * Ext JS Library 1.1.1
20591  * Copyright(c) 2006-2007, Ext JS, LLC.
20592  *
20593  * Originally Released Under LGPL - original licence link has changed is not relivant.
20594  *
20595  * Fork - LGPL
20596  * <script type="text/javascript">
20597  */
20598 /**
20599  * @class Roo.dd.DropZone
20600  * @extends Roo.dd.DropTarget
20601  * This class provides a container DD instance that proxies for multiple child node targets.<br />
20602  * By default, this class requires that child nodes accepting drop are registered with {@link Roo.dd.Registry}.
20603  * @constructor
20604  * @param {String/HTMLElement/Element} el The container element
20605  * @param {Object} config
20606  */
20607 Roo.dd.DropZone = function(el, config){
20608     Roo.dd.DropZone.superclass.constructor.call(this, el, config);
20609 };
20610
20611 Roo.extend(Roo.dd.DropZone, Roo.dd.DropTarget, {
20612     /**
20613      * Returns a custom data object associated with the DOM node that is the target of the event.  By default
20614      * this looks up the event target in the {@link Roo.dd.Registry}, although you can override this method to
20615      * provide your own custom lookup.
20616      * @param {Event} e The event
20617      * @return {Object} data The custom data
20618      */
20619     getTargetFromEvent : function(e){
20620         return Roo.dd.Registry.getTargetFromEvent(e);
20621     },
20622
20623     /**
20624      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has entered a drop node
20625      * that it has registered.  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     onNodeEnter : function(n, dd, e, data){
20634         
20635     },
20636
20637     /**
20638      * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is over a drop node
20639      * that it has registered.  The default implementation returns this.dropNotAllowed, so it should be
20640      * overridden to provide the proper feedback.
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 {String} status The CSS class that communicates the drop status back to the source so that the
20647      * underlying {@link Roo.dd.StatusProxy} can be updated
20648      */
20649     onNodeOver : function(n, dd, e, data){
20650         return this.dropAllowed;
20651     },
20652
20653     /**
20654      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dragged out of
20655      * the drop node without dropping.  This method has no default implementation and should be overridden to provide
20656      * node-specific processing if necessary.
20657      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
20658      * {@link #getTargetFromEvent} for this node)
20659      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
20660      * @param {Event} e The event
20661      * @param {Object} data An object containing arbitrary data supplied by the drag source
20662      */
20663     onNodeOut : function(n, dd, e, data){
20664         
20665     },
20666
20667     /**
20668      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped onto
20669      * the drop node.  The default implementation returns false, so it should be overridden to provide the
20670      * appropriate processing of the drop event and return true so that the drag source's repair action does not run.
20671      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
20672      * {@link #getTargetFromEvent} for this node)
20673      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
20674      * @param {Event} e The event
20675      * @param {Object} data An object containing arbitrary data supplied by the drag source
20676      * @return {Boolean} True if the drop was valid, else false
20677      */
20678     onNodeDrop : function(n, dd, e, data){
20679         return false;
20680     },
20681
20682     /**
20683      * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is being dragged over it,
20684      * but not over any of its registered drop nodes.  The default implementation returns this.dropNotAllowed, so
20685      * it should be overridden to provide the proper feedback if necessary.
20686      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
20687      * @param {Event} e The event
20688      * @param {Object} data An object containing arbitrary data supplied by the drag source
20689      * @return {String} status The CSS class that communicates the drop status back to the source so that the
20690      * underlying {@link Roo.dd.StatusProxy} can be updated
20691      */
20692     onContainerOver : function(dd, e, data){
20693         return this.dropNotAllowed;
20694     },
20695
20696     /**
20697      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped on it,
20698      * but not on any of its registered drop nodes.  The default implementation returns false, so it should be
20699      * overridden to provide the appropriate processing of the drop event if you need the drop zone itself to
20700      * be able to accept drops.  It should return true when valid so that the drag source's repair action does not run.
20701      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
20702      * @param {Event} e The event
20703      * @param {Object} data An object containing arbitrary data supplied by the drag source
20704      * @return {Boolean} True if the drop was valid, else false
20705      */
20706     onContainerDrop : function(dd, e, data){
20707         return false;
20708     },
20709
20710     /**
20711      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source is now over
20712      * the zone.  The default implementation returns this.dropNotAllowed and expects that only registered drop
20713      * nodes can process drag drop operations, so if you need the drop zone itself to be able to process drops
20714      * you should override this method and provide a custom implementation.
20715      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
20716      * @param {Event} e The event
20717      * @param {Object} data An object containing arbitrary data supplied by the drag source
20718      * @return {String} status The CSS class that communicates the drop status back to the source so that the
20719      * underlying {@link Roo.dd.StatusProxy} can be updated
20720      */
20721     notifyEnter : function(dd, e, data){
20722         return this.dropNotAllowed;
20723     },
20724
20725     /**
20726      * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the drop zone.
20727      * This method will be called on every mouse movement while the drag source is over the drop zone.
20728      * It will call {@link #onNodeOver} while the drag source is over a registered node, and will also automatically
20729      * delegate to the appropriate node-specific methods as necessary when the drag source enters and exits
20730      * registered nodes ({@link #onNodeEnter}, {@link #onNodeOut}). If the drag source is not currently over a
20731      * registered node, it will call {@link #onContainerOver}.
20732      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
20733      * @param {Event} e The event
20734      * @param {Object} data An object containing arbitrary data supplied by the drag source
20735      * @return {String} status The CSS class that communicates the drop status back to the source so that the
20736      * underlying {@link Roo.dd.StatusProxy} can be updated
20737      */
20738     notifyOver : function(dd, e, data){
20739         var n = this.getTargetFromEvent(e);
20740         if(!n){ // not over valid drop target
20741             if(this.lastOverNode){
20742                 this.onNodeOut(this.lastOverNode, dd, e, data);
20743                 this.lastOverNode = null;
20744             }
20745             return this.onContainerOver(dd, e, data);
20746         }
20747         if(this.lastOverNode != n){
20748             if(this.lastOverNode){
20749                 this.onNodeOut(this.lastOverNode, dd, e, data);
20750             }
20751             this.onNodeEnter(n, dd, e, data);
20752             this.lastOverNode = n;
20753         }
20754         return this.onNodeOver(n, dd, e, data);
20755     },
20756
20757     /**
20758      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source has been dragged
20759      * out of the zone without dropping.  If the drag source is currently over a registered node, the notification
20760      * will be delegated to {@link #onNodeOut} for node-specific handling, otherwise it will be ignored.
20761      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
20762      * @param {Event} e The event
20763      * @param {Object} data An object containing arbitrary data supplied by the drag zone
20764      */
20765     notifyOut : function(dd, e, data){
20766         if(this.lastOverNode){
20767             this.onNodeOut(this.lastOverNode, dd, e, data);
20768             this.lastOverNode = null;
20769         }
20770     },
20771
20772     /**
20773      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the dragged item has
20774      * been dropped on it.  The drag zone will look up the target node based on the event passed in, and if there
20775      * is a node registered for that event, it will delegate to {@link #onNodeDrop} for node-specific handling,
20776      * otherwise it will call {@link #onContainerDrop}.
20777      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
20778      * @param {Event} e The event
20779      * @param {Object} data An object containing arbitrary data supplied by the drag source
20780      * @return {Boolean} True if the drop was valid, else false
20781      */
20782     notifyDrop : function(dd, e, data){
20783         if(this.lastOverNode){
20784             this.onNodeOut(this.lastOverNode, dd, e, data);
20785             this.lastOverNode = null;
20786         }
20787         var n = this.getTargetFromEvent(e);
20788         return n ?
20789             this.onNodeDrop(n, dd, e, data) :
20790             this.onContainerDrop(dd, e, data);
20791     },
20792
20793     // private
20794     triggerCacheRefresh : function(){
20795         Roo.dd.DDM.refreshCache(this.groups);
20796     }  
20797 });/*
20798  * Based on:
20799  * Ext JS Library 1.1.1
20800  * Copyright(c) 2006-2007, Ext JS, LLC.
20801  *
20802  * Originally Released Under LGPL - original licence link has changed is not relivant.
20803  *
20804  * Fork - LGPL
20805  * <script type="text/javascript">
20806  */
20807
20808
20809 /**
20810  * @class Roo.data.SortTypes
20811  * @singleton
20812  * Defines the default sorting (casting?) comparison functions used when sorting data.
20813  */
20814 Roo.data.SortTypes = {
20815     /**
20816      * Default sort that does nothing
20817      * @param {Mixed} s The value being converted
20818      * @return {Mixed} The comparison value
20819      */
20820     none : function(s){
20821         return s;
20822     },
20823     
20824     /**
20825      * The regular expression used to strip tags
20826      * @type {RegExp}
20827      * @property
20828      */
20829     stripTagsRE : /<\/?[^>]+>/gi,
20830     
20831     /**
20832      * Strips all HTML tags to sort on text only
20833      * @param {Mixed} s The value being converted
20834      * @return {String} The comparison value
20835      */
20836     asText : function(s){
20837         return String(s).replace(this.stripTagsRE, "");
20838     },
20839     
20840     /**
20841      * Strips all HTML tags to sort on text only - Case insensitive
20842      * @param {Mixed} s The value being converted
20843      * @return {String} The comparison value
20844      */
20845     asUCText : function(s){
20846         return String(s).toUpperCase().replace(this.stripTagsRE, "");
20847     },
20848     
20849     /**
20850      * Case insensitive string
20851      * @param {Mixed} s The value being converted
20852      * @return {String} The comparison value
20853      */
20854     asUCString : function(s) {
20855         return String(s).toUpperCase();
20856     },
20857     
20858     /**
20859      * Date sorting
20860      * @param {Mixed} s The value being converted
20861      * @return {Number} The comparison value
20862      */
20863     asDate : function(s) {
20864         if(!s){
20865             return 0;
20866         }
20867         if(s instanceof Date){
20868             return s.getTime();
20869         }
20870         return Date.parse(String(s));
20871     },
20872     
20873     /**
20874      * Float sorting
20875      * @param {Mixed} s The value being converted
20876      * @return {Float} The comparison value
20877      */
20878     asFloat : function(s) {
20879         var val = parseFloat(String(s).replace(/,/g, ""));
20880         if(isNaN(val)) val = 0;
20881         return val;
20882     },
20883     
20884     /**
20885      * Integer sorting
20886      * @param {Mixed} s The value being converted
20887      * @return {Number} The comparison value
20888      */
20889     asInt : function(s) {
20890         var val = parseInt(String(s).replace(/,/g, ""));
20891         if(isNaN(val)) val = 0;
20892         return val;
20893     }
20894 };/*
20895  * Based on:
20896  * Ext JS Library 1.1.1
20897  * Copyright(c) 2006-2007, Ext JS, LLC.
20898  *
20899  * Originally Released Under LGPL - original licence link has changed is not relivant.
20900  *
20901  * Fork - LGPL
20902  * <script type="text/javascript">
20903  */
20904
20905 /**
20906 * @class Roo.data.Record
20907  * Instances of this class encapsulate both record <em>definition</em> information, and record
20908  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
20909  * to access Records cached in an {@link Roo.data.Store} object.<br>
20910  * <p>
20911  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
20912  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
20913  * objects.<br>
20914  * <p>
20915  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
20916  * @constructor
20917  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
20918  * {@link #create}. The parameters are the same.
20919  * @param {Array} data An associative Array of data values keyed by the field name.
20920  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
20921  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
20922  * not specified an integer id is generated.
20923  */
20924 Roo.data.Record = function(data, id){
20925     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
20926     this.data = data;
20927 };
20928
20929 /**
20930  * Generate a constructor for a specific record layout.
20931  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
20932  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
20933  * Each field definition object may contain the following properties: <ul>
20934  * <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,
20935  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
20936  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
20937  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
20938  * is being used, then this is a string containing the javascript expression to reference the data relative to 
20939  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
20940  * to the data item relative to the record element. If the mapping expression is the same as the field name,
20941  * this may be omitted.</p></li>
20942  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
20943  * <ul><li>auto (Default, implies no conversion)</li>
20944  * <li>string</li>
20945  * <li>int</li>
20946  * <li>float</li>
20947  * <li>boolean</li>
20948  * <li>date</li></ul></p></li>
20949  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
20950  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
20951  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
20952  * by the Reader into an object that will be stored in the Record. It is passed the
20953  * following parameters:<ul>
20954  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
20955  * </ul></p></li>
20956  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
20957  * </ul>
20958  * <br>usage:<br><pre><code>
20959 var TopicRecord = Roo.data.Record.create(
20960     {name: 'title', mapping: 'topic_title'},
20961     {name: 'author', mapping: 'username'},
20962     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
20963     {name: 'lastPost', mapping: 'post_time', type: 'date'},
20964     {name: 'lastPoster', mapping: 'user2'},
20965     {name: 'excerpt', mapping: 'post_text'}
20966 );
20967
20968 var myNewRecord = new TopicRecord({
20969     title: 'Do my job please',
20970     author: 'noobie',
20971     totalPosts: 1,
20972     lastPost: new Date(),
20973     lastPoster: 'Animal',
20974     excerpt: 'No way dude!'
20975 });
20976 myStore.add(myNewRecord);
20977 </code></pre>
20978  * @method create
20979  * @static
20980  */
20981 Roo.data.Record.create = function(o){
20982     var f = function(){
20983         f.superclass.constructor.apply(this, arguments);
20984     };
20985     Roo.extend(f, Roo.data.Record);
20986     var p = f.prototype;
20987     p.fields = new Roo.util.MixedCollection(false, function(field){
20988         return field.name;
20989     });
20990     for(var i = 0, len = o.length; i < len; i++){
20991         p.fields.add(new Roo.data.Field(o[i]));
20992     }
20993     f.getField = function(name){
20994         return p.fields.get(name);  
20995     };
20996     return f;
20997 };
20998
20999 Roo.data.Record.AUTO_ID = 1000;
21000 Roo.data.Record.EDIT = 'edit';
21001 Roo.data.Record.REJECT = 'reject';
21002 Roo.data.Record.COMMIT = 'commit';
21003
21004 Roo.data.Record.prototype = {
21005     /**
21006      * Readonly flag - true if this record has been modified.
21007      * @type Boolean
21008      */
21009     dirty : false,
21010     editing : false,
21011     error: null,
21012     modified: null,
21013
21014     // private
21015     join : function(store){
21016         this.store = store;
21017     },
21018
21019     /**
21020      * Set the named field to the specified value.
21021      * @param {String} name The name of the field to set.
21022      * @param {Object} value The value to set the field to.
21023      */
21024     set : function(name, value){
21025         if(this.data[name] == value){
21026             return;
21027         }
21028         this.dirty = true;
21029         if(!this.modified){
21030             this.modified = {};
21031         }
21032         if(typeof this.modified[name] == 'undefined'){
21033             this.modified[name] = this.data[name];
21034         }
21035         this.data[name] = value;
21036         if(!this.editing && this.store){
21037             this.store.afterEdit(this);
21038         }       
21039     },
21040
21041     /**
21042      * Get the value of the named field.
21043      * @param {String} name The name of the field to get the value of.
21044      * @return {Object} The value of the field.
21045      */
21046     get : function(name){
21047         return this.data[name]; 
21048     },
21049
21050     // private
21051     beginEdit : function(){
21052         this.editing = true;
21053         this.modified = {}; 
21054     },
21055
21056     // private
21057     cancelEdit : function(){
21058         this.editing = false;
21059         delete this.modified;
21060     },
21061
21062     // private
21063     endEdit : function(){
21064         this.editing = false;
21065         if(this.dirty && this.store){
21066             this.store.afterEdit(this);
21067         }
21068     },
21069
21070     /**
21071      * Usually called by the {@link Roo.data.Store} which owns the Record.
21072      * Rejects all changes made to the Record since either creation, or the last commit operation.
21073      * Modified fields are reverted to their original values.
21074      * <p>
21075      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
21076      * of reject operations.
21077      */
21078     reject : function(){
21079         var m = this.modified;
21080         for(var n in m){
21081             if(typeof m[n] != "function"){
21082                 this.data[n] = m[n];
21083             }
21084         }
21085         this.dirty = false;
21086         delete this.modified;
21087         this.editing = false;
21088         if(this.store){
21089             this.store.afterReject(this);
21090         }
21091     },
21092
21093     /**
21094      * Usually called by the {@link Roo.data.Store} which owns the Record.
21095      * Commits all changes made to the Record since either creation, or the last commit operation.
21096      * <p>
21097      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
21098      * of commit operations.
21099      */
21100     commit : function(){
21101         this.dirty = false;
21102         delete this.modified;
21103         this.editing = false;
21104         if(this.store){
21105             this.store.afterCommit(this);
21106         }
21107     },
21108
21109     // private
21110     hasError : function(){
21111         return this.error != null;
21112     },
21113
21114     // private
21115     clearError : function(){
21116         this.error = null;
21117     },
21118
21119     /**
21120      * Creates a copy of this record.
21121      * @param {String} id (optional) A new record id if you don't want to use this record's id
21122      * @return {Record}
21123      */
21124     copy : function(newId) {
21125         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
21126     }
21127 };/*
21128  * Based on:
21129  * Ext JS Library 1.1.1
21130  * Copyright(c) 2006-2007, Ext JS, LLC.
21131  *
21132  * Originally Released Under LGPL - original licence link has changed is not relivant.
21133  *
21134  * Fork - LGPL
21135  * <script type="text/javascript">
21136  */
21137
21138
21139
21140 /**
21141  * @class Roo.data.Store
21142  * @extends Roo.util.Observable
21143  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
21144  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
21145  * <p>
21146  * 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
21147  * has no knowledge of the format of the data returned by the Proxy.<br>
21148  * <p>
21149  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
21150  * instances from the data object. These records are cached and made available through accessor functions.
21151  * @constructor
21152  * Creates a new Store.
21153  * @param {Object} config A config object containing the objects needed for the Store to access data,
21154  * and read the data into Records.
21155  */
21156 Roo.data.Store = function(config){
21157     this.data = new Roo.util.MixedCollection(false);
21158     this.data.getKey = function(o){
21159         return o.id;
21160     };
21161     this.baseParams = {};
21162     // private
21163     this.paramNames = {
21164         "start" : "start",
21165         "limit" : "limit",
21166         "sort" : "sort",
21167         "dir" : "dir",
21168         "multisort" : "_multisort"
21169     };
21170
21171     if(config && config.data){
21172         this.inlineData = config.data;
21173         delete config.data;
21174     }
21175
21176     Roo.apply(this, config);
21177     
21178     if(this.reader){ // reader passed
21179         this.reader = Roo.factory(this.reader, Roo.data);
21180         this.reader.xmodule = this.xmodule || false;
21181         if(!this.recordType){
21182             this.recordType = this.reader.recordType;
21183         }
21184         if(this.reader.onMetaChange){
21185             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
21186         }
21187     }
21188
21189     if(this.recordType){
21190         this.fields = this.recordType.prototype.fields;
21191     }
21192     this.modified = [];
21193
21194     this.addEvents({
21195         /**
21196          * @event datachanged
21197          * Fires when the data cache has changed, and a widget which is using this Store
21198          * as a Record cache should refresh its view.
21199          * @param {Store} this
21200          */
21201         datachanged : true,
21202         /**
21203          * @event metachange
21204          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
21205          * @param {Store} this
21206          * @param {Object} meta The JSON metadata
21207          */
21208         metachange : true,
21209         /**
21210          * @event add
21211          * Fires when Records have been added to the Store
21212          * @param {Store} this
21213          * @param {Roo.data.Record[]} records The array of Records added
21214          * @param {Number} index The index at which the record(s) were added
21215          */
21216         add : true,
21217         /**
21218          * @event remove
21219          * Fires when a Record has been removed from the Store
21220          * @param {Store} this
21221          * @param {Roo.data.Record} record The Record that was removed
21222          * @param {Number} index The index at which the record was removed
21223          */
21224         remove : true,
21225         /**
21226          * @event update
21227          * Fires when a Record has been updated
21228          * @param {Store} this
21229          * @param {Roo.data.Record} record The Record that was updated
21230          * @param {String} operation The update operation being performed.  Value may be one of:
21231          * <pre><code>
21232  Roo.data.Record.EDIT
21233  Roo.data.Record.REJECT
21234  Roo.data.Record.COMMIT
21235          * </code></pre>
21236          */
21237         update : true,
21238         /**
21239          * @event clear
21240          * Fires when the data cache has been cleared.
21241          * @param {Store} this
21242          */
21243         clear : true,
21244         /**
21245          * @event beforeload
21246          * Fires before a request is made for a new data object.  If the beforeload handler returns false
21247          * the load action will be canceled.
21248          * @param {Store} this
21249          * @param {Object} options The loading options that were specified (see {@link #load} for details)
21250          */
21251         beforeload : true,
21252         /**
21253          * @event beforeloadadd
21254          * Fires after a new set of Records has been loaded.
21255          * @param {Store} this
21256          * @param {Roo.data.Record[]} records The Records that were loaded
21257          * @param {Object} options The loading options that were specified (see {@link #load} for details)
21258          */
21259         beforeloadadd : true,
21260         /**
21261          * @event load
21262          * Fires after a new set of Records has been loaded, before they are added to the store.
21263          * @param {Store} this
21264          * @param {Roo.data.Record[]} records The Records that were loaded
21265          * @param {Object} options The loading options that were specified (see {@link #load} for details)
21266          * @params {Object} return from reader
21267          */
21268         load : true,
21269         /**
21270          * @event loadexception
21271          * Fires if an exception occurs in the Proxy during loading.
21272          * Called with the signature of the Proxy's "loadexception" event.
21273          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
21274          * 
21275          * @param {Proxy} 
21276          * @param {Object} return from JsonData.reader() - success, totalRecords, records
21277          * @param {Object} load options 
21278          * @param {Object} jsonData from your request (normally this contains the Exception)
21279          */
21280         loadexception : true
21281     });
21282     
21283     if(this.proxy){
21284         this.proxy = Roo.factory(this.proxy, Roo.data);
21285         this.proxy.xmodule = this.xmodule || false;
21286         this.relayEvents(this.proxy,  ["loadexception"]);
21287     }
21288     this.sortToggle = {};
21289     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
21290
21291     Roo.data.Store.superclass.constructor.call(this);
21292
21293     if(this.inlineData){
21294         this.loadData(this.inlineData);
21295         delete this.inlineData;
21296     }
21297 };
21298
21299 Roo.extend(Roo.data.Store, Roo.util.Observable, {
21300      /**
21301     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
21302     * without a remote query - used by combo/forms at present.
21303     */
21304     
21305     /**
21306     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
21307     */
21308     /**
21309     * @cfg {Array} data Inline data to be loaded when the store is initialized.
21310     */
21311     /**
21312     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
21313     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
21314     */
21315     /**
21316     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
21317     * on any HTTP request
21318     */
21319     /**
21320     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
21321     */
21322     /**
21323     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
21324     */
21325     multiSort: false,
21326     /**
21327     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
21328     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
21329     */
21330     remoteSort : false,
21331
21332     /**
21333     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
21334      * loaded or when a record is removed. (defaults to false).
21335     */
21336     pruneModifiedRecords : false,
21337
21338     // private
21339     lastOptions : null,
21340
21341     /**
21342      * Add Records to the Store and fires the add event.
21343      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
21344      */
21345     add : function(records){
21346         records = [].concat(records);
21347         for(var i = 0, len = records.length; i < len; i++){
21348             records[i].join(this);
21349         }
21350         var index = this.data.length;
21351         this.data.addAll(records);
21352         this.fireEvent("add", this, records, index);
21353     },
21354
21355     /**
21356      * Remove a Record from the Store and fires the remove event.
21357      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
21358      */
21359     remove : function(record){
21360         var index = this.data.indexOf(record);
21361         this.data.removeAt(index);
21362         if(this.pruneModifiedRecords){
21363             this.modified.remove(record);
21364         }
21365         this.fireEvent("remove", this, record, index);
21366     },
21367
21368     /**
21369      * Remove all Records from the Store and fires the clear event.
21370      */
21371     removeAll : function(){
21372         this.data.clear();
21373         if(this.pruneModifiedRecords){
21374             this.modified = [];
21375         }
21376         this.fireEvent("clear", this);
21377     },
21378
21379     /**
21380      * Inserts Records to the Store at the given index and fires the add event.
21381      * @param {Number} index The start index at which to insert the passed Records.
21382      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
21383      */
21384     insert : function(index, records){
21385         records = [].concat(records);
21386         for(var i = 0, len = records.length; i < len; i++){
21387             this.data.insert(index, records[i]);
21388             records[i].join(this);
21389         }
21390         this.fireEvent("add", this, records, index);
21391     },
21392
21393     /**
21394      * Get the index within the cache of the passed Record.
21395      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
21396      * @return {Number} The index of the passed Record. Returns -1 if not found.
21397      */
21398     indexOf : function(record){
21399         return this.data.indexOf(record);
21400     },
21401
21402     /**
21403      * Get the index within the cache of the Record with the passed id.
21404      * @param {String} id The id of the Record to find.
21405      * @return {Number} The index of the Record. Returns -1 if not found.
21406      */
21407     indexOfId : function(id){
21408         return this.data.indexOfKey(id);
21409     },
21410
21411     /**
21412      * Get the Record with the specified id.
21413      * @param {String} id The id of the Record to find.
21414      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
21415      */
21416     getById : function(id){
21417         return this.data.key(id);
21418     },
21419
21420     /**
21421      * Get the Record at the specified index.
21422      * @param {Number} index The index of the Record to find.
21423      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
21424      */
21425     getAt : function(index){
21426         return this.data.itemAt(index);
21427     },
21428
21429     /**
21430      * Returns a range of Records between specified indices.
21431      * @param {Number} startIndex (optional) The starting index (defaults to 0)
21432      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
21433      * @return {Roo.data.Record[]} An array of Records
21434      */
21435     getRange : function(start, end){
21436         return this.data.getRange(start, end);
21437     },
21438
21439     // private
21440     storeOptions : function(o){
21441         o = Roo.apply({}, o);
21442         delete o.callback;
21443         delete o.scope;
21444         this.lastOptions = o;
21445     },
21446
21447     /**
21448      * Loads the Record cache from the configured Proxy using the configured Reader.
21449      * <p>
21450      * If using remote paging, then the first load call must specify the <em>start</em>
21451      * and <em>limit</em> properties in the options.params property to establish the initial
21452      * position within the dataset, and the number of Records to cache on each read from the Proxy.
21453      * <p>
21454      * <strong>It is important to note that for remote data sources, loading is asynchronous,
21455      * and this call will return before the new data has been loaded. Perform any post-processing
21456      * in a callback function, or in a "load" event handler.</strong>
21457      * <p>
21458      * @param {Object} options An object containing properties which control loading options:<ul>
21459      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
21460      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
21461      * passed the following arguments:<ul>
21462      * <li>r : Roo.data.Record[]</li>
21463      * <li>options: Options object from the load call</li>
21464      * <li>success: Boolean success indicator</li></ul></li>
21465      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
21466      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
21467      * </ul>
21468      */
21469     load : function(options){
21470         options = options || {};
21471         if(this.fireEvent("beforeload", this, options) !== false){
21472             this.storeOptions(options);
21473             var p = Roo.apply(options.params || {}, this.baseParams);
21474             // if meta was not loaded from remote source.. try requesting it.
21475             if (!this.reader.metaFromRemote) {
21476                 p._requestMeta = 1;
21477             }
21478             if(this.sortInfo && this.remoteSort){
21479                 var pn = this.paramNames;
21480                 p[pn["sort"]] = this.sortInfo.field;
21481                 p[pn["dir"]] = this.sortInfo.direction;
21482             }
21483             if (this.multiSort) {
21484                 var pn = this.paramNames;
21485                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
21486             }
21487             
21488             this.proxy.load(p, this.reader, this.loadRecords, this, options);
21489         }
21490     },
21491
21492     /**
21493      * Reloads the Record cache from the configured Proxy using the configured Reader and
21494      * the options from the last load operation performed.
21495      * @param {Object} options (optional) An object containing properties which may override the options
21496      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
21497      * the most recently used options are reused).
21498      */
21499     reload : function(options){
21500         this.load(Roo.applyIf(options||{}, this.lastOptions));
21501     },
21502
21503     // private
21504     // Called as a callback by the Reader during a load operation.
21505     loadRecords : function(o, options, success){
21506         if(!o || success === false){
21507             if(success !== false){
21508                 this.fireEvent("load", this, [], options, o);
21509             }
21510             if(options.callback){
21511                 options.callback.call(options.scope || this, [], options, false);
21512             }
21513             return;
21514         }
21515         // if data returned failure - throw an exception.
21516         if (o.success === false) {
21517             // show a message if no listener is registered.
21518             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
21519                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
21520             }
21521             // loadmask wil be hooked into this..
21522             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
21523             return;
21524         }
21525         var r = o.records, t = o.totalRecords || r.length;
21526         
21527         this.fireEvent("beforeloadadd", this, r, options, o);
21528         
21529         if(!options || options.add !== true){
21530             if(this.pruneModifiedRecords){
21531                 this.modified = [];
21532             }
21533             for(var i = 0, len = r.length; i < len; i++){
21534                 r[i].join(this);
21535             }
21536             if(this.snapshot){
21537                 this.data = this.snapshot;
21538                 delete this.snapshot;
21539             }
21540             this.data.clear();
21541             this.data.addAll(r);
21542             this.totalLength = t;
21543             this.applySort();
21544             this.fireEvent("datachanged", this);
21545         }else{
21546             this.totalLength = Math.max(t, this.data.length+r.length);
21547             this.add(r);
21548         }
21549         this.fireEvent("load", this, r, options, o);
21550         if(options.callback){
21551             options.callback.call(options.scope || this, r, options, true);
21552         }
21553     },
21554
21555
21556     /**
21557      * Loads data from a passed data block. A Reader which understands the format of the data
21558      * must have been configured in the constructor.
21559      * @param {Object} data The data block from which to read the Records.  The format of the data expected
21560      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
21561      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
21562      */
21563     loadData : function(o, append){
21564         var r = this.reader.readRecords(o);
21565         this.loadRecords(r, {add: append}, true);
21566     },
21567
21568     /**
21569      * Gets the number of cached records.
21570      * <p>
21571      * <em>If using paging, this may not be the total size of the dataset. If the data object
21572      * used by the Reader contains the dataset size, then the getTotalCount() function returns
21573      * the data set size</em>
21574      */
21575     getCount : function(){
21576         return this.data.length || 0;
21577     },
21578
21579     /**
21580      * Gets the total number of records in the dataset as returned by the server.
21581      * <p>
21582      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
21583      * the dataset size</em>
21584      */
21585     getTotalCount : function(){
21586         return this.totalLength || 0;
21587     },
21588
21589     /**
21590      * Returns the sort state of the Store as an object with two properties:
21591      * <pre><code>
21592  field {String} The name of the field by which the Records are sorted
21593  direction {String} The sort order, "ASC" or "DESC"
21594      * </code></pre>
21595      */
21596     getSortState : function(){
21597         return this.sortInfo;
21598     },
21599
21600     // private
21601     applySort : function(){
21602         if(this.sortInfo && !this.remoteSort){
21603             var s = this.sortInfo, f = s.field;
21604             var st = this.fields.get(f).sortType;
21605             var fn = function(r1, r2){
21606                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
21607                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
21608             };
21609             this.data.sort(s.direction, fn);
21610             if(this.snapshot && this.snapshot != this.data){
21611                 this.snapshot.sort(s.direction, fn);
21612             }
21613         }
21614     },
21615
21616     /**
21617      * Sets the default sort column and order to be used by the next load operation.
21618      * @param {String} fieldName The name of the field to sort by.
21619      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
21620      */
21621     setDefaultSort : function(field, dir){
21622         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
21623     },
21624
21625     /**
21626      * Sort the Records.
21627      * If remote sorting is used, the sort is performed on the server, and the cache is
21628      * reloaded. If local sorting is used, the cache is sorted internally.
21629      * @param {String} fieldName The name of the field to sort by.
21630      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
21631      */
21632     sort : function(fieldName, dir){
21633         var f = this.fields.get(fieldName);
21634         if(!dir){
21635             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
21636             
21637             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
21638                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
21639             }else{
21640                 dir = f.sortDir;
21641             }
21642         }
21643         this.sortToggle[f.name] = dir;
21644         this.sortInfo = {field: f.name, direction: dir};
21645         if(!this.remoteSort){
21646             this.applySort();
21647             this.fireEvent("datachanged", this);
21648         }else{
21649             this.load(this.lastOptions);
21650         }
21651     },
21652
21653     /**
21654      * Calls the specified function for each of the Records in the cache.
21655      * @param {Function} fn The function to call. The Record is passed as the first parameter.
21656      * Returning <em>false</em> aborts and exits the iteration.
21657      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
21658      */
21659     each : function(fn, scope){
21660         this.data.each(fn, scope);
21661     },
21662
21663     /**
21664      * Gets all records modified since the last commit.  Modified records are persisted across load operations
21665      * (e.g., during paging).
21666      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
21667      */
21668     getModifiedRecords : function(){
21669         return this.modified;
21670     },
21671
21672     // private
21673     createFilterFn : function(property, value, anyMatch){
21674         if(!value.exec){ // not a regex
21675             value = String(value);
21676             if(value.length == 0){
21677                 return false;
21678             }
21679             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
21680         }
21681         return function(r){
21682             return value.test(r.data[property]);
21683         };
21684     },
21685
21686     /**
21687      * Sums the value of <i>property</i> for each record between start and end and returns the result.
21688      * @param {String} property A field on your records
21689      * @param {Number} start The record index to start at (defaults to 0)
21690      * @param {Number} end The last record index to include (defaults to length - 1)
21691      * @return {Number} The sum
21692      */
21693     sum : function(property, start, end){
21694         var rs = this.data.items, v = 0;
21695         start = start || 0;
21696         end = (end || end === 0) ? end : rs.length-1;
21697
21698         for(var i = start; i <= end; i++){
21699             v += (rs[i].data[property] || 0);
21700         }
21701         return v;
21702     },
21703
21704     /**
21705      * Filter the records by a specified property.
21706      * @param {String} field A field on your records
21707      * @param {String/RegExp} value Either a string that the field
21708      * should start with or a RegExp to test against the field
21709      * @param {Boolean} anyMatch True to match any part not just the beginning
21710      */
21711     filter : function(property, value, anyMatch){
21712         var fn = this.createFilterFn(property, value, anyMatch);
21713         return fn ? this.filterBy(fn) : this.clearFilter();
21714     },
21715
21716     /**
21717      * Filter by a function. The specified function will be called with each
21718      * record in this data source. If the function returns true the record is included,
21719      * otherwise it is filtered.
21720      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
21721      * @param {Object} scope (optional) The scope of the function (defaults to this)
21722      */
21723     filterBy : function(fn, scope){
21724         this.snapshot = this.snapshot || this.data;
21725         this.data = this.queryBy(fn, scope||this);
21726         this.fireEvent("datachanged", this);
21727     },
21728
21729     /**
21730      * Query the records by a specified property.
21731      * @param {String} field A field on your records
21732      * @param {String/RegExp} value Either a string that the field
21733      * should start with or a RegExp to test against the field
21734      * @param {Boolean} anyMatch True to match any part not just the beginning
21735      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
21736      */
21737     query : function(property, value, anyMatch){
21738         var fn = this.createFilterFn(property, value, anyMatch);
21739         return fn ? this.queryBy(fn) : this.data.clone();
21740     },
21741
21742     /**
21743      * Query by a function. The specified function will be called with each
21744      * record in this data source. If the function returns true the record is included
21745      * in the results.
21746      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
21747      * @param {Object} scope (optional) The scope of the function (defaults to this)
21748       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
21749      **/
21750     queryBy : function(fn, scope){
21751         var data = this.snapshot || this.data;
21752         return data.filterBy(fn, scope||this);
21753     },
21754
21755     /**
21756      * Collects unique values for a particular dataIndex from this store.
21757      * @param {String} dataIndex The property to collect
21758      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
21759      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
21760      * @return {Array} An array of the unique values
21761      **/
21762     collect : function(dataIndex, allowNull, bypassFilter){
21763         var d = (bypassFilter === true && this.snapshot) ?
21764                 this.snapshot.items : this.data.items;
21765         var v, sv, r = [], l = {};
21766         for(var i = 0, len = d.length; i < len; i++){
21767             v = d[i].data[dataIndex];
21768             sv = String(v);
21769             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
21770                 l[sv] = true;
21771                 r[r.length] = v;
21772             }
21773         }
21774         return r;
21775     },
21776
21777     /**
21778      * Revert to a view of the Record cache with no filtering applied.
21779      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
21780      */
21781     clearFilter : function(suppressEvent){
21782         if(this.snapshot && this.snapshot != this.data){
21783             this.data = this.snapshot;
21784             delete this.snapshot;
21785             if(suppressEvent !== true){
21786                 this.fireEvent("datachanged", this);
21787             }
21788         }
21789     },
21790
21791     // private
21792     afterEdit : function(record){
21793         if(this.modified.indexOf(record) == -1){
21794             this.modified.push(record);
21795         }
21796         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
21797     },
21798     
21799     // private
21800     afterReject : function(record){
21801         this.modified.remove(record);
21802         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
21803     },
21804
21805     // private
21806     afterCommit : function(record){
21807         this.modified.remove(record);
21808         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
21809     },
21810
21811     /**
21812      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
21813      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
21814      */
21815     commitChanges : function(){
21816         var m = this.modified.slice(0);
21817         this.modified = [];
21818         for(var i = 0, len = m.length; i < len; i++){
21819             m[i].commit();
21820         }
21821     },
21822
21823     /**
21824      * Cancel outstanding changes on all changed records.
21825      */
21826     rejectChanges : function(){
21827         var m = this.modified.slice(0);
21828         this.modified = [];
21829         for(var i = 0, len = m.length; i < len; i++){
21830             m[i].reject();
21831         }
21832     },
21833
21834     onMetaChange : function(meta, rtype, o){
21835         this.recordType = rtype;
21836         this.fields = rtype.prototype.fields;
21837         delete this.snapshot;
21838         this.sortInfo = meta.sortInfo || this.sortInfo;
21839         this.modified = [];
21840         this.fireEvent('metachange', this, this.reader.meta);
21841     },
21842     
21843     moveIndex : function(data, type)
21844     {
21845         var index = this.indexOf(data);
21846         
21847         var newIndex = index + type;
21848         
21849         this.remove(data);
21850         
21851         this.insert(newIndex, data);
21852         
21853     }
21854 });/*
21855  * Based on:
21856  * Ext JS Library 1.1.1
21857  * Copyright(c) 2006-2007, Ext JS, LLC.
21858  *
21859  * Originally Released Under LGPL - original licence link has changed is not relivant.
21860  *
21861  * Fork - LGPL
21862  * <script type="text/javascript">
21863  */
21864
21865 /**
21866  * @class Roo.data.SimpleStore
21867  * @extends Roo.data.Store
21868  * Small helper class to make creating Stores from Array data easier.
21869  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
21870  * @cfg {Array} fields An array of field definition objects, or field name strings.
21871  * @cfg {Array} data The multi-dimensional array of data
21872  * @constructor
21873  * @param {Object} config
21874  */
21875 Roo.data.SimpleStore = function(config){
21876     Roo.data.SimpleStore.superclass.constructor.call(this, {
21877         isLocal : true,
21878         reader: new Roo.data.ArrayReader({
21879                 id: config.id
21880             },
21881             Roo.data.Record.create(config.fields)
21882         ),
21883         proxy : new Roo.data.MemoryProxy(config.data)
21884     });
21885     this.load();
21886 };
21887 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
21888  * Based on:
21889  * Ext JS Library 1.1.1
21890  * Copyright(c) 2006-2007, Ext JS, LLC.
21891  *
21892  * Originally Released Under LGPL - original licence link has changed is not relivant.
21893  *
21894  * Fork - LGPL
21895  * <script type="text/javascript">
21896  */
21897
21898 /**
21899 /**
21900  * @extends Roo.data.Store
21901  * @class Roo.data.JsonStore
21902  * Small helper class to make creating Stores for JSON data easier. <br/>
21903 <pre><code>
21904 var store = new Roo.data.JsonStore({
21905     url: 'get-images.php',
21906     root: 'images',
21907     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
21908 });
21909 </code></pre>
21910  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
21911  * JsonReader and HttpProxy (unless inline data is provided).</b>
21912  * @cfg {Array} fields An array of field definition objects, or field name strings.
21913  * @constructor
21914  * @param {Object} config
21915  */
21916 Roo.data.JsonStore = function(c){
21917     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
21918         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
21919         reader: new Roo.data.JsonReader(c, c.fields)
21920     }));
21921 };
21922 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
21923  * Based on:
21924  * Ext JS Library 1.1.1
21925  * Copyright(c) 2006-2007, Ext JS, LLC.
21926  *
21927  * Originally Released Under LGPL - original licence link has changed is not relivant.
21928  *
21929  * Fork - LGPL
21930  * <script type="text/javascript">
21931  */
21932
21933  
21934 Roo.data.Field = function(config){
21935     if(typeof config == "string"){
21936         config = {name: config};
21937     }
21938     Roo.apply(this, config);
21939     
21940     if(!this.type){
21941         this.type = "auto";
21942     }
21943     
21944     var st = Roo.data.SortTypes;
21945     // named sortTypes are supported, here we look them up
21946     if(typeof this.sortType == "string"){
21947         this.sortType = st[this.sortType];
21948     }
21949     
21950     // set default sortType for strings and dates
21951     if(!this.sortType){
21952         switch(this.type){
21953             case "string":
21954                 this.sortType = st.asUCString;
21955                 break;
21956             case "date":
21957                 this.sortType = st.asDate;
21958                 break;
21959             default:
21960                 this.sortType = st.none;
21961         }
21962     }
21963
21964     // define once
21965     var stripRe = /[\$,%]/g;
21966
21967     // prebuilt conversion function for this field, instead of
21968     // switching every time we're reading a value
21969     if(!this.convert){
21970         var cv, dateFormat = this.dateFormat;
21971         switch(this.type){
21972             case "":
21973             case "auto":
21974             case undefined:
21975                 cv = function(v){ return v; };
21976                 break;
21977             case "string":
21978                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
21979                 break;
21980             case "int":
21981                 cv = function(v){
21982                     return v !== undefined && v !== null && v !== '' ?
21983                            parseInt(String(v).replace(stripRe, ""), 10) : '';
21984                     };
21985                 break;
21986             case "float":
21987                 cv = function(v){
21988                     return v !== undefined && v !== null && v !== '' ?
21989                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
21990                     };
21991                 break;
21992             case "bool":
21993             case "boolean":
21994                 cv = function(v){ return v === true || v === "true" || v == 1; };
21995                 break;
21996             case "date":
21997                 cv = function(v){
21998                     if(!v){
21999                         return '';
22000                     }
22001                     if(v instanceof Date){
22002                         return v;
22003                     }
22004                     if(dateFormat){
22005                         if(dateFormat == "timestamp"){
22006                             return new Date(v*1000);
22007                         }
22008                         return Date.parseDate(v, dateFormat);
22009                     }
22010                     var parsed = Date.parse(v);
22011                     return parsed ? new Date(parsed) : null;
22012                 };
22013              break;
22014             
22015         }
22016         this.convert = cv;
22017     }
22018 };
22019
22020 Roo.data.Field.prototype = {
22021     dateFormat: null,
22022     defaultValue: "",
22023     mapping: null,
22024     sortType : null,
22025     sortDir : "ASC"
22026 };/*
22027  * Based on:
22028  * Ext JS Library 1.1.1
22029  * Copyright(c) 2006-2007, Ext JS, LLC.
22030  *
22031  * Originally Released Under LGPL - original licence link has changed is not relivant.
22032  *
22033  * Fork - LGPL
22034  * <script type="text/javascript">
22035  */
22036  
22037 // Base class for reading structured data from a data source.  This class is intended to be
22038 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
22039
22040 /**
22041  * @class Roo.data.DataReader
22042  * Base class for reading structured data from a data source.  This class is intended to be
22043  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
22044  */
22045
22046 Roo.data.DataReader = function(meta, recordType){
22047     
22048     this.meta = meta;
22049     
22050     this.recordType = recordType instanceof Array ? 
22051         Roo.data.Record.create(recordType) : recordType;
22052 };
22053
22054 Roo.data.DataReader.prototype = {
22055      /**
22056      * Create an empty record
22057      * @param {Object} data (optional) - overlay some values
22058      * @return {Roo.data.Record} record created.
22059      */
22060     newRow :  function(d) {
22061         var da =  {};
22062         this.recordType.prototype.fields.each(function(c) {
22063             switch( c.type) {
22064                 case 'int' : da[c.name] = 0; break;
22065                 case 'date' : da[c.name] = new Date(); break;
22066                 case 'float' : da[c.name] = 0.0; break;
22067                 case 'boolean' : da[c.name] = false; break;
22068                 default : da[c.name] = ""; break;
22069             }
22070             
22071         });
22072         return new this.recordType(Roo.apply(da, d));
22073     }
22074     
22075 };/*
22076  * Based on:
22077  * Ext JS Library 1.1.1
22078  * Copyright(c) 2006-2007, Ext JS, LLC.
22079  *
22080  * Originally Released Under LGPL - original licence link has changed is not relivant.
22081  *
22082  * Fork - LGPL
22083  * <script type="text/javascript">
22084  */
22085
22086 /**
22087  * @class Roo.data.DataProxy
22088  * @extends Roo.data.Observable
22089  * This class is an abstract base class for implementations which provide retrieval of
22090  * unformatted data objects.<br>
22091  * <p>
22092  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
22093  * (of the appropriate type which knows how to parse the data object) to provide a block of
22094  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
22095  * <p>
22096  * Custom implementations must implement the load method as described in
22097  * {@link Roo.data.HttpProxy#load}.
22098  */
22099 Roo.data.DataProxy = function(){
22100     this.addEvents({
22101         /**
22102          * @event beforeload
22103          * Fires before a network request is made to retrieve a data object.
22104          * @param {Object} This DataProxy object.
22105          * @param {Object} params The params parameter to the load function.
22106          */
22107         beforeload : true,
22108         /**
22109          * @event load
22110          * Fires before the load method's callback is called.
22111          * @param {Object} This DataProxy object.
22112          * @param {Object} o The data object.
22113          * @param {Object} arg The callback argument object passed to the load function.
22114          */
22115         load : true,
22116         /**
22117          * @event loadexception
22118          * Fires if an Exception occurs during data retrieval.
22119          * @param {Object} This DataProxy object.
22120          * @param {Object} o The data object.
22121          * @param {Object} arg The callback argument object passed to the load function.
22122          * @param {Object} e The Exception.
22123          */
22124         loadexception : true
22125     });
22126     Roo.data.DataProxy.superclass.constructor.call(this);
22127 };
22128
22129 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
22130
22131     /**
22132      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
22133      */
22134 /*
22135  * Based on:
22136  * Ext JS Library 1.1.1
22137  * Copyright(c) 2006-2007, Ext JS, LLC.
22138  *
22139  * Originally Released Under LGPL - original licence link has changed is not relivant.
22140  *
22141  * Fork - LGPL
22142  * <script type="text/javascript">
22143  */
22144 /**
22145  * @class Roo.data.MemoryProxy
22146  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
22147  * to the Reader when its load method is called.
22148  * @constructor
22149  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
22150  */
22151 Roo.data.MemoryProxy = function(data){
22152     if (data.data) {
22153         data = data.data;
22154     }
22155     Roo.data.MemoryProxy.superclass.constructor.call(this);
22156     this.data = data;
22157 };
22158
22159 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
22160     /**
22161      * Load data from the requested source (in this case an in-memory
22162      * data object passed to the constructor), read the data object into
22163      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
22164      * process that block using the passed callback.
22165      * @param {Object} params This parameter is not used by the MemoryProxy class.
22166      * @param {Roo.data.DataReader} reader The Reader object which converts the data
22167      * object into a block of Roo.data.Records.
22168      * @param {Function} callback The function into which to pass the block of Roo.data.records.
22169      * The function must be passed <ul>
22170      * <li>The Record block object</li>
22171      * <li>The "arg" argument from the load function</li>
22172      * <li>A boolean success indicator</li>
22173      * </ul>
22174      * @param {Object} scope The scope in which to call the callback
22175      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
22176      */
22177     load : function(params, reader, callback, scope, arg){
22178         params = params || {};
22179         var result;
22180         try {
22181             result = reader.readRecords(this.data);
22182         }catch(e){
22183             this.fireEvent("loadexception", this, arg, null, e);
22184             callback.call(scope, null, arg, false);
22185             return;
22186         }
22187         callback.call(scope, result, arg, true);
22188     },
22189     
22190     // private
22191     update : function(params, records){
22192         
22193     }
22194 });/*
22195  * Based on:
22196  * Ext JS Library 1.1.1
22197  * Copyright(c) 2006-2007, Ext JS, LLC.
22198  *
22199  * Originally Released Under LGPL - original licence link has changed is not relivant.
22200  *
22201  * Fork - LGPL
22202  * <script type="text/javascript">
22203  */
22204 /**
22205  * @class Roo.data.HttpProxy
22206  * @extends Roo.data.DataProxy
22207  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
22208  * configured to reference a certain URL.<br><br>
22209  * <p>
22210  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
22211  * from which the running page was served.<br><br>
22212  * <p>
22213  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
22214  * <p>
22215  * Be aware that to enable the browser to parse an XML document, the server must set
22216  * the Content-Type header in the HTTP response to "text/xml".
22217  * @constructor
22218  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
22219  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
22220  * will be used to make the request.
22221  */
22222 Roo.data.HttpProxy = function(conn){
22223     Roo.data.HttpProxy.superclass.constructor.call(this);
22224     // is conn a conn config or a real conn?
22225     this.conn = conn;
22226     this.useAjax = !conn || !conn.events;
22227   
22228 };
22229
22230 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
22231     // thse are take from connection...
22232     
22233     /**
22234      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
22235      */
22236     /**
22237      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
22238      * extra parameters to each request made by this object. (defaults to undefined)
22239      */
22240     /**
22241      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
22242      *  to each request made by this object. (defaults to undefined)
22243      */
22244     /**
22245      * @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)
22246      */
22247     /**
22248      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
22249      */
22250      /**
22251      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
22252      * @type Boolean
22253      */
22254   
22255
22256     /**
22257      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
22258      * @type Boolean
22259      */
22260     /**
22261      * Return the {@link Roo.data.Connection} object being used by this Proxy.
22262      * @return {Connection} The Connection object. This object may be used to subscribe to events on
22263      * a finer-grained basis than the DataProxy events.
22264      */
22265     getConnection : function(){
22266         return this.useAjax ? Roo.Ajax : this.conn;
22267     },
22268
22269     /**
22270      * Load data from the configured {@link Roo.data.Connection}, read the data object into
22271      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
22272      * process that block using the passed callback.
22273      * @param {Object} params An object containing properties which are to be used as HTTP parameters
22274      * for the request to the remote server.
22275      * @param {Roo.data.DataReader} reader The Reader object which converts the data
22276      * object into a block of Roo.data.Records.
22277      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
22278      * The function must be passed <ul>
22279      * <li>The Record block object</li>
22280      * <li>The "arg" argument from the load function</li>
22281      * <li>A boolean success indicator</li>
22282      * </ul>
22283      * @param {Object} scope The scope in which to call the callback
22284      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
22285      */
22286     load : function(params, reader, callback, scope, arg){
22287         if(this.fireEvent("beforeload", this, params) !== false){
22288             var  o = {
22289                 params : params || {},
22290                 request: {
22291                     callback : callback,
22292                     scope : scope,
22293                     arg : arg
22294                 },
22295                 reader: reader,
22296                 callback : this.loadResponse,
22297                 scope: this
22298             };
22299             if(this.useAjax){
22300                 Roo.applyIf(o, this.conn);
22301                 if(this.activeRequest){
22302                     Roo.Ajax.abort(this.activeRequest);
22303                 }
22304                 this.activeRequest = Roo.Ajax.request(o);
22305             }else{
22306                 this.conn.request(o);
22307             }
22308         }else{
22309             callback.call(scope||this, null, arg, false);
22310         }
22311     },
22312
22313     // private
22314     loadResponse : function(o, success, response){
22315         delete this.activeRequest;
22316         if(!success){
22317             this.fireEvent("loadexception", this, o, response);
22318             o.request.callback.call(o.request.scope, null, o.request.arg, false);
22319             return;
22320         }
22321         var result;
22322         try {
22323             result = o.reader.read(response);
22324         }catch(e){
22325             this.fireEvent("loadexception", this, o, response, e);
22326             o.request.callback.call(o.request.scope, null, o.request.arg, false);
22327             return;
22328         }
22329         
22330         this.fireEvent("load", this, o, o.request.arg);
22331         o.request.callback.call(o.request.scope, result, o.request.arg, true);
22332     },
22333
22334     // private
22335     update : function(dataSet){
22336
22337     },
22338
22339     // private
22340     updateResponse : function(dataSet){
22341
22342     }
22343 });/*
22344  * Based on:
22345  * Ext JS Library 1.1.1
22346  * Copyright(c) 2006-2007, Ext JS, LLC.
22347  *
22348  * Originally Released Under LGPL - original licence link has changed is not relivant.
22349  *
22350  * Fork - LGPL
22351  * <script type="text/javascript">
22352  */
22353
22354 /**
22355  * @class Roo.data.ScriptTagProxy
22356  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
22357  * other than the originating domain of the running page.<br><br>
22358  * <p>
22359  * <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
22360  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
22361  * <p>
22362  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
22363  * source code that is used as the source inside a &lt;script> tag.<br><br>
22364  * <p>
22365  * In order for the browser to process the returned data, the server must wrap the data object
22366  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
22367  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
22368  * depending on whether the callback name was passed:
22369  * <p>
22370  * <pre><code>
22371 boolean scriptTag = false;
22372 String cb = request.getParameter("callback");
22373 if (cb != null) {
22374     scriptTag = true;
22375     response.setContentType("text/javascript");
22376 } else {
22377     response.setContentType("application/x-json");
22378 }
22379 Writer out = response.getWriter();
22380 if (scriptTag) {
22381     out.write(cb + "(");
22382 }
22383 out.print(dataBlock.toJsonString());
22384 if (scriptTag) {
22385     out.write(");");
22386 }
22387 </pre></code>
22388  *
22389  * @constructor
22390  * @param {Object} config A configuration object.
22391  */
22392 Roo.data.ScriptTagProxy = function(config){
22393     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
22394     Roo.apply(this, config);
22395     this.head = document.getElementsByTagName("head")[0];
22396 };
22397
22398 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
22399
22400 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
22401     /**
22402      * @cfg {String} url The URL from which to request the data object.
22403      */
22404     /**
22405      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
22406      */
22407     timeout : 30000,
22408     /**
22409      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
22410      * the server the name of the callback function set up by the load call to process the returned data object.
22411      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
22412      * javascript output which calls this named function passing the data object as its only parameter.
22413      */
22414     callbackParam : "callback",
22415     /**
22416      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
22417      * name to the request.
22418      */
22419     nocache : true,
22420
22421     /**
22422      * Load data from the configured URL, read the data object into
22423      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
22424      * process that block using the passed callback.
22425      * @param {Object} params An object containing properties which are to be used as HTTP parameters
22426      * for the request to the remote server.
22427      * @param {Roo.data.DataReader} reader The Reader object which converts the data
22428      * object into a block of Roo.data.Records.
22429      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
22430      * The function must be passed <ul>
22431      * <li>The Record block object</li>
22432      * <li>The "arg" argument from the load function</li>
22433      * <li>A boolean success indicator</li>
22434      * </ul>
22435      * @param {Object} scope The scope in which to call the callback
22436      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
22437      */
22438     load : function(params, reader, callback, scope, arg){
22439         if(this.fireEvent("beforeload", this, params) !== false){
22440
22441             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
22442
22443             var url = this.url;
22444             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
22445             if(this.nocache){
22446                 url += "&_dc=" + (new Date().getTime());
22447             }
22448             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
22449             var trans = {
22450                 id : transId,
22451                 cb : "stcCallback"+transId,
22452                 scriptId : "stcScript"+transId,
22453                 params : params,
22454                 arg : arg,
22455                 url : url,
22456                 callback : callback,
22457                 scope : scope,
22458                 reader : reader
22459             };
22460             var conn = this;
22461
22462             window[trans.cb] = function(o){
22463                 conn.handleResponse(o, trans);
22464             };
22465
22466             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
22467
22468             if(this.autoAbort !== false){
22469                 this.abort();
22470             }
22471
22472             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
22473
22474             var script = document.createElement("script");
22475             script.setAttribute("src", url);
22476             script.setAttribute("type", "text/javascript");
22477             script.setAttribute("id", trans.scriptId);
22478             this.head.appendChild(script);
22479
22480             this.trans = trans;
22481         }else{
22482             callback.call(scope||this, null, arg, false);
22483         }
22484     },
22485
22486     // private
22487     isLoading : function(){
22488         return this.trans ? true : false;
22489     },
22490
22491     /**
22492      * Abort the current server request.
22493      */
22494     abort : function(){
22495         if(this.isLoading()){
22496             this.destroyTrans(this.trans);
22497         }
22498     },
22499
22500     // private
22501     destroyTrans : function(trans, isLoaded){
22502         this.head.removeChild(document.getElementById(trans.scriptId));
22503         clearTimeout(trans.timeoutId);
22504         if(isLoaded){
22505             window[trans.cb] = undefined;
22506             try{
22507                 delete window[trans.cb];
22508             }catch(e){}
22509         }else{
22510             // if hasn't been loaded, wait for load to remove it to prevent script error
22511             window[trans.cb] = function(){
22512                 window[trans.cb] = undefined;
22513                 try{
22514                     delete window[trans.cb];
22515                 }catch(e){}
22516             };
22517         }
22518     },
22519
22520     // private
22521     handleResponse : function(o, trans){
22522         this.trans = false;
22523         this.destroyTrans(trans, true);
22524         var result;
22525         try {
22526             result = trans.reader.readRecords(o);
22527         }catch(e){
22528             this.fireEvent("loadexception", this, o, trans.arg, e);
22529             trans.callback.call(trans.scope||window, null, trans.arg, false);
22530             return;
22531         }
22532         this.fireEvent("load", this, o, trans.arg);
22533         trans.callback.call(trans.scope||window, result, trans.arg, true);
22534     },
22535
22536     // private
22537     handleFailure : function(trans){
22538         this.trans = false;
22539         this.destroyTrans(trans, false);
22540         this.fireEvent("loadexception", this, null, trans.arg);
22541         trans.callback.call(trans.scope||window, null, trans.arg, false);
22542     }
22543 });/*
22544  * Based on:
22545  * Ext JS Library 1.1.1
22546  * Copyright(c) 2006-2007, Ext JS, LLC.
22547  *
22548  * Originally Released Under LGPL - original licence link has changed is not relivant.
22549  *
22550  * Fork - LGPL
22551  * <script type="text/javascript">
22552  */
22553
22554 /**
22555  * @class Roo.data.JsonReader
22556  * @extends Roo.data.DataReader
22557  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
22558  * based on mappings in a provided Roo.data.Record constructor.
22559  * 
22560  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
22561  * in the reply previously. 
22562  * 
22563  * <p>
22564  * Example code:
22565  * <pre><code>
22566 var RecordDef = Roo.data.Record.create([
22567     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
22568     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
22569 ]);
22570 var myReader = new Roo.data.JsonReader({
22571     totalProperty: "results",    // The property which contains the total dataset size (optional)
22572     root: "rows",                // The property which contains an Array of row objects
22573     id: "id"                     // The property within each row object that provides an ID for the record (optional)
22574 }, RecordDef);
22575 </code></pre>
22576  * <p>
22577  * This would consume a JSON file like this:
22578  * <pre><code>
22579 { 'results': 2, 'rows': [
22580     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
22581     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
22582 }
22583 </code></pre>
22584  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
22585  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
22586  * paged from the remote server.
22587  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
22588  * @cfg {String} root name of the property which contains the Array of row objects.
22589  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
22590  * @constructor
22591  * Create a new JsonReader
22592  * @param {Object} meta Metadata configuration options
22593  * @param {Object} recordType Either an Array of field definition objects,
22594  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
22595  */
22596 Roo.data.JsonReader = function(meta, recordType){
22597     
22598     meta = meta || {};
22599     // set some defaults:
22600     Roo.applyIf(meta, {
22601         totalProperty: 'total',
22602         successProperty : 'success',
22603         root : 'data',
22604         id : 'id'
22605     });
22606     
22607     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
22608 };
22609 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
22610     
22611     /**
22612      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
22613      * Used by Store query builder to append _requestMeta to params.
22614      * 
22615      */
22616     metaFromRemote : false,
22617     /**
22618      * This method is only used by a DataProxy which has retrieved data from a remote server.
22619      * @param {Object} response The XHR object which contains the JSON data in its responseText.
22620      * @return {Object} data A data block which is used by an Roo.data.Store object as
22621      * a cache of Roo.data.Records.
22622      */
22623     read : function(response){
22624         var json = response.responseText;
22625        
22626         var o = /* eval:var:o */ eval("("+json+")");
22627         if(!o) {
22628             throw {message: "JsonReader.read: Json object not found"};
22629         }
22630         
22631         if(o.metaData){
22632             
22633             delete this.ef;
22634             this.metaFromRemote = true;
22635             this.meta = o.metaData;
22636             this.recordType = Roo.data.Record.create(o.metaData.fields);
22637             this.onMetaChange(this.meta, this.recordType, o);
22638         }
22639         return this.readRecords(o);
22640     },
22641
22642     // private function a store will implement
22643     onMetaChange : function(meta, recordType, o){
22644
22645     },
22646
22647     /**
22648          * @ignore
22649          */
22650     simpleAccess: function(obj, subsc) {
22651         return obj[subsc];
22652     },
22653
22654         /**
22655          * @ignore
22656          */
22657     getJsonAccessor: function(){
22658         var re = /[\[\.]/;
22659         return function(expr) {
22660             try {
22661                 return(re.test(expr))
22662                     ? new Function("obj", "return obj." + expr)
22663                     : function(obj){
22664                         return obj[expr];
22665                     };
22666             } catch(e){}
22667             return Roo.emptyFn;
22668         };
22669     }(),
22670
22671     /**
22672      * Create a data block containing Roo.data.Records from an XML document.
22673      * @param {Object} o An object which contains an Array of row objects in the property specified
22674      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
22675      * which contains the total size of the dataset.
22676      * @return {Object} data A data block which is used by an Roo.data.Store object as
22677      * a cache of Roo.data.Records.
22678      */
22679     readRecords : function(o){
22680         /**
22681          * After any data loads, the raw JSON data is available for further custom processing.
22682          * @type Object
22683          */
22684         this.o = o;
22685         var s = this.meta, Record = this.recordType,
22686             f = Record.prototype.fields, fi = f.items, fl = f.length;
22687
22688 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
22689         if (!this.ef) {
22690             if(s.totalProperty) {
22691                     this.getTotal = this.getJsonAccessor(s.totalProperty);
22692                 }
22693                 if(s.successProperty) {
22694                     this.getSuccess = this.getJsonAccessor(s.successProperty);
22695                 }
22696                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
22697                 if (s.id) {
22698                         var g = this.getJsonAccessor(s.id);
22699                         this.getId = function(rec) {
22700                                 var r = g(rec);
22701                                 return (r === undefined || r === "") ? null : r;
22702                         };
22703                 } else {
22704                         this.getId = function(){return null;};
22705                 }
22706             this.ef = [];
22707             for(var jj = 0; jj < fl; jj++){
22708                 f = fi[jj];
22709                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
22710                 this.ef[jj] = this.getJsonAccessor(map);
22711             }
22712         }
22713
22714         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
22715         if(s.totalProperty){
22716             var vt = parseInt(this.getTotal(o), 10);
22717             if(!isNaN(vt)){
22718                 totalRecords = vt;
22719             }
22720         }
22721         if(s.successProperty){
22722             var vs = this.getSuccess(o);
22723             if(vs === false || vs === 'false'){
22724                 success = false;
22725             }
22726         }
22727         var records = [];
22728             for(var i = 0; i < c; i++){
22729                     var n = root[i];
22730                 var values = {};
22731                 var id = this.getId(n);
22732                 for(var j = 0; j < fl; j++){
22733                     f = fi[j];
22734                 var v = this.ef[j](n);
22735                 if (!f.convert) {
22736                     Roo.log('missing convert for ' + f.name);
22737                     Roo.log(f);
22738                     continue;
22739                 }
22740                 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
22741                 }
22742                 var record = new Record(values, id);
22743                 record.json = n;
22744                 records[i] = record;
22745             }
22746             return {
22747             raw : o,
22748                 success : success,
22749                 records : records,
22750                 totalRecords : totalRecords
22751             };
22752     }
22753 });/*
22754  * Based on:
22755  * Ext JS Library 1.1.1
22756  * Copyright(c) 2006-2007, Ext JS, LLC.
22757  *
22758  * Originally Released Under LGPL - original licence link has changed is not relivant.
22759  *
22760  * Fork - LGPL
22761  * <script type="text/javascript">
22762  */
22763
22764 /**
22765  * @class Roo.data.XmlReader
22766  * @extends Roo.data.DataReader
22767  * Data reader class to create an Array of {@link Roo.data.Record} objects from an XML document
22768  * based on mappings in a provided Roo.data.Record constructor.<br><br>
22769  * <p>
22770  * <em>Note that in order for the browser to parse a returned XML document, the Content-Type
22771  * header in the HTTP response must be set to "text/xml".</em>
22772  * <p>
22773  * Example code:
22774  * <pre><code>
22775 var RecordDef = Roo.data.Record.create([
22776    {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
22777    {name: 'occupation'}                 // This field will use "occupation" as the mapping.
22778 ]);
22779 var myReader = new Roo.data.XmlReader({
22780    totalRecords: "results", // The element which contains the total dataset size (optional)
22781    record: "row",           // The repeated element which contains row information
22782    id: "id"                 // The element within the row that provides an ID for the record (optional)
22783 }, RecordDef);
22784 </code></pre>
22785  * <p>
22786  * This would consume an XML file like this:
22787  * <pre><code>
22788 &lt;?xml?>
22789 &lt;dataset>
22790  &lt;results>2&lt;/results>
22791  &lt;row>
22792    &lt;id>1&lt;/id>
22793    &lt;name>Bill&lt;/name>
22794    &lt;occupation>Gardener&lt;/occupation>
22795  &lt;/row>
22796  &lt;row>
22797    &lt;id>2&lt;/id>
22798    &lt;name>Ben&lt;/name>
22799    &lt;occupation>Horticulturalist&lt;/occupation>
22800  &lt;/row>
22801 &lt;/dataset>
22802 </code></pre>
22803  * @cfg {String} totalRecords The DomQuery path from which to retrieve the total number of records
22804  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
22805  * paged from the remote server.
22806  * @cfg {String} record The DomQuery path to the repeated element which contains record information.
22807  * @cfg {String} success The DomQuery path to the success attribute used by forms.
22808  * @cfg {String} id The DomQuery path relative from the record element to the element that contains
22809  * a record identifier value.
22810  * @constructor
22811  * Create a new XmlReader
22812  * @param {Object} meta Metadata configuration options
22813  * @param {Mixed} recordType The definition of the data record type to produce.  This can be either a valid
22814  * Record subclass created with {@link Roo.data.Record#create}, or an array of objects with which to call
22815  * Roo.data.Record.create.  See the {@link Roo.data.Record} class for more details.
22816  */
22817 Roo.data.XmlReader = function(meta, recordType){
22818     meta = meta || {};
22819     Roo.data.XmlReader.superclass.constructor.call(this, meta, recordType||meta.fields);
22820 };
22821 Roo.extend(Roo.data.XmlReader, Roo.data.DataReader, {
22822     /**
22823      * This method is only used by a DataProxy which has retrieved data from a remote server.
22824          * @param {Object} response The XHR object which contains the parsed XML document.  The response is expected
22825          * to contain a method called 'responseXML' that returns an XML document object.
22826      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
22827      * a cache of Roo.data.Records.
22828      */
22829     read : function(response){
22830         var doc = response.responseXML;
22831         if(!doc) {
22832             throw {message: "XmlReader.read: XML Document not available"};
22833         }
22834         return this.readRecords(doc);
22835     },
22836
22837     /**
22838      * Create a data block containing Roo.data.Records from an XML document.
22839          * @param {Object} doc A parsed XML document.
22840      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
22841      * a cache of Roo.data.Records.
22842      */
22843     readRecords : function(doc){
22844         /**
22845          * After any data loads/reads, the raw XML Document is available for further custom processing.
22846          * @type XMLDocument
22847          */
22848         this.xmlData = doc;
22849         var root = doc.documentElement || doc;
22850         var q = Roo.DomQuery;
22851         var recordType = this.recordType, fields = recordType.prototype.fields;
22852         var sid = this.meta.id;
22853         var totalRecords = 0, success = true;
22854         if(this.meta.totalRecords){
22855             totalRecords = q.selectNumber(this.meta.totalRecords, root, 0);
22856         }
22857         
22858         if(this.meta.success){
22859             var sv = q.selectValue(this.meta.success, root, true);
22860             success = sv !== false && sv !== 'false';
22861         }
22862         var records = [];
22863         var ns = q.select(this.meta.record, root);
22864         for(var i = 0, len = ns.length; i < len; i++) {
22865                 var n = ns[i];
22866                 var values = {};
22867                 var id = sid ? q.selectValue(sid, n) : undefined;
22868                 for(var j = 0, jlen = fields.length; j < jlen; j++){
22869                     var f = fields.items[j];
22870                 var v = q.selectValue(f.mapping || f.name, n, f.defaultValue);
22871                     v = f.convert(v);
22872                     values[f.name] = v;
22873                 }
22874                 var record = new recordType(values, id);
22875                 record.node = n;
22876                 records[records.length] = record;
22877             }
22878
22879             return {
22880                 success : success,
22881                 records : records,
22882                 totalRecords : totalRecords || records.length
22883             };
22884     }
22885 });/*
22886  * Based on:
22887  * Ext JS Library 1.1.1
22888  * Copyright(c) 2006-2007, Ext JS, LLC.
22889  *
22890  * Originally Released Under LGPL - original licence link has changed is not relivant.
22891  *
22892  * Fork - LGPL
22893  * <script type="text/javascript">
22894  */
22895
22896 /**
22897  * @class Roo.data.ArrayReader
22898  * @extends Roo.data.DataReader
22899  * Data reader class to create an Array of Roo.data.Record objects from an Array.
22900  * Each element of that Array represents a row of data fields. The
22901  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
22902  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
22903  * <p>
22904  * Example code:.
22905  * <pre><code>
22906 var RecordDef = Roo.data.Record.create([
22907     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
22908     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
22909 ]);
22910 var myReader = new Roo.data.ArrayReader({
22911     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
22912 }, RecordDef);
22913 </code></pre>
22914  * <p>
22915  * This would consume an Array like this:
22916  * <pre><code>
22917 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
22918   </code></pre>
22919  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
22920  * @constructor
22921  * Create a new JsonReader
22922  * @param {Object} meta Metadata configuration options.
22923  * @param {Object} recordType Either an Array of field definition objects
22924  * as specified to {@link Roo.data.Record#create},
22925  * or an {@link Roo.data.Record} object
22926  * created using {@link Roo.data.Record#create}.
22927  */
22928 Roo.data.ArrayReader = function(meta, recordType){
22929     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
22930 };
22931
22932 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
22933     /**
22934      * Create a data block containing Roo.data.Records from an XML document.
22935      * @param {Object} o An Array of row objects which represents the dataset.
22936      * @return {Object} data A data block which is used by an Roo.data.Store object as
22937      * a cache of Roo.data.Records.
22938      */
22939     readRecords : function(o){
22940         var sid = this.meta ? this.meta.id : null;
22941         var recordType = this.recordType, fields = recordType.prototype.fields;
22942         var records = [];
22943         var root = o;
22944             for(var i = 0; i < root.length; i++){
22945                     var n = root[i];
22946                 var values = {};
22947                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
22948                 for(var j = 0, jlen = fields.length; j < jlen; j++){
22949                 var f = fields.items[j];
22950                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
22951                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
22952                 v = f.convert(v);
22953                 values[f.name] = v;
22954             }
22955                 var record = new recordType(values, id);
22956                 record.json = n;
22957                 records[records.length] = record;
22958             }
22959             return {
22960                 records : records,
22961                 totalRecords : records.length
22962             };
22963     }
22964 });/*
22965  * Based on:
22966  * Ext JS Library 1.1.1
22967  * Copyright(c) 2006-2007, Ext JS, LLC.
22968  *
22969  * Originally Released Under LGPL - original licence link has changed is not relivant.
22970  *
22971  * Fork - LGPL
22972  * <script type="text/javascript">
22973  */
22974
22975
22976 /**
22977  * @class Roo.data.Tree
22978  * @extends Roo.util.Observable
22979  * Represents a tree data structure and bubbles all the events for its nodes. The nodes
22980  * in the tree have most standard DOM functionality.
22981  * @constructor
22982  * @param {Node} root (optional) The root node
22983  */
22984 Roo.data.Tree = function(root){
22985    this.nodeHash = {};
22986    /**
22987     * The root node for this tree
22988     * @type Node
22989     */
22990    this.root = null;
22991    if(root){
22992        this.setRootNode(root);
22993    }
22994    this.addEvents({
22995        /**
22996         * @event append
22997         * Fires when a new child node is appended to a node in this tree.
22998         * @param {Tree} tree The owner tree
22999         * @param {Node} parent The parent node
23000         * @param {Node} node The newly appended node
23001         * @param {Number} index The index of the newly appended node
23002         */
23003        "append" : true,
23004        /**
23005         * @event remove
23006         * Fires when a child node is removed from a node in this tree.
23007         * @param {Tree} tree The owner tree
23008         * @param {Node} parent The parent node
23009         * @param {Node} node The child node removed
23010         */
23011        "remove" : true,
23012        /**
23013         * @event move
23014         * Fires when a node is moved to a new location in the tree
23015         * @param {Tree} tree The owner tree
23016         * @param {Node} node The node moved
23017         * @param {Node} oldParent The old parent of this node
23018         * @param {Node} newParent The new parent of this node
23019         * @param {Number} index The index it was moved to
23020         */
23021        "move" : true,
23022        /**
23023         * @event insert
23024         * Fires when a new child node is inserted in a node in this tree.
23025         * @param {Tree} tree The owner tree
23026         * @param {Node} parent The parent node
23027         * @param {Node} node The child node inserted
23028         * @param {Node} refNode The child node the node was inserted before
23029         */
23030        "insert" : true,
23031        /**
23032         * @event beforeappend
23033         * Fires before a new child is appended to a node in this tree, return false to cancel the append.
23034         * @param {Tree} tree The owner tree
23035         * @param {Node} parent The parent node
23036         * @param {Node} node The child node to be appended
23037         */
23038        "beforeappend" : true,
23039        /**
23040         * @event beforeremove
23041         * Fires before a child is removed from a node in this tree, return false to cancel the remove.
23042         * @param {Tree} tree The owner tree
23043         * @param {Node} parent The parent node
23044         * @param {Node} node The child node to be removed
23045         */
23046        "beforeremove" : true,
23047        /**
23048         * @event beforemove
23049         * Fires before a node is moved to a new location in the tree. Return false to cancel the move.
23050         * @param {Tree} tree The owner tree
23051         * @param {Node} node The node being moved
23052         * @param {Node} oldParent The parent of the node
23053         * @param {Node} newParent The new parent the node is moving to
23054         * @param {Number} index The index it is being moved to
23055         */
23056        "beforemove" : true,
23057        /**
23058         * @event beforeinsert
23059         * Fires before a new child is inserted in a node in this tree, return false to cancel the insert.
23060         * @param {Tree} tree The owner tree
23061         * @param {Node} parent The parent node
23062         * @param {Node} node The child node to be inserted
23063         * @param {Node} refNode The child node the node is being inserted before
23064         */
23065        "beforeinsert" : true
23066    });
23067
23068     Roo.data.Tree.superclass.constructor.call(this);
23069 };
23070
23071 Roo.extend(Roo.data.Tree, Roo.util.Observable, {
23072     pathSeparator: "/",
23073
23074     proxyNodeEvent : function(){
23075         return this.fireEvent.apply(this, arguments);
23076     },
23077
23078     /**
23079      * Returns the root node for this tree.
23080      * @return {Node}
23081      */
23082     getRootNode : function(){
23083         return this.root;
23084     },
23085
23086     /**
23087      * Sets the root node for this tree.
23088      * @param {Node} node
23089      * @return {Node}
23090      */
23091     setRootNode : function(node){
23092         this.root = node;
23093         node.ownerTree = this;
23094         node.isRoot = true;
23095         this.registerNode(node);
23096         return node;
23097     },
23098
23099     /**
23100      * Gets a node in this tree by its id.
23101      * @param {String} id
23102      * @return {Node}
23103      */
23104     getNodeById : function(id){
23105         return this.nodeHash[id];
23106     },
23107
23108     registerNode : function(node){
23109         this.nodeHash[node.id] = node;
23110     },
23111
23112     unregisterNode : function(node){
23113         delete this.nodeHash[node.id];
23114     },
23115
23116     toString : function(){
23117         return "[Tree"+(this.id?" "+this.id:"")+"]";
23118     }
23119 });
23120
23121 /**
23122  * @class Roo.data.Node
23123  * @extends Roo.util.Observable
23124  * @cfg {Boolean} leaf true if this node is a leaf and does not have children
23125  * @cfg {String} id The id for this node. If one is not specified, one is generated.
23126  * @constructor
23127  * @param {Object} attributes The attributes/config for the node
23128  */
23129 Roo.data.Node = function(attributes){
23130     /**
23131      * The attributes supplied for the node. You can use this property to access any custom attributes you supplied.
23132      * @type {Object}
23133      */
23134     this.attributes = attributes || {};
23135     this.leaf = this.attributes.leaf;
23136     /**
23137      * The node id. @type String
23138      */
23139     this.id = this.attributes.id;
23140     if(!this.id){
23141         this.id = Roo.id(null, "ynode-");
23142         this.attributes.id = this.id;
23143     }
23144      
23145     
23146     /**
23147      * All child nodes of this node. @type Array
23148      */
23149     this.childNodes = [];
23150     if(!this.childNodes.indexOf){ // indexOf is a must
23151         this.childNodes.indexOf = function(o){
23152             for(var i = 0, len = this.length; i < len; i++){
23153                 if(this[i] == o) {
23154                     return i;
23155                 }
23156             }
23157             return -1;
23158         };
23159     }
23160     /**
23161      * The parent node for this node. @type Node
23162      */
23163     this.parentNode = null;
23164     /**
23165      * The first direct child node of this node, or null if this node has no child nodes. @type Node
23166      */
23167     this.firstChild = null;
23168     /**
23169      * The last direct child node of this node, or null if this node has no child nodes. @type Node
23170      */
23171     this.lastChild = null;
23172     /**
23173      * The node immediately preceding this node in the tree, or null if there is no sibling node. @type Node
23174      */
23175     this.previousSibling = null;
23176     /**
23177      * The node immediately following this node in the tree, or null if there is no sibling node. @type Node
23178      */
23179     this.nextSibling = null;
23180
23181     this.addEvents({
23182        /**
23183         * @event append
23184         * Fires when a new child node is appended
23185         * @param {Tree} tree The owner tree
23186         * @param {Node} this This node
23187         * @param {Node} node The newly appended node
23188         * @param {Number} index The index of the newly appended node
23189         */
23190        "append" : true,
23191        /**
23192         * @event remove
23193         * Fires when a child node is removed
23194         * @param {Tree} tree The owner tree
23195         * @param {Node} this This node
23196         * @param {Node} node The removed node
23197         */
23198        "remove" : true,
23199        /**
23200         * @event move
23201         * Fires when this node is moved to a new location in the tree
23202         * @param {Tree} tree The owner tree
23203         * @param {Node} this This node
23204         * @param {Node} oldParent The old parent of this node
23205         * @param {Node} newParent The new parent of this node
23206         * @param {Number} index The index it was moved to
23207         */
23208        "move" : true,
23209        /**
23210         * @event insert
23211         * Fires when a new child node is inserted.
23212         * @param {Tree} tree The owner tree
23213         * @param {Node} this This node
23214         * @param {Node} node The child node inserted
23215         * @param {Node} refNode The child node the node was inserted before
23216         */
23217        "insert" : true,
23218        /**
23219         * @event beforeappend
23220         * Fires before a new child is appended, return false to cancel the append.
23221         * @param {Tree} tree The owner tree
23222         * @param {Node} this This node
23223         * @param {Node} node The child node to be appended
23224         */
23225        "beforeappend" : true,
23226        /**
23227         * @event beforeremove
23228         * Fires before a child is removed, return false to cancel the remove.
23229         * @param {Tree} tree The owner tree
23230         * @param {Node} this This node
23231         * @param {Node} node The child node to be removed
23232         */
23233        "beforeremove" : true,
23234        /**
23235         * @event beforemove
23236         * Fires before this node is moved to a new location in the tree. Return false to cancel the move.
23237         * @param {Tree} tree The owner tree
23238         * @param {Node} this This node
23239         * @param {Node} oldParent The parent of this node
23240         * @param {Node} newParent The new parent this node is moving to
23241         * @param {Number} index The index it is being moved to
23242         */
23243        "beforemove" : true,
23244        /**
23245         * @event beforeinsert
23246         * Fires before a new child is inserted, return false to cancel the insert.
23247         * @param {Tree} tree The owner tree
23248         * @param {Node} this This node
23249         * @param {Node} node The child node to be inserted
23250         * @param {Node} refNode The child node the node is being inserted before
23251         */
23252        "beforeinsert" : true
23253    });
23254     this.listeners = this.attributes.listeners;
23255     Roo.data.Node.superclass.constructor.call(this);
23256 };
23257
23258 Roo.extend(Roo.data.Node, Roo.util.Observable, {
23259     fireEvent : function(evtName){
23260         // first do standard event for this node
23261         if(Roo.data.Node.superclass.fireEvent.apply(this, arguments) === false){
23262             return false;
23263         }
23264         // then bubble it up to the tree if the event wasn't cancelled
23265         var ot = this.getOwnerTree();
23266         if(ot){
23267             if(ot.proxyNodeEvent.apply(ot, arguments) === false){
23268                 return false;
23269             }
23270         }
23271         return true;
23272     },
23273
23274     /**
23275      * Returns true if this node is a leaf
23276      * @return {Boolean}
23277      */
23278     isLeaf : function(){
23279         return this.leaf === true;
23280     },
23281
23282     // private
23283     setFirstChild : function(node){
23284         this.firstChild = node;
23285     },
23286
23287     //private
23288     setLastChild : function(node){
23289         this.lastChild = node;
23290     },
23291
23292
23293     /**
23294      * Returns true if this node is the last child of its parent
23295      * @return {Boolean}
23296      */
23297     isLast : function(){
23298        return (!this.parentNode ? true : this.parentNode.lastChild == this);
23299     },
23300
23301     /**
23302      * Returns true if this node is the first child of its parent
23303      * @return {Boolean}
23304      */
23305     isFirst : function(){
23306        return (!this.parentNode ? true : this.parentNode.firstChild == this);
23307     },
23308
23309     hasChildNodes : function(){
23310         return !this.isLeaf() && this.childNodes.length > 0;
23311     },
23312
23313     /**
23314      * Insert node(s) as the last child node of this node.
23315      * @param {Node/Array} node The node or Array of nodes to append
23316      * @return {Node} The appended node if single append, or null if an array was passed
23317      */
23318     appendChild : function(node){
23319         var multi = false;
23320         if(node instanceof Array){
23321             multi = node;
23322         }else if(arguments.length > 1){
23323             multi = arguments;
23324         }
23325         // if passed an array or multiple args do them one by one
23326         if(multi){
23327             for(var i = 0, len = multi.length; i < len; i++) {
23328                 this.appendChild(multi[i]);
23329             }
23330         }else{
23331             if(this.fireEvent("beforeappend", this.ownerTree, this, node) === false){
23332                 return false;
23333             }
23334             var index = this.childNodes.length;
23335             var oldParent = node.parentNode;
23336             // it's a move, make sure we move it cleanly
23337             if(oldParent){
23338                 if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index) === false){
23339                     return false;
23340                 }
23341                 oldParent.removeChild(node);
23342             }
23343             index = this.childNodes.length;
23344             if(index == 0){
23345                 this.setFirstChild(node);
23346             }
23347             this.childNodes.push(node);
23348             node.parentNode = this;
23349             var ps = this.childNodes[index-1];
23350             if(ps){
23351                 node.previousSibling = ps;
23352                 ps.nextSibling = node;
23353             }else{
23354                 node.previousSibling = null;
23355             }
23356             node.nextSibling = null;
23357             this.setLastChild(node);
23358             node.setOwnerTree(this.getOwnerTree());
23359             this.fireEvent("append", this.ownerTree, this, node, index);
23360             if(oldParent){
23361                 node.fireEvent("move", this.ownerTree, node, oldParent, this, index);
23362             }
23363             return node;
23364         }
23365     },
23366
23367     /**
23368      * Removes a child node from this node.
23369      * @param {Node} node The node to remove
23370      * @return {Node} The removed node
23371      */
23372     removeChild : function(node){
23373         var index = this.childNodes.indexOf(node);
23374         if(index == -1){
23375             return false;
23376         }
23377         if(this.fireEvent("beforeremove", this.ownerTree, this, node) === false){
23378             return false;
23379         }
23380
23381         // remove it from childNodes collection
23382         this.childNodes.splice(index, 1);
23383
23384         // update siblings
23385         if(node.previousSibling){
23386             node.previousSibling.nextSibling = node.nextSibling;
23387         }
23388         if(node.nextSibling){
23389             node.nextSibling.previousSibling = node.previousSibling;
23390         }
23391
23392         // update child refs
23393         if(this.firstChild == node){
23394             this.setFirstChild(node.nextSibling);
23395         }
23396         if(this.lastChild == node){
23397             this.setLastChild(node.previousSibling);
23398         }
23399
23400         node.setOwnerTree(null);
23401         // clear any references from the node
23402         node.parentNode = null;
23403         node.previousSibling = null;
23404         node.nextSibling = null;
23405         this.fireEvent("remove", this.ownerTree, this, node);
23406         return node;
23407     },
23408
23409     /**
23410      * Inserts the first node before the second node in this nodes childNodes collection.
23411      * @param {Node} node The node to insert
23412      * @param {Node} refNode The node to insert before (if null the node is appended)
23413      * @return {Node} The inserted node
23414      */
23415     insertBefore : function(node, refNode){
23416         if(!refNode){ // like standard Dom, refNode can be null for append
23417             return this.appendChild(node);
23418         }
23419         // nothing to do
23420         if(node == refNode){
23421             return false;
23422         }
23423
23424         if(this.fireEvent("beforeinsert", this.ownerTree, this, node, refNode) === false){
23425             return false;
23426         }
23427         var index = this.childNodes.indexOf(refNode);
23428         var oldParent = node.parentNode;
23429         var refIndex = index;
23430
23431         // when moving internally, indexes will change after remove
23432         if(oldParent == this && this.childNodes.indexOf(node) < index){
23433             refIndex--;
23434         }
23435
23436         // it's a move, make sure we move it cleanly
23437         if(oldParent){
23438             if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index, refNode) === false){
23439                 return false;
23440             }
23441             oldParent.removeChild(node);
23442         }
23443         if(refIndex == 0){
23444             this.setFirstChild(node);
23445         }
23446         this.childNodes.splice(refIndex, 0, node);
23447         node.parentNode = this;
23448         var ps = this.childNodes[refIndex-1];
23449         if(ps){
23450             node.previousSibling = ps;
23451             ps.nextSibling = node;
23452         }else{
23453             node.previousSibling = null;
23454         }
23455         node.nextSibling = refNode;
23456         refNode.previousSibling = node;
23457         node.setOwnerTree(this.getOwnerTree());
23458         this.fireEvent("insert", this.ownerTree, this, node, refNode);
23459         if(oldParent){
23460             node.fireEvent("move", this.ownerTree, node, oldParent, this, refIndex, refNode);
23461         }
23462         return node;
23463     },
23464
23465     /**
23466      * Returns the child node at the specified index.
23467      * @param {Number} index
23468      * @return {Node}
23469      */
23470     item : function(index){
23471         return this.childNodes[index];
23472     },
23473
23474     /**
23475      * Replaces one child node in this node with another.
23476      * @param {Node} newChild The replacement node
23477      * @param {Node} oldChild The node to replace
23478      * @return {Node} The replaced node
23479      */
23480     replaceChild : function(newChild, oldChild){
23481         this.insertBefore(newChild, oldChild);
23482         this.removeChild(oldChild);
23483         return oldChild;
23484     },
23485
23486     /**
23487      * Returns the index of a child node
23488      * @param {Node} node
23489      * @return {Number} The index of the node or -1 if it was not found
23490      */
23491     indexOf : function(child){
23492         return this.childNodes.indexOf(child);
23493     },
23494
23495     /**
23496      * Returns the tree this node is in.
23497      * @return {Tree}
23498      */
23499     getOwnerTree : function(){
23500         // if it doesn't have one, look for one
23501         if(!this.ownerTree){
23502             var p = this;
23503             while(p){
23504                 if(p.ownerTree){
23505                     this.ownerTree = p.ownerTree;
23506                     break;
23507                 }
23508                 p = p.parentNode;
23509             }
23510         }
23511         return this.ownerTree;
23512     },
23513
23514     /**
23515      * Returns depth of this node (the root node has a depth of 0)
23516      * @return {Number}
23517      */
23518     getDepth : function(){
23519         var depth = 0;
23520         var p = this;
23521         while(p.parentNode){
23522             ++depth;
23523             p = p.parentNode;
23524         }
23525         return depth;
23526     },
23527
23528     // private
23529     setOwnerTree : function(tree){
23530         // if it's move, we need to update everyone
23531         if(tree != this.ownerTree){
23532             if(this.ownerTree){
23533                 this.ownerTree.unregisterNode(this);
23534             }
23535             this.ownerTree = tree;
23536             var cs = this.childNodes;
23537             for(var i = 0, len = cs.length; i < len; i++) {
23538                 cs[i].setOwnerTree(tree);
23539             }
23540             if(tree){
23541                 tree.registerNode(this);
23542             }
23543         }
23544     },
23545
23546     /**
23547      * Returns the path for this node. The path can be used to expand or select this node programmatically.
23548      * @param {String} attr (optional) The attr to use for the path (defaults to the node's id)
23549      * @return {String} The path
23550      */
23551     getPath : function(attr){
23552         attr = attr || "id";
23553         var p = this.parentNode;
23554         var b = [this.attributes[attr]];
23555         while(p){
23556             b.unshift(p.attributes[attr]);
23557             p = p.parentNode;
23558         }
23559         var sep = this.getOwnerTree().pathSeparator;
23560         return sep + b.join(sep);
23561     },
23562
23563     /**
23564      * Bubbles up the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
23565      * function call will be the scope provided or the current node. The arguments to the function
23566      * will be the args provided or the current node. If the function returns false at any point,
23567      * the bubble is stopped.
23568      * @param {Function} fn The function to call
23569      * @param {Object} scope (optional) The scope of the function (defaults to current node)
23570      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
23571      */
23572     bubble : function(fn, scope, args){
23573         var p = this;
23574         while(p){
23575             if(fn.call(scope || p, args || p) === false){
23576                 break;
23577             }
23578             p = p.parentNode;
23579         }
23580     },
23581
23582     /**
23583      * Cascades down the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
23584      * function call will be the scope provided or the current node. The arguments to the function
23585      * will be the args provided or the current node. If the function returns false at any point,
23586      * the cascade is stopped on that branch.
23587      * @param {Function} fn The function to call
23588      * @param {Object} scope (optional) The scope of the function (defaults to current node)
23589      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
23590      */
23591     cascade : function(fn, scope, args){
23592         if(fn.call(scope || this, args || this) !== false){
23593             var cs = this.childNodes;
23594             for(var i = 0, len = cs.length; i < len; i++) {
23595                 cs[i].cascade(fn, scope, args);
23596             }
23597         }
23598     },
23599
23600     /**
23601      * Interates the child nodes of this node, calling the specified function with each node. The scope (<i>this</i>) of
23602      * function call will be the scope provided or the current node. The arguments to the function
23603      * will be the args provided or the current node. If the function returns false at any point,
23604      * the iteration stops.
23605      * @param {Function} fn The function to call
23606      * @param {Object} scope (optional) The scope of the function (defaults to current node)
23607      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
23608      */
23609     eachChild : function(fn, scope, args){
23610         var cs = this.childNodes;
23611         for(var i = 0, len = cs.length; i < len; i++) {
23612                 if(fn.call(scope || this, args || cs[i]) === false){
23613                     break;
23614                 }
23615         }
23616     },
23617
23618     /**
23619      * Finds the first child that has the attribute with the specified value.
23620      * @param {String} attribute The attribute name
23621      * @param {Mixed} value The value to search for
23622      * @return {Node} The found child or null if none was found
23623      */
23624     findChild : function(attribute, value){
23625         var cs = this.childNodes;
23626         for(var i = 0, len = cs.length; i < len; i++) {
23627                 if(cs[i].attributes[attribute] == value){
23628                     return cs[i];
23629                 }
23630         }
23631         return null;
23632     },
23633
23634     /**
23635      * Finds the first child by a custom function. The child matches if the function passed
23636      * returns true.
23637      * @param {Function} fn
23638      * @param {Object} scope (optional)
23639      * @return {Node} The found child or null if none was found
23640      */
23641     findChildBy : function(fn, scope){
23642         var cs = this.childNodes;
23643         for(var i = 0, len = cs.length; i < len; i++) {
23644                 if(fn.call(scope||cs[i], cs[i]) === true){
23645                     return cs[i];
23646                 }
23647         }
23648         return null;
23649     },
23650
23651     /**
23652      * Sorts this nodes children using the supplied sort function
23653      * @param {Function} fn
23654      * @param {Object} scope (optional)
23655      */
23656     sort : function(fn, scope){
23657         var cs = this.childNodes;
23658         var len = cs.length;
23659         if(len > 0){
23660             var sortFn = scope ? function(){fn.apply(scope, arguments);} : fn;
23661             cs.sort(sortFn);
23662             for(var i = 0; i < len; i++){
23663                 var n = cs[i];
23664                 n.previousSibling = cs[i-1];
23665                 n.nextSibling = cs[i+1];
23666                 if(i == 0){
23667                     this.setFirstChild(n);
23668                 }
23669                 if(i == len-1){
23670                     this.setLastChild(n);
23671                 }
23672             }
23673         }
23674     },
23675
23676     /**
23677      * Returns true if this node is an ancestor (at any point) of the passed node.
23678      * @param {Node} node
23679      * @return {Boolean}
23680      */
23681     contains : function(node){
23682         return node.isAncestor(this);
23683     },
23684
23685     /**
23686      * Returns true if the passed node is an ancestor (at any point) of this node.
23687      * @param {Node} node
23688      * @return {Boolean}
23689      */
23690     isAncestor : function(node){
23691         var p = this.parentNode;
23692         while(p){
23693             if(p == node){
23694                 return true;
23695             }
23696             p = p.parentNode;
23697         }
23698         return false;
23699     },
23700
23701     toString : function(){
23702         return "[Node"+(this.id?" "+this.id:"")+"]";
23703     }
23704 });/*
23705  * Based on:
23706  * Ext JS Library 1.1.1
23707  * Copyright(c) 2006-2007, Ext JS, LLC.
23708  *
23709  * Originally Released Under LGPL - original licence link has changed is not relivant.
23710  *
23711  * Fork - LGPL
23712  * <script type="text/javascript">
23713  */
23714  (function(){ 
23715 /**
23716  * @class Roo.Layer
23717  * @extends Roo.Element
23718  * An extended {@link Roo.Element} object that supports a shadow and shim, constrain to viewport and
23719  * automatic maintaining of shadow/shim positions.
23720  * @cfg {Boolean} shim False to disable the iframe shim in browsers which need one (defaults to true)
23721  * @cfg {String/Boolean} shadow True to create a shadow element with default class "x-layer-shadow", or
23722  * you can pass a string with a CSS class name. False turns off the shadow.
23723  * @cfg {Object} dh DomHelper object config to create element with (defaults to {tag: "div", cls: "x-layer"}).
23724  * @cfg {Boolean} constrain False to disable constrain to viewport (defaults to true)
23725  * @cfg {String} cls CSS class to add to the element
23726  * @cfg {Number} zindex Starting z-index (defaults to 11000)
23727  * @cfg {Number} shadowOffset Number of pixels to offset the shadow (defaults to 3)
23728  * @constructor
23729  * @param {Object} config An object with config options.
23730  * @param {String/HTMLElement} existingEl (optional) Uses an existing DOM element. If the element is not found it creates it.
23731  */
23732
23733 Roo.Layer = function(config, existingEl){
23734     config = config || {};
23735     var dh = Roo.DomHelper;
23736     var cp = config.parentEl, pel = cp ? Roo.getDom(cp) : document.body;
23737     if(existingEl){
23738         this.dom = Roo.getDom(existingEl);
23739     }
23740     if(!this.dom){
23741         var o = config.dh || {tag: "div", cls: "x-layer"};
23742         this.dom = dh.append(pel, o);
23743     }
23744     if(config.cls){
23745         this.addClass(config.cls);
23746     }
23747     this.constrain = config.constrain !== false;
23748     this.visibilityMode = Roo.Element.VISIBILITY;
23749     if(config.id){
23750         this.id = this.dom.id = config.id;
23751     }else{
23752         this.id = Roo.id(this.dom);
23753     }
23754     this.zindex = config.zindex || this.getZIndex();
23755     this.position("absolute", this.zindex);
23756     if(config.shadow){
23757         this.shadowOffset = config.shadowOffset || 4;
23758         this.shadow = new Roo.Shadow({
23759             offset : this.shadowOffset,
23760             mode : config.shadow
23761         });
23762     }else{
23763         this.shadowOffset = 0;
23764     }
23765     this.useShim = config.shim !== false && Roo.useShims;
23766     this.useDisplay = config.useDisplay;
23767     this.hide();
23768 };
23769
23770 var supr = Roo.Element.prototype;
23771
23772 // shims are shared among layer to keep from having 100 iframes
23773 var shims = [];
23774
23775 Roo.extend(Roo.Layer, Roo.Element, {
23776
23777     getZIndex : function(){
23778         return this.zindex || parseInt(this.getStyle("z-index"), 10) || 11000;
23779     },
23780
23781     getShim : function(){
23782         if(!this.useShim){
23783             return null;
23784         }
23785         if(this.shim){
23786             return this.shim;
23787         }
23788         var shim = shims.shift();
23789         if(!shim){
23790             shim = this.createShim();
23791             shim.enableDisplayMode('block');
23792             shim.dom.style.display = 'none';
23793             shim.dom.style.visibility = 'visible';
23794         }
23795         var pn = this.dom.parentNode;
23796         if(shim.dom.parentNode != pn){
23797             pn.insertBefore(shim.dom, this.dom);
23798         }
23799         shim.setStyle('z-index', this.getZIndex()-2);
23800         this.shim = shim;
23801         return shim;
23802     },
23803
23804     hideShim : function(){
23805         if(this.shim){
23806             this.shim.setDisplayed(false);
23807             shims.push(this.shim);
23808             delete this.shim;
23809         }
23810     },
23811
23812     disableShadow : function(){
23813         if(this.shadow){
23814             this.shadowDisabled = true;
23815             this.shadow.hide();
23816             this.lastShadowOffset = this.shadowOffset;
23817             this.shadowOffset = 0;
23818         }
23819     },
23820
23821     enableShadow : function(show){
23822         if(this.shadow){
23823             this.shadowDisabled = false;
23824             this.shadowOffset = this.lastShadowOffset;
23825             delete this.lastShadowOffset;
23826             if(show){
23827                 this.sync(true);
23828             }
23829         }
23830     },
23831
23832     // private
23833     // this code can execute repeatedly in milliseconds (i.e. during a drag) so
23834     // code size was sacrificed for effeciency (e.g. no getBox/setBox, no XY calls)
23835     sync : function(doShow){
23836         var sw = this.shadow;
23837         if(!this.updating && this.isVisible() && (sw || this.useShim)){
23838             var sh = this.getShim();
23839
23840             var w = this.getWidth(),
23841                 h = this.getHeight();
23842
23843             var l = this.getLeft(true),
23844                 t = this.getTop(true);
23845
23846             if(sw && !this.shadowDisabled){
23847                 if(doShow && !sw.isVisible()){
23848                     sw.show(this);
23849                 }else{
23850                     sw.realign(l, t, w, h);
23851                 }
23852                 if(sh){
23853                     if(doShow){
23854                        sh.show();
23855                     }
23856                     // fit the shim behind the shadow, so it is shimmed too
23857                     var a = sw.adjusts, s = sh.dom.style;
23858                     s.left = (Math.min(l, l+a.l))+"px";
23859                     s.top = (Math.min(t, t+a.t))+"px";
23860                     s.width = (w+a.w)+"px";
23861                     s.height = (h+a.h)+"px";
23862                 }
23863             }else if(sh){
23864                 if(doShow){
23865                    sh.show();
23866                 }
23867                 sh.setSize(w, h);
23868                 sh.setLeftTop(l, t);
23869             }
23870             
23871         }
23872     },
23873
23874     // private
23875     destroy : function(){
23876         this.hideShim();
23877         if(this.shadow){
23878             this.shadow.hide();
23879         }
23880         this.removeAllListeners();
23881         var pn = this.dom.parentNode;
23882         if(pn){
23883             pn.removeChild(this.dom);
23884         }
23885         Roo.Element.uncache(this.id);
23886     },
23887
23888     remove : function(){
23889         this.destroy();
23890     },
23891
23892     // private
23893     beginUpdate : function(){
23894         this.updating = true;
23895     },
23896
23897     // private
23898     endUpdate : function(){
23899         this.updating = false;
23900         this.sync(true);
23901     },
23902
23903     // private
23904     hideUnders : function(negOffset){
23905         if(this.shadow){
23906             this.shadow.hide();
23907         }
23908         this.hideShim();
23909     },
23910
23911     // private
23912     constrainXY : function(){
23913         if(this.constrain){
23914             var vw = Roo.lib.Dom.getViewWidth(),
23915                 vh = Roo.lib.Dom.getViewHeight();
23916             var s = Roo.get(document).getScroll();
23917
23918             var xy = this.getXY();
23919             var x = xy[0], y = xy[1];   
23920             var w = this.dom.offsetWidth+this.shadowOffset, h = this.dom.offsetHeight+this.shadowOffset;
23921             // only move it if it needs it
23922             var moved = false;
23923             // first validate right/bottom
23924             if((x + w) > vw+s.left){
23925                 x = vw - w - this.shadowOffset;
23926                 moved = true;
23927             }
23928             if((y + h) > vh+s.top){
23929                 y = vh - h - this.shadowOffset;
23930                 moved = true;
23931             }
23932             // then make sure top/left isn't negative
23933             if(x < s.left){
23934                 x = s.left;
23935                 moved = true;
23936             }
23937             if(y < s.top){
23938                 y = s.top;
23939                 moved = true;
23940             }
23941             if(moved){
23942                 if(this.avoidY){
23943                     var ay = this.avoidY;
23944                     if(y <= ay && (y+h) >= ay){
23945                         y = ay-h-5;   
23946                     }
23947                 }
23948                 xy = [x, y];
23949                 this.storeXY(xy);
23950                 supr.setXY.call(this, xy);
23951                 this.sync();
23952             }
23953         }
23954     },
23955
23956     isVisible : function(){
23957         return this.visible;    
23958     },
23959
23960     // private
23961     showAction : function(){
23962         this.visible = true; // track visibility to prevent getStyle calls
23963         if(this.useDisplay === true){
23964             this.setDisplayed("");
23965         }else if(this.lastXY){
23966             supr.setXY.call(this, this.lastXY);
23967         }else if(this.lastLT){
23968             supr.setLeftTop.call(this, this.lastLT[0], this.lastLT[1]);
23969         }
23970     },
23971
23972     // private
23973     hideAction : function(){
23974         this.visible = false;
23975         if(this.useDisplay === true){
23976             this.setDisplayed(false);
23977         }else{
23978             this.setLeftTop(-10000,-10000);
23979         }
23980     },
23981
23982     // overridden Element method
23983     setVisible : function(v, a, d, c, e){
23984         if(v){
23985             this.showAction();
23986         }
23987         if(a && v){
23988             var cb = function(){
23989                 this.sync(true);
23990                 if(c){
23991                     c();
23992                 }
23993             }.createDelegate(this);
23994             supr.setVisible.call(this, true, true, d, cb, e);
23995         }else{
23996             if(!v){
23997                 this.hideUnders(true);
23998             }
23999             var cb = c;
24000             if(a){
24001                 cb = function(){
24002                     this.hideAction();
24003                     if(c){
24004                         c();
24005                     }
24006                 }.createDelegate(this);
24007             }
24008             supr.setVisible.call(this, v, a, d, cb, e);
24009             if(v){
24010                 this.sync(true);
24011             }else if(!a){
24012                 this.hideAction();
24013             }
24014         }
24015     },
24016
24017     storeXY : function(xy){
24018         delete this.lastLT;
24019         this.lastXY = xy;
24020     },
24021
24022     storeLeftTop : function(left, top){
24023         delete this.lastXY;
24024         this.lastLT = [left, top];
24025     },
24026
24027     // private
24028     beforeFx : function(){
24029         this.beforeAction();
24030         return Roo.Layer.superclass.beforeFx.apply(this, arguments);
24031     },
24032
24033     // private
24034     afterFx : function(){
24035         Roo.Layer.superclass.afterFx.apply(this, arguments);
24036         this.sync(this.isVisible());
24037     },
24038
24039     // private
24040     beforeAction : function(){
24041         if(!this.updating && this.shadow){
24042             this.shadow.hide();
24043         }
24044     },
24045
24046     // overridden Element method
24047     setLeft : function(left){
24048         this.storeLeftTop(left, this.getTop(true));
24049         supr.setLeft.apply(this, arguments);
24050         this.sync();
24051     },
24052
24053     setTop : function(top){
24054         this.storeLeftTop(this.getLeft(true), top);
24055         supr.setTop.apply(this, arguments);
24056         this.sync();
24057     },
24058
24059     setLeftTop : function(left, top){
24060         this.storeLeftTop(left, top);
24061         supr.setLeftTop.apply(this, arguments);
24062         this.sync();
24063     },
24064
24065     setXY : function(xy, a, d, c, e){
24066         this.fixDisplay();
24067         this.beforeAction();
24068         this.storeXY(xy);
24069         var cb = this.createCB(c);
24070         supr.setXY.call(this, xy, a, d, cb, e);
24071         if(!a){
24072             cb();
24073         }
24074     },
24075
24076     // private
24077     createCB : function(c){
24078         var el = this;
24079         return function(){
24080             el.constrainXY();
24081             el.sync(true);
24082             if(c){
24083                 c();
24084             }
24085         };
24086     },
24087
24088     // overridden Element method
24089     setX : function(x, a, d, c, e){
24090         this.setXY([x, this.getY()], a, d, c, e);
24091     },
24092
24093     // overridden Element method
24094     setY : function(y, a, d, c, e){
24095         this.setXY([this.getX(), y], a, d, c, e);
24096     },
24097
24098     // overridden Element method
24099     setSize : function(w, h, a, d, c, e){
24100         this.beforeAction();
24101         var cb = this.createCB(c);
24102         supr.setSize.call(this, w, h, a, d, cb, e);
24103         if(!a){
24104             cb();
24105         }
24106     },
24107
24108     // overridden Element method
24109     setWidth : function(w, a, d, c, e){
24110         this.beforeAction();
24111         var cb = this.createCB(c);
24112         supr.setWidth.call(this, w, a, d, cb, e);
24113         if(!a){
24114             cb();
24115         }
24116     },
24117
24118     // overridden Element method
24119     setHeight : function(h, a, d, c, e){
24120         this.beforeAction();
24121         var cb = this.createCB(c);
24122         supr.setHeight.call(this, h, a, d, cb, e);
24123         if(!a){
24124             cb();
24125         }
24126     },
24127
24128     // overridden Element method
24129     setBounds : function(x, y, w, h, a, d, c, e){
24130         this.beforeAction();
24131         var cb = this.createCB(c);
24132         if(!a){
24133             this.storeXY([x, y]);
24134             supr.setXY.call(this, [x, y]);
24135             supr.setSize.call(this, w, h, a, d, cb, e);
24136             cb();
24137         }else{
24138             supr.setBounds.call(this, x, y, w, h, a, d, cb, e);
24139         }
24140         return this;
24141     },
24142     
24143     /**
24144      * Sets the z-index of this layer and adjusts any shadow and shim z-indexes. The layer z-index is automatically
24145      * incremented by two more than the value passed in so that it always shows above any shadow or shim (the shadow
24146      * element, if any, will be assigned z-index + 1, and the shim element, if any, will be assigned the unmodified z-index).
24147      * @param {Number} zindex The new z-index to set
24148      * @return {this} The Layer
24149      */
24150     setZIndex : function(zindex){
24151         this.zindex = zindex;
24152         this.setStyle("z-index", zindex + 2);
24153         if(this.shadow){
24154             this.shadow.setZIndex(zindex + 1);
24155         }
24156         if(this.shim){
24157             this.shim.setStyle("z-index", zindex);
24158         }
24159     }
24160 });
24161 })();/*
24162  * Based on:
24163  * Ext JS Library 1.1.1
24164  * Copyright(c) 2006-2007, Ext JS, LLC.
24165  *
24166  * Originally Released Under LGPL - original licence link has changed is not relivant.
24167  *
24168  * Fork - LGPL
24169  * <script type="text/javascript">
24170  */
24171
24172
24173 /**
24174  * @class Roo.Shadow
24175  * Simple class that can provide a shadow effect for any element.  Note that the element MUST be absolutely positioned,
24176  * and the shadow does not provide any shimming.  This should be used only in simple cases -- for more advanced
24177  * functionality that can also provide the same shadow effect, see the {@link Roo.Layer} class.
24178  * @constructor
24179  * Create a new Shadow
24180  * @param {Object} config The config object
24181  */
24182 Roo.Shadow = function(config){
24183     Roo.apply(this, config);
24184     if(typeof this.mode != "string"){
24185         this.mode = this.defaultMode;
24186     }
24187     var o = this.offset, a = {h: 0};
24188     var rad = Math.floor(this.offset/2);
24189     switch(this.mode.toLowerCase()){ // all this hideous nonsense calculates the various offsets for shadows
24190         case "drop":
24191             a.w = 0;
24192             a.l = a.t = o;
24193             a.t -= 1;
24194             if(Roo.isIE){
24195                 a.l -= this.offset + rad;
24196                 a.t -= this.offset + rad;
24197                 a.w -= rad;
24198                 a.h -= rad;
24199                 a.t += 1;
24200             }
24201         break;
24202         case "sides":
24203             a.w = (o*2);
24204             a.l = -o;
24205             a.t = o-1;
24206             if(Roo.isIE){
24207                 a.l -= (this.offset - rad);
24208                 a.t -= this.offset + rad;
24209                 a.l += 1;
24210                 a.w -= (this.offset - rad)*2;
24211                 a.w -= rad + 1;
24212                 a.h -= 1;
24213             }
24214         break;
24215         case "frame":
24216             a.w = a.h = (o*2);
24217             a.l = a.t = -o;
24218             a.t += 1;
24219             a.h -= 2;
24220             if(Roo.isIE){
24221                 a.l -= (this.offset - rad);
24222                 a.t -= (this.offset - rad);
24223                 a.l += 1;
24224                 a.w -= (this.offset + rad + 1);
24225                 a.h -= (this.offset + rad);
24226                 a.h += 1;
24227             }
24228         break;
24229     };
24230
24231     this.adjusts = a;
24232 };
24233
24234 Roo.Shadow.prototype = {
24235     /**
24236      * @cfg {String} mode
24237      * The shadow display mode.  Supports the following options:<br />
24238      * sides: Shadow displays on both sides and bottom only<br />
24239      * frame: Shadow displays equally on all four sides<br />
24240      * drop: Traditional bottom-right drop shadow (default)
24241      */
24242     /**
24243      * @cfg {String} offset
24244      * The number of pixels to offset the shadow from the element (defaults to 4)
24245      */
24246     offset: 4,
24247
24248     // private
24249     defaultMode: "drop",
24250
24251     /**
24252      * Displays the shadow under the target element
24253      * @param {String/HTMLElement/Element} targetEl The id or element under which the shadow should display
24254      */
24255     show : function(target){
24256         target = Roo.get(target);
24257         if(!this.el){
24258             this.el = Roo.Shadow.Pool.pull();
24259             if(this.el.dom.nextSibling != target.dom){
24260                 this.el.insertBefore(target);
24261             }
24262         }
24263         this.el.setStyle("z-index", this.zIndex || parseInt(target.getStyle("z-index"), 10)-1);
24264         if(Roo.isIE){
24265             this.el.dom.style.filter="progid:DXImageTransform.Microsoft.alpha(opacity=50) progid:DXImageTransform.Microsoft.Blur(pixelradius="+(this.offset)+")";
24266         }
24267         this.realign(
24268             target.getLeft(true),
24269             target.getTop(true),
24270             target.getWidth(),
24271             target.getHeight()
24272         );
24273         this.el.dom.style.display = "block";
24274     },
24275
24276     /**
24277      * Returns true if the shadow is visible, else false
24278      */
24279     isVisible : function(){
24280         return this.el ? true : false;  
24281     },
24282
24283     /**
24284      * Direct alignment when values are already available. Show must be called at least once before
24285      * calling this method to ensure it is initialized.
24286      * @param {Number} left The target element left position
24287      * @param {Number} top The target element top position
24288      * @param {Number} width The target element width
24289      * @param {Number} height The target element height
24290      */
24291     realign : function(l, t, w, h){
24292         if(!this.el){
24293             return;
24294         }
24295         var a = this.adjusts, d = this.el.dom, s = d.style;
24296         var iea = 0;
24297         s.left = (l+a.l)+"px";
24298         s.top = (t+a.t)+"px";
24299         var sw = (w+a.w), sh = (h+a.h), sws = sw +"px", shs = sh + "px";
24300  
24301         if(s.width != sws || s.height != shs){
24302             s.width = sws;
24303             s.height = shs;
24304             if(!Roo.isIE){
24305                 var cn = d.childNodes;
24306                 var sww = Math.max(0, (sw-12))+"px";
24307                 cn[0].childNodes[1].style.width = sww;
24308                 cn[1].childNodes[1].style.width = sww;
24309                 cn[2].childNodes[1].style.width = sww;
24310                 cn[1].style.height = Math.max(0, (sh-12))+"px";
24311             }
24312         }
24313     },
24314
24315     /**
24316      * Hides this shadow
24317      */
24318     hide : function(){
24319         if(this.el){
24320             this.el.dom.style.display = "none";
24321             Roo.Shadow.Pool.push(this.el);
24322             delete this.el;
24323         }
24324     },
24325
24326     /**
24327      * Adjust the z-index of this shadow
24328      * @param {Number} zindex The new z-index
24329      */
24330     setZIndex : function(z){
24331         this.zIndex = z;
24332         if(this.el){
24333             this.el.setStyle("z-index", z);
24334         }
24335     }
24336 };
24337
24338 // Private utility class that manages the internal Shadow cache
24339 Roo.Shadow.Pool = function(){
24340     var p = [];
24341     var markup = Roo.isIE ?
24342                  '<div class="x-ie-shadow"></div>' :
24343                  '<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>';
24344     return {
24345         pull : function(){
24346             var sh = p.shift();
24347             if(!sh){
24348                 sh = Roo.get(Roo.DomHelper.insertHtml("beforeBegin", document.body.firstChild, markup));
24349                 sh.autoBoxAdjust = false;
24350             }
24351             return sh;
24352         },
24353
24354         push : function(sh){
24355             p.push(sh);
24356         }
24357     };
24358 }();/*
24359  * Based on:
24360  * Ext JS Library 1.1.1
24361  * Copyright(c) 2006-2007, Ext JS, LLC.
24362  *
24363  * Originally Released Under LGPL - original licence link has changed is not relivant.
24364  *
24365  * Fork - LGPL
24366  * <script type="text/javascript">
24367  */
24368
24369
24370 /**
24371  * @class Roo.SplitBar
24372  * @extends Roo.util.Observable
24373  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
24374  * <br><br>
24375  * Usage:
24376  * <pre><code>
24377 var split = new Roo.SplitBar("elementToDrag", "elementToSize",
24378                    Roo.SplitBar.HORIZONTAL, Roo.SplitBar.LEFT);
24379 split.setAdapter(new Roo.SplitBar.AbsoluteLayoutAdapter("container"));
24380 split.minSize = 100;
24381 split.maxSize = 600;
24382 split.animate = true;
24383 split.on('moved', splitterMoved);
24384 </code></pre>
24385  * @constructor
24386  * Create a new SplitBar
24387  * @param {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
24388  * @param {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
24389  * @param {Number} orientation (optional) Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
24390  * @param {Number} placement (optional) Either Roo.SplitBar.LEFT or Roo.SplitBar.RIGHT for horizontal or  
24391                         Roo.SplitBar.TOP or Roo.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
24392                         position of the SplitBar).
24393  */
24394 Roo.SplitBar = function(dragElement, resizingElement, orientation, placement, existingProxy){
24395     
24396     /** @private */
24397     this.el = Roo.get(dragElement, true);
24398     this.el.dom.unselectable = "on";
24399     /** @private */
24400     this.resizingEl = Roo.get(resizingElement, true);
24401
24402     /**
24403      * @private
24404      * The orientation of the split. Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
24405      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
24406      * @type Number
24407      */
24408     this.orientation = orientation || Roo.SplitBar.HORIZONTAL;
24409     
24410     /**
24411      * The minimum size of the resizing element. (Defaults to 0)
24412      * @type Number
24413      */
24414     this.minSize = 0;
24415     
24416     /**
24417      * The maximum size of the resizing element. (Defaults to 2000)
24418      * @type Number
24419      */
24420     this.maxSize = 2000;
24421     
24422     /**
24423      * Whether to animate the transition to the new size
24424      * @type Boolean
24425      */
24426     this.animate = false;
24427     
24428     /**
24429      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
24430      * @type Boolean
24431      */
24432     this.useShim = false;
24433     
24434     /** @private */
24435     this.shim = null;
24436     
24437     if(!existingProxy){
24438         /** @private */
24439         this.proxy = Roo.SplitBar.createProxy(this.orientation);
24440     }else{
24441         this.proxy = Roo.get(existingProxy).dom;
24442     }
24443     /** @private */
24444     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
24445     
24446     /** @private */
24447     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
24448     
24449     /** @private */
24450     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
24451     
24452     /** @private */
24453     this.dragSpecs = {};
24454     
24455     /**
24456      * @private The adapter to use to positon and resize elements
24457      */
24458     this.adapter = new Roo.SplitBar.BasicLayoutAdapter();
24459     this.adapter.init(this);
24460     
24461     if(this.orientation == Roo.SplitBar.HORIZONTAL){
24462         /** @private */
24463         this.placement = placement || (this.el.getX() > this.resizingEl.getX() ? Roo.SplitBar.LEFT : Roo.SplitBar.RIGHT);
24464         this.el.addClass("x-splitbar-h");
24465     }else{
24466         /** @private */
24467         this.placement = placement || (this.el.getY() > this.resizingEl.getY() ? Roo.SplitBar.TOP : Roo.SplitBar.BOTTOM);
24468         this.el.addClass("x-splitbar-v");
24469     }
24470     
24471     this.addEvents({
24472         /**
24473          * @event resize
24474          * Fires when the splitter is moved (alias for {@link #event-moved})
24475          * @param {Roo.SplitBar} this
24476          * @param {Number} newSize the new width or height
24477          */
24478         "resize" : true,
24479         /**
24480          * @event moved
24481          * Fires when the splitter is moved
24482          * @param {Roo.SplitBar} this
24483          * @param {Number} newSize the new width or height
24484          */
24485         "moved" : true,
24486         /**
24487          * @event beforeresize
24488          * Fires before the splitter is dragged
24489          * @param {Roo.SplitBar} this
24490          */
24491         "beforeresize" : true,
24492
24493         "beforeapply" : true
24494     });
24495
24496     Roo.util.Observable.call(this);
24497 };
24498
24499 Roo.extend(Roo.SplitBar, Roo.util.Observable, {
24500     onStartProxyDrag : function(x, y){
24501         this.fireEvent("beforeresize", this);
24502         if(!this.overlay){
24503             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "x-drag-overlay", html: "&#160;"}, true);
24504             o.unselectable();
24505             o.enableDisplayMode("block");
24506             // all splitbars share the same overlay
24507             Roo.SplitBar.prototype.overlay = o;
24508         }
24509         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
24510         this.overlay.show();
24511         Roo.get(this.proxy).setDisplayed("block");
24512         var size = this.adapter.getElementSize(this);
24513         this.activeMinSize = this.getMinimumSize();;
24514         this.activeMaxSize = this.getMaximumSize();;
24515         var c1 = size - this.activeMinSize;
24516         var c2 = Math.max(this.activeMaxSize - size, 0);
24517         if(this.orientation == Roo.SplitBar.HORIZONTAL){
24518             this.dd.resetConstraints();
24519             this.dd.setXConstraint(
24520                 this.placement == Roo.SplitBar.LEFT ? c1 : c2, 
24521                 this.placement == Roo.SplitBar.LEFT ? c2 : c1
24522             );
24523             this.dd.setYConstraint(0, 0);
24524         }else{
24525             this.dd.resetConstraints();
24526             this.dd.setXConstraint(0, 0);
24527             this.dd.setYConstraint(
24528                 this.placement == Roo.SplitBar.TOP ? c1 : c2, 
24529                 this.placement == Roo.SplitBar.TOP ? c2 : c1
24530             );
24531          }
24532         this.dragSpecs.startSize = size;
24533         this.dragSpecs.startPoint = [x, y];
24534         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
24535     },
24536     
24537     /** 
24538      * @private Called after the drag operation by the DDProxy
24539      */
24540     onEndProxyDrag : function(e){
24541         Roo.get(this.proxy).setDisplayed(false);
24542         var endPoint = Roo.lib.Event.getXY(e);
24543         if(this.overlay){
24544             this.overlay.hide();
24545         }
24546         var newSize;
24547         if(this.orientation == Roo.SplitBar.HORIZONTAL){
24548             newSize = this.dragSpecs.startSize + 
24549                 (this.placement == Roo.SplitBar.LEFT ?
24550                     endPoint[0] - this.dragSpecs.startPoint[0] :
24551                     this.dragSpecs.startPoint[0] - endPoint[0]
24552                 );
24553         }else{
24554             newSize = this.dragSpecs.startSize + 
24555                 (this.placement == Roo.SplitBar.TOP ?
24556                     endPoint[1] - this.dragSpecs.startPoint[1] :
24557                     this.dragSpecs.startPoint[1] - endPoint[1]
24558                 );
24559         }
24560         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
24561         if(newSize != this.dragSpecs.startSize){
24562             if(this.fireEvent('beforeapply', this, newSize) !== false){
24563                 this.adapter.setElementSize(this, newSize);
24564                 this.fireEvent("moved", this, newSize);
24565                 this.fireEvent("resize", this, newSize);
24566             }
24567         }
24568     },
24569     
24570     /**
24571      * Get the adapter this SplitBar uses
24572      * @return The adapter object
24573      */
24574     getAdapter : function(){
24575         return this.adapter;
24576     },
24577     
24578     /**
24579      * Set the adapter this SplitBar uses
24580      * @param {Object} adapter A SplitBar adapter object
24581      */
24582     setAdapter : function(adapter){
24583         this.adapter = adapter;
24584         this.adapter.init(this);
24585     },
24586     
24587     /**
24588      * Gets the minimum size for the resizing element
24589      * @return {Number} The minimum size
24590      */
24591     getMinimumSize : function(){
24592         return this.minSize;
24593     },
24594     
24595     /**
24596      * Sets the minimum size for the resizing element
24597      * @param {Number} minSize The minimum size
24598      */
24599     setMinimumSize : function(minSize){
24600         this.minSize = minSize;
24601     },
24602     
24603     /**
24604      * Gets the maximum size for the resizing element
24605      * @return {Number} The maximum size
24606      */
24607     getMaximumSize : function(){
24608         return this.maxSize;
24609     },
24610     
24611     /**
24612      * Sets the maximum size for the resizing element
24613      * @param {Number} maxSize The maximum size
24614      */
24615     setMaximumSize : function(maxSize){
24616         this.maxSize = maxSize;
24617     },
24618     
24619     /**
24620      * Sets the initialize size for the resizing element
24621      * @param {Number} size The initial size
24622      */
24623     setCurrentSize : function(size){
24624         var oldAnimate = this.animate;
24625         this.animate = false;
24626         this.adapter.setElementSize(this, size);
24627         this.animate = oldAnimate;
24628     },
24629     
24630     /**
24631      * Destroy this splitbar. 
24632      * @param {Boolean} removeEl True to remove the element
24633      */
24634     destroy : function(removeEl){
24635         if(this.shim){
24636             this.shim.remove();
24637         }
24638         this.dd.unreg();
24639         this.proxy.parentNode.removeChild(this.proxy);
24640         if(removeEl){
24641             this.el.remove();
24642         }
24643     }
24644 });
24645
24646 /**
24647  * @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.
24648  */
24649 Roo.SplitBar.createProxy = function(dir){
24650     var proxy = new Roo.Element(document.createElement("div"));
24651     proxy.unselectable();
24652     var cls = 'x-splitbar-proxy';
24653     proxy.addClass(cls + ' ' + (dir == Roo.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
24654     document.body.appendChild(proxy.dom);
24655     return proxy.dom;
24656 };
24657
24658 /** 
24659  * @class Roo.SplitBar.BasicLayoutAdapter
24660  * Default Adapter. It assumes the splitter and resizing element are not positioned
24661  * elements and only gets/sets the width of the element. Generally used for table based layouts.
24662  */
24663 Roo.SplitBar.BasicLayoutAdapter = function(){
24664 };
24665
24666 Roo.SplitBar.BasicLayoutAdapter.prototype = {
24667     // do nothing for now
24668     init : function(s){
24669     
24670     },
24671     /**
24672      * Called before drag operations to get the current size of the resizing element. 
24673      * @param {Roo.SplitBar} s The SplitBar using this adapter
24674      */
24675      getElementSize : function(s){
24676         if(s.orientation == Roo.SplitBar.HORIZONTAL){
24677             return s.resizingEl.getWidth();
24678         }else{
24679             return s.resizingEl.getHeight();
24680         }
24681     },
24682     
24683     /**
24684      * Called after drag operations to set the size of the resizing element.
24685      * @param {Roo.SplitBar} s The SplitBar using this adapter
24686      * @param {Number} newSize The new size to set
24687      * @param {Function} onComplete A function to be invoked when resizing is complete
24688      */
24689     setElementSize : function(s, newSize, onComplete){
24690         if(s.orientation == Roo.SplitBar.HORIZONTAL){
24691             if(!s.animate){
24692                 s.resizingEl.setWidth(newSize);
24693                 if(onComplete){
24694                     onComplete(s, newSize);
24695                 }
24696             }else{
24697                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
24698             }
24699         }else{
24700             
24701             if(!s.animate){
24702                 s.resizingEl.setHeight(newSize);
24703                 if(onComplete){
24704                     onComplete(s, newSize);
24705                 }
24706             }else{
24707                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
24708             }
24709         }
24710     }
24711 };
24712
24713 /** 
24714  *@class Roo.SplitBar.AbsoluteLayoutAdapter
24715  * @extends Roo.SplitBar.BasicLayoutAdapter
24716  * Adapter that  moves the splitter element to align with the resized sizing element. 
24717  * Used with an absolute positioned SplitBar.
24718  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
24719  * document.body, make sure you assign an id to the body element.
24720  */
24721 Roo.SplitBar.AbsoluteLayoutAdapter = function(container){
24722     this.basic = new Roo.SplitBar.BasicLayoutAdapter();
24723     this.container = Roo.get(container);
24724 };
24725
24726 Roo.SplitBar.AbsoluteLayoutAdapter.prototype = {
24727     init : function(s){
24728         this.basic.init(s);
24729     },
24730     
24731     getElementSize : function(s){
24732         return this.basic.getElementSize(s);
24733     },
24734     
24735     setElementSize : function(s, newSize, onComplete){
24736         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
24737     },
24738     
24739     moveSplitter : function(s){
24740         var yes = Roo.SplitBar;
24741         switch(s.placement){
24742             case yes.LEFT:
24743                 s.el.setX(s.resizingEl.getRight());
24744                 break;
24745             case yes.RIGHT:
24746                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
24747                 break;
24748             case yes.TOP:
24749                 s.el.setY(s.resizingEl.getBottom());
24750                 break;
24751             case yes.BOTTOM:
24752                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
24753                 break;
24754         }
24755     }
24756 };
24757
24758 /**
24759  * Orientation constant - Create a vertical SplitBar
24760  * @static
24761  * @type Number
24762  */
24763 Roo.SplitBar.VERTICAL = 1;
24764
24765 /**
24766  * Orientation constant - Create a horizontal SplitBar
24767  * @static
24768  * @type Number
24769  */
24770 Roo.SplitBar.HORIZONTAL = 2;
24771
24772 /**
24773  * Placement constant - The resizing element is to the left of the splitter element
24774  * @static
24775  * @type Number
24776  */
24777 Roo.SplitBar.LEFT = 1;
24778
24779 /**
24780  * Placement constant - The resizing element is to the right of the splitter element
24781  * @static
24782  * @type Number
24783  */
24784 Roo.SplitBar.RIGHT = 2;
24785
24786 /**
24787  * Placement constant - The resizing element is positioned above the splitter element
24788  * @static
24789  * @type Number
24790  */
24791 Roo.SplitBar.TOP = 3;
24792
24793 /**
24794  * Placement constant - The resizing element is positioned under splitter element
24795  * @static
24796  * @type Number
24797  */
24798 Roo.SplitBar.BOTTOM = 4;
24799 /*
24800  * Based on:
24801  * Ext JS Library 1.1.1
24802  * Copyright(c) 2006-2007, Ext JS, LLC.
24803  *
24804  * Originally Released Under LGPL - original licence link has changed is not relivant.
24805  *
24806  * Fork - LGPL
24807  * <script type="text/javascript">
24808  */
24809
24810 /**
24811  * @class Roo.View
24812  * @extends Roo.util.Observable
24813  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
24814  * This class also supports single and multi selection modes. <br>
24815  * Create a data model bound view:
24816  <pre><code>
24817  var store = new Roo.data.Store(...);
24818
24819  var view = new Roo.View({
24820     el : "my-element",
24821     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
24822  
24823     singleSelect: true,
24824     selectedClass: "ydataview-selected",
24825     store: store
24826  });
24827
24828  // listen for node click?
24829  view.on("click", function(vw, index, node, e){
24830  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
24831  });
24832
24833  // load XML data
24834  dataModel.load("foobar.xml");
24835  </code></pre>
24836  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
24837  * <br><br>
24838  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
24839  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
24840  * 
24841  * Note: old style constructor is still suported (container, template, config)
24842  * 
24843  * @constructor
24844  * Create a new View
24845  * @param {Object} config The config object
24846  * 
24847  */
24848 Roo.View = function(config, depreciated_tpl, depreciated_config){
24849     
24850     if (typeof(depreciated_tpl) == 'undefined') {
24851         // new way.. - universal constructor.
24852         Roo.apply(this, config);
24853         this.el  = Roo.get(this.el);
24854     } else {
24855         // old format..
24856         this.el  = Roo.get(config);
24857         this.tpl = depreciated_tpl;
24858         Roo.apply(this, depreciated_config);
24859     }
24860     this.wrapEl  = this.el.wrap().wrap();
24861     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
24862     
24863     
24864     if(typeof(this.tpl) == "string"){
24865         this.tpl = new Roo.Template(this.tpl);
24866     } else {
24867         // support xtype ctors..
24868         this.tpl = new Roo.factory(this.tpl, Roo);
24869     }
24870     
24871     
24872     this.tpl.compile();
24873    
24874   
24875     
24876      
24877     /** @private */
24878     this.addEvents({
24879         /**
24880          * @event beforeclick
24881          * Fires before a click is processed. Returns false to cancel the default action.
24882          * @param {Roo.View} this
24883          * @param {Number} index The index of the target node
24884          * @param {HTMLElement} node The target node
24885          * @param {Roo.EventObject} e The raw event object
24886          */
24887             "beforeclick" : true,
24888         /**
24889          * @event click
24890          * Fires when a template node is clicked.
24891          * @param {Roo.View} this
24892          * @param {Number} index The index of the target node
24893          * @param {HTMLElement} node The target node
24894          * @param {Roo.EventObject} e The raw event object
24895          */
24896             "click" : true,
24897         /**
24898          * @event dblclick
24899          * Fires when a template node is double clicked.
24900          * @param {Roo.View} this
24901          * @param {Number} index The index of the target node
24902          * @param {HTMLElement} node The target node
24903          * @param {Roo.EventObject} e The raw event object
24904          */
24905             "dblclick" : true,
24906         /**
24907          * @event contextmenu
24908          * Fires when a template node is right clicked.
24909          * @param {Roo.View} this
24910          * @param {Number} index The index of the target node
24911          * @param {HTMLElement} node The target node
24912          * @param {Roo.EventObject} e The raw event object
24913          */
24914             "contextmenu" : true,
24915         /**
24916          * @event selectionchange
24917          * Fires when the selected nodes change.
24918          * @param {Roo.View} this
24919          * @param {Array} selections Array of the selected nodes
24920          */
24921             "selectionchange" : true,
24922     
24923         /**
24924          * @event beforeselect
24925          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
24926          * @param {Roo.View} this
24927          * @param {HTMLElement} node The node to be selected
24928          * @param {Array} selections Array of currently selected nodes
24929          */
24930             "beforeselect" : true,
24931         /**
24932          * @event preparedata
24933          * Fires on every row to render, to allow you to change the data.
24934          * @param {Roo.View} this
24935          * @param {Object} data to be rendered (change this)
24936          */
24937           "preparedata" : true
24938           
24939           
24940         });
24941
24942
24943
24944     this.el.on({
24945         "click": this.onClick,
24946         "dblclick": this.onDblClick,
24947         "contextmenu": this.onContextMenu,
24948         scope:this
24949     });
24950
24951     this.selections = [];
24952     this.nodes = [];
24953     this.cmp = new Roo.CompositeElementLite([]);
24954     if(this.store){
24955         this.store = Roo.factory(this.store, Roo.data);
24956         this.setStore(this.store, true);
24957     }
24958     
24959     if ( this.footer && this.footer.xtype) {
24960            
24961          var fctr = this.wrapEl.appendChild(document.createElement("div"));
24962         
24963         this.footer.dataSource = this.store
24964         this.footer.container = fctr;
24965         this.footer = Roo.factory(this.footer, Roo);
24966         fctr.insertFirst(this.el);
24967         
24968         // this is a bit insane - as the paging toolbar seems to detach the el..
24969 //        dom.parentNode.parentNode.parentNode
24970          // they get detached?
24971     }
24972     
24973     
24974     Roo.View.superclass.constructor.call(this);
24975     
24976     
24977 };
24978
24979 Roo.extend(Roo.View, Roo.util.Observable, {
24980     
24981      /**
24982      * @cfg {Roo.data.Store} store Data store to load data from.
24983      */
24984     store : false,
24985     
24986     /**
24987      * @cfg {String|Roo.Element} el The container element.
24988      */
24989     el : '',
24990     
24991     /**
24992      * @cfg {String|Roo.Template} tpl The template used by this View 
24993      */
24994     tpl : false,
24995     /**
24996      * @cfg {String} dataName the named area of the template to use as the data area
24997      *                          Works with domtemplates roo-name="name"
24998      */
24999     dataName: false,
25000     /**
25001      * @cfg {String} selectedClass The css class to add to selected nodes
25002      */
25003     selectedClass : "x-view-selected",
25004      /**
25005      * @cfg {String} emptyText The empty text to show when nothing is loaded.
25006      */
25007     emptyText : "",
25008     
25009     /**
25010      * @cfg {String} text to display on mask (default Loading)
25011      */
25012     mask : false,
25013     /**
25014      * @cfg {Boolean} multiSelect Allow multiple selection
25015      */
25016     multiSelect : false,
25017     /**
25018      * @cfg {Boolean} singleSelect Allow single selection
25019      */
25020     singleSelect:  false,
25021     
25022     /**
25023      * @cfg {Boolean} toggleSelect - selecting 
25024      */
25025     toggleSelect : false,
25026     
25027     /**
25028      * Returns the element this view is bound to.
25029      * @return {Roo.Element}
25030      */
25031     getEl : function(){
25032         return this.wrapEl;
25033     },
25034     
25035     
25036
25037     /**
25038      * Refreshes the view. - called by datachanged on the store. - do not call directly.
25039      */
25040     refresh : function(){
25041         Roo.log('refresh');
25042         var t = this.tpl;
25043         
25044         // if we are using something like 'domtemplate', then
25045         // the what gets used is:
25046         // t.applySubtemplate(NAME, data, wrapping data..)
25047         // the outer template then get' applied with
25048         //     the store 'extra data'
25049         // and the body get's added to the
25050         //      roo-name="data" node?
25051         //      <span class='roo-tpl-{name}'></span> ?????
25052         
25053         
25054         
25055         this.clearSelections();
25056         this.el.update("");
25057         var html = [];
25058         var records = this.store.getRange();
25059         if(records.length < 1) {
25060             
25061             // is this valid??  = should it render a template??
25062             
25063             this.el.update(this.emptyText);
25064             return;
25065         }
25066         var el = this.el;
25067         if (this.dataName) {
25068             this.el.update(t.apply(this.store.meta)); //????
25069             el = this.el.child('.roo-tpl-' + this.dataName);
25070         }
25071         
25072         for(var i = 0, len = records.length; i < len; i++){
25073             var data = this.prepareData(records[i].data, i, records[i]);
25074             this.fireEvent("preparedata", this, data, i, records[i]);
25075             html[html.length] = Roo.util.Format.trim(
25076                 this.dataName ?
25077                     t.applySubtemplate(this.dataName, data, this.store.meta) :
25078                     t.apply(data)
25079             );
25080         }
25081         
25082         
25083         
25084         el.update(html.join(""));
25085         this.nodes = el.dom.childNodes;
25086         this.updateIndexes(0);
25087     },
25088     
25089
25090     /**
25091      * Function to override to reformat the data that is sent to
25092      * the template for each node.
25093      * DEPRICATED - use the preparedata event handler.
25094      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
25095      * a JSON object for an UpdateManager bound view).
25096      */
25097     prepareData : function(data, index, record)
25098     {
25099         this.fireEvent("preparedata", this, data, index, record);
25100         return data;
25101     },
25102
25103     onUpdate : function(ds, record){
25104          Roo.log('on update');   
25105         this.clearSelections();
25106         var index = this.store.indexOf(record);
25107         var n = this.nodes[index];
25108         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
25109         n.parentNode.removeChild(n);
25110         this.updateIndexes(index, index);
25111     },
25112
25113     
25114     
25115 // --------- FIXME     
25116     onAdd : function(ds, records, index)
25117     {
25118         Roo.log(['on Add', ds, records, index] );        
25119         this.clearSelections();
25120         if(this.nodes.length == 0){
25121             this.refresh();
25122             return;
25123         }
25124         var n = this.nodes[index];
25125         for(var i = 0, len = records.length; i < len; i++){
25126             var d = this.prepareData(records[i].data, i, records[i]);
25127             if(n){
25128                 this.tpl.insertBefore(n, d);
25129             }else{
25130                 
25131                 this.tpl.append(this.el, d);
25132             }
25133         }
25134         this.updateIndexes(index);
25135     },
25136
25137     onRemove : function(ds, record, index){
25138         Roo.log('onRemove');
25139         this.clearSelections();
25140         var el = this.dataName  ?
25141             this.el.child('.roo-tpl-' + this.dataName) :
25142             this.el; 
25143         
25144         el.dom.removeChild(this.nodes[index]);
25145         this.updateIndexes(index);
25146     },
25147
25148     /**
25149      * Refresh an individual node.
25150      * @param {Number} index
25151      */
25152     refreshNode : function(index){
25153         this.onUpdate(this.store, this.store.getAt(index));
25154     },
25155
25156     updateIndexes : function(startIndex, endIndex){
25157         var ns = this.nodes;
25158         startIndex = startIndex || 0;
25159         endIndex = endIndex || ns.length - 1;
25160         for(var i = startIndex; i <= endIndex; i++){
25161             ns[i].nodeIndex = i;
25162         }
25163     },
25164
25165     /**
25166      * Changes the data store this view uses and refresh the view.
25167      * @param {Store} store
25168      */
25169     setStore : function(store, initial){
25170         if(!initial && this.store){
25171             this.store.un("datachanged", this.refresh);
25172             this.store.un("add", this.onAdd);
25173             this.store.un("remove", this.onRemove);
25174             this.store.un("update", this.onUpdate);
25175             this.store.un("clear", this.refresh);
25176             this.store.un("beforeload", this.onBeforeLoad);
25177             this.store.un("load", this.onLoad);
25178             this.store.un("loadexception", this.onLoad);
25179         }
25180         if(store){
25181           
25182             store.on("datachanged", this.refresh, this);
25183             store.on("add", this.onAdd, this);
25184             store.on("remove", this.onRemove, this);
25185             store.on("update", this.onUpdate, this);
25186             store.on("clear", this.refresh, this);
25187             store.on("beforeload", this.onBeforeLoad, this);
25188             store.on("load", this.onLoad, this);
25189             store.on("loadexception", this.onLoad, this);
25190         }
25191         
25192         if(store){
25193             this.refresh();
25194         }
25195     },
25196     /**
25197      * onbeforeLoad - masks the loading area.
25198      *
25199      */
25200     onBeforeLoad : function(store,opts)
25201     {
25202          Roo.log('onBeforeLoad');   
25203         if (!opts.add) {
25204             this.el.update("");
25205         }
25206         this.el.mask(this.mask ? this.mask : "Loading" ); 
25207     },
25208     onLoad : function ()
25209     {
25210         this.el.unmask();
25211     },
25212     
25213
25214     /**
25215      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
25216      * @param {HTMLElement} node
25217      * @return {HTMLElement} The template node
25218      */
25219     findItemFromChild : function(node){
25220         var el = this.dataName  ?
25221             this.el.child('.roo-tpl-' + this.dataName,true) :
25222             this.el.dom; 
25223         
25224         if(!node || node.parentNode == el){
25225                     return node;
25226             }
25227             var p = node.parentNode;
25228             while(p && p != el){
25229             if(p.parentNode == el){
25230                 return p;
25231             }
25232             p = p.parentNode;
25233         }
25234             return null;
25235     },
25236
25237     /** @ignore */
25238     onClick : function(e){
25239         var item = this.findItemFromChild(e.getTarget());
25240         if(item){
25241             var index = this.indexOf(item);
25242             if(this.onItemClick(item, index, e) !== false){
25243                 this.fireEvent("click", this, index, item, e);
25244             }
25245         }else{
25246             this.clearSelections();
25247         }
25248     },
25249
25250     /** @ignore */
25251     onContextMenu : function(e){
25252         var item = this.findItemFromChild(e.getTarget());
25253         if(item){
25254             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
25255         }
25256     },
25257
25258     /** @ignore */
25259     onDblClick : function(e){
25260         var item = this.findItemFromChild(e.getTarget());
25261         if(item){
25262             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
25263         }
25264     },
25265
25266     onItemClick : function(item, index, e)
25267     {
25268         if(this.fireEvent("beforeclick", this, index, item, e) === false){
25269             return false;
25270         }
25271         if (this.toggleSelect) {
25272             var m = this.isSelected(item) ? 'unselect' : 'select';
25273             Roo.log(m);
25274             var _t = this;
25275             _t[m](item, true, false);
25276             return true;
25277         }
25278         if(this.multiSelect || this.singleSelect){
25279             if(this.multiSelect && e.shiftKey && this.lastSelection){
25280                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
25281             }else{
25282                 this.select(item, this.multiSelect && e.ctrlKey);
25283                 this.lastSelection = item;
25284             }
25285             e.preventDefault();
25286         }
25287         return true;
25288     },
25289
25290     /**
25291      * Get the number of selected nodes.
25292      * @return {Number}
25293      */
25294     getSelectionCount : function(){
25295         return this.selections.length;
25296     },
25297
25298     /**
25299      * Get the currently selected nodes.
25300      * @return {Array} An array of HTMLElements
25301      */
25302     getSelectedNodes : function(){
25303         return this.selections;
25304     },
25305
25306     /**
25307      * Get the indexes of the selected nodes.
25308      * @return {Array}
25309      */
25310     getSelectedIndexes : function(){
25311         var indexes = [], s = this.selections;
25312         for(var i = 0, len = s.length; i < len; i++){
25313             indexes.push(s[i].nodeIndex);
25314         }
25315         return indexes;
25316     },
25317
25318     /**
25319      * Clear all selections
25320      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
25321      */
25322     clearSelections : function(suppressEvent){
25323         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
25324             this.cmp.elements = this.selections;
25325             this.cmp.removeClass(this.selectedClass);
25326             this.selections = [];
25327             if(!suppressEvent){
25328                 this.fireEvent("selectionchange", this, this.selections);
25329             }
25330         }
25331     },
25332
25333     /**
25334      * Returns true if the passed node is selected
25335      * @param {HTMLElement/Number} node The node or node index
25336      * @return {Boolean}
25337      */
25338     isSelected : function(node){
25339         var s = this.selections;
25340         if(s.length < 1){
25341             return false;
25342         }
25343         node = this.getNode(node);
25344         return s.indexOf(node) !== -1;
25345     },
25346
25347     /**
25348      * Selects nodes.
25349      * @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
25350      * @param {Boolean} keepExisting (optional) true to keep existing selections
25351      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
25352      */
25353     select : function(nodeInfo, keepExisting, suppressEvent){
25354         if(nodeInfo instanceof Array){
25355             if(!keepExisting){
25356                 this.clearSelections(true);
25357             }
25358             for(var i = 0, len = nodeInfo.length; i < len; i++){
25359                 this.select(nodeInfo[i], true, true);
25360             }
25361             return;
25362         } 
25363         var node = this.getNode(nodeInfo);
25364         if(!node || this.isSelected(node)){
25365             return; // already selected.
25366         }
25367         if(!keepExisting){
25368             this.clearSelections(true);
25369         }
25370         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
25371             Roo.fly(node).addClass(this.selectedClass);
25372             this.selections.push(node);
25373             if(!suppressEvent){
25374                 this.fireEvent("selectionchange", this, this.selections);
25375             }
25376         }
25377         
25378         
25379     },
25380       /**
25381      * Unselects nodes.
25382      * @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
25383      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
25384      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
25385      */
25386     unselect : function(nodeInfo, keepExisting, suppressEvent)
25387     {
25388         if(nodeInfo instanceof Array){
25389             Roo.each(this.selections, function(s) {
25390                 this.unselect(s, nodeInfo);
25391             }, this);
25392             return;
25393         }
25394         var node = this.getNode(nodeInfo);
25395         if(!node || !this.isSelected(node)){
25396             Roo.log("not selected");
25397             return; // not selected.
25398         }
25399         // fireevent???
25400         var ns = [];
25401         Roo.each(this.selections, function(s) {
25402             if (s == node ) {
25403                 Roo.fly(node).removeClass(this.selectedClass);
25404
25405                 return;
25406             }
25407             ns.push(s);
25408         },this);
25409         
25410         this.selections= ns;
25411         this.fireEvent("selectionchange", this, this.selections);
25412     },
25413
25414     /**
25415      * Gets a template node.
25416      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
25417      * @return {HTMLElement} The node or null if it wasn't found
25418      */
25419     getNode : function(nodeInfo){
25420         if(typeof nodeInfo == "string"){
25421             return document.getElementById(nodeInfo);
25422         }else if(typeof nodeInfo == "number"){
25423             return this.nodes[nodeInfo];
25424         }
25425         return nodeInfo;
25426     },
25427
25428     /**
25429      * Gets a range template nodes.
25430      * @param {Number} startIndex
25431      * @param {Number} endIndex
25432      * @return {Array} An array of nodes
25433      */
25434     getNodes : function(start, end){
25435         var ns = this.nodes;
25436         start = start || 0;
25437         end = typeof end == "undefined" ? ns.length - 1 : end;
25438         var nodes = [];
25439         if(start <= end){
25440             for(var i = start; i <= end; i++){
25441                 nodes.push(ns[i]);
25442             }
25443         } else{
25444             for(var i = start; i >= end; i--){
25445                 nodes.push(ns[i]);
25446             }
25447         }
25448         return nodes;
25449     },
25450
25451     /**
25452      * Finds the index of the passed node
25453      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
25454      * @return {Number} The index of the node or -1
25455      */
25456     indexOf : function(node){
25457         node = this.getNode(node);
25458         if(typeof node.nodeIndex == "number"){
25459             return node.nodeIndex;
25460         }
25461         var ns = this.nodes;
25462         for(var i = 0, len = ns.length; i < len; i++){
25463             if(ns[i] == node){
25464                 return i;
25465             }
25466         }
25467         return -1;
25468     }
25469 });
25470 /*
25471  * Based on:
25472  * Ext JS Library 1.1.1
25473  * Copyright(c) 2006-2007, Ext JS, LLC.
25474  *
25475  * Originally Released Under LGPL - original licence link has changed is not relivant.
25476  *
25477  * Fork - LGPL
25478  * <script type="text/javascript">
25479  */
25480
25481 /**
25482  * @class Roo.JsonView
25483  * @extends Roo.View
25484  * Shortcut class to create a JSON + {@link Roo.UpdateManager} template view. Usage:
25485 <pre><code>
25486 var view = new Roo.JsonView({
25487     container: "my-element",
25488     tpl: '&lt;div id="{id}"&gt;{foo} - {bar}&lt;/div&gt;', // auto create template
25489     multiSelect: true, 
25490     jsonRoot: "data" 
25491 });
25492
25493 // listen for node click?
25494 view.on("click", function(vw, index, node, e){
25495     alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
25496 });
25497
25498 // direct load of JSON data
25499 view.load("foobar.php");
25500
25501 // Example from my blog list
25502 var tpl = new Roo.Template(
25503     '&lt;div class="entry"&gt;' +
25504     '&lt;a class="entry-title" href="{link}"&gt;{title}&lt;/a&gt;' +
25505     "&lt;h4&gt;{date} by {author} | {comments} Comments&lt;/h4&gt;{description}" +
25506     "&lt;/div&gt;&lt;hr /&gt;"
25507 );
25508
25509 var moreView = new Roo.JsonView({
25510     container :  "entry-list", 
25511     template : tpl,
25512     jsonRoot: "posts"
25513 });
25514 moreView.on("beforerender", this.sortEntries, this);
25515 moreView.load({
25516     url: "/blog/get-posts.php",
25517     params: "allposts=true",
25518     text: "Loading Blog Entries..."
25519 });
25520 </code></pre>
25521
25522 * Note: old code is supported with arguments : (container, template, config)
25523
25524
25525  * @constructor
25526  * Create a new JsonView
25527  * 
25528  * @param {Object} config The config object
25529  * 
25530  */
25531 Roo.JsonView = function(config, depreciated_tpl, depreciated_config){
25532     
25533     
25534     Roo.JsonView.superclass.constructor.call(this, config, depreciated_tpl, depreciated_config);
25535
25536     var um = this.el.getUpdateManager();
25537     um.setRenderer(this);
25538     um.on("update", this.onLoad, this);
25539     um.on("failure", this.onLoadException, this);
25540
25541     /**
25542      * @event beforerender
25543      * Fires before rendering of the downloaded JSON data.
25544      * @param {Roo.JsonView} this
25545      * @param {Object} data The JSON data loaded
25546      */
25547     /**
25548      * @event load
25549      * Fires when data is loaded.
25550      * @param {Roo.JsonView} this
25551      * @param {Object} data The JSON data loaded
25552      * @param {Object} response The raw Connect response object
25553      */
25554     /**
25555      * @event loadexception
25556      * Fires when loading fails.
25557      * @param {Roo.JsonView} this
25558      * @param {Object} response The raw Connect response object
25559      */
25560     this.addEvents({
25561         'beforerender' : true,
25562         'load' : true,
25563         'loadexception' : true
25564     });
25565 };
25566 Roo.extend(Roo.JsonView, Roo.View, {
25567     /**
25568      * @type {String} The root property in the loaded JSON object that contains the data
25569      */
25570     jsonRoot : "",
25571
25572     /**
25573      * Refreshes the view.
25574      */
25575     refresh : function(){
25576         this.clearSelections();
25577         this.el.update("");
25578         var html = [];
25579         var o = this.jsonData;
25580         if(o && o.length > 0){
25581             for(var i = 0, len = o.length; i < len; i++){
25582                 var data = this.prepareData(o[i], i, o);
25583                 html[html.length] = this.tpl.apply(data);
25584             }
25585         }else{
25586             html.push(this.emptyText);
25587         }
25588         this.el.update(html.join(""));
25589         this.nodes = this.el.dom.childNodes;
25590         this.updateIndexes(0);
25591     },
25592
25593     /**
25594      * 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.
25595      * @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:
25596      <pre><code>
25597      view.load({
25598          url: "your-url.php",
25599          params: {param1: "foo", param2: "bar"}, // or a URL encoded string
25600          callback: yourFunction,
25601          scope: yourObject, //(optional scope)
25602          discardUrl: false,
25603          nocache: false,
25604          text: "Loading...",
25605          timeout: 30,
25606          scripts: false
25607      });
25608      </code></pre>
25609      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
25610      * 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.
25611      * @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}
25612      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
25613      * @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.
25614      */
25615     load : function(){
25616         var um = this.el.getUpdateManager();
25617         um.update.apply(um, arguments);
25618     },
25619
25620     render : function(el, response){
25621         this.clearSelections();
25622         this.el.update("");
25623         var o;
25624         try{
25625             o = Roo.util.JSON.decode(response.responseText);
25626             if(this.jsonRoot){
25627                 
25628                 o = o[this.jsonRoot];
25629             }
25630         } catch(e){
25631         }
25632         /**
25633          * The current JSON data or null
25634          */
25635         this.jsonData = o;
25636         this.beforeRender();
25637         this.refresh();
25638     },
25639
25640 /**
25641  * Get the number of records in the current JSON dataset
25642  * @return {Number}
25643  */
25644     getCount : function(){
25645         return this.jsonData ? this.jsonData.length : 0;
25646     },
25647
25648 /**
25649  * Returns the JSON object for the specified node(s)
25650  * @param {HTMLElement/Array} node The node or an array of nodes
25651  * @return {Object/Array} If you pass in an array, you get an array back, otherwise
25652  * you get the JSON object for the node
25653  */
25654     getNodeData : function(node){
25655         if(node instanceof Array){
25656             var data = [];
25657             for(var i = 0, len = node.length; i < len; i++){
25658                 data.push(this.getNodeData(node[i]));
25659             }
25660             return data;
25661         }
25662         return this.jsonData[this.indexOf(node)] || null;
25663     },
25664
25665     beforeRender : function(){
25666         this.snapshot = this.jsonData;
25667         if(this.sortInfo){
25668             this.sort.apply(this, this.sortInfo);
25669         }
25670         this.fireEvent("beforerender", this, this.jsonData);
25671     },
25672
25673     onLoad : function(el, o){
25674         this.fireEvent("load", this, this.jsonData, o);
25675     },
25676
25677     onLoadException : function(el, o){
25678         this.fireEvent("loadexception", this, o);
25679     },
25680
25681 /**
25682  * Filter the data by a specific property.
25683  * @param {String} property A property on your JSON objects
25684  * @param {String/RegExp} value Either string that the property values
25685  * should start with, or a RegExp to test against the property
25686  */
25687     filter : function(property, value){
25688         if(this.jsonData){
25689             var data = [];
25690             var ss = this.snapshot;
25691             if(typeof value == "string"){
25692                 var vlen = value.length;
25693                 if(vlen == 0){
25694                     this.clearFilter();
25695                     return;
25696                 }
25697                 value = value.toLowerCase();
25698                 for(var i = 0, len = ss.length; i < len; i++){
25699                     var o = ss[i];
25700                     if(o[property].substr(0, vlen).toLowerCase() == value){
25701                         data.push(o);
25702                     }
25703                 }
25704             } else if(value.exec){ // regex?
25705                 for(var i = 0, len = ss.length; i < len; i++){
25706                     var o = ss[i];
25707                     if(value.test(o[property])){
25708                         data.push(o);
25709                     }
25710                 }
25711             } else{
25712                 return;
25713             }
25714             this.jsonData = data;
25715             this.refresh();
25716         }
25717     },
25718
25719 /**
25720  * Filter by a function. The passed function will be called with each
25721  * object in the current dataset. If the function returns true the value is kept,
25722  * otherwise it is filtered.
25723  * @param {Function} fn
25724  * @param {Object} scope (optional) The scope of the function (defaults to this JsonView)
25725  */
25726     filterBy : function(fn, scope){
25727         if(this.jsonData){
25728             var data = [];
25729             var ss = this.snapshot;
25730             for(var i = 0, len = ss.length; i < len; i++){
25731                 var o = ss[i];
25732                 if(fn.call(scope || this, o)){
25733                     data.push(o);
25734                 }
25735             }
25736             this.jsonData = data;
25737             this.refresh();
25738         }
25739     },
25740
25741 /**
25742  * Clears the current filter.
25743  */
25744     clearFilter : function(){
25745         if(this.snapshot && this.jsonData != this.snapshot){
25746             this.jsonData = this.snapshot;
25747             this.refresh();
25748         }
25749     },
25750
25751
25752 /**
25753  * Sorts the data for this view and refreshes it.
25754  * @param {String} property A property on your JSON objects to sort on
25755  * @param {String} direction (optional) "desc" or "asc" (defaults to "asc")
25756  * @param {Function} sortType (optional) A function to call to convert the data to a sortable value.
25757  */
25758     sort : function(property, dir, sortType){
25759         this.sortInfo = Array.prototype.slice.call(arguments, 0);
25760         if(this.jsonData){
25761             var p = property;
25762             var dsc = dir && dir.toLowerCase() == "desc";
25763             var f = function(o1, o2){
25764                 var v1 = sortType ? sortType(o1[p]) : o1[p];
25765                 var v2 = sortType ? sortType(o2[p]) : o2[p];
25766                 ;
25767                 if(v1 < v2){
25768                     return dsc ? +1 : -1;
25769                 } else if(v1 > v2){
25770                     return dsc ? -1 : +1;
25771                 } else{
25772                     return 0;
25773                 }
25774             };
25775             this.jsonData.sort(f);
25776             this.refresh();
25777             if(this.jsonData != this.snapshot){
25778                 this.snapshot.sort(f);
25779             }
25780         }
25781     }
25782 });/*
25783  * Based on:
25784  * Ext JS Library 1.1.1
25785  * Copyright(c) 2006-2007, Ext JS, LLC.
25786  *
25787  * Originally Released Under LGPL - original licence link has changed is not relivant.
25788  *
25789  * Fork - LGPL
25790  * <script type="text/javascript">
25791  */
25792  
25793
25794 /**
25795  * @class Roo.ColorPalette
25796  * @extends Roo.Component
25797  * Simple color palette class for choosing colors.  The palette can be rendered to any container.<br />
25798  * Here's an example of typical usage:
25799  * <pre><code>
25800 var cp = new Roo.ColorPalette({value:'993300'});  // initial selected color
25801 cp.render('my-div');
25802
25803 cp.on('select', function(palette, selColor){
25804     // do something with selColor
25805 });
25806 </code></pre>
25807  * @constructor
25808  * Create a new ColorPalette
25809  * @param {Object} config The config object
25810  */
25811 Roo.ColorPalette = function(config){
25812     Roo.ColorPalette.superclass.constructor.call(this, config);
25813     this.addEvents({
25814         /**
25815              * @event select
25816              * Fires when a color is selected
25817              * @param {ColorPalette} this
25818              * @param {String} color The 6-digit color hex code (without the # symbol)
25819              */
25820         select: true
25821     });
25822
25823     if(this.handler){
25824         this.on("select", this.handler, this.scope, true);
25825     }
25826 };
25827 Roo.extend(Roo.ColorPalette, Roo.Component, {
25828     /**
25829      * @cfg {String} itemCls
25830      * The CSS class to apply to the containing element (defaults to "x-color-palette")
25831      */
25832     itemCls : "x-color-palette",
25833     /**
25834      * @cfg {String} value
25835      * The initial color to highlight (should be a valid 6-digit color hex code without the # symbol).  Note that
25836      * the hex codes are case-sensitive.
25837      */
25838     value : null,
25839     clickEvent:'click',
25840     // private
25841     ctype: "Roo.ColorPalette",
25842
25843     /**
25844      * @cfg {Boolean} allowReselect If set to true then reselecting a color that is already selected fires the selection event
25845      */
25846     allowReselect : false,
25847
25848     /**
25849      * <p>An array of 6-digit color hex code strings (without the # symbol).  This array can contain any number
25850      * of colors, and each hex code should be unique.  The width of the palette is controlled via CSS by adjusting
25851      * the width property of the 'x-color-palette' class (or assigning a custom class), so you can balance the number
25852      * of colors with the width setting until the box is symmetrical.</p>
25853      * <p>You can override individual colors if needed:</p>
25854      * <pre><code>
25855 var cp = new Roo.ColorPalette();
25856 cp.colors[0] = "FF0000";  // change the first box to red
25857 </code></pre>
25858
25859 Or you can provide a custom array of your own for complete control:
25860 <pre><code>
25861 var cp = new Roo.ColorPalette();
25862 cp.colors = ["000000", "993300", "333300"];
25863 </code></pre>
25864      * @type Array
25865      */
25866     colors : [
25867         "000000", "993300", "333300", "003300", "003366", "000080", "333399", "333333",
25868         "800000", "FF6600", "808000", "008000", "008080", "0000FF", "666699", "808080",
25869         "FF0000", "FF9900", "99CC00", "339966", "33CCCC", "3366FF", "800080", "969696",
25870         "FF00FF", "FFCC00", "FFFF00", "00FF00", "00FFFF", "00CCFF", "993366", "C0C0C0",
25871         "FF99CC", "FFCC99", "FFFF99", "CCFFCC", "CCFFFF", "99CCFF", "CC99FF", "FFFFFF"
25872     ],
25873
25874     // private
25875     onRender : function(container, position){
25876         var t = new Roo.MasterTemplate(
25877             '<tpl><a href="#" class="color-{0}" hidefocus="on"><em><span style="background:#{0}" unselectable="on">&#160;</span></em></a></tpl>'
25878         );
25879         var c = this.colors;
25880         for(var i = 0, len = c.length; i < len; i++){
25881             t.add([c[i]]);
25882         }
25883         var el = document.createElement("div");
25884         el.className = this.itemCls;
25885         t.overwrite(el);
25886         container.dom.insertBefore(el, position);
25887         this.el = Roo.get(el);
25888         this.el.on(this.clickEvent, this.handleClick,  this, {delegate: "a"});
25889         if(this.clickEvent != 'click'){
25890             this.el.on('click', Roo.emptyFn,  this, {delegate: "a", preventDefault:true});
25891         }
25892     },
25893
25894     // private
25895     afterRender : function(){
25896         Roo.ColorPalette.superclass.afterRender.call(this);
25897         if(this.value){
25898             var s = this.value;
25899             this.value = null;
25900             this.select(s);
25901         }
25902     },
25903
25904     // private
25905     handleClick : function(e, t){
25906         e.preventDefault();
25907         if(!this.disabled){
25908             var c = t.className.match(/(?:^|\s)color-(.{6})(?:\s|$)/)[1];
25909             this.select(c.toUpperCase());
25910         }
25911     },
25912
25913     /**
25914      * Selects the specified color in the palette (fires the select event)
25915      * @param {String} color A valid 6-digit color hex code (# will be stripped if included)
25916      */
25917     select : function(color){
25918         color = color.replace("#", "");
25919         if(color != this.value || this.allowReselect){
25920             var el = this.el;
25921             if(this.value){
25922                 el.child("a.color-"+this.value).removeClass("x-color-palette-sel");
25923             }
25924             el.child("a.color-"+color).addClass("x-color-palette-sel");
25925             this.value = color;
25926             this.fireEvent("select", this, color);
25927         }
25928     }
25929 });/*
25930  * Based on:
25931  * Ext JS Library 1.1.1
25932  * Copyright(c) 2006-2007, Ext JS, LLC.
25933  *
25934  * Originally Released Under LGPL - original licence link has changed is not relivant.
25935  *
25936  * Fork - LGPL
25937  * <script type="text/javascript">
25938  */
25939  
25940 /**
25941  * @class Roo.DatePicker
25942  * @extends Roo.Component
25943  * Simple date picker class.
25944  * @constructor
25945  * Create a new DatePicker
25946  * @param {Object} config The config object
25947  */
25948 Roo.DatePicker = function(config){
25949     Roo.DatePicker.superclass.constructor.call(this, config);
25950
25951     this.value = config && config.value ?
25952                  config.value.clearTime() : new Date().clearTime();
25953
25954     this.addEvents({
25955         /**
25956              * @event select
25957              * Fires when a date is selected
25958              * @param {DatePicker} this
25959              * @param {Date} date The selected date
25960              */
25961         'select': true,
25962         /**
25963              * @event monthchange
25964              * Fires when the displayed month changes 
25965              * @param {DatePicker} this
25966              * @param {Date} date The selected month
25967              */
25968         'monthchange': true
25969     });
25970
25971     if(this.handler){
25972         this.on("select", this.handler,  this.scope || this);
25973     }
25974     // build the disabledDatesRE
25975     if(!this.disabledDatesRE && this.disabledDates){
25976         var dd = this.disabledDates;
25977         var re = "(?:";
25978         for(var i = 0; i < dd.length; i++){
25979             re += dd[i];
25980             if(i != dd.length-1) re += "|";
25981         }
25982         this.disabledDatesRE = new RegExp(re + ")");
25983     }
25984 };
25985
25986 Roo.extend(Roo.DatePicker, Roo.Component, {
25987     /**
25988      * @cfg {String} todayText
25989      * The text to display on the button that selects the current date (defaults to "Today")
25990      */
25991     todayText : "Today",
25992     /**
25993      * @cfg {String} okText
25994      * The text to display on the ok button
25995      */
25996     okText : "&#160;OK&#160;", // &#160; to give the user extra clicking room
25997     /**
25998      * @cfg {String} cancelText
25999      * The text to display on the cancel button
26000      */
26001     cancelText : "Cancel",
26002     /**
26003      * @cfg {String} todayTip
26004      * The tooltip to display for the button that selects the current date (defaults to "{current date} (Spacebar)")
26005      */
26006     todayTip : "{0} (Spacebar)",
26007     /**
26008      * @cfg {Date} minDate
26009      * Minimum allowable date (JavaScript date object, defaults to null)
26010      */
26011     minDate : null,
26012     /**
26013      * @cfg {Date} maxDate
26014      * Maximum allowable date (JavaScript date object, defaults to null)
26015      */
26016     maxDate : null,
26017     /**
26018      * @cfg {String} minText
26019      * The error text to display if the minDate validation fails (defaults to "This date is before the minimum date")
26020      */
26021     minText : "This date is before the minimum date",
26022     /**
26023      * @cfg {String} maxText
26024      * The error text to display if the maxDate validation fails (defaults to "This date is after the maximum date")
26025      */
26026     maxText : "This date is after the maximum date",
26027     /**
26028      * @cfg {String} format
26029      * The default date format string which can be overriden for localization support.  The format must be
26030      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
26031      */
26032     format : "m/d/y",
26033     /**
26034      * @cfg {Array} disabledDays
26035      * An array of days to disable, 0-based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
26036      */
26037     disabledDays : null,
26038     /**
26039      * @cfg {String} disabledDaysText
26040      * The tooltip to display when the date falls on a disabled day (defaults to "")
26041      */
26042     disabledDaysText : "",
26043     /**
26044      * @cfg {RegExp} disabledDatesRE
26045      * JavaScript regular expression used to disable a pattern of dates (defaults to null)
26046      */
26047     disabledDatesRE : null,
26048     /**
26049      * @cfg {String} disabledDatesText
26050      * The tooltip text to display when the date falls on a disabled date (defaults to "")
26051      */
26052     disabledDatesText : "",
26053     /**
26054      * @cfg {Boolean} constrainToViewport
26055      * True to constrain the date picker to the viewport (defaults to true)
26056      */
26057     constrainToViewport : true,
26058     /**
26059      * @cfg {Array} monthNames
26060      * An array of textual month names which can be overriden for localization support (defaults to Date.monthNames)
26061      */
26062     monthNames : Date.monthNames,
26063     /**
26064      * @cfg {Array} dayNames
26065      * An array of textual day names which can be overriden for localization support (defaults to Date.dayNames)
26066      */
26067     dayNames : Date.dayNames,
26068     /**
26069      * @cfg {String} nextText
26070      * The next month navigation button tooltip (defaults to 'Next Month (Control+Right)')
26071      */
26072     nextText: 'Next Month (Control+Right)',
26073     /**
26074      * @cfg {String} prevText
26075      * The previous month navigation button tooltip (defaults to 'Previous Month (Control+Left)')
26076      */
26077     prevText: 'Previous Month (Control+Left)',
26078     /**
26079      * @cfg {String} monthYearText
26080      * The header month selector tooltip (defaults to 'Choose a month (Control+Up/Down to move years)')
26081      */
26082     monthYearText: 'Choose a month (Control+Up/Down to move years)',
26083     /**
26084      * @cfg {Number} startDay
26085      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
26086      */
26087     startDay : 0,
26088     /**
26089      * @cfg {Bool} showClear
26090      * Show a clear button (usefull for date form elements that can be blank.)
26091      */
26092     
26093     showClear: false,
26094     
26095     /**
26096      * Sets the value of the date field
26097      * @param {Date} value The date to set
26098      */
26099     setValue : function(value){
26100         var old = this.value;
26101         
26102         if (typeof(value) == 'string') {
26103          
26104             value = Date.parseDate(value, this.format);
26105         }
26106         if (!value) {
26107             value = new Date();
26108         }
26109         
26110         this.value = value.clearTime(true);
26111         if(this.el){
26112             this.update(this.value);
26113         }
26114     },
26115
26116     /**
26117      * Gets the current selected value of the date field
26118      * @return {Date} The selected date
26119      */
26120     getValue : function(){
26121         return this.value;
26122     },
26123
26124     // private
26125     focus : function(){
26126         if(this.el){
26127             this.update(this.activeDate);
26128         }
26129     },
26130
26131     // privateval
26132     onRender : function(container, position){
26133         
26134         var m = [
26135              '<table cellspacing="0">',
26136                 '<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>',
26137                 '<tr><td colspan="3"><table class="x-date-inner" cellspacing="0"><thead><tr>'];
26138         var dn = this.dayNames;
26139         for(var i = 0; i < 7; i++){
26140             var d = this.startDay+i;
26141             if(d > 6){
26142                 d = d-7;
26143             }
26144             m.push("<th><span>", dn[d].substr(0,1), "</span></th>");
26145         }
26146         m[m.length] = "</tr></thead><tbody><tr>";
26147         for(var i = 0; i < 42; i++) {
26148             if(i % 7 == 0 && i != 0){
26149                 m[m.length] = "</tr><tr>";
26150             }
26151             m[m.length] = '<td><a href="#" hidefocus="on" class="x-date-date" tabIndex="1"><em><span></span></em></a></td>';
26152         }
26153         m[m.length] = '</tr></tbody></table></td></tr><tr>'+
26154             '<td colspan="3" class="x-date-bottom" align="center"></td></tr></table><div class="x-date-mp"></div>';
26155
26156         var el = document.createElement("div");
26157         el.className = "x-date-picker";
26158         el.innerHTML = m.join("");
26159
26160         container.dom.insertBefore(el, position);
26161
26162         this.el = Roo.get(el);
26163         this.eventEl = Roo.get(el.firstChild);
26164
26165         new Roo.util.ClickRepeater(this.el.child("td.x-date-left a"), {
26166             handler: this.showPrevMonth,
26167             scope: this,
26168             preventDefault:true,
26169             stopDefault:true
26170         });
26171
26172         new Roo.util.ClickRepeater(this.el.child("td.x-date-right a"), {
26173             handler: this.showNextMonth,
26174             scope: this,
26175             preventDefault:true,
26176             stopDefault:true
26177         });
26178
26179         this.eventEl.on("mousewheel", this.handleMouseWheel,  this);
26180
26181         this.monthPicker = this.el.down('div.x-date-mp');
26182         this.monthPicker.enableDisplayMode('block');
26183         
26184         var kn = new Roo.KeyNav(this.eventEl, {
26185             "left" : function(e){
26186                 e.ctrlKey ?
26187                     this.showPrevMonth() :
26188                     this.update(this.activeDate.add("d", -1));
26189             },
26190
26191             "right" : function(e){
26192                 e.ctrlKey ?
26193                     this.showNextMonth() :
26194                     this.update(this.activeDate.add("d", 1));
26195             },
26196
26197             "up" : function(e){
26198                 e.ctrlKey ?
26199                     this.showNextYear() :
26200                     this.update(this.activeDate.add("d", -7));
26201             },
26202
26203             "down" : function(e){
26204                 e.ctrlKey ?
26205                     this.showPrevYear() :
26206                     this.update(this.activeDate.add("d", 7));
26207             },
26208
26209             "pageUp" : function(e){
26210                 this.showNextMonth();
26211             },
26212
26213             "pageDown" : function(e){
26214                 this.showPrevMonth();
26215             },
26216
26217             "enter" : function(e){
26218                 e.stopPropagation();
26219                 return true;
26220             },
26221
26222             scope : this
26223         });
26224
26225         this.eventEl.on("click", this.handleDateClick,  this, {delegate: "a.x-date-date"});
26226
26227         this.eventEl.addKeyListener(Roo.EventObject.SPACE, this.selectToday,  this);
26228
26229         this.el.unselectable();
26230         
26231         this.cells = this.el.select("table.x-date-inner tbody td");
26232         this.textNodes = this.el.query("table.x-date-inner tbody span");
26233
26234         this.mbtn = new Roo.Button(this.el.child("td.x-date-middle", true), {
26235             text: "&#160;",
26236             tooltip: this.monthYearText
26237         });
26238
26239         this.mbtn.on('click', this.showMonthPicker, this);
26240         this.mbtn.el.child(this.mbtn.menuClassTarget).addClass("x-btn-with-menu");
26241
26242
26243         var today = (new Date()).dateFormat(this.format);
26244         
26245         var baseTb = new Roo.Toolbar(this.el.child("td.x-date-bottom", true));
26246         if (this.showClear) {
26247             baseTb.add( new Roo.Toolbar.Fill());
26248         }
26249         baseTb.add({
26250             text: String.format(this.todayText, today),
26251             tooltip: String.format(this.todayTip, today),
26252             handler: this.selectToday,
26253             scope: this
26254         });
26255         
26256         //var todayBtn = new Roo.Button(this.el.child("td.x-date-bottom", true), {
26257             
26258         //});
26259         if (this.showClear) {
26260             
26261             baseTb.add( new Roo.Toolbar.Fill());
26262             baseTb.add({
26263                 text: '&#160;',
26264                 cls: 'x-btn-icon x-btn-clear',
26265                 handler: function() {
26266                     //this.value = '';
26267                     this.fireEvent("select", this, '');
26268                 },
26269                 scope: this
26270             });
26271         }
26272         
26273         
26274         if(Roo.isIE){
26275             this.el.repaint();
26276         }
26277         this.update(this.value);
26278     },
26279
26280     createMonthPicker : function(){
26281         if(!this.monthPicker.dom.firstChild){
26282             var buf = ['<table border="0" cellspacing="0">'];
26283             for(var i = 0; i < 6; i++){
26284                 buf.push(
26285                     '<tr><td class="x-date-mp-month"><a href="#">', this.monthNames[i].substr(0, 3), '</a></td>',
26286                     '<td class="x-date-mp-month x-date-mp-sep"><a href="#">', this.monthNames[i+6].substr(0, 3), '</a></td>',
26287                     i == 0 ?
26288                     '<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>' :
26289                     '<td class="x-date-mp-year"><a href="#"></a></td><td class="x-date-mp-year"><a href="#"></a></td></tr>'
26290                 );
26291             }
26292             buf.push(
26293                 '<tr class="x-date-mp-btns"><td colspan="4"><button type="button" class="x-date-mp-ok">',
26294                     this.okText,
26295                     '</button><button type="button" class="x-date-mp-cancel">',
26296                     this.cancelText,
26297                     '</button></td></tr>',
26298                 '</table>'
26299             );
26300             this.monthPicker.update(buf.join(''));
26301             this.monthPicker.on('click', this.onMonthClick, this);
26302             this.monthPicker.on('dblclick', this.onMonthDblClick, this);
26303
26304             this.mpMonths = this.monthPicker.select('td.x-date-mp-month');
26305             this.mpYears = this.monthPicker.select('td.x-date-mp-year');
26306
26307             this.mpMonths.each(function(m, a, i){
26308                 i += 1;
26309                 if((i%2) == 0){
26310                     m.dom.xmonth = 5 + Math.round(i * .5);
26311                 }else{
26312                     m.dom.xmonth = Math.round((i-1) * .5);
26313                 }
26314             });
26315         }
26316     },
26317
26318     showMonthPicker : function(){
26319         this.createMonthPicker();
26320         var size = this.el.getSize();
26321         this.monthPicker.setSize(size);
26322         this.monthPicker.child('table').setSize(size);
26323
26324         this.mpSelMonth = (this.activeDate || this.value).getMonth();
26325         this.updateMPMonth(this.mpSelMonth);
26326         this.mpSelYear = (this.activeDate || this.value).getFullYear();
26327         this.updateMPYear(this.mpSelYear);
26328
26329         this.monthPicker.slideIn('t', {duration:.2});
26330     },
26331
26332     updateMPYear : function(y){
26333         this.mpyear = y;
26334         var ys = this.mpYears.elements;
26335         for(var i = 1; i <= 10; i++){
26336             var td = ys[i-1], y2;
26337             if((i%2) == 0){
26338                 y2 = y + Math.round(i * .5);
26339                 td.firstChild.innerHTML = y2;
26340                 td.xyear = y2;
26341             }else{
26342                 y2 = y - (5-Math.round(i * .5));
26343                 td.firstChild.innerHTML = y2;
26344                 td.xyear = y2;
26345             }
26346             this.mpYears.item(i-1)[y2 == this.mpSelYear ? 'addClass' : 'removeClass']('x-date-mp-sel');
26347         }
26348     },
26349
26350     updateMPMonth : function(sm){
26351         this.mpMonths.each(function(m, a, i){
26352             m[m.dom.xmonth == sm ? 'addClass' : 'removeClass']('x-date-mp-sel');
26353         });
26354     },
26355
26356     selectMPMonth: function(m){
26357         
26358     },
26359
26360     onMonthClick : function(e, t){
26361         e.stopEvent();
26362         var el = new Roo.Element(t), pn;
26363         if(el.is('button.x-date-mp-cancel')){
26364             this.hideMonthPicker();
26365         }
26366         else if(el.is('button.x-date-mp-ok')){
26367             this.update(new Date(this.mpSelYear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
26368             this.hideMonthPicker();
26369         }
26370         else if(pn = el.up('td.x-date-mp-month', 2)){
26371             this.mpMonths.removeClass('x-date-mp-sel');
26372             pn.addClass('x-date-mp-sel');
26373             this.mpSelMonth = pn.dom.xmonth;
26374         }
26375         else if(pn = el.up('td.x-date-mp-year', 2)){
26376             this.mpYears.removeClass('x-date-mp-sel');
26377             pn.addClass('x-date-mp-sel');
26378             this.mpSelYear = pn.dom.xyear;
26379         }
26380         else if(el.is('a.x-date-mp-prev')){
26381             this.updateMPYear(this.mpyear-10);
26382         }
26383         else if(el.is('a.x-date-mp-next')){
26384             this.updateMPYear(this.mpyear+10);
26385         }
26386     },
26387
26388     onMonthDblClick : function(e, t){
26389         e.stopEvent();
26390         var el = new Roo.Element(t), pn;
26391         if(pn = el.up('td.x-date-mp-month', 2)){
26392             this.update(new Date(this.mpSelYear, pn.dom.xmonth, (this.activeDate || this.value).getDate()));
26393             this.hideMonthPicker();
26394         }
26395         else if(pn = el.up('td.x-date-mp-year', 2)){
26396             this.update(new Date(pn.dom.xyear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
26397             this.hideMonthPicker();
26398         }
26399     },
26400
26401     hideMonthPicker : function(disableAnim){
26402         if(this.monthPicker){
26403             if(disableAnim === true){
26404                 this.monthPicker.hide();
26405             }else{
26406                 this.monthPicker.slideOut('t', {duration:.2});
26407             }
26408         }
26409     },
26410
26411     // private
26412     showPrevMonth : function(e){
26413         this.update(this.activeDate.add("mo", -1));
26414     },
26415
26416     // private
26417     showNextMonth : function(e){
26418         this.update(this.activeDate.add("mo", 1));
26419     },
26420
26421     // private
26422     showPrevYear : function(){
26423         this.update(this.activeDate.add("y", -1));
26424     },
26425
26426     // private
26427     showNextYear : function(){
26428         this.update(this.activeDate.add("y", 1));
26429     },
26430
26431     // private
26432     handleMouseWheel : function(e){
26433         var delta = e.getWheelDelta();
26434         if(delta > 0){
26435             this.showPrevMonth();
26436             e.stopEvent();
26437         } else if(delta < 0){
26438             this.showNextMonth();
26439             e.stopEvent();
26440         }
26441     },
26442
26443     // private
26444     handleDateClick : function(e, t){
26445         e.stopEvent();
26446         if(t.dateValue && !Roo.fly(t.parentNode).hasClass("x-date-disabled")){
26447             this.setValue(new Date(t.dateValue));
26448             this.fireEvent("select", this, this.value);
26449         }
26450     },
26451
26452     // private
26453     selectToday : function(){
26454         this.setValue(new Date().clearTime());
26455         this.fireEvent("select", this, this.value);
26456     },
26457
26458     // private
26459     update : function(date)
26460     {
26461         var vd = this.activeDate;
26462         this.activeDate = date;
26463         if(vd && this.el){
26464             var t = date.getTime();
26465             if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
26466                 this.cells.removeClass("x-date-selected");
26467                 this.cells.each(function(c){
26468                    if(c.dom.firstChild.dateValue == t){
26469                        c.addClass("x-date-selected");
26470                        setTimeout(function(){
26471                             try{c.dom.firstChild.focus();}catch(e){}
26472                        }, 50);
26473                        return false;
26474                    }
26475                 });
26476                 return;
26477             }
26478         }
26479         
26480         var days = date.getDaysInMonth();
26481         var firstOfMonth = date.getFirstDateOfMonth();
26482         var startingPos = firstOfMonth.getDay()-this.startDay;
26483
26484         if(startingPos <= this.startDay){
26485             startingPos += 7;
26486         }
26487
26488         var pm = date.add("mo", -1);
26489         var prevStart = pm.getDaysInMonth()-startingPos;
26490
26491         var cells = this.cells.elements;
26492         var textEls = this.textNodes;
26493         days += startingPos;
26494
26495         // convert everything to numbers so it's fast
26496         var day = 86400000;
26497         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
26498         var today = new Date().clearTime().getTime();
26499         var sel = date.clearTime().getTime();
26500         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
26501         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
26502         var ddMatch = this.disabledDatesRE;
26503         var ddText = this.disabledDatesText;
26504         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
26505         var ddaysText = this.disabledDaysText;
26506         var format = this.format;
26507
26508         var setCellClass = function(cal, cell){
26509             cell.title = "";
26510             var t = d.getTime();
26511             cell.firstChild.dateValue = t;
26512             if(t == today){
26513                 cell.className += " x-date-today";
26514                 cell.title = cal.todayText;
26515             }
26516             if(t == sel){
26517                 cell.className += " x-date-selected";
26518                 setTimeout(function(){
26519                     try{cell.firstChild.focus();}catch(e){}
26520                 }, 50);
26521             }
26522             // disabling
26523             if(t < min) {
26524                 cell.className = " x-date-disabled";
26525                 cell.title = cal.minText;
26526                 return;
26527             }
26528             if(t > max) {
26529                 cell.className = " x-date-disabled";
26530                 cell.title = cal.maxText;
26531                 return;
26532             }
26533             if(ddays){
26534                 if(ddays.indexOf(d.getDay()) != -1){
26535                     cell.title = ddaysText;
26536                     cell.className = " x-date-disabled";
26537                 }
26538             }
26539             if(ddMatch && format){
26540                 var fvalue = d.dateFormat(format);
26541                 if(ddMatch.test(fvalue)){
26542                     cell.title = ddText.replace("%0", fvalue);
26543                     cell.className = " x-date-disabled";
26544                 }
26545             }
26546         };
26547
26548         var i = 0;
26549         for(; i < startingPos; i++) {
26550             textEls[i].innerHTML = (++prevStart);
26551             d.setDate(d.getDate()+1);
26552             cells[i].className = "x-date-prevday";
26553             setCellClass(this, cells[i]);
26554         }
26555         for(; i < days; i++){
26556             intDay = i - startingPos + 1;
26557             textEls[i].innerHTML = (intDay);
26558             d.setDate(d.getDate()+1);
26559             cells[i].className = "x-date-active";
26560             setCellClass(this, cells[i]);
26561         }
26562         var extraDays = 0;
26563         for(; i < 42; i++) {
26564              textEls[i].innerHTML = (++extraDays);
26565              d.setDate(d.getDate()+1);
26566              cells[i].className = "x-date-nextday";
26567              setCellClass(this, cells[i]);
26568         }
26569
26570         this.mbtn.setText(this.monthNames[date.getMonth()] + " " + date.getFullYear());
26571         this.fireEvent('monthchange', this, date);
26572         
26573         if(!this.internalRender){
26574             var main = this.el.dom.firstChild;
26575             var w = main.offsetWidth;
26576             this.el.setWidth(w + this.el.getBorderWidth("lr"));
26577             Roo.fly(main).setWidth(w);
26578             this.internalRender = true;
26579             // opera does not respect the auto grow header center column
26580             // then, after it gets a width opera refuses to recalculate
26581             // without a second pass
26582             if(Roo.isOpera && !this.secondPass){
26583                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
26584                 this.secondPass = true;
26585                 this.update.defer(10, this, [date]);
26586             }
26587         }
26588         
26589         
26590     }
26591 });        /*
26592  * Based on:
26593  * Ext JS Library 1.1.1
26594  * Copyright(c) 2006-2007, Ext JS, LLC.
26595  *
26596  * Originally Released Under LGPL - original licence link has changed is not relivant.
26597  *
26598  * Fork - LGPL
26599  * <script type="text/javascript">
26600  */
26601 /**
26602  * @class Roo.TabPanel
26603  * @extends Roo.util.Observable
26604  * A lightweight tab container.
26605  * <br><br>
26606  * Usage:
26607  * <pre><code>
26608 // basic tabs 1, built from existing content
26609 var tabs = new Roo.TabPanel("tabs1");
26610 tabs.addTab("script", "View Script");
26611 tabs.addTab("markup", "View Markup");
26612 tabs.activate("script");
26613
26614 // more advanced tabs, built from javascript
26615 var jtabs = new Roo.TabPanel("jtabs");
26616 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
26617
26618 // set up the UpdateManager
26619 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
26620 var updater = tab2.getUpdateManager();
26621 updater.setDefaultUrl("ajax1.htm");
26622 tab2.on('activate', updater.refresh, updater, true);
26623
26624 // Use setUrl for Ajax loading
26625 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
26626 tab3.setUrl("ajax2.htm", null, true);
26627
26628 // Disabled tab
26629 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
26630 tab4.disable();
26631
26632 jtabs.activate("jtabs-1");
26633  * </code></pre>
26634  * @constructor
26635  * Create a new TabPanel.
26636  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
26637  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
26638  */
26639 Roo.TabPanel = function(container, config){
26640     /**
26641     * The container element for this TabPanel.
26642     * @type Roo.Element
26643     */
26644     this.el = Roo.get(container, true);
26645     if(config){
26646         if(typeof config == "boolean"){
26647             this.tabPosition = config ? "bottom" : "top";
26648         }else{
26649             Roo.apply(this, config);
26650         }
26651     }
26652     if(this.tabPosition == "bottom"){
26653         this.bodyEl = Roo.get(this.createBody(this.el.dom));
26654         this.el.addClass("x-tabs-bottom");
26655     }
26656     this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
26657     this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
26658     this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
26659     if(Roo.isIE){
26660         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
26661     }
26662     if(this.tabPosition != "bottom"){
26663         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
26664          * @type Roo.Element
26665          */
26666         this.bodyEl = Roo.get(this.createBody(this.el.dom));
26667         this.el.addClass("x-tabs-top");
26668     }
26669     this.items = [];
26670
26671     this.bodyEl.setStyle("position", "relative");
26672
26673     this.active = null;
26674     this.activateDelegate = this.activate.createDelegate(this);
26675
26676     this.addEvents({
26677         /**
26678          * @event tabchange
26679          * Fires when the active tab changes
26680          * @param {Roo.TabPanel} this
26681          * @param {Roo.TabPanelItem} activePanel The new active tab
26682          */
26683         "tabchange": true,
26684         /**
26685          * @event beforetabchange
26686          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
26687          * @param {Roo.TabPanel} this
26688          * @param {Object} e Set cancel to true on this object to cancel the tab change
26689          * @param {Roo.TabPanelItem} tab The tab being changed to
26690          */
26691         "beforetabchange" : true
26692     });
26693
26694     Roo.EventManager.onWindowResize(this.onResize, this);
26695     this.cpad = this.el.getPadding("lr");
26696     this.hiddenCount = 0;
26697
26698
26699     // toolbar on the tabbar support...
26700     if (this.toolbar) {
26701         var tcfg = this.toolbar;
26702         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
26703         this.toolbar = new Roo.Toolbar(tcfg);
26704         if (Roo.isSafari) {
26705             var tbl = tcfg.container.child('table', true);
26706             tbl.setAttribute('width', '100%');
26707         }
26708         
26709     }
26710    
26711
26712
26713     Roo.TabPanel.superclass.constructor.call(this);
26714 };
26715
26716 Roo.extend(Roo.TabPanel, Roo.util.Observable, {
26717     /*
26718      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
26719      */
26720     tabPosition : "top",
26721     /*
26722      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
26723      */
26724     currentTabWidth : 0,
26725     /*
26726      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
26727      */
26728     minTabWidth : 40,
26729     /*
26730      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
26731      */
26732     maxTabWidth : 250,
26733     /*
26734      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
26735      */
26736     preferredTabWidth : 175,
26737     /*
26738      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
26739      */
26740     resizeTabs : false,
26741     /*
26742      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
26743      */
26744     monitorResize : true,
26745     /*
26746      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
26747      */
26748     toolbar : false,
26749
26750     /**
26751      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
26752      * @param {String} id The id of the div to use <b>or create</b>
26753      * @param {String} text The text for the tab
26754      * @param {String} content (optional) Content to put in the TabPanelItem body
26755      * @param {Boolean} closable (optional) True to create a close icon on the tab
26756      * @return {Roo.TabPanelItem} The created TabPanelItem
26757      */
26758     addTab : function(id, text, content, closable){
26759         var item = new Roo.TabPanelItem(this, id, text, closable);
26760         this.addTabItem(item);
26761         if(content){
26762             item.setContent(content);
26763         }
26764         return item;
26765     },
26766
26767     /**
26768      * Returns the {@link Roo.TabPanelItem} with the specified id/index
26769      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
26770      * @return {Roo.TabPanelItem}
26771      */
26772     getTab : function(id){
26773         return this.items[id];
26774     },
26775
26776     /**
26777      * Hides the {@link Roo.TabPanelItem} with the specified id/index
26778      * @param {String/Number} id The id or index of the TabPanelItem to hide.
26779      */
26780     hideTab : function(id){
26781         var t = this.items[id];
26782         if(!t.isHidden()){
26783            t.setHidden(true);
26784            this.hiddenCount++;
26785            this.autoSizeTabs();
26786         }
26787     },
26788
26789     /**
26790      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
26791      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
26792      */
26793     unhideTab : function(id){
26794         var t = this.items[id];
26795         if(t.isHidden()){
26796            t.setHidden(false);
26797            this.hiddenCount--;
26798            this.autoSizeTabs();
26799         }
26800     },
26801
26802     /**
26803      * Adds an existing {@link Roo.TabPanelItem}.
26804      * @param {Roo.TabPanelItem} item The TabPanelItem to add
26805      */
26806     addTabItem : function(item){
26807         this.items[item.id] = item;
26808         this.items.push(item);
26809         if(this.resizeTabs){
26810            item.setWidth(this.currentTabWidth || this.preferredTabWidth);
26811            this.autoSizeTabs();
26812         }else{
26813             item.autoSize();
26814         }
26815     },
26816
26817     /**
26818      * Removes a {@link Roo.TabPanelItem}.
26819      * @param {String/Number} id The id or index of the TabPanelItem to remove.
26820      */
26821     removeTab : function(id){
26822         var items = this.items;
26823         var tab = items[id];
26824         if(!tab) { return; }
26825         var index = items.indexOf(tab);
26826         if(this.active == tab && items.length > 1){
26827             var newTab = this.getNextAvailable(index);
26828             if(newTab) {
26829                 newTab.activate();
26830             }
26831         }
26832         this.stripEl.dom.removeChild(tab.pnode.dom);
26833         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
26834             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
26835         }
26836         items.splice(index, 1);
26837         delete this.items[tab.id];
26838         tab.fireEvent("close", tab);
26839         tab.purgeListeners();
26840         this.autoSizeTabs();
26841     },
26842
26843     getNextAvailable : function(start){
26844         var items = this.items;
26845         var index = start;
26846         // look for a next tab that will slide over to
26847         // replace the one being removed
26848         while(index < items.length){
26849             var item = items[++index];
26850             if(item && !item.isHidden()){
26851                 return item;
26852             }
26853         }
26854         // if one isn't found select the previous tab (on the left)
26855         index = start;
26856         while(index >= 0){
26857             var item = items[--index];
26858             if(item && !item.isHidden()){
26859                 return item;
26860             }
26861         }
26862         return null;
26863     },
26864
26865     /**
26866      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
26867      * @param {String/Number} id The id or index of the TabPanelItem to disable.
26868      */
26869     disableTab : function(id){
26870         var tab = this.items[id];
26871         if(tab && this.active != tab){
26872             tab.disable();
26873         }
26874     },
26875
26876     /**
26877      * Enables a {@link Roo.TabPanelItem} that is disabled.
26878      * @param {String/Number} id The id or index of the TabPanelItem to enable.
26879      */
26880     enableTab : function(id){
26881         var tab = this.items[id];
26882         tab.enable();
26883     },
26884
26885     /**
26886      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
26887      * @param {String/Number} id The id or index of the TabPanelItem to activate.
26888      * @return {Roo.TabPanelItem} The TabPanelItem.
26889      */
26890     activate : function(id){
26891         var tab = this.items[id];
26892         if(!tab){
26893             return null;
26894         }
26895         if(tab == this.active || tab.disabled){
26896             return tab;
26897         }
26898         var e = {};
26899         this.fireEvent("beforetabchange", this, e, tab);
26900         if(e.cancel !== true && !tab.disabled){
26901             if(this.active){
26902                 this.active.hide();
26903             }
26904             this.active = this.items[id];
26905             this.active.show();
26906             this.fireEvent("tabchange", this, this.active);
26907         }
26908         return tab;
26909     },
26910
26911     /**
26912      * Gets the active {@link Roo.TabPanelItem}.
26913      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
26914      */
26915     getActiveTab : function(){
26916         return this.active;
26917     },
26918
26919     /**
26920      * Updates the tab body element to fit the height of the container element
26921      * for overflow scrolling
26922      * @param {Number} targetHeight (optional) Override the starting height from the elements height
26923      */
26924     syncHeight : function(targetHeight){
26925         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
26926         var bm = this.bodyEl.getMargins();
26927         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
26928         this.bodyEl.setHeight(newHeight);
26929         return newHeight;
26930     },
26931
26932     onResize : function(){
26933         if(this.monitorResize){
26934             this.autoSizeTabs();
26935         }
26936     },
26937
26938     /**
26939      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
26940      */
26941     beginUpdate : function(){
26942         this.updating = true;
26943     },
26944
26945     /**
26946      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
26947      */
26948     endUpdate : function(){
26949         this.updating = false;
26950         this.autoSizeTabs();
26951     },
26952
26953     /**
26954      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
26955      */
26956     autoSizeTabs : function(){
26957         var count = this.items.length;
26958         var vcount = count - this.hiddenCount;
26959         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) return;
26960         var w = Math.max(this.el.getWidth() - this.cpad, 10);
26961         var availWidth = Math.floor(w / vcount);
26962         var b = this.stripBody;
26963         if(b.getWidth() > w){
26964             var tabs = this.items;
26965             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
26966             if(availWidth < this.minTabWidth){
26967                 /*if(!this.sleft){    // incomplete scrolling code
26968                     this.createScrollButtons();
26969                 }
26970                 this.showScroll();
26971                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
26972             }
26973         }else{
26974             if(this.currentTabWidth < this.preferredTabWidth){
26975                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
26976             }
26977         }
26978     },
26979
26980     /**
26981      * Returns the number of tabs in this TabPanel.
26982      * @return {Number}
26983      */
26984      getCount : function(){
26985          return this.items.length;
26986      },
26987
26988     /**
26989      * Resizes all the tabs to the passed width
26990      * @param {Number} The new width
26991      */
26992     setTabWidth : function(width){
26993         this.currentTabWidth = width;
26994         for(var i = 0, len = this.items.length; i < len; i++) {
26995                 if(!this.items[i].isHidden())this.items[i].setWidth(width);
26996         }
26997     },
26998
26999     /**
27000      * Destroys this TabPanel
27001      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
27002      */
27003     destroy : function(removeEl){
27004         Roo.EventManager.removeResizeListener(this.onResize, this);
27005         for(var i = 0, len = this.items.length; i < len; i++){
27006             this.items[i].purgeListeners();
27007         }
27008         if(removeEl === true){
27009             this.el.update("");
27010             this.el.remove();
27011         }
27012     }
27013 });
27014
27015 /**
27016  * @class Roo.TabPanelItem
27017  * @extends Roo.util.Observable
27018  * Represents an individual item (tab plus body) in a TabPanel.
27019  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
27020  * @param {String} id The id of this TabPanelItem
27021  * @param {String} text The text for the tab of this TabPanelItem
27022  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
27023  */
27024 Roo.TabPanelItem = function(tabPanel, id, text, closable){
27025     /**
27026      * The {@link Roo.TabPanel} this TabPanelItem belongs to
27027      * @type Roo.TabPanel
27028      */
27029     this.tabPanel = tabPanel;
27030     /**
27031      * The id for this TabPanelItem
27032      * @type String
27033      */
27034     this.id = id;
27035     /** @private */
27036     this.disabled = false;
27037     /** @private */
27038     this.text = text;
27039     /** @private */
27040     this.loaded = false;
27041     this.closable = closable;
27042
27043     /**
27044      * The body element for this TabPanelItem.
27045      * @type Roo.Element
27046      */
27047     this.bodyEl = Roo.get(tabPanel.createItemBody(tabPanel.bodyEl.dom, id));
27048     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
27049     this.bodyEl.setStyle("display", "block");
27050     this.bodyEl.setStyle("zoom", "1");
27051     this.hideAction();
27052
27053     var els = tabPanel.createStripElements(tabPanel.stripEl.dom, text, closable);
27054     /** @private */
27055     this.el = Roo.get(els.el, true);
27056     this.inner = Roo.get(els.inner, true);
27057     this.textEl = Roo.get(this.el.dom.firstChild.firstChild.firstChild, true);
27058     this.pnode = Roo.get(els.el.parentNode, true);
27059     this.el.on("mousedown", this.onTabMouseDown, this);
27060     this.el.on("click", this.onTabClick, this);
27061     /** @private */
27062     if(closable){
27063         var c = Roo.get(els.close, true);
27064         c.dom.title = this.closeText;
27065         c.addClassOnOver("close-over");
27066         c.on("click", this.closeClick, this);
27067      }
27068
27069     this.addEvents({
27070          /**
27071          * @event activate
27072          * Fires when this tab becomes the active tab.
27073          * @param {Roo.TabPanel} tabPanel The parent TabPanel
27074          * @param {Roo.TabPanelItem} this
27075          */
27076         "activate": true,
27077         /**
27078          * @event beforeclose
27079          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
27080          * @param {Roo.TabPanelItem} this
27081          * @param {Object} e Set cancel to true on this object to cancel the close.
27082          */
27083         "beforeclose": true,
27084         /**
27085          * @event close
27086          * Fires when this tab is closed.
27087          * @param {Roo.TabPanelItem} this
27088          */
27089          "close": true,
27090         /**
27091          * @event deactivate
27092          * Fires when this tab is no longer the active tab.
27093          * @param {Roo.TabPanel} tabPanel The parent TabPanel
27094          * @param {Roo.TabPanelItem} this
27095          */
27096          "deactivate" : true
27097     });
27098     this.hidden = false;
27099
27100     Roo.TabPanelItem.superclass.constructor.call(this);
27101 };
27102
27103 Roo.extend(Roo.TabPanelItem, Roo.util.Observable, {
27104     purgeListeners : function(){
27105        Roo.util.Observable.prototype.purgeListeners.call(this);
27106        this.el.removeAllListeners();
27107     },
27108     /**
27109      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
27110      */
27111     show : function(){
27112         this.pnode.addClass("on");
27113         this.showAction();
27114         if(Roo.isOpera){
27115             this.tabPanel.stripWrap.repaint();
27116         }
27117         this.fireEvent("activate", this.tabPanel, this);
27118     },
27119
27120     /**
27121      * Returns true if this tab is the active tab.
27122      * @return {Boolean}
27123      */
27124     isActive : function(){
27125         return this.tabPanel.getActiveTab() == this;
27126     },
27127
27128     /**
27129      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
27130      */
27131     hide : function(){
27132         this.pnode.removeClass("on");
27133         this.hideAction();
27134         this.fireEvent("deactivate", this.tabPanel, this);
27135     },
27136
27137     hideAction : function(){
27138         this.bodyEl.hide();
27139         this.bodyEl.setStyle("position", "absolute");
27140         this.bodyEl.setLeft("-20000px");
27141         this.bodyEl.setTop("-20000px");
27142     },
27143
27144     showAction : function(){
27145         this.bodyEl.setStyle("position", "relative");
27146         this.bodyEl.setTop("");
27147         this.bodyEl.setLeft("");
27148         this.bodyEl.show();
27149     },
27150
27151     /**
27152      * Set the tooltip for the tab.
27153      * @param {String} tooltip The tab's tooltip
27154      */
27155     setTooltip : function(text){
27156         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
27157             this.textEl.dom.qtip = text;
27158             this.textEl.dom.removeAttribute('title');
27159         }else{
27160             this.textEl.dom.title = text;
27161         }
27162     },
27163
27164     onTabClick : function(e){
27165         e.preventDefault();
27166         this.tabPanel.activate(this.id);
27167     },
27168
27169     onTabMouseDown : function(e){
27170         e.preventDefault();
27171         this.tabPanel.activate(this.id);
27172     },
27173
27174     getWidth : function(){
27175         return this.inner.getWidth();
27176     },
27177
27178     setWidth : function(width){
27179         var iwidth = width - this.pnode.getPadding("lr");
27180         this.inner.setWidth(iwidth);
27181         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
27182         this.pnode.setWidth(width);
27183     },
27184
27185     /**
27186      * Show or hide the tab
27187      * @param {Boolean} hidden True to hide or false to show.
27188      */
27189     setHidden : function(hidden){
27190         this.hidden = hidden;
27191         this.pnode.setStyle("display", hidden ? "none" : "");
27192     },
27193
27194     /**
27195      * Returns true if this tab is "hidden"
27196      * @return {Boolean}
27197      */
27198     isHidden : function(){
27199         return this.hidden;
27200     },
27201
27202     /**
27203      * Returns the text for this tab
27204      * @return {String}
27205      */
27206     getText : function(){
27207         return this.text;
27208     },
27209
27210     autoSize : function(){
27211         //this.el.beginMeasure();
27212         this.textEl.setWidth(1);
27213         /*
27214          *  #2804 [new] Tabs in Roojs
27215          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
27216          */
27217         this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr") + 2);
27218         //this.el.endMeasure();
27219     },
27220
27221     /**
27222      * Sets the text for the tab (Note: this also sets the tooltip text)
27223      * @param {String} text The tab's text and tooltip
27224      */
27225     setText : function(text){
27226         this.text = text;
27227         this.textEl.update(text);
27228         this.setTooltip(text);
27229         if(!this.tabPanel.resizeTabs){
27230             this.autoSize();
27231         }
27232     },
27233     /**
27234      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
27235      */
27236     activate : function(){
27237         this.tabPanel.activate(this.id);
27238     },
27239
27240     /**
27241      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
27242      */
27243     disable : function(){
27244         if(this.tabPanel.active != this){
27245             this.disabled = true;
27246             this.pnode.addClass("disabled");
27247         }
27248     },
27249
27250     /**
27251      * Enables this TabPanelItem if it was previously disabled.
27252      */
27253     enable : function(){
27254         this.disabled = false;
27255         this.pnode.removeClass("disabled");
27256     },
27257
27258     /**
27259      * Sets the content for this TabPanelItem.
27260      * @param {String} content The content
27261      * @param {Boolean} loadScripts true to look for and load scripts
27262      */
27263     setContent : function(content, loadScripts){
27264         this.bodyEl.update(content, loadScripts);
27265     },
27266
27267     /**
27268      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
27269      * @return {Roo.UpdateManager} The UpdateManager
27270      */
27271     getUpdateManager : function(){
27272         return this.bodyEl.getUpdateManager();
27273     },
27274
27275     /**
27276      * Set a URL to be used to load the content for this TabPanelItem.
27277      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
27278      * @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)
27279      * @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)
27280      * @return {Roo.UpdateManager} The UpdateManager
27281      */
27282     setUrl : function(url, params, loadOnce){
27283         if(this.refreshDelegate){
27284             this.un('activate', this.refreshDelegate);
27285         }
27286         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
27287         this.on("activate", this.refreshDelegate);
27288         return this.bodyEl.getUpdateManager();
27289     },
27290
27291     /** @private */
27292     _handleRefresh : function(url, params, loadOnce){
27293         if(!loadOnce || !this.loaded){
27294             var updater = this.bodyEl.getUpdateManager();
27295             updater.update(url, params, this._setLoaded.createDelegate(this));
27296         }
27297     },
27298
27299     /**
27300      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
27301      *   Will fail silently if the setUrl method has not been called.
27302      *   This does not activate the panel, just updates its content.
27303      */
27304     refresh : function(){
27305         if(this.refreshDelegate){
27306            this.loaded = false;
27307            this.refreshDelegate();
27308         }
27309     },
27310
27311     /** @private */
27312     _setLoaded : function(){
27313         this.loaded = true;
27314     },
27315
27316     /** @private */
27317     closeClick : function(e){
27318         var o = {};
27319         e.stopEvent();
27320         this.fireEvent("beforeclose", this, o);
27321         if(o.cancel !== true){
27322             this.tabPanel.removeTab(this.id);
27323         }
27324     },
27325     /**
27326      * The text displayed in the tooltip for the close icon.
27327      * @type String
27328      */
27329     closeText : "Close this tab"
27330 });
27331
27332 /** @private */
27333 Roo.TabPanel.prototype.createStrip = function(container){
27334     var strip = document.createElement("div");
27335     strip.className = "x-tabs-wrap";
27336     container.appendChild(strip);
27337     return strip;
27338 };
27339 /** @private */
27340 Roo.TabPanel.prototype.createStripList = function(strip){
27341     // div wrapper for retard IE
27342     // returns the "tr" element.
27343     strip.innerHTML = '<div class="x-tabs-strip-wrap">'+
27344         '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
27345         '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
27346     return strip.firstChild.firstChild.firstChild.firstChild;
27347 };
27348 /** @private */
27349 Roo.TabPanel.prototype.createBody = function(container){
27350     var body = document.createElement("div");
27351     Roo.id(body, "tab-body");
27352     Roo.fly(body).addClass("x-tabs-body");
27353     container.appendChild(body);
27354     return body;
27355 };
27356 /** @private */
27357 Roo.TabPanel.prototype.createItemBody = function(bodyEl, id){
27358     var body = Roo.getDom(id);
27359     if(!body){
27360         body = document.createElement("div");
27361         body.id = id;
27362     }
27363     Roo.fly(body).addClass("x-tabs-item-body");
27364     bodyEl.insertBefore(body, bodyEl.firstChild);
27365     return body;
27366 };
27367 /** @private */
27368 Roo.TabPanel.prototype.createStripElements = function(stripEl, text, closable){
27369     var td = document.createElement("td");
27370     stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
27371     //stripEl.appendChild(td);
27372     if(closable){
27373         td.className = "x-tabs-closable";
27374         if(!this.closeTpl){
27375             this.closeTpl = new Roo.Template(
27376                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
27377                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
27378                '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
27379             );
27380         }
27381         var el = this.closeTpl.overwrite(td, {"text": text});
27382         var close = el.getElementsByTagName("div")[0];
27383         var inner = el.getElementsByTagName("em")[0];
27384         return {"el": el, "close": close, "inner": inner};
27385     } else {
27386         if(!this.tabTpl){
27387             this.tabTpl = new Roo.Template(
27388                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
27389                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
27390             );
27391         }
27392         var el = this.tabTpl.overwrite(td, {"text": text});
27393         var inner = el.getElementsByTagName("em")[0];
27394         return {"el": el, "inner": inner};
27395     }
27396 };/*
27397  * Based on:
27398  * Ext JS Library 1.1.1
27399  * Copyright(c) 2006-2007, Ext JS, LLC.
27400  *
27401  * Originally Released Under LGPL - original licence link has changed is not relivant.
27402  *
27403  * Fork - LGPL
27404  * <script type="text/javascript">
27405  */
27406
27407 /**
27408  * @class Roo.Button
27409  * @extends Roo.util.Observable
27410  * Simple Button class
27411  * @cfg {String} text The button text
27412  * @cfg {String} icon The path to an image to display in the button (the image will be set as the background-image
27413  * CSS property of the button by default, so if you want a mixed icon/text button, set cls:"x-btn-text-icon")
27414  * @cfg {Function} handler A function called when the button is clicked (can be used instead of click event)
27415  * @cfg {Object} scope The scope of the handler
27416  * @cfg {Number} minWidth The minimum width for this button (used to give a set of buttons a common width)
27417  * @cfg {String/Object} tooltip The tooltip for the button - can be a string or QuickTips config object
27418  * @cfg {Boolean} hidden True to start hidden (defaults to false)
27419  * @cfg {Boolean} disabled True to start disabled (defaults to false)
27420  * @cfg {Boolean} pressed True to start pressed (only if enableToggle = true)
27421  * @cfg {String} toggleGroup The group this toggle button is a member of (only 1 per group can be pressed, only
27422    applies if enableToggle = true)
27423  * @cfg {String/HTMLElement/Element} renderTo The element to append the button to
27424  * @cfg {Boolean/Object} repeat True to repeat fire the click event while the mouse is down. This can also be
27425   an {@link Roo.util.ClickRepeater} config object (defaults to false).
27426  * @constructor
27427  * Create a new button
27428  * @param {Object} config The config object
27429  */
27430 Roo.Button = function(renderTo, config)
27431 {
27432     if (!config) {
27433         config = renderTo;
27434         renderTo = config.renderTo || false;
27435     }
27436     
27437     Roo.apply(this, config);
27438     this.addEvents({
27439         /**
27440              * @event click
27441              * Fires when this button is clicked
27442              * @param {Button} this
27443              * @param {EventObject} e The click event
27444              */
27445             "click" : true,
27446         /**
27447              * @event toggle
27448              * Fires when the "pressed" state of this button changes (only if enableToggle = true)
27449              * @param {Button} this
27450              * @param {Boolean} pressed
27451              */
27452             "toggle" : true,
27453         /**
27454              * @event mouseover
27455              * Fires when the mouse hovers over the button
27456              * @param {Button} this
27457              * @param {Event} e The event object
27458              */
27459         'mouseover' : true,
27460         /**
27461              * @event mouseout
27462              * Fires when the mouse exits the button
27463              * @param {Button} this
27464              * @param {Event} e The event object
27465              */
27466         'mouseout': true,
27467          /**
27468              * @event render
27469              * Fires when the button is rendered
27470              * @param {Button} this
27471              */
27472         'render': true
27473     });
27474     if(this.menu){
27475         this.menu = Roo.menu.MenuMgr.get(this.menu);
27476     }
27477     // register listeners first!!  - so render can be captured..
27478     Roo.util.Observable.call(this);
27479     if(renderTo){
27480         this.render(renderTo);
27481     }
27482     
27483   
27484 };
27485
27486 Roo.extend(Roo.Button, Roo.util.Observable, {
27487     /**
27488      * 
27489      */
27490     
27491     /**
27492      * Read-only. True if this button is hidden
27493      * @type Boolean
27494      */
27495     hidden : false,
27496     /**
27497      * Read-only. True if this button is disabled
27498      * @type Boolean
27499      */
27500     disabled : false,
27501     /**
27502      * Read-only. True if this button is pressed (only if enableToggle = true)
27503      * @type Boolean
27504      */
27505     pressed : false,
27506
27507     /**
27508      * @cfg {Number} tabIndex 
27509      * The DOM tabIndex for this button (defaults to undefined)
27510      */
27511     tabIndex : undefined,
27512
27513     /**
27514      * @cfg {Boolean} enableToggle
27515      * True to enable pressed/not pressed toggling (defaults to false)
27516      */
27517     enableToggle: false,
27518     /**
27519      * @cfg {Mixed} menu
27520      * Standard menu attribute consisting of a reference to a menu object, a menu id or a menu config blob (defaults to undefined).
27521      */
27522     menu : undefined,
27523     /**
27524      * @cfg {String} menuAlign
27525      * The position to align the menu to (see {@link Roo.Element#alignTo} for more details, defaults to 'tl-bl?').
27526      */
27527     menuAlign : "tl-bl?",
27528
27529     /**
27530      * @cfg {String} iconCls
27531      * A css class which sets a background image to be used as the icon for this button (defaults to undefined).
27532      */
27533     iconCls : undefined,
27534     /**
27535      * @cfg {String} type
27536      * The button's type, corresponding to the DOM input element type attribute.  Either "submit," "reset" or "button" (default).
27537      */
27538     type : 'button',
27539
27540     // private
27541     menuClassTarget: 'tr',
27542
27543     /**
27544      * @cfg {String} clickEvent
27545      * The type of event to map to the button's event handler (defaults to 'click')
27546      */
27547     clickEvent : 'click',
27548
27549     /**
27550      * @cfg {Boolean} handleMouseEvents
27551      * False to disable visual cues on mouseover, mouseout and mousedown (defaults to true)
27552      */
27553     handleMouseEvents : true,
27554
27555     /**
27556      * @cfg {String} tooltipType
27557      * The type of tooltip to use. Either "qtip" (default) for QuickTips or "title" for title attribute.
27558      */
27559     tooltipType : 'qtip',
27560
27561     /**
27562      * @cfg {String} cls
27563      * A CSS class to apply to the button's main element.
27564      */
27565     
27566     /**
27567      * @cfg {Roo.Template} template (Optional)
27568      * An {@link Roo.Template} with which to create the Button's main element. This Template must
27569      * contain numeric substitution parameter 0 if it is to display the tRoo property. Changing the template could
27570      * require code modifications if required elements (e.g. a button) aren't present.
27571      */
27572
27573     // private
27574     render : function(renderTo){
27575         var btn;
27576         if(this.hideParent){
27577             this.parentEl = Roo.get(renderTo);
27578         }
27579         if(!this.dhconfig){
27580             if(!this.template){
27581                 if(!Roo.Button.buttonTemplate){
27582                     // hideous table template
27583                     Roo.Button.buttonTemplate = new Roo.Template(
27584                         '<table border="0" cellpadding="0" cellspacing="0" class="x-btn-wrap"><tbody><tr>',
27585                         '<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>',
27586                         "</tr></tbody></table>");
27587                 }
27588                 this.template = Roo.Button.buttonTemplate;
27589             }
27590             btn = this.template.append(renderTo, [this.text || '&#160;', this.type], true);
27591             var btnEl = btn.child("button:first");
27592             btnEl.on('focus', this.onFocus, this);
27593             btnEl.on('blur', this.onBlur, this);
27594             if(this.cls){
27595                 btn.addClass(this.cls);
27596             }
27597             if(this.icon){
27598                 btnEl.setStyle('background-image', 'url(' +this.icon +')');
27599             }
27600             if(this.iconCls){
27601                 btnEl.addClass(this.iconCls);
27602                 if(!this.cls){
27603                     btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
27604                 }
27605             }
27606             if(this.tabIndex !== undefined){
27607                 btnEl.dom.tabIndex = this.tabIndex;
27608             }
27609             if(this.tooltip){
27610                 if(typeof this.tooltip == 'object'){
27611                     Roo.QuickTips.tips(Roo.apply({
27612                           target: btnEl.id
27613                     }, this.tooltip));
27614                 } else {
27615                     btnEl.dom[this.tooltipType] = this.tooltip;
27616                 }
27617             }
27618         }else{
27619             btn = Roo.DomHelper.append(Roo.get(renderTo).dom, this.dhconfig, true);
27620         }
27621         this.el = btn;
27622         if(this.id){
27623             this.el.dom.id = this.el.id = this.id;
27624         }
27625         if(this.menu){
27626             this.el.child(this.menuClassTarget).addClass("x-btn-with-menu");
27627             this.menu.on("show", this.onMenuShow, this);
27628             this.menu.on("hide", this.onMenuHide, this);
27629         }
27630         btn.addClass("x-btn");
27631         if(Roo.isIE && !Roo.isIE7){
27632             this.autoWidth.defer(1, this);
27633         }else{
27634             this.autoWidth();
27635         }
27636         if(this.handleMouseEvents){
27637             btn.on("mouseover", this.onMouseOver, this);
27638             btn.on("mouseout", this.onMouseOut, this);
27639             btn.on("mousedown", this.onMouseDown, this);
27640         }
27641         btn.on(this.clickEvent, this.onClick, this);
27642         //btn.on("mouseup", this.onMouseUp, this);
27643         if(this.hidden){
27644             this.hide();
27645         }
27646         if(this.disabled){
27647             this.disable();
27648         }
27649         Roo.ButtonToggleMgr.register(this);
27650         if(this.pressed){
27651             this.el.addClass("x-btn-pressed");
27652         }
27653         if(this.repeat){
27654             var repeater = new Roo.util.ClickRepeater(btn,
27655                 typeof this.repeat == "object" ? this.repeat : {}
27656             );
27657             repeater.on("click", this.onClick,  this);
27658         }
27659         
27660         this.fireEvent('render', this);
27661         
27662     },
27663     /**
27664      * Returns the button's underlying element
27665      * @return {Roo.Element} The element
27666      */
27667     getEl : function(){
27668         return this.el;  
27669     },
27670     
27671     /**
27672      * Destroys this Button and removes any listeners.
27673      */
27674     destroy : function(){
27675         Roo.ButtonToggleMgr.unregister(this);
27676         this.el.removeAllListeners();
27677         this.purgeListeners();
27678         this.el.remove();
27679     },
27680
27681     // private
27682     autoWidth : function(){
27683         if(this.el){
27684             this.el.setWidth("auto");
27685             if(Roo.isIE7 && Roo.isStrict){
27686                 var ib = this.el.child('button');
27687                 if(ib && ib.getWidth() > 20){
27688                     ib.clip();
27689                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
27690                 }
27691             }
27692             if(this.minWidth){
27693                 if(this.hidden){
27694                     this.el.beginMeasure();
27695                 }
27696                 if(this.el.getWidth() < this.minWidth){
27697                     this.el.setWidth(this.minWidth);
27698                 }
27699                 if(this.hidden){
27700                     this.el.endMeasure();
27701                 }
27702             }
27703         }
27704     },
27705
27706     /**
27707      * Assigns this button's click handler
27708      * @param {Function} handler The function to call when the button is clicked
27709      * @param {Object} scope (optional) Scope for the function passed in
27710      */
27711     setHandler : function(handler, scope){
27712         this.handler = handler;
27713         this.scope = scope;  
27714     },
27715     
27716     /**
27717      * Sets this button's text
27718      * @param {String} text The button text
27719      */
27720     setText : function(text){
27721         this.text = text;
27722         if(this.el){
27723             this.el.child("td.x-btn-center button.x-btn-text").update(text);
27724         }
27725         this.autoWidth();
27726     },
27727     
27728     /**
27729      * Gets the text for this button
27730      * @return {String} The button text
27731      */
27732     getText : function(){
27733         return this.text;  
27734     },
27735     
27736     /**
27737      * Show this button
27738      */
27739     show: function(){
27740         this.hidden = false;
27741         if(this.el){
27742             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "");
27743         }
27744     },
27745     
27746     /**
27747      * Hide this button
27748      */
27749     hide: function(){
27750         this.hidden = true;
27751         if(this.el){
27752             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "none");
27753         }
27754     },
27755     
27756     /**
27757      * Convenience function for boolean show/hide
27758      * @param {Boolean} visible True to show, false to hide
27759      */
27760     setVisible: function(visible){
27761         if(visible) {
27762             this.show();
27763         }else{
27764             this.hide();
27765         }
27766     },
27767     
27768     /**
27769      * If a state it passed, it becomes the pressed state otherwise the current state is toggled.
27770      * @param {Boolean} state (optional) Force a particular state
27771      */
27772     toggle : function(state){
27773         state = state === undefined ? !this.pressed : state;
27774         if(state != this.pressed){
27775             if(state){
27776                 this.el.addClass("x-btn-pressed");
27777                 this.pressed = true;
27778                 this.fireEvent("toggle", this, true);
27779             }else{
27780                 this.el.removeClass("x-btn-pressed");
27781                 this.pressed = false;
27782                 this.fireEvent("toggle", this, false);
27783             }
27784             if(this.toggleHandler){
27785                 this.toggleHandler.call(this.scope || this, this, state);
27786             }
27787         }
27788     },
27789     
27790     /**
27791      * Focus the button
27792      */
27793     focus : function(){
27794         this.el.child('button:first').focus();
27795     },
27796     
27797     /**
27798      * Disable this button
27799      */
27800     disable : function(){
27801         if(this.el){
27802             this.el.addClass("x-btn-disabled");
27803         }
27804         this.disabled = true;
27805     },
27806     
27807     /**
27808      * Enable this button
27809      */
27810     enable : function(){
27811         if(this.el){
27812             this.el.removeClass("x-btn-disabled");
27813         }
27814         this.disabled = false;
27815     },
27816
27817     /**
27818      * Convenience function for boolean enable/disable
27819      * @param {Boolean} enabled True to enable, false to disable
27820      */
27821     setDisabled : function(v){
27822         this[v !== true ? "enable" : "disable"]();
27823     },
27824
27825     // private
27826     onClick : function(e){
27827         if(e){
27828             e.preventDefault();
27829         }
27830         if(e.button != 0){
27831             return;
27832         }
27833         if(!this.disabled){
27834             if(this.enableToggle){
27835                 this.toggle();
27836             }
27837             if(this.menu && !this.menu.isVisible()){
27838                 this.menu.show(this.el, this.menuAlign);
27839             }
27840             this.fireEvent("click", this, e);
27841             if(this.handler){
27842                 this.el.removeClass("x-btn-over");
27843                 this.handler.call(this.scope || this, this, e);
27844             }
27845         }
27846     },
27847     // private
27848     onMouseOver : function(e){
27849         if(!this.disabled){
27850             this.el.addClass("x-btn-over");
27851             this.fireEvent('mouseover', this, e);
27852         }
27853     },
27854     // private
27855     onMouseOut : function(e){
27856         if(!e.within(this.el,  true)){
27857             this.el.removeClass("x-btn-over");
27858             this.fireEvent('mouseout', this, e);
27859         }
27860     },
27861     // private
27862     onFocus : function(e){
27863         if(!this.disabled){
27864             this.el.addClass("x-btn-focus");
27865         }
27866     },
27867     // private
27868     onBlur : function(e){
27869         this.el.removeClass("x-btn-focus");
27870     },
27871     // private
27872     onMouseDown : function(e){
27873         if(!this.disabled && e.button == 0){
27874             this.el.addClass("x-btn-click");
27875             Roo.get(document).on('mouseup', this.onMouseUp, this);
27876         }
27877     },
27878     // private
27879     onMouseUp : function(e){
27880         if(e.button == 0){
27881             this.el.removeClass("x-btn-click");
27882             Roo.get(document).un('mouseup', this.onMouseUp, this);
27883         }
27884     },
27885     // private
27886     onMenuShow : function(e){
27887         this.el.addClass("x-btn-menu-active");
27888     },
27889     // private
27890     onMenuHide : function(e){
27891         this.el.removeClass("x-btn-menu-active");
27892     }   
27893 });
27894
27895 // Private utility class used by Button
27896 Roo.ButtonToggleMgr = function(){
27897    var groups = {};
27898    
27899    function toggleGroup(btn, state){
27900        if(state){
27901            var g = groups[btn.toggleGroup];
27902            for(var i = 0, l = g.length; i < l; i++){
27903                if(g[i] != btn){
27904                    g[i].toggle(false);
27905                }
27906            }
27907        }
27908    }
27909    
27910    return {
27911        register : function(btn){
27912            if(!btn.toggleGroup){
27913                return;
27914            }
27915            var g = groups[btn.toggleGroup];
27916            if(!g){
27917                g = groups[btn.toggleGroup] = [];
27918            }
27919            g.push(btn);
27920            btn.on("toggle", toggleGroup);
27921        },
27922        
27923        unregister : function(btn){
27924            if(!btn.toggleGroup){
27925                return;
27926            }
27927            var g = groups[btn.toggleGroup];
27928            if(g){
27929                g.remove(btn);
27930                btn.un("toggle", toggleGroup);
27931            }
27932        }
27933    };
27934 }();/*
27935  * Based on:
27936  * Ext JS Library 1.1.1
27937  * Copyright(c) 2006-2007, Ext JS, LLC.
27938  *
27939  * Originally Released Under LGPL - original licence link has changed is not relivant.
27940  *
27941  * Fork - LGPL
27942  * <script type="text/javascript">
27943  */
27944  
27945 /**
27946  * @class Roo.SplitButton
27947  * @extends Roo.Button
27948  * A split button that provides a built-in dropdown arrow that can fire an event separately from the default
27949  * click event of the button.  Typically this would be used to display a dropdown menu that provides additional
27950  * options to the primary button action, but any custom handler can provide the arrowclick implementation.
27951  * @cfg {Function} arrowHandler A function called when the arrow button is clicked (can be used instead of click event)
27952  * @cfg {String} arrowTooltip The title attribute of the arrow
27953  * @constructor
27954  * Create a new menu button
27955  * @param {String/HTMLElement/Element} renderTo The element to append the button to
27956  * @param {Object} config The config object
27957  */
27958 Roo.SplitButton = function(renderTo, config){
27959     Roo.SplitButton.superclass.constructor.call(this, renderTo, config);
27960     /**
27961      * @event arrowclick
27962      * Fires when this button's arrow is clicked
27963      * @param {SplitButton} this
27964      * @param {EventObject} e The click event
27965      */
27966     this.addEvents({"arrowclick":true});
27967 };
27968
27969 Roo.extend(Roo.SplitButton, Roo.Button, {
27970     render : function(renderTo){
27971         // this is one sweet looking template!
27972         var tpl = new Roo.Template(
27973             '<table cellspacing="0" class="x-btn-menu-wrap x-btn"><tr><td>',
27974             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-text-wrap"><tbody>',
27975             '<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>',
27976             "</tbody></table></td><td>",
27977             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-arrow-wrap"><tbody>',
27978             '<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>',
27979             "</tbody></table></td></tr></table>"
27980         );
27981         var btn = tpl.append(renderTo, [this.text, this.type], true);
27982         var btnEl = btn.child("button");
27983         if(this.cls){
27984             btn.addClass(this.cls);
27985         }
27986         if(this.icon){
27987             btnEl.setStyle('background-image', 'url(' +this.icon +')');
27988         }
27989         if(this.iconCls){
27990             btnEl.addClass(this.iconCls);
27991             if(!this.cls){
27992                 btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
27993             }
27994         }
27995         this.el = btn;
27996         if(this.handleMouseEvents){
27997             btn.on("mouseover", this.onMouseOver, this);
27998             btn.on("mouseout", this.onMouseOut, this);
27999             btn.on("mousedown", this.onMouseDown, this);
28000             btn.on("mouseup", this.onMouseUp, this);
28001         }
28002         btn.on(this.clickEvent, this.onClick, this);
28003         if(this.tooltip){
28004             if(typeof this.tooltip == 'object'){
28005                 Roo.QuickTips.tips(Roo.apply({
28006                       target: btnEl.id
28007                 }, this.tooltip));
28008             } else {
28009                 btnEl.dom[this.tooltipType] = this.tooltip;
28010             }
28011         }
28012         if(this.arrowTooltip){
28013             btn.child("button:nth(2)").dom[this.tooltipType] = this.arrowTooltip;
28014         }
28015         if(this.hidden){
28016             this.hide();
28017         }
28018         if(this.disabled){
28019             this.disable();
28020         }
28021         if(this.pressed){
28022             this.el.addClass("x-btn-pressed");
28023         }
28024         if(Roo.isIE && !Roo.isIE7){
28025             this.autoWidth.defer(1, this);
28026         }else{
28027             this.autoWidth();
28028         }
28029         if(this.menu){
28030             this.menu.on("show", this.onMenuShow, this);
28031             this.menu.on("hide", this.onMenuHide, this);
28032         }
28033         this.fireEvent('render', this);
28034     },
28035
28036     // private
28037     autoWidth : function(){
28038         if(this.el){
28039             var tbl = this.el.child("table:first");
28040             var tbl2 = this.el.child("table:last");
28041             this.el.setWidth("auto");
28042             tbl.setWidth("auto");
28043             if(Roo.isIE7 && Roo.isStrict){
28044                 var ib = this.el.child('button:first');
28045                 if(ib && ib.getWidth() > 20){
28046                     ib.clip();
28047                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
28048                 }
28049             }
28050             if(this.minWidth){
28051                 if(this.hidden){
28052                     this.el.beginMeasure();
28053                 }
28054                 if((tbl.getWidth()+tbl2.getWidth()) < this.minWidth){
28055                     tbl.setWidth(this.minWidth-tbl2.getWidth());
28056                 }
28057                 if(this.hidden){
28058                     this.el.endMeasure();
28059                 }
28060             }
28061             this.el.setWidth(tbl.getWidth()+tbl2.getWidth());
28062         } 
28063     },
28064     /**
28065      * Sets this button's click handler
28066      * @param {Function} handler The function to call when the button is clicked
28067      * @param {Object} scope (optional) Scope for the function passed above
28068      */
28069     setHandler : function(handler, scope){
28070         this.handler = handler;
28071         this.scope = scope;  
28072     },
28073     
28074     /**
28075      * Sets this button's arrow click handler
28076      * @param {Function} handler The function to call when the arrow is clicked
28077      * @param {Object} scope (optional) Scope for the function passed above
28078      */
28079     setArrowHandler : function(handler, scope){
28080         this.arrowHandler = handler;
28081         this.scope = scope;  
28082     },
28083     
28084     /**
28085      * Focus the button
28086      */
28087     focus : function(){
28088         if(this.el){
28089             this.el.child("button:first").focus();
28090         }
28091     },
28092
28093     // private
28094     onClick : function(e){
28095         e.preventDefault();
28096         if(!this.disabled){
28097             if(e.getTarget(".x-btn-menu-arrow-wrap")){
28098                 if(this.menu && !this.menu.isVisible()){
28099                     this.menu.show(this.el, this.menuAlign);
28100                 }
28101                 this.fireEvent("arrowclick", this, e);
28102                 if(this.arrowHandler){
28103                     this.arrowHandler.call(this.scope || this, this, e);
28104                 }
28105             }else{
28106                 this.fireEvent("click", this, e);
28107                 if(this.handler){
28108                     this.handler.call(this.scope || this, this, e);
28109                 }
28110             }
28111         }
28112     },
28113     // private
28114     onMouseDown : function(e){
28115         if(!this.disabled){
28116             Roo.fly(e.getTarget("table")).addClass("x-btn-click");
28117         }
28118     },
28119     // private
28120     onMouseUp : function(e){
28121         Roo.fly(e.getTarget("table")).removeClass("x-btn-click");
28122     }   
28123 });
28124
28125
28126 // backwards compat
28127 Roo.MenuButton = Roo.SplitButton;/*
28128  * Based on:
28129  * Ext JS Library 1.1.1
28130  * Copyright(c) 2006-2007, Ext JS, LLC.
28131  *
28132  * Originally Released Under LGPL - original licence link has changed is not relivant.
28133  *
28134  * Fork - LGPL
28135  * <script type="text/javascript">
28136  */
28137
28138 /**
28139  * @class Roo.Toolbar
28140  * Basic Toolbar class.
28141  * @constructor
28142  * Creates a new Toolbar
28143  * @param {Object} container The config object
28144  */ 
28145 Roo.Toolbar = function(container, buttons, config)
28146 {
28147     /// old consturctor format still supported..
28148     if(container instanceof Array){ // omit the container for later rendering
28149         buttons = container;
28150         config = buttons;
28151         container = null;
28152     }
28153     if (typeof(container) == 'object' && container.xtype) {
28154         config = container;
28155         container = config.container;
28156         buttons = config.buttons || []; // not really - use items!!
28157     }
28158     var xitems = [];
28159     if (config && config.items) {
28160         xitems = config.items;
28161         delete config.items;
28162     }
28163     Roo.apply(this, config);
28164     this.buttons = buttons;
28165     
28166     if(container){
28167         this.render(container);
28168     }
28169     this.xitems = xitems;
28170     Roo.each(xitems, function(b) {
28171         this.add(b);
28172     }, this);
28173     
28174 };
28175
28176 Roo.Toolbar.prototype = {
28177     /**
28178      * @cfg {Array} items
28179      * array of button configs or elements to add (will be converted to a MixedCollection)
28180      */
28181     
28182     /**
28183      * @cfg {String/HTMLElement/Element} container
28184      * The id or element that will contain the toolbar
28185      */
28186     // private
28187     render : function(ct){
28188         this.el = Roo.get(ct);
28189         if(this.cls){
28190             this.el.addClass(this.cls);
28191         }
28192         // using a table allows for vertical alignment
28193         // 100% width is needed by Safari...
28194         this.el.update('<div class="x-toolbar x-small-editor"><table cellspacing="0"><tr></tr></table></div>');
28195         this.tr = this.el.child("tr", true);
28196         var autoId = 0;
28197         this.items = new Roo.util.MixedCollection(false, function(o){
28198             return o.id || ("item" + (++autoId));
28199         });
28200         if(this.buttons){
28201             this.add.apply(this, this.buttons);
28202             delete this.buttons;
28203         }
28204     },
28205
28206     /**
28207      * Adds element(s) to the toolbar -- this function takes a variable number of 
28208      * arguments of mixed type and adds them to the toolbar.
28209      * @param {Mixed} arg1 The following types of arguments are all valid:<br />
28210      * <ul>
28211      * <li>{@link Roo.Toolbar.Button} config: A valid button config object (equivalent to {@link #addButton})</li>
28212      * <li>HtmlElement: Any standard HTML element (equivalent to {@link #addElement})</li>
28213      * <li>Field: Any form field (equivalent to {@link #addField})</li>
28214      * <li>Item: Any subclass of {@link Roo.Toolbar.Item} (equivalent to {@link #addItem})</li>
28215      * <li>String: Any generic string (gets wrapped in a {@link Roo.Toolbar.TextItem}, equivalent to {@link #addText}).
28216      * Note that there are a few special strings that are treated differently as explained nRoo.</li>
28217      * <li>'separator' or '-': Creates a separator element (equivalent to {@link #addSeparator})</li>
28218      * <li>' ': Creates a spacer element (equivalent to {@link #addSpacer})</li>
28219      * <li>'->': Creates a fill element (equivalent to {@link #addFill})</li>
28220      * </ul>
28221      * @param {Mixed} arg2
28222      * @param {Mixed} etc.
28223      */
28224     add : function(){
28225         var a = arguments, l = a.length;
28226         for(var i = 0; i < l; i++){
28227             this._add(a[i]);
28228         }
28229     },
28230     // private..
28231     _add : function(el) {
28232         
28233         if (el.xtype) {
28234             el = Roo.factory(el, typeof(Roo.Toolbar[el.xtype]) == 'undefined' ? Roo.form : Roo.Toolbar);
28235         }
28236         
28237         if (el.applyTo){ // some kind of form field
28238             return this.addField(el);
28239         } 
28240         if (el.render){ // some kind of Toolbar.Item
28241             return this.addItem(el);
28242         }
28243         if (typeof el == "string"){ // string
28244             if(el == "separator" || el == "-"){
28245                 return this.addSeparator();
28246             }
28247             if (el == " "){
28248                 return this.addSpacer();
28249             }
28250             if(el == "->"){
28251                 return this.addFill();
28252             }
28253             return this.addText(el);
28254             
28255         }
28256         if(el.tagName){ // element
28257             return this.addElement(el);
28258         }
28259         if(typeof el == "object"){ // must be button config?
28260             return this.addButton(el);
28261         }
28262         // and now what?!?!
28263         return false;
28264         
28265     },
28266     
28267     /**
28268      * Add an Xtype element
28269      * @param {Object} xtype Xtype Object
28270      * @return {Object} created Object
28271      */
28272     addxtype : function(e){
28273         return this.add(e);  
28274     },
28275     
28276     /**
28277      * Returns the Element for this toolbar.
28278      * @return {Roo.Element}
28279      */
28280     getEl : function(){
28281         return this.el;  
28282     },
28283     
28284     /**
28285      * Adds a separator
28286      * @return {Roo.Toolbar.Item} The separator item
28287      */
28288     addSeparator : function(){
28289         return this.addItem(new Roo.Toolbar.Separator());
28290     },
28291
28292     /**
28293      * Adds a spacer element
28294      * @return {Roo.Toolbar.Spacer} The spacer item
28295      */
28296     addSpacer : function(){
28297         return this.addItem(new Roo.Toolbar.Spacer());
28298     },
28299
28300     /**
28301      * Adds a fill element that forces subsequent additions to the right side of the toolbar
28302      * @return {Roo.Toolbar.Fill} The fill item
28303      */
28304     addFill : function(){
28305         return this.addItem(new Roo.Toolbar.Fill());
28306     },
28307
28308     /**
28309      * Adds any standard HTML element to the toolbar
28310      * @param {String/HTMLElement/Element} el The element or id of the element to add
28311      * @return {Roo.Toolbar.Item} The element's item
28312      */
28313     addElement : function(el){
28314         return this.addItem(new Roo.Toolbar.Item(el));
28315     },
28316     /**
28317      * Collection of items on the toolbar.. (only Toolbar Items, so use fields to retrieve fields)
28318      * @type Roo.util.MixedCollection  
28319      */
28320     items : false,
28321      
28322     /**
28323      * Adds any Toolbar.Item or subclass
28324      * @param {Roo.Toolbar.Item} item
28325      * @return {Roo.Toolbar.Item} The item
28326      */
28327     addItem : function(item){
28328         var td = this.nextBlock();
28329         item.render(td);
28330         this.items.add(item);
28331         return item;
28332     },
28333     
28334     /**
28335      * Adds a button (or buttons). See {@link Roo.Toolbar.Button} for more info on the config.
28336      * @param {Object/Array} config A button config or array of configs
28337      * @return {Roo.Toolbar.Button/Array}
28338      */
28339     addButton : function(config){
28340         if(config instanceof Array){
28341             var buttons = [];
28342             for(var i = 0, len = config.length; i < len; i++) {
28343                 buttons.push(this.addButton(config[i]));
28344             }
28345             return buttons;
28346         }
28347         var b = config;
28348         if(!(config instanceof Roo.Toolbar.Button)){
28349             b = config.split ?
28350                 new Roo.Toolbar.SplitButton(config) :
28351                 new Roo.Toolbar.Button(config);
28352         }
28353         var td = this.nextBlock();
28354         b.render(td);
28355         this.items.add(b);
28356         return b;
28357     },
28358     
28359     /**
28360      * Adds text to the toolbar
28361      * @param {String} text The text to add
28362      * @return {Roo.Toolbar.Item} The element's item
28363      */
28364     addText : function(text){
28365         return this.addItem(new Roo.Toolbar.TextItem(text));
28366     },
28367     
28368     /**
28369      * Inserts any {@link Roo.Toolbar.Item}/{@link Roo.Toolbar.Button} at the specified index.
28370      * @param {Number} index The index where the item is to be inserted
28371      * @param {Object/Roo.Toolbar.Item/Roo.Toolbar.Button (may be Array)} item The button, or button config object to be inserted.
28372      * @return {Roo.Toolbar.Button/Item}
28373      */
28374     insertButton : function(index, item){
28375         if(item instanceof Array){
28376             var buttons = [];
28377             for(var i = 0, len = item.length; i < len; i++) {
28378                buttons.push(this.insertButton(index + i, item[i]));
28379             }
28380             return buttons;
28381         }
28382         if (!(item instanceof Roo.Toolbar.Button)){
28383            item = new Roo.Toolbar.Button(item);
28384         }
28385         var td = document.createElement("td");
28386         this.tr.insertBefore(td, this.tr.childNodes[index]);
28387         item.render(td);
28388         this.items.insert(index, item);
28389         return item;
28390     },
28391     
28392     /**
28393      * Adds a new element to the toolbar from the passed {@link Roo.DomHelper} config.
28394      * @param {Object} config
28395      * @return {Roo.Toolbar.Item} The element's item
28396      */
28397     addDom : function(config, returnEl){
28398         var td = this.nextBlock();
28399         Roo.DomHelper.overwrite(td, config);
28400         var ti = new Roo.Toolbar.Item(td.firstChild);
28401         ti.render(td);
28402         this.items.add(ti);
28403         return ti;
28404     },
28405
28406     /**
28407      * Collection of fields on the toolbar.. usefull for quering (value is false if there are no fields)
28408      * @type Roo.util.MixedCollection  
28409      */
28410     fields : false,
28411     
28412     /**
28413      * Adds a dynamically rendered Roo.form field (TextField, ComboBox, etc).
28414      * Note: the field should not have been rendered yet. For a field that has already been
28415      * rendered, use {@link #addElement}.
28416      * @param {Roo.form.Field} field
28417      * @return {Roo.ToolbarItem}
28418      */
28419      
28420       
28421     addField : function(field) {
28422         if (!this.fields) {
28423             var autoId = 0;
28424             this.fields = new Roo.util.MixedCollection(false, function(o){
28425                 return o.id || ("item" + (++autoId));
28426             });
28427
28428         }
28429         
28430         var td = this.nextBlock();
28431         field.render(td);
28432         var ti = new Roo.Toolbar.Item(td.firstChild);
28433         ti.render(td);
28434         this.items.add(ti);
28435         this.fields.add(field);
28436         return ti;
28437     },
28438     /**
28439      * Hide the toolbar
28440      * @method hide
28441      */
28442      
28443       
28444     hide : function()
28445     {
28446         this.el.child('div').setVisibilityMode(Roo.Element.DISPLAY);
28447         this.el.child('div').hide();
28448     },
28449     /**
28450      * Show the toolbar
28451      * @method show
28452      */
28453     show : function()
28454     {
28455         this.el.child('div').show();
28456     },
28457       
28458     // private
28459     nextBlock : function(){
28460         var td = document.createElement("td");
28461         this.tr.appendChild(td);
28462         return td;
28463     },
28464
28465     // private
28466     destroy : function(){
28467         if(this.items){ // rendered?
28468             Roo.destroy.apply(Roo, this.items.items);
28469         }
28470         if(this.fields){ // rendered?
28471             Roo.destroy.apply(Roo, this.fields.items);
28472         }
28473         Roo.Element.uncache(this.el, this.tr);
28474     }
28475 };
28476
28477 /**
28478  * @class Roo.Toolbar.Item
28479  * The base class that other classes should extend in order to get some basic common toolbar item functionality.
28480  * @constructor
28481  * Creates a new Item
28482  * @param {HTMLElement} el 
28483  */
28484 Roo.Toolbar.Item = function(el){
28485     this.el = Roo.getDom(el);
28486     this.id = Roo.id(this.el);
28487     this.hidden = false;
28488 };
28489
28490 Roo.Toolbar.Item.prototype = {
28491     
28492     /**
28493      * Get this item's HTML Element
28494      * @return {HTMLElement}
28495      */
28496     getEl : function(){
28497        return this.el;  
28498     },
28499
28500     // private
28501     render : function(td){
28502         this.td = td;
28503         td.appendChild(this.el);
28504     },
28505     
28506     /**
28507      * Removes and destroys this item.
28508      */
28509     destroy : function(){
28510         this.td.parentNode.removeChild(this.td);
28511     },
28512     
28513     /**
28514      * Shows this item.
28515      */
28516     show: function(){
28517         this.hidden = false;
28518         this.td.style.display = "";
28519     },
28520     
28521     /**
28522      * Hides this item.
28523      */
28524     hide: function(){
28525         this.hidden = true;
28526         this.td.style.display = "none";
28527     },
28528     
28529     /**
28530      * Convenience function for boolean show/hide.
28531      * @param {Boolean} visible true to show/false to hide
28532      */
28533     setVisible: function(visible){
28534         if(visible) {
28535             this.show();
28536         }else{
28537             this.hide();
28538         }
28539     },
28540     
28541     /**
28542      * Try to focus this item.
28543      */
28544     focus : function(){
28545         Roo.fly(this.el).focus();
28546     },
28547     
28548     /**
28549      * Disables this item.
28550      */
28551     disable : function(){
28552         Roo.fly(this.td).addClass("x-item-disabled");
28553         this.disabled = true;
28554         this.el.disabled = true;
28555     },
28556     
28557     /**
28558      * Enables this item.
28559      */
28560     enable : function(){
28561         Roo.fly(this.td).removeClass("x-item-disabled");
28562         this.disabled = false;
28563         this.el.disabled = false;
28564     }
28565 };
28566
28567
28568 /**
28569  * @class Roo.Toolbar.Separator
28570  * @extends Roo.Toolbar.Item
28571  * A simple toolbar separator class
28572  * @constructor
28573  * Creates a new Separator
28574  */
28575 Roo.Toolbar.Separator = function(){
28576     var s = document.createElement("span");
28577     s.className = "ytb-sep";
28578     Roo.Toolbar.Separator.superclass.constructor.call(this, s);
28579 };
28580 Roo.extend(Roo.Toolbar.Separator, Roo.Toolbar.Item, {
28581     enable:Roo.emptyFn,
28582     disable:Roo.emptyFn,
28583     focus:Roo.emptyFn
28584 });
28585
28586 /**
28587  * @class Roo.Toolbar.Spacer
28588  * @extends Roo.Toolbar.Item
28589  * A simple element that adds extra horizontal space to a toolbar.
28590  * @constructor
28591  * Creates a new Spacer
28592  */
28593 Roo.Toolbar.Spacer = function(){
28594     var s = document.createElement("div");
28595     s.className = "ytb-spacer";
28596     Roo.Toolbar.Spacer.superclass.constructor.call(this, s);
28597 };
28598 Roo.extend(Roo.Toolbar.Spacer, Roo.Toolbar.Item, {
28599     enable:Roo.emptyFn,
28600     disable:Roo.emptyFn,
28601     focus:Roo.emptyFn
28602 });
28603
28604 /**
28605  * @class Roo.Toolbar.Fill
28606  * @extends Roo.Toolbar.Spacer
28607  * A simple element that adds a greedy (100% width) horizontal space to a toolbar.
28608  * @constructor
28609  * Creates a new Spacer
28610  */
28611 Roo.Toolbar.Fill = Roo.extend(Roo.Toolbar.Spacer, {
28612     // private
28613     render : function(td){
28614         td.style.width = '100%';
28615         Roo.Toolbar.Fill.superclass.render.call(this, td);
28616     }
28617 });
28618
28619 /**
28620  * @class Roo.Toolbar.TextItem
28621  * @extends Roo.Toolbar.Item
28622  * A simple class that renders text directly into a toolbar.
28623  * @constructor
28624  * Creates a new TextItem
28625  * @param {String} text
28626  */
28627 Roo.Toolbar.TextItem = function(text){
28628     if (typeof(text) == 'object') {
28629         text = text.text;
28630     }
28631     var s = document.createElement("span");
28632     s.className = "ytb-text";
28633     s.innerHTML = text;
28634     Roo.Toolbar.TextItem.superclass.constructor.call(this, s);
28635 };
28636 Roo.extend(Roo.Toolbar.TextItem, Roo.Toolbar.Item, {
28637     enable:Roo.emptyFn,
28638     disable:Roo.emptyFn,
28639     focus:Roo.emptyFn
28640 });
28641
28642 /**
28643  * @class Roo.Toolbar.Button
28644  * @extends Roo.Button
28645  * A button that renders into a toolbar.
28646  * @constructor
28647  * Creates a new Button
28648  * @param {Object} config A standard {@link Roo.Button} config object
28649  */
28650 Roo.Toolbar.Button = function(config){
28651     Roo.Toolbar.Button.superclass.constructor.call(this, null, config);
28652 };
28653 Roo.extend(Roo.Toolbar.Button, Roo.Button, {
28654     render : function(td){
28655         this.td = td;
28656         Roo.Toolbar.Button.superclass.render.call(this, td);
28657     },
28658     
28659     /**
28660      * Removes and destroys this button
28661      */
28662     destroy : function(){
28663         Roo.Toolbar.Button.superclass.destroy.call(this);
28664         this.td.parentNode.removeChild(this.td);
28665     },
28666     
28667     /**
28668      * Shows this button
28669      */
28670     show: function(){
28671         this.hidden = false;
28672         this.td.style.display = "";
28673     },
28674     
28675     /**
28676      * Hides this button
28677      */
28678     hide: function(){
28679         this.hidden = true;
28680         this.td.style.display = "none";
28681     },
28682
28683     /**
28684      * Disables this item
28685      */
28686     disable : function(){
28687         Roo.fly(this.td).addClass("x-item-disabled");
28688         this.disabled = true;
28689     },
28690
28691     /**
28692      * Enables this item
28693      */
28694     enable : function(){
28695         Roo.fly(this.td).removeClass("x-item-disabled");
28696         this.disabled = false;
28697     }
28698 });
28699 // backwards compat
28700 Roo.ToolbarButton = Roo.Toolbar.Button;
28701
28702 /**
28703  * @class Roo.Toolbar.SplitButton
28704  * @extends Roo.SplitButton
28705  * A menu button that renders into a toolbar.
28706  * @constructor
28707  * Creates a new SplitButton
28708  * @param {Object} config A standard {@link Roo.SplitButton} config object
28709  */
28710 Roo.Toolbar.SplitButton = function(config){
28711     Roo.Toolbar.SplitButton.superclass.constructor.call(this, null, config);
28712 };
28713 Roo.extend(Roo.Toolbar.SplitButton, Roo.SplitButton, {
28714     render : function(td){
28715         this.td = td;
28716         Roo.Toolbar.SplitButton.superclass.render.call(this, td);
28717     },
28718     
28719     /**
28720      * Removes and destroys this button
28721      */
28722     destroy : function(){
28723         Roo.Toolbar.SplitButton.superclass.destroy.call(this);
28724         this.td.parentNode.removeChild(this.td);
28725     },
28726     
28727     /**
28728      * Shows this button
28729      */
28730     show: function(){
28731         this.hidden = false;
28732         this.td.style.display = "";
28733     },
28734     
28735     /**
28736      * Hides this button
28737      */
28738     hide: function(){
28739         this.hidden = true;
28740         this.td.style.display = "none";
28741     }
28742 });
28743
28744 // backwards compat
28745 Roo.Toolbar.MenuButton = Roo.Toolbar.SplitButton;/*
28746  * Based on:
28747  * Ext JS Library 1.1.1
28748  * Copyright(c) 2006-2007, Ext JS, LLC.
28749  *
28750  * Originally Released Under LGPL - original licence link has changed is not relivant.
28751  *
28752  * Fork - LGPL
28753  * <script type="text/javascript">
28754  */
28755  
28756 /**
28757  * @class Roo.PagingToolbar
28758  * @extends Roo.Toolbar
28759  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
28760  * @constructor
28761  * Create a new PagingToolbar
28762  * @param {Object} config The config object
28763  */
28764 Roo.PagingToolbar = function(el, ds, config)
28765 {
28766     // old args format still supported... - xtype is prefered..
28767     if (typeof(el) == 'object' && el.xtype) {
28768         // created from xtype...
28769         config = el;
28770         ds = el.dataSource;
28771         el = config.container;
28772     }
28773     var items = [];
28774     if (config.items) {
28775         items = config.items;
28776         config.items = [];
28777     }
28778     
28779     Roo.PagingToolbar.superclass.constructor.call(this, el, null, config);
28780     this.ds = ds;
28781     this.cursor = 0;
28782     this.renderButtons(this.el);
28783     this.bind(ds);
28784     
28785     // supprot items array.
28786    
28787     Roo.each(items, function(e) {
28788         this.add(Roo.factory(e));
28789     },this);
28790     
28791 };
28792
28793 Roo.extend(Roo.PagingToolbar, Roo.Toolbar, {
28794     /**
28795      * @cfg {Roo.data.Store} dataSource
28796      * The underlying data store providing the paged data
28797      */
28798     /**
28799      * @cfg {String/HTMLElement/Element} container
28800      * container The id or element that will contain the toolbar
28801      */
28802     /**
28803      * @cfg {Boolean} displayInfo
28804      * True to display the displayMsg (defaults to false)
28805      */
28806     /**
28807      * @cfg {Number} pageSize
28808      * The number of records to display per page (defaults to 20)
28809      */
28810     pageSize: 20,
28811     /**
28812      * @cfg {String} displayMsg
28813      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
28814      */
28815     displayMsg : 'Displaying {0} - {1} of {2}',
28816     /**
28817      * @cfg {String} emptyMsg
28818      * The message to display when no records are found (defaults to "No data to display")
28819      */
28820     emptyMsg : 'No data to display',
28821     /**
28822      * Customizable piece of the default paging text (defaults to "Page")
28823      * @type String
28824      */
28825     beforePageText : "Page",
28826     /**
28827      * Customizable piece of the default paging text (defaults to "of %0")
28828      * @type String
28829      */
28830     afterPageText : "of {0}",
28831     /**
28832      * Customizable piece of the default paging text (defaults to "First Page")
28833      * @type String
28834      */
28835     firstText : "First Page",
28836     /**
28837      * Customizable piece of the default paging text (defaults to "Previous Page")
28838      * @type String
28839      */
28840     prevText : "Previous Page",
28841     /**
28842      * Customizable piece of the default paging text (defaults to "Next Page")
28843      * @type String
28844      */
28845     nextText : "Next Page",
28846     /**
28847      * Customizable piece of the default paging text (defaults to "Last Page")
28848      * @type String
28849      */
28850     lastText : "Last Page",
28851     /**
28852      * Customizable piece of the default paging text (defaults to "Refresh")
28853      * @type String
28854      */
28855     refreshText : "Refresh",
28856
28857     // private
28858     renderButtons : function(el){
28859         Roo.PagingToolbar.superclass.render.call(this, el);
28860         this.first = this.addButton({
28861             tooltip: this.firstText,
28862             cls: "x-btn-icon x-grid-page-first",
28863             disabled: true,
28864             handler: this.onClick.createDelegate(this, ["first"])
28865         });
28866         this.prev = this.addButton({
28867             tooltip: this.prevText,
28868             cls: "x-btn-icon x-grid-page-prev",
28869             disabled: true,
28870             handler: this.onClick.createDelegate(this, ["prev"])
28871         });
28872         //this.addSeparator();
28873         this.add(this.beforePageText);
28874         this.field = Roo.get(this.addDom({
28875            tag: "input",
28876            type: "text",
28877            size: "3",
28878            value: "1",
28879            cls: "x-grid-page-number"
28880         }).el);
28881         this.field.on("keydown", this.onPagingKeydown, this);
28882         this.field.on("focus", function(){this.dom.select();});
28883         this.afterTextEl = this.addText(String.format(this.afterPageText, 1));
28884         this.field.setHeight(18);
28885         //this.addSeparator();
28886         this.next = this.addButton({
28887             tooltip: this.nextText,
28888             cls: "x-btn-icon x-grid-page-next",
28889             disabled: true,
28890             handler: this.onClick.createDelegate(this, ["next"])
28891         });
28892         this.last = this.addButton({
28893             tooltip: this.lastText,
28894             cls: "x-btn-icon x-grid-page-last",
28895             disabled: true,
28896             handler: this.onClick.createDelegate(this, ["last"])
28897         });
28898         //this.addSeparator();
28899         this.loading = this.addButton({
28900             tooltip: this.refreshText,
28901             cls: "x-btn-icon x-grid-loading",
28902             handler: this.onClick.createDelegate(this, ["refresh"])
28903         });
28904
28905         if(this.displayInfo){
28906             this.displayEl = Roo.fly(this.el.dom.firstChild).createChild({cls:'x-paging-info'});
28907         }
28908     },
28909
28910     // private
28911     updateInfo : function(){
28912         if(this.displayEl){
28913             var count = this.ds.getCount();
28914             var msg = count == 0 ?
28915                 this.emptyMsg :
28916                 String.format(
28917                     this.displayMsg,
28918                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
28919                 );
28920             this.displayEl.update(msg);
28921         }
28922     },
28923
28924     // private
28925     onLoad : function(ds, r, o){
28926        this.cursor = o.params ? o.params.start : 0;
28927        var d = this.getPageData(), ap = d.activePage, ps = d.pages;
28928
28929        this.afterTextEl.el.innerHTML = String.format(this.afterPageText, d.pages);
28930        this.field.dom.value = ap;
28931        this.first.setDisabled(ap == 1);
28932        this.prev.setDisabled(ap == 1);
28933        this.next.setDisabled(ap == ps);
28934        this.last.setDisabled(ap == ps);
28935        this.loading.enable();
28936        this.updateInfo();
28937     },
28938
28939     // private
28940     getPageData : function(){
28941         var total = this.ds.getTotalCount();
28942         return {
28943             total : total,
28944             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
28945             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
28946         };
28947     },
28948
28949     // private
28950     onLoadError : function(){
28951         this.loading.enable();
28952     },
28953
28954     // private
28955     onPagingKeydown : function(e){
28956         var k = e.getKey();
28957         var d = this.getPageData();
28958         if(k == e.RETURN){
28959             var v = this.field.dom.value, pageNum;
28960             if(!v || isNaN(pageNum = parseInt(v, 10))){
28961                 this.field.dom.value = d.activePage;
28962                 return;
28963             }
28964             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
28965             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
28966             e.stopEvent();
28967         }
28968         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))
28969         {
28970           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
28971           this.field.dom.value = pageNum;
28972           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
28973           e.stopEvent();
28974         }
28975         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
28976         {
28977           var v = this.field.dom.value, pageNum; 
28978           var increment = (e.shiftKey) ? 10 : 1;
28979           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
28980             increment *= -1;
28981           if(!v || isNaN(pageNum = parseInt(v, 10))) {
28982             this.field.dom.value = d.activePage;
28983             return;
28984           }
28985           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
28986           {
28987             this.field.dom.value = parseInt(v, 10) + increment;
28988             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
28989             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
28990           }
28991           e.stopEvent();
28992         }
28993     },
28994
28995     // private
28996     beforeLoad : function(){
28997         if(this.loading){
28998             this.loading.disable();
28999         }
29000     },
29001
29002     // private
29003     onClick : function(which){
29004         var ds = this.ds;
29005         switch(which){
29006             case "first":
29007                 ds.load({params:{start: 0, limit: this.pageSize}});
29008             break;
29009             case "prev":
29010                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
29011             break;
29012             case "next":
29013                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
29014             break;
29015             case "last":
29016                 var total = ds.getTotalCount();
29017                 var extra = total % this.pageSize;
29018                 var lastStart = extra ? (total - extra) : total-this.pageSize;
29019                 ds.load({params:{start: lastStart, limit: this.pageSize}});
29020             break;
29021             case "refresh":
29022                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
29023             break;
29024         }
29025     },
29026
29027     /**
29028      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
29029      * @param {Roo.data.Store} store The data store to unbind
29030      */
29031     unbind : function(ds){
29032         ds.un("beforeload", this.beforeLoad, this);
29033         ds.un("load", this.onLoad, this);
29034         ds.un("loadexception", this.onLoadError, this);
29035         ds.un("remove", this.updateInfo, this);
29036         ds.un("add", this.updateInfo, this);
29037         this.ds = undefined;
29038     },
29039
29040     /**
29041      * Binds the paging toolbar to the specified {@link Roo.data.Store}
29042      * @param {Roo.data.Store} store The data store to bind
29043      */
29044     bind : function(ds){
29045         ds.on("beforeload", this.beforeLoad, this);
29046         ds.on("load", this.onLoad, this);
29047         ds.on("loadexception", this.onLoadError, this);
29048         ds.on("remove", this.updateInfo, this);
29049         ds.on("add", this.updateInfo, this);
29050         this.ds = ds;
29051     }
29052 });/*
29053  * Based on:
29054  * Ext JS Library 1.1.1
29055  * Copyright(c) 2006-2007, Ext JS, LLC.
29056  *
29057  * Originally Released Under LGPL - original licence link has changed is not relivant.
29058  *
29059  * Fork - LGPL
29060  * <script type="text/javascript">
29061  */
29062
29063 /**
29064  * @class Roo.Resizable
29065  * @extends Roo.util.Observable
29066  * <p>Applies drag handles to an element to make it resizable. The drag handles are inserted into the element
29067  * and positioned absolute. Some elements, such as a textarea or image, don't support this. To overcome that, you can wrap
29068  * 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
29069  * the element will be wrapped for you automatically.</p>
29070  * <p>Here is the list of valid resize handles:</p>
29071  * <pre>
29072 Value   Description
29073 ------  -------------------
29074  'n'     north
29075  's'     south
29076  'e'     east
29077  'w'     west
29078  'nw'    northwest
29079  'sw'    southwest
29080  'se'    southeast
29081  'ne'    northeast
29082  'hd'    horizontal drag
29083  'all'   all
29084 </pre>
29085  * <p>Here's an example showing the creation of a typical Resizable:</p>
29086  * <pre><code>
29087 var resizer = new Roo.Resizable("element-id", {
29088     handles: 'all',
29089     minWidth: 200,
29090     minHeight: 100,
29091     maxWidth: 500,
29092     maxHeight: 400,
29093     pinned: true
29094 });
29095 resizer.on("resize", myHandler);
29096 </code></pre>
29097  * <p>To hide a particular handle, set its display to none in CSS, or through script:<br>
29098  * resizer.east.setDisplayed(false);</p>
29099  * @cfg {Boolean/String/Element} resizeChild True to resize the first child, or id/element to resize (defaults to false)
29100  * @cfg {Array/String} adjustments String "auto" or an array [width, height] with values to be <b>added</b> to the
29101  * resize operation's new size (defaults to [0, 0])
29102  * @cfg {Number} minWidth The minimum width for the element (defaults to 5)
29103  * @cfg {Number} minHeight The minimum height for the element (defaults to 5)
29104  * @cfg {Number} maxWidth The maximum width for the element (defaults to 10000)
29105  * @cfg {Number} maxHeight The maximum height for the element (defaults to 10000)
29106  * @cfg {Boolean} enabled False to disable resizing (defaults to true)
29107  * @cfg {Boolean} wrap True to wrap an element with a div if needed (required for textareas and images, defaults to false)
29108  * @cfg {Number} width The width of the element in pixels (defaults to null)
29109  * @cfg {Number} height The height of the element in pixels (defaults to null)
29110  * @cfg {Boolean} animate True to animate the resize (not compatible with dynamic sizing, defaults to false)
29111  * @cfg {Number} duration Animation duration if animate = true (defaults to .35)
29112  * @cfg {Boolean} dynamic True to resize the element while dragging instead of using a proxy (defaults to false)
29113  * @cfg {String} handles String consisting of the resize handles to display (defaults to undefined)
29114  * @cfg {Boolean} multiDirectional <b>Deprecated</b>.  The old style of adding multi-direction resize handles, deprecated
29115  * in favor of the handles config option (defaults to false)
29116  * @cfg {Boolean} disableTrackOver True to disable mouse tracking. This is only applied at config time. (defaults to false)
29117  * @cfg {String} easing Animation easing if animate = true (defaults to 'easingOutStrong')
29118  * @cfg {Number} widthIncrement The increment to snap the width resize in pixels (dynamic must be true, defaults to 0)
29119  * @cfg {Number} heightIncrement The increment to snap the height resize in pixels (dynamic must be true, defaults to 0)
29120  * @cfg {Boolean} pinned True to ensure that the resize handles are always visible, false to display them only when the
29121  * user mouses over the resizable borders. This is only applied at config time. (defaults to false)
29122  * @cfg {Boolean} preserveRatio True to preserve the original ratio between height and width during resize (defaults to false)
29123  * @cfg {Boolean} transparent True for transparent handles. This is only applied at config time. (defaults to false)
29124  * @cfg {Number} minX The minimum allowed page X for the element (only used for west resizing, defaults to 0)
29125  * @cfg {Number} minY The minimum allowed page Y for the element (only used for north resizing, defaults to 0)
29126  * @cfg {Boolean} draggable Convenience to initialize drag drop (defaults to false)
29127  * @constructor
29128  * Create a new resizable component
29129  * @param {String/HTMLElement/Roo.Element} el The id or element to resize
29130  * @param {Object} config configuration options
29131   */
29132 Roo.Resizable = function(el, config)
29133 {
29134     this.el = Roo.get(el);
29135
29136     if(config && config.wrap){
29137         config.resizeChild = this.el;
29138         this.el = this.el.wrap(typeof config.wrap == "object" ? config.wrap : {cls:"xresizable-wrap"});
29139         this.el.id = this.el.dom.id = config.resizeChild.id + "-rzwrap";
29140         this.el.setStyle("overflow", "hidden");
29141         this.el.setPositioning(config.resizeChild.getPositioning());
29142         config.resizeChild.clearPositioning();
29143         if(!config.width || !config.height){
29144             var csize = config.resizeChild.getSize();
29145             this.el.setSize(csize.width, csize.height);
29146         }
29147         if(config.pinned && !config.adjustments){
29148             config.adjustments = "auto";
29149         }
29150     }
29151
29152     this.proxy = this.el.createProxy({tag: "div", cls: "x-resizable-proxy", id: this.el.id + "-rzproxy"});
29153     this.proxy.unselectable();
29154     this.proxy.enableDisplayMode('block');
29155
29156     Roo.apply(this, config);
29157
29158     if(this.pinned){
29159         this.disableTrackOver = true;
29160         this.el.addClass("x-resizable-pinned");
29161     }
29162     // if the element isn't positioned, make it relative
29163     var position = this.el.getStyle("position");
29164     if(position != "absolute" && position != "fixed"){
29165         this.el.setStyle("position", "relative");
29166     }
29167     if(!this.handles){ // no handles passed, must be legacy style
29168         this.handles = 's,e,se';
29169         if(this.multiDirectional){
29170             this.handles += ',n,w';
29171         }
29172     }
29173     if(this.handles == "all"){
29174         this.handles = "n s e w ne nw se sw";
29175     }
29176     var hs = this.handles.split(/\s*?[,;]\s*?| /);
29177     var ps = Roo.Resizable.positions;
29178     for(var i = 0, len = hs.length; i < len; i++){
29179         if(hs[i] && ps[hs[i]]){
29180             var pos = ps[hs[i]];
29181             this[pos] = new Roo.Resizable.Handle(this, pos, this.disableTrackOver, this.transparent);
29182         }
29183     }
29184     // legacy
29185     this.corner = this.southeast;
29186     
29187     // updateBox = the box can move..
29188     if(this.handles.indexOf("n") != -1 || this.handles.indexOf("w") != -1 || this.handles.indexOf("hd") != -1) {
29189         this.updateBox = true;
29190     }
29191
29192     this.activeHandle = null;
29193
29194     if(this.resizeChild){
29195         if(typeof this.resizeChild == "boolean"){
29196             this.resizeChild = Roo.get(this.el.dom.firstChild, true);
29197         }else{
29198             this.resizeChild = Roo.get(this.resizeChild, true);
29199         }
29200     }
29201     
29202     if(this.adjustments == "auto"){
29203         var rc = this.resizeChild;
29204         var hw = this.west, he = this.east, hn = this.north, hs = this.south;
29205         if(rc && (hw || hn)){
29206             rc.position("relative");
29207             rc.setLeft(hw ? hw.el.getWidth() : 0);
29208             rc.setTop(hn ? hn.el.getHeight() : 0);
29209         }
29210         this.adjustments = [
29211             (he ? -he.el.getWidth() : 0) + (hw ? -hw.el.getWidth() : 0),
29212             (hn ? -hn.el.getHeight() : 0) + (hs ? -hs.el.getHeight() : 0) -1
29213         ];
29214     }
29215
29216     if(this.draggable){
29217         this.dd = this.dynamic ?
29218             this.el.initDD(null) : this.el.initDDProxy(null, {dragElId: this.proxy.id});
29219         this.dd.setHandleElId(this.resizeChild ? this.resizeChild.id : this.el.id);
29220     }
29221
29222     // public events
29223     this.addEvents({
29224         /**
29225          * @event beforeresize
29226          * Fired before resize is allowed. Set enabled to false to cancel resize.
29227          * @param {Roo.Resizable} this
29228          * @param {Roo.EventObject} e The mousedown event
29229          */
29230         "beforeresize" : true,
29231         /**
29232          * @event resizing
29233          * Fired a resizing.
29234          * @param {Roo.Resizable} this
29235          * @param {Number} x The new x position
29236          * @param {Number} y The new y position
29237          * @param {Number} w The new w width
29238          * @param {Number} h The new h hight
29239          * @param {Roo.EventObject} e The mouseup event
29240          */
29241         "resizing" : true,
29242         /**
29243          * @event resize
29244          * Fired after a resize.
29245          * @param {Roo.Resizable} this
29246          * @param {Number} width The new width
29247          * @param {Number} height The new height
29248          * @param {Roo.EventObject} e The mouseup event
29249          */
29250         "resize" : true
29251     });
29252
29253     if(this.width !== null && this.height !== null){
29254         this.resizeTo(this.width, this.height);
29255     }else{
29256         this.updateChildSize();
29257     }
29258     if(Roo.isIE){
29259         this.el.dom.style.zoom = 1;
29260     }
29261     Roo.Resizable.superclass.constructor.call(this);
29262 };
29263
29264 Roo.extend(Roo.Resizable, Roo.util.Observable, {
29265         resizeChild : false,
29266         adjustments : [0, 0],
29267         minWidth : 5,
29268         minHeight : 5,
29269         maxWidth : 10000,
29270         maxHeight : 10000,
29271         enabled : true,
29272         animate : false,
29273         duration : .35,
29274         dynamic : false,
29275         handles : false,
29276         multiDirectional : false,
29277         disableTrackOver : false,
29278         easing : 'easeOutStrong',
29279         widthIncrement : 0,
29280         heightIncrement : 0,
29281         pinned : false,
29282         width : null,
29283         height : null,
29284         preserveRatio : false,
29285         transparent: false,
29286         minX: 0,
29287         minY: 0,
29288         draggable: false,
29289
29290         /**
29291          * @cfg {String/HTMLElement/Element} constrainTo Constrain the resize to a particular element
29292          */
29293         constrainTo: undefined,
29294         /**
29295          * @cfg {Roo.lib.Region} resizeRegion Constrain the resize to a particular region
29296          */
29297         resizeRegion: undefined,
29298
29299
29300     /**
29301      * Perform a manual resize
29302      * @param {Number} width
29303      * @param {Number} height
29304      */
29305     resizeTo : function(width, height){
29306         this.el.setSize(width, height);
29307         this.updateChildSize();
29308         this.fireEvent("resize", this, width, height, null);
29309     },
29310
29311     // private
29312     startSizing : function(e, handle){
29313         this.fireEvent("beforeresize", this, e);
29314         if(this.enabled){ // 2nd enabled check in case disabled before beforeresize handler
29315
29316             if(!this.overlay){
29317                 this.overlay = this.el.createProxy({tag: "div", cls: "x-resizable-overlay", html: "&#160;"});
29318                 this.overlay.unselectable();
29319                 this.overlay.enableDisplayMode("block");
29320                 this.overlay.on("mousemove", this.onMouseMove, this);
29321                 this.overlay.on("mouseup", this.onMouseUp, this);
29322             }
29323             this.overlay.setStyle("cursor", handle.el.getStyle("cursor"));
29324
29325             this.resizing = true;
29326             this.startBox = this.el.getBox();
29327             this.startPoint = e.getXY();
29328             this.offsets = [(this.startBox.x + this.startBox.width) - this.startPoint[0],
29329                             (this.startBox.y + this.startBox.height) - this.startPoint[1]];
29330
29331             this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
29332             this.overlay.show();
29333
29334             if(this.constrainTo) {
29335                 var ct = Roo.get(this.constrainTo);
29336                 this.resizeRegion = ct.getRegion().adjust(
29337                     ct.getFrameWidth('t'),
29338                     ct.getFrameWidth('l'),
29339                     -ct.getFrameWidth('b'),
29340                     -ct.getFrameWidth('r')
29341                 );
29342             }
29343
29344             this.proxy.setStyle('visibility', 'hidden'); // workaround display none
29345             this.proxy.show();
29346             this.proxy.setBox(this.startBox);
29347             if(!this.dynamic){
29348                 this.proxy.setStyle('visibility', 'visible');
29349             }
29350         }
29351     },
29352
29353     // private
29354     onMouseDown : function(handle, e){
29355         if(this.enabled){
29356             e.stopEvent();
29357             this.activeHandle = handle;
29358             this.startSizing(e, handle);
29359         }
29360     },
29361
29362     // private
29363     onMouseUp : function(e){
29364         var size = this.resizeElement();
29365         this.resizing = false;
29366         this.handleOut();
29367         this.overlay.hide();
29368         this.proxy.hide();
29369         this.fireEvent("resize", this, size.width, size.height, e);
29370     },
29371
29372     // private
29373     updateChildSize : function(){
29374         
29375         if(this.resizeChild){
29376             var el = this.el;
29377             var child = this.resizeChild;
29378             var adj = this.adjustments;
29379             if(el.dom.offsetWidth){
29380                 var b = el.getSize(true);
29381                 child.setSize(b.width+adj[0], b.height+adj[1]);
29382             }
29383             // Second call here for IE
29384             // The first call enables instant resizing and
29385             // the second call corrects scroll bars if they
29386             // exist
29387             if(Roo.isIE){
29388                 setTimeout(function(){
29389                     if(el.dom.offsetWidth){
29390                         var b = el.getSize(true);
29391                         child.setSize(b.width+adj[0], b.height+adj[1]);
29392                     }
29393                 }, 10);
29394             }
29395         }
29396     },
29397
29398     // private
29399     snap : function(value, inc, min){
29400         if(!inc || !value) return value;
29401         var newValue = value;
29402         var m = value % inc;
29403         if(m > 0){
29404             if(m > (inc/2)){
29405                 newValue = value + (inc-m);
29406             }else{
29407                 newValue = value - m;
29408             }
29409         }
29410         return Math.max(min, newValue);
29411     },
29412
29413     // private
29414     resizeElement : function(){
29415         var box = this.proxy.getBox();
29416         if(this.updateBox){
29417             this.el.setBox(box, false, this.animate, this.duration, null, this.easing);
29418         }else{
29419             this.el.setSize(box.width, box.height, this.animate, this.duration, null, this.easing);
29420         }
29421         this.updateChildSize();
29422         if(!this.dynamic){
29423             this.proxy.hide();
29424         }
29425         return box;
29426     },
29427
29428     // private
29429     constrain : function(v, diff, m, mx){
29430         if(v - diff < m){
29431             diff = v - m;
29432         }else if(v - diff > mx){
29433             diff = mx - v;
29434         }
29435         return diff;
29436     },
29437
29438     // private
29439     onMouseMove : function(e){
29440         
29441         if(this.enabled){
29442             try{// try catch so if something goes wrong the user doesn't get hung
29443
29444             if(this.resizeRegion && !this.resizeRegion.contains(e.getPoint())) {
29445                 return;
29446             }
29447
29448             //var curXY = this.startPoint;
29449             var curSize = this.curSize || this.startBox;
29450             var x = this.startBox.x, y = this.startBox.y;
29451             var ox = x, oy = y;
29452             var w = curSize.width, h = curSize.height;
29453             var ow = w, oh = h;
29454             var mw = this.minWidth, mh = this.minHeight;
29455             var mxw = this.maxWidth, mxh = this.maxHeight;
29456             var wi = this.widthIncrement;
29457             var hi = this.heightIncrement;
29458
29459             var eventXY = e.getXY();
29460             var diffX = -(this.startPoint[0] - Math.max(this.minX, eventXY[0]));
29461             var diffY = -(this.startPoint[1] - Math.max(this.minY, eventXY[1]));
29462
29463             var pos = this.activeHandle.position;
29464
29465             switch(pos){
29466                 case "east":
29467                     w += diffX;
29468                     w = Math.min(Math.max(mw, w), mxw);
29469                     break;
29470              
29471                 case "south":
29472                     h += diffY;
29473                     h = Math.min(Math.max(mh, h), mxh);
29474                     break;
29475                 case "southeast":
29476                     w += diffX;
29477                     h += diffY;
29478                     w = Math.min(Math.max(mw, w), mxw);
29479                     h = Math.min(Math.max(mh, h), mxh);
29480                     break;
29481                 case "north":
29482                     diffY = this.constrain(h, diffY, mh, mxh);
29483                     y += diffY;
29484                     h -= diffY;
29485                     break;
29486                 case "hdrag":
29487                     
29488                     if (wi) {
29489                         var adiffX = Math.abs(diffX);
29490                         var sub = (adiffX % wi); // how much 
29491                         if (sub > (wi/2)) { // far enough to snap
29492                             diffX = (diffX > 0) ? diffX-sub + wi : diffX+sub - wi;
29493                         } else {
29494                             // remove difference.. 
29495                             diffX = (diffX > 0) ? diffX-sub : diffX+sub;
29496                         }
29497                     }
29498                     x += diffX;
29499                     x = Math.max(this.minX, x);
29500                     break;
29501                 case "west":
29502                     diffX = this.constrain(w, diffX, mw, mxw);
29503                     x += diffX;
29504                     w -= diffX;
29505                     break;
29506                 case "northeast":
29507                     w += diffX;
29508                     w = Math.min(Math.max(mw, w), mxw);
29509                     diffY = this.constrain(h, diffY, mh, mxh);
29510                     y += diffY;
29511                     h -= diffY;
29512                     break;
29513                 case "northwest":
29514                     diffX = this.constrain(w, diffX, mw, mxw);
29515                     diffY = this.constrain(h, diffY, mh, mxh);
29516                     y += diffY;
29517                     h -= diffY;
29518                     x += diffX;
29519                     w -= diffX;
29520                     break;
29521                case "southwest":
29522                     diffX = this.constrain(w, diffX, mw, mxw);
29523                     h += diffY;
29524                     h = Math.min(Math.max(mh, h), mxh);
29525                     x += diffX;
29526                     w -= diffX;
29527                     break;
29528             }
29529
29530             var sw = this.snap(w, wi, mw);
29531             var sh = this.snap(h, hi, mh);
29532             if(sw != w || sh != h){
29533                 switch(pos){
29534                     case "northeast":
29535                         y -= sh - h;
29536                     break;
29537                     case "north":
29538                         y -= sh - h;
29539                         break;
29540                     case "southwest":
29541                         x -= sw - w;
29542                     break;
29543                     case "west":
29544                         x -= sw - w;
29545                         break;
29546                     case "northwest":
29547                         x -= sw - w;
29548                         y -= sh - h;
29549                     break;
29550                 }
29551                 w = sw;
29552                 h = sh;
29553             }
29554
29555             if(this.preserveRatio){
29556                 switch(pos){
29557                     case "southeast":
29558                     case "east":
29559                         h = oh * (w/ow);
29560                         h = Math.min(Math.max(mh, h), mxh);
29561                         w = ow * (h/oh);
29562                        break;
29563                     case "south":
29564                         w = ow * (h/oh);
29565                         w = Math.min(Math.max(mw, w), mxw);
29566                         h = oh * (w/ow);
29567                         break;
29568                     case "northeast":
29569                         w = ow * (h/oh);
29570                         w = Math.min(Math.max(mw, w), mxw);
29571                         h = oh * (w/ow);
29572                     break;
29573                     case "north":
29574                         var tw = w;
29575                         w = ow * (h/oh);
29576                         w = Math.min(Math.max(mw, w), mxw);
29577                         h = oh * (w/ow);
29578                         x += (tw - w) / 2;
29579                         break;
29580                     case "southwest":
29581                         h = oh * (w/ow);
29582                         h = Math.min(Math.max(mh, h), mxh);
29583                         var tw = w;
29584                         w = ow * (h/oh);
29585                         x += tw - w;
29586                         break;
29587                     case "west":
29588                         var th = h;
29589                         h = oh * (w/ow);
29590                         h = Math.min(Math.max(mh, h), mxh);
29591                         y += (th - h) / 2;
29592                         var tw = w;
29593                         w = ow * (h/oh);
29594                         x += tw - w;
29595                        break;
29596                     case "northwest":
29597                         var tw = w;
29598                         var th = h;
29599                         h = oh * (w/ow);
29600                         h = Math.min(Math.max(mh, h), mxh);
29601                         w = ow * (h/oh);
29602                         y += th - h;
29603                         x += tw - w;
29604                        break;
29605
29606                 }
29607             }
29608             if (pos == 'hdrag') {
29609                 w = ow;
29610             }
29611             this.proxy.setBounds(x, y, w, h);
29612             if(this.dynamic){
29613                 this.resizeElement();
29614             }
29615             }catch(e){}
29616         }
29617         this.fireEvent("resizing", this, x, y, w, h, e);
29618     },
29619
29620     // private
29621     handleOver : function(){
29622         if(this.enabled){
29623             this.el.addClass("x-resizable-over");
29624         }
29625     },
29626
29627     // private
29628     handleOut : function(){
29629         if(!this.resizing){
29630             this.el.removeClass("x-resizable-over");
29631         }
29632     },
29633
29634     /**
29635      * Returns the element this component is bound to.
29636      * @return {Roo.Element}
29637      */
29638     getEl : function(){
29639         return this.el;
29640     },
29641
29642     /**
29643      * Returns the resizeChild element (or null).
29644      * @return {Roo.Element}
29645      */
29646     getResizeChild : function(){
29647         return this.resizeChild;
29648     },
29649     groupHandler : function()
29650     {
29651         
29652     },
29653     /**
29654      * Destroys this resizable. If the element was wrapped and
29655      * removeEl is not true then the element remains.
29656      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
29657      */
29658     destroy : function(removeEl){
29659         this.proxy.remove();
29660         if(this.overlay){
29661             this.overlay.removeAllListeners();
29662             this.overlay.remove();
29663         }
29664         var ps = Roo.Resizable.positions;
29665         for(var k in ps){
29666             if(typeof ps[k] != "function" && this[ps[k]]){
29667                 var h = this[ps[k]];
29668                 h.el.removeAllListeners();
29669                 h.el.remove();
29670             }
29671         }
29672         if(removeEl){
29673             this.el.update("");
29674             this.el.remove();
29675         }
29676     }
29677 });
29678
29679 // private
29680 // hash to map config positions to true positions
29681 Roo.Resizable.positions = {
29682     n: "north", s: "south", e: "east", w: "west", se: "southeast", sw: "southwest", nw: "northwest", ne: "northeast", 
29683     hd: "hdrag"
29684 };
29685
29686 // private
29687 Roo.Resizable.Handle = function(rz, pos, disableTrackOver, transparent){
29688     if(!this.tpl){
29689         // only initialize the template if resizable is used
29690         var tpl = Roo.DomHelper.createTemplate(
29691             {tag: "div", cls: "x-resizable-handle x-resizable-handle-{0}"}
29692         );
29693         tpl.compile();
29694         Roo.Resizable.Handle.prototype.tpl = tpl;
29695     }
29696     this.position = pos;
29697     this.rz = rz;
29698     // show north drag fro topdra
29699     var handlepos = pos == 'hdrag' ? 'north' : pos;
29700     
29701     this.el = this.tpl.append(rz.el.dom, [handlepos], true);
29702     if (pos == 'hdrag') {
29703         this.el.setStyle('cursor', 'pointer');
29704     }
29705     this.el.unselectable();
29706     if(transparent){
29707         this.el.setOpacity(0);
29708     }
29709     this.el.on("mousedown", this.onMouseDown, this);
29710     if(!disableTrackOver){
29711         this.el.on("mouseover", this.onMouseOver, this);
29712         this.el.on("mouseout", this.onMouseOut, this);
29713     }
29714 };
29715
29716 // private
29717 Roo.Resizable.Handle.prototype = {
29718     afterResize : function(rz){
29719         Roo.log('after?');
29720         // do nothing
29721     },
29722     // private
29723     onMouseDown : function(e){
29724         this.rz.onMouseDown(this, e);
29725     },
29726     // private
29727     onMouseOver : function(e){
29728         this.rz.handleOver(this, e);
29729     },
29730     // private
29731     onMouseOut : function(e){
29732         this.rz.handleOut(this, e);
29733     }
29734 };/*
29735  * Based on:
29736  * Ext JS Library 1.1.1
29737  * Copyright(c) 2006-2007, Ext JS, LLC.
29738  *
29739  * Originally Released Under LGPL - original licence link has changed is not relivant.
29740  *
29741  * Fork - LGPL
29742  * <script type="text/javascript">
29743  */
29744
29745 /**
29746  * @class Roo.Editor
29747  * @extends Roo.Component
29748  * A base editor field that handles displaying/hiding on demand and has some built-in sizing and event handling logic.
29749  * @constructor
29750  * Create a new Editor
29751  * @param {Roo.form.Field} field The Field object (or descendant)
29752  * @param {Object} config The config object
29753  */
29754 Roo.Editor = function(field, config){
29755     Roo.Editor.superclass.constructor.call(this, config);
29756     this.field = field;
29757     this.addEvents({
29758         /**
29759              * @event beforestartedit
29760              * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
29761              * false from the handler of this event.
29762              * @param {Editor} this
29763              * @param {Roo.Element} boundEl The underlying element bound to this editor
29764              * @param {Mixed} value The field value being set
29765              */
29766         "beforestartedit" : true,
29767         /**
29768              * @event startedit
29769              * Fires when this editor is displayed
29770              * @param {Roo.Element} boundEl The underlying element bound to this editor
29771              * @param {Mixed} value The starting field value
29772              */
29773         "startedit" : true,
29774         /**
29775              * @event beforecomplete
29776              * Fires after a change has been made to the field, but before the change is reflected in the underlying
29777              * field.  Saving the change to the field can be canceled by returning false from the handler of this event.
29778              * Note that if the value has not changed and ignoreNoChange = true, the editing will still end but this
29779              * event will not fire since no edit actually occurred.
29780              * @param {Editor} this
29781              * @param {Mixed} value The current field value
29782              * @param {Mixed} startValue The original field value
29783              */
29784         "beforecomplete" : true,
29785         /**
29786              * @event complete
29787              * Fires after editing is complete and any changed value has been written to the underlying field.
29788              * @param {Editor} this
29789              * @param {Mixed} value The current field value
29790              * @param {Mixed} startValue The original field value
29791              */
29792         "complete" : true,
29793         /**
29794          * @event specialkey
29795          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
29796          * {@link Roo.EventObject#getKey} to determine which key was pressed.
29797          * @param {Roo.form.Field} this
29798          * @param {Roo.EventObject} e The event object
29799          */
29800         "specialkey" : true
29801     });
29802 };
29803
29804 Roo.extend(Roo.Editor, Roo.Component, {
29805     /**
29806      * @cfg {Boolean/String} autosize
29807      * True for the editor to automatically adopt the size of the underlying field, "width" to adopt the width only,
29808      * or "height" to adopt the height only (defaults to false)
29809      */
29810     /**
29811      * @cfg {Boolean} revertInvalid
29812      * True to automatically revert the field value and cancel the edit when the user completes an edit and the field
29813      * validation fails (defaults to true)
29814      */
29815     /**
29816      * @cfg {Boolean} ignoreNoChange
29817      * True to skip the the edit completion process (no save, no events fired) if the user completes an edit and
29818      * the value has not changed (defaults to false).  Applies only to string values - edits for other data types
29819      * will never be ignored.
29820      */
29821     /**
29822      * @cfg {Boolean} hideEl
29823      * False to keep the bound element visible while the editor is displayed (defaults to true)
29824      */
29825     /**
29826      * @cfg {Mixed} value
29827      * The data value of the underlying field (defaults to "")
29828      */
29829     value : "",
29830     /**
29831      * @cfg {String} alignment
29832      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "c-c?").
29833      */
29834     alignment: "c-c?",
29835     /**
29836      * @cfg {Boolean/String} shadow "sides" for sides/bottom only, "frame" for 4-way shadow, and "drop"
29837      * for bottom-right shadow (defaults to "frame")
29838      */
29839     shadow : "frame",
29840     /**
29841      * @cfg {Boolean} constrain True to constrain the editor to the viewport
29842      */
29843     constrain : false,
29844     /**
29845      * @cfg {Boolean} completeOnEnter True to complete the edit when the enter key is pressed (defaults to false)
29846      */
29847     completeOnEnter : false,
29848     /**
29849      * @cfg {Boolean} cancelOnEsc True to cancel the edit when the escape key is pressed (defaults to false)
29850      */
29851     cancelOnEsc : false,
29852     /**
29853      * @cfg {Boolean} updateEl True to update the innerHTML of the bound element when the update completes (defaults to false)
29854      */
29855     updateEl : false,
29856
29857     // private
29858     onRender : function(ct, position){
29859         this.el = new Roo.Layer({
29860             shadow: this.shadow,
29861             cls: "x-editor",
29862             parentEl : ct,
29863             shim : this.shim,
29864             shadowOffset:4,
29865             id: this.id,
29866             constrain: this.constrain
29867         });
29868         this.el.setStyle("overflow", Roo.isGecko ? "auto" : "hidden");
29869         if(this.field.msgTarget != 'title'){
29870             this.field.msgTarget = 'qtip';
29871         }
29872         this.field.render(this.el);
29873         if(Roo.isGecko){
29874             this.field.el.dom.setAttribute('autocomplete', 'off');
29875         }
29876         this.field.on("specialkey", this.onSpecialKey, this);
29877         if(this.swallowKeys){
29878             this.field.el.swallowEvent(['keydown','keypress']);
29879         }
29880         this.field.show();
29881         this.field.on("blur", this.onBlur, this);
29882         if(this.field.grow){
29883             this.field.on("autosize", this.el.sync,  this.el, {delay:1});
29884         }
29885     },
29886
29887     onSpecialKey : function(field, e)
29888     {
29889         //Roo.log('editor onSpecialKey');
29890         if(this.completeOnEnter && e.getKey() == e.ENTER){
29891             e.stopEvent();
29892             this.completeEdit();
29893             return;
29894         }
29895         // do not fire special key otherwise it might hide close the editor...
29896         if(e.getKey() == e.ENTER){    
29897             return;
29898         }
29899         if(this.cancelOnEsc && e.getKey() == e.ESC){
29900             this.cancelEdit();
29901             return;
29902         } 
29903         this.fireEvent('specialkey', field, e);
29904     
29905     },
29906
29907     /**
29908      * Starts the editing process and shows the editor.
29909      * @param {String/HTMLElement/Element} el The element to edit
29910      * @param {String} value (optional) A value to initialize the editor with. If a value is not provided, it defaults
29911       * to the innerHTML of el.
29912      */
29913     startEdit : function(el, value){
29914         if(this.editing){
29915             this.completeEdit();
29916         }
29917         this.boundEl = Roo.get(el);
29918         var v = value !== undefined ? value : this.boundEl.dom.innerHTML;
29919         if(!this.rendered){
29920             this.render(this.parentEl || document.body);
29921         }
29922         if(this.fireEvent("beforestartedit", this, this.boundEl, v) === false){
29923             return;
29924         }
29925         this.startValue = v;
29926         this.field.setValue(v);
29927         if(this.autoSize){
29928             var sz = this.boundEl.getSize();
29929             switch(this.autoSize){
29930                 case "width":
29931                 this.setSize(sz.width,  "");
29932                 break;
29933                 case "height":
29934                 this.setSize("",  sz.height);
29935                 break;
29936                 default:
29937                 this.setSize(sz.width,  sz.height);
29938             }
29939         }
29940         this.el.alignTo(this.boundEl, this.alignment);
29941         this.editing = true;
29942         if(Roo.QuickTips){
29943             Roo.QuickTips.disable();
29944         }
29945         this.show();
29946     },
29947
29948     /**
29949      * Sets the height and width of this editor.
29950      * @param {Number} width The new width
29951      * @param {Number} height The new height
29952      */
29953     setSize : function(w, h){
29954         this.field.setSize(w, h);
29955         if(this.el){
29956             this.el.sync();
29957         }
29958     },
29959
29960     /**
29961      * Realigns the editor to the bound field based on the current alignment config value.
29962      */
29963     realign : function(){
29964         this.el.alignTo(this.boundEl, this.alignment);
29965     },
29966
29967     /**
29968      * Ends the editing process, persists the changed value to the underlying field, and hides the editor.
29969      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after edit (defaults to false)
29970      */
29971     completeEdit : function(remainVisible){
29972         if(!this.editing){
29973             return;
29974         }
29975         var v = this.getValue();
29976         if(this.revertInvalid !== false && !this.field.isValid()){
29977             v = this.startValue;
29978             this.cancelEdit(true);
29979         }
29980         if(String(v) === String(this.startValue) && this.ignoreNoChange){
29981             this.editing = false;
29982             this.hide();
29983             return;
29984         }
29985         if(this.fireEvent("beforecomplete", this, v, this.startValue) !== false){
29986             this.editing = false;
29987             if(this.updateEl && this.boundEl){
29988                 this.boundEl.update(v);
29989             }
29990             if(remainVisible !== true){
29991                 this.hide();
29992             }
29993             this.fireEvent("complete", this, v, this.startValue);
29994         }
29995     },
29996
29997     // private
29998     onShow : function(){
29999         this.el.show();
30000         if(this.hideEl !== false){
30001             this.boundEl.hide();
30002         }
30003         this.field.show();
30004         if(Roo.isIE && !this.fixIEFocus){ // IE has problems with focusing the first time
30005             this.fixIEFocus = true;
30006             this.deferredFocus.defer(50, this);
30007         }else{
30008             this.field.focus();
30009         }
30010         this.fireEvent("startedit", this.boundEl, this.startValue);
30011     },
30012
30013     deferredFocus : function(){
30014         if(this.editing){
30015             this.field.focus();
30016         }
30017     },
30018
30019     /**
30020      * Cancels the editing process and hides the editor without persisting any changes.  The field value will be
30021      * reverted to the original starting value.
30022      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after
30023      * cancel (defaults to false)
30024      */
30025     cancelEdit : function(remainVisible){
30026         if(this.editing){
30027             this.setValue(this.startValue);
30028             if(remainVisible !== true){
30029                 this.hide();
30030             }
30031         }
30032     },
30033
30034     // private
30035     onBlur : function(){
30036         if(this.allowBlur !== true && this.editing){
30037             this.completeEdit();
30038         }
30039     },
30040
30041     // private
30042     onHide : function(){
30043         if(this.editing){
30044             this.completeEdit();
30045             return;
30046         }
30047         this.field.blur();
30048         if(this.field.collapse){
30049             this.field.collapse();
30050         }
30051         this.el.hide();
30052         if(this.hideEl !== false){
30053             this.boundEl.show();
30054         }
30055         if(Roo.QuickTips){
30056             Roo.QuickTips.enable();
30057         }
30058     },
30059
30060     /**
30061      * Sets the data value of the editor
30062      * @param {Mixed} value Any valid value supported by the underlying field
30063      */
30064     setValue : function(v){
30065         this.field.setValue(v);
30066     },
30067
30068     /**
30069      * Gets the data value of the editor
30070      * @return {Mixed} The data value
30071      */
30072     getValue : function(){
30073         return this.field.getValue();
30074     }
30075 });/*
30076  * Based on:
30077  * Ext JS Library 1.1.1
30078  * Copyright(c) 2006-2007, Ext JS, LLC.
30079  *
30080  * Originally Released Under LGPL - original licence link has changed is not relivant.
30081  *
30082  * Fork - LGPL
30083  * <script type="text/javascript">
30084  */
30085  
30086 /**
30087  * @class Roo.BasicDialog
30088  * @extends Roo.util.Observable
30089  * Lightweight Dialog Class.  The code below shows the creation of a typical dialog using existing HTML markup:
30090  * <pre><code>
30091 var dlg = new Roo.BasicDialog("my-dlg", {
30092     height: 200,
30093     width: 300,
30094     minHeight: 100,
30095     minWidth: 150,
30096     modal: true,
30097     proxyDrag: true,
30098     shadow: true
30099 });
30100 dlg.addKeyListener(27, dlg.hide, dlg); // ESC can also close the dialog
30101 dlg.addButton('OK', dlg.hide, dlg);    // Could call a save function instead of hiding
30102 dlg.addButton('Cancel', dlg.hide, dlg);
30103 dlg.show();
30104 </code></pre>
30105   <b>A Dialog should always be a direct child of the body element.</b>
30106  * @cfg {Boolean/DomHelper} autoCreate True to auto create from scratch, or using a DomHelper Object (defaults to false)
30107  * @cfg {String} title Default text to display in the title bar (defaults to null)
30108  * @cfg {Number} width Width of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
30109  * @cfg {Number} height Height of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
30110  * @cfg {Number} x The default left page coordinate of the dialog (defaults to center screen)
30111  * @cfg {Number} y The default top page coordinate of the dialog (defaults to center screen)
30112  * @cfg {String/Element} animateTarget Id or element from which the dialog should animate while opening
30113  * (defaults to null with no animation)
30114  * @cfg {Boolean} resizable False to disable manual dialog resizing (defaults to true)
30115  * @cfg {String} resizeHandles Which resize handles to display - see the {@link Roo.Resizable} handles config
30116  * property for valid values (defaults to 'all')
30117  * @cfg {Number} minHeight The minimum allowable height for a resizable dialog (defaults to 80)
30118  * @cfg {Number} minWidth The minimum allowable width for a resizable dialog (defaults to 200)
30119  * @cfg {Boolean} modal True to show the dialog modally, preventing user interaction with the rest of the page (defaults to false)
30120  * @cfg {Boolean} autoScroll True to allow the dialog body contents to overflow and display scrollbars (defaults to false)
30121  * @cfg {Boolean} closable False to remove the built-in top-right corner close button (defaults to true)
30122  * @cfg {Boolean} collapsible False to remove the built-in top-right corner collapse button (defaults to true)
30123  * @cfg {Boolean} constraintoviewport True to keep the dialog constrained within the visible viewport boundaries (defaults to true)
30124  * @cfg {Boolean} syncHeightBeforeShow True to cause the dimensions to be recalculated before the dialog is shown (defaults to false)
30125  * @cfg {Boolean} draggable False to disable dragging of the dialog within the viewport (defaults to true)
30126  * @cfg {Boolean} autoTabs If true, all elements with class 'x-dlg-tab' will get automatically converted to tabs (defaults to false)
30127  * @cfg {String} tabTag The tag name of tab elements, used when autoTabs = true (defaults to 'div')
30128  * @cfg {Boolean} proxyDrag True to drag a lightweight proxy element rather than the dialog itself, used when
30129  * draggable = true (defaults to false)
30130  * @cfg {Boolean} fixedcenter True to ensure that anytime the dialog is shown or resized it gets centered (defaults to false)
30131  * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
30132  * shadow (defaults to false)
30133  * @cfg {Number} shadowOffset The number of pixels to offset the shadow if displayed (defaults to 5)
30134  * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "right")
30135  * @cfg {Number} minButtonWidth Minimum width of all dialog buttons (defaults to 75)
30136  * @cfg {Array} buttons Array of buttons
30137  * @cfg {Boolean} shim True to create an iframe shim that prevents selects from showing through (defaults to false)
30138  * @constructor
30139  * Create a new BasicDialog.
30140  * @param {String/HTMLElement/Roo.Element} el The container element or DOM node, or its id
30141  * @param {Object} config Configuration options
30142  */
30143 Roo.BasicDialog = function(el, config){
30144     this.el = Roo.get(el);
30145     var dh = Roo.DomHelper;
30146     if(!this.el && config && config.autoCreate){
30147         if(typeof config.autoCreate == "object"){
30148             if(!config.autoCreate.id){
30149                 config.autoCreate.id = el;
30150             }
30151             this.el = dh.append(document.body,
30152                         config.autoCreate, true);
30153         }else{
30154             this.el = dh.append(document.body,
30155                         {tag: "div", id: el, style:'visibility:hidden;'}, true);
30156         }
30157     }
30158     el = this.el;
30159     el.setDisplayed(true);
30160     el.hide = this.hideAction;
30161     this.id = el.id;
30162     el.addClass("x-dlg");
30163
30164     Roo.apply(this, config);
30165
30166     this.proxy = el.createProxy("x-dlg-proxy");
30167     this.proxy.hide = this.hideAction;
30168     this.proxy.setOpacity(.5);
30169     this.proxy.hide();
30170
30171     if(config.width){
30172         el.setWidth(config.width);
30173     }
30174     if(config.height){
30175         el.setHeight(config.height);
30176     }
30177     this.size = el.getSize();
30178     if(typeof config.x != "undefined" && typeof config.y != "undefined"){
30179         this.xy = [config.x,config.y];
30180     }else{
30181         this.xy = el.getCenterXY(true);
30182     }
30183     /** The header element @type Roo.Element */
30184     this.header = el.child("> .x-dlg-hd");
30185     /** The body element @type Roo.Element */
30186     this.body = el.child("> .x-dlg-bd");
30187     /** The footer element @type Roo.Element */
30188     this.footer = el.child("> .x-dlg-ft");
30189
30190     if(!this.header){
30191         this.header = el.createChild({tag: "div", cls:"x-dlg-hd", html: "&#160;"}, this.body ? this.body.dom : null);
30192     }
30193     if(!this.body){
30194         this.body = el.createChild({tag: "div", cls:"x-dlg-bd"});
30195     }
30196
30197     this.header.unselectable();
30198     if(this.title){
30199         this.header.update(this.title);
30200     }
30201     // this element allows the dialog to be focused for keyboard event
30202     this.focusEl = el.createChild({tag: "a", href:"#", cls:"x-dlg-focus", tabIndex:"-1"});
30203     this.focusEl.swallowEvent("click", true);
30204
30205     this.header.wrap({cls:"x-dlg-hd-right"}).wrap({cls:"x-dlg-hd-left"}, true);
30206
30207     // wrap the body and footer for special rendering
30208     this.bwrap = this.body.wrap({tag: "div", cls:"x-dlg-dlg-body"});
30209     if(this.footer){
30210         this.bwrap.dom.appendChild(this.footer.dom);
30211     }
30212
30213     this.bg = this.el.createChild({
30214         tag: "div", cls:"x-dlg-bg",
30215         html: '<div class="x-dlg-bg-left"><div class="x-dlg-bg-right"><div class="x-dlg-bg-center">&#160;</div></div></div>'
30216     });
30217     this.centerBg = this.bg.child("div.x-dlg-bg-center");
30218
30219
30220     if(this.autoScroll !== false && !this.autoTabs){
30221         this.body.setStyle("overflow", "auto");
30222     }
30223
30224     this.toolbox = this.el.createChild({cls: "x-dlg-toolbox"});
30225
30226     if(this.closable !== false){
30227         this.el.addClass("x-dlg-closable");
30228         this.close = this.toolbox.createChild({cls:"x-dlg-close"});
30229         this.close.on("click", this.closeClick, this);
30230         this.close.addClassOnOver("x-dlg-close-over");
30231     }
30232     if(this.collapsible !== false){
30233         this.collapseBtn = this.toolbox.createChild({cls:"x-dlg-collapse"});
30234         this.collapseBtn.on("click", this.collapseClick, this);
30235         this.collapseBtn.addClassOnOver("x-dlg-collapse-over");
30236         this.header.on("dblclick", this.collapseClick, this);
30237     }
30238     if(this.resizable !== false){
30239         this.el.addClass("x-dlg-resizable");
30240         this.resizer = new Roo.Resizable(el, {
30241             minWidth: this.minWidth || 80,
30242             minHeight:this.minHeight || 80,
30243             handles: this.resizeHandles || "all",
30244             pinned: true
30245         });
30246         this.resizer.on("beforeresize", this.beforeResize, this);
30247         this.resizer.on("resize", this.onResize, this);
30248     }
30249     if(this.draggable !== false){
30250         el.addClass("x-dlg-draggable");
30251         if (!this.proxyDrag) {
30252             var dd = new Roo.dd.DD(el.dom.id, "WindowDrag");
30253         }
30254         else {
30255             var dd = new Roo.dd.DDProxy(el.dom.id, "WindowDrag", {dragElId: this.proxy.id});
30256         }
30257         dd.setHandleElId(this.header.id);
30258         dd.endDrag = this.endMove.createDelegate(this);
30259         dd.startDrag = this.startMove.createDelegate(this);
30260         dd.onDrag = this.onDrag.createDelegate(this);
30261         dd.scroll = false;
30262         this.dd = dd;
30263     }
30264     if(this.modal){
30265         this.mask = dh.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
30266         this.mask.enableDisplayMode("block");
30267         this.mask.hide();
30268         this.el.addClass("x-dlg-modal");
30269     }
30270     if(this.shadow){
30271         this.shadow = new Roo.Shadow({
30272             mode : typeof this.shadow == "string" ? this.shadow : "sides",
30273             offset : this.shadowOffset
30274         });
30275     }else{
30276         this.shadowOffset = 0;
30277     }
30278     if(Roo.useShims && this.shim !== false){
30279         this.shim = this.el.createShim();
30280         this.shim.hide = this.hideAction;
30281         this.shim.hide();
30282     }else{
30283         this.shim = false;
30284     }
30285     if(this.autoTabs){
30286         this.initTabs();
30287     }
30288     if (this.buttons) { 
30289         var bts= this.buttons;
30290         this.buttons = [];
30291         Roo.each(bts, function(b) {
30292             this.addButton(b);
30293         }, this);
30294     }
30295     
30296     
30297     this.addEvents({
30298         /**
30299          * @event keydown
30300          * Fires when a key is pressed
30301          * @param {Roo.BasicDialog} this
30302          * @param {Roo.EventObject} e
30303          */
30304         "keydown" : true,
30305         /**
30306          * @event move
30307          * Fires when this dialog is moved by the user.
30308          * @param {Roo.BasicDialog} this
30309          * @param {Number} x The new page X
30310          * @param {Number} y The new page Y
30311          */
30312         "move" : true,
30313         /**
30314          * @event resize
30315          * Fires when this dialog is resized by the user.
30316          * @param {Roo.BasicDialog} this
30317          * @param {Number} width The new width
30318          * @param {Number} height The new height
30319          */
30320         "resize" : true,
30321         /**
30322          * @event beforehide
30323          * Fires before this dialog is hidden.
30324          * @param {Roo.BasicDialog} this
30325          */
30326         "beforehide" : true,
30327         /**
30328          * @event hide
30329          * Fires when this dialog is hidden.
30330          * @param {Roo.BasicDialog} this
30331          */
30332         "hide" : true,
30333         /**
30334          * @event beforeshow
30335          * Fires before this dialog is shown.
30336          * @param {Roo.BasicDialog} this
30337          */
30338         "beforeshow" : true,
30339         /**
30340          * @event show
30341          * Fires when this dialog is shown.
30342          * @param {Roo.BasicDialog} this
30343          */
30344         "show" : true
30345     });
30346     el.on("keydown", this.onKeyDown, this);
30347     el.on("mousedown", this.toFront, this);
30348     Roo.EventManager.onWindowResize(this.adjustViewport, this, true);
30349     this.el.hide();
30350     Roo.DialogManager.register(this);
30351     Roo.BasicDialog.superclass.constructor.call(this);
30352 };
30353
30354 Roo.extend(Roo.BasicDialog, Roo.util.Observable, {
30355     shadowOffset: Roo.isIE ? 6 : 5,
30356     minHeight: 80,
30357     minWidth: 200,
30358     minButtonWidth: 75,
30359     defaultButton: null,
30360     buttonAlign: "right",
30361     tabTag: 'div',
30362     firstShow: true,
30363
30364     /**
30365      * Sets the dialog title text
30366      * @param {String} text The title text to display
30367      * @return {Roo.BasicDialog} this
30368      */
30369     setTitle : function(text){
30370         this.header.update(text);
30371         return this;
30372     },
30373
30374     // private
30375     closeClick : function(){
30376         this.hide();
30377     },
30378
30379     // private
30380     collapseClick : function(){
30381         this[this.collapsed ? "expand" : "collapse"]();
30382     },
30383
30384     /**
30385      * Collapses the dialog to its minimized state (only the title bar is visible).
30386      * Equivalent to the user clicking the collapse dialog button.
30387      */
30388     collapse : function(){
30389         if(!this.collapsed){
30390             this.collapsed = true;
30391             this.el.addClass("x-dlg-collapsed");
30392             this.restoreHeight = this.el.getHeight();
30393             this.resizeTo(this.el.getWidth(), this.header.getHeight());
30394         }
30395     },
30396
30397     /**
30398      * Expands a collapsed dialog back to its normal state.  Equivalent to the user
30399      * clicking the expand dialog button.
30400      */
30401     expand : function(){
30402         if(this.collapsed){
30403             this.collapsed = false;
30404             this.el.removeClass("x-dlg-collapsed");
30405             this.resizeTo(this.el.getWidth(), this.restoreHeight);
30406         }
30407     },
30408
30409     /**
30410      * Reinitializes the tabs component, clearing out old tabs and finding new ones.
30411      * @return {Roo.TabPanel} The tabs component
30412      */
30413     initTabs : function(){
30414         var tabs = this.getTabs();
30415         while(tabs.getTab(0)){
30416             tabs.removeTab(0);
30417         }
30418         this.el.select(this.tabTag+'.x-dlg-tab').each(function(el){
30419             var dom = el.dom;
30420             tabs.addTab(Roo.id(dom), dom.title);
30421             dom.title = "";
30422         });
30423         tabs.activate(0);
30424         return tabs;
30425     },
30426
30427     // private
30428     beforeResize : function(){
30429         this.resizer.minHeight = Math.max(this.minHeight, this.getHeaderFooterHeight(true)+40);
30430     },
30431
30432     // private
30433     onResize : function(){
30434         this.refreshSize();
30435         this.syncBodyHeight();
30436         this.adjustAssets();
30437         this.focus();
30438         this.fireEvent("resize", this, this.size.width, this.size.height);
30439     },
30440
30441     // private
30442     onKeyDown : function(e){
30443         if(this.isVisible()){
30444             this.fireEvent("keydown", this, e);
30445         }
30446     },
30447
30448     /**
30449      * Resizes the dialog.
30450      * @param {Number} width
30451      * @param {Number} height
30452      * @return {Roo.BasicDialog} this
30453      */
30454     resizeTo : function(width, height){
30455         this.el.setSize(width, height);
30456         this.size = {width: width, height: height};
30457         this.syncBodyHeight();
30458         if(this.fixedcenter){
30459             this.center();
30460         }
30461         if(this.isVisible()){
30462             this.constrainXY();
30463             this.adjustAssets();
30464         }
30465         this.fireEvent("resize", this, width, height);
30466         return this;
30467     },
30468
30469
30470     /**
30471      * Resizes the dialog to fit the specified content size.
30472      * @param {Number} width
30473      * @param {Number} height
30474      * @return {Roo.BasicDialog} this
30475      */
30476     setContentSize : function(w, h){
30477         h += this.getHeaderFooterHeight() + this.body.getMargins("tb");
30478         w += this.body.getMargins("lr") + this.bwrap.getMargins("lr") + this.centerBg.getPadding("lr");
30479         //if(!this.el.isBorderBox()){
30480             h +=  this.body.getPadding("tb") + this.bwrap.getBorderWidth("tb") + this.body.getBorderWidth("tb") + this.el.getBorderWidth("tb");
30481             w += this.body.getPadding("lr") + this.bwrap.getBorderWidth("lr") + this.body.getBorderWidth("lr") + this.bwrap.getPadding("lr") + this.el.getBorderWidth("lr");
30482         //}
30483         if(this.tabs){
30484             h += this.tabs.stripWrap.getHeight() + this.tabs.bodyEl.getMargins("tb") + this.tabs.bodyEl.getPadding("tb");
30485             w += this.tabs.bodyEl.getMargins("lr") + this.tabs.bodyEl.getPadding("lr");
30486         }
30487         this.resizeTo(w, h);
30488         return this;
30489     },
30490
30491     /**
30492      * Adds a key listener for when this dialog is displayed.  This allows you to hook in a function that will be
30493      * executed in response to a particular key being pressed while the dialog is active.
30494      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the following options:
30495      *                                  {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
30496      * @param {Function} fn The function to call
30497      * @param {Object} scope (optional) The scope of the function
30498      * @return {Roo.BasicDialog} this
30499      */
30500     addKeyListener : function(key, fn, scope){
30501         var keyCode, shift, ctrl, alt;
30502         if(typeof key == "object" && !(key instanceof Array)){
30503             keyCode = key["key"];
30504             shift = key["shift"];
30505             ctrl = key["ctrl"];
30506             alt = key["alt"];
30507         }else{
30508             keyCode = key;
30509         }
30510         var handler = function(dlg, e){
30511             if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) &&  (!alt || e.altKey)){
30512                 var k = e.getKey();
30513                 if(keyCode instanceof Array){
30514                     for(var i = 0, len = keyCode.length; i < len; i++){
30515                         if(keyCode[i] == k){
30516                           fn.call(scope || window, dlg, k, e);
30517                           return;
30518                         }
30519                     }
30520                 }else{
30521                     if(k == keyCode){
30522                         fn.call(scope || window, dlg, k, e);
30523                     }
30524                 }
30525             }
30526         };
30527         this.on("keydown", handler);
30528         return this;
30529     },
30530
30531     /**
30532      * Returns the TabPanel component (creates it if it doesn't exist).
30533      * Note: If you wish to simply check for the existence of tabs without creating them,
30534      * check for a null 'tabs' property.
30535      * @return {Roo.TabPanel} The tabs component
30536      */
30537     getTabs : function(){
30538         if(!this.tabs){
30539             this.el.addClass("x-dlg-auto-tabs");
30540             this.body.addClass(this.tabPosition == "bottom" ? "x-tabs-bottom" : "x-tabs-top");
30541             this.tabs = new Roo.TabPanel(this.body.dom, this.tabPosition == "bottom");
30542         }
30543         return this.tabs;
30544     },
30545
30546     /**
30547      * Adds a button to the footer section of the dialog.
30548      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
30549      * object or a valid Roo.DomHelper element config
30550      * @param {Function} handler The function called when the button is clicked
30551      * @param {Object} scope (optional) The scope of the handler function (accepts position as a property)
30552      * @return {Roo.Button} The new button
30553      */
30554     addButton : function(config, handler, scope){
30555         var dh = Roo.DomHelper;
30556         if(!this.footer){
30557             this.footer = dh.append(this.bwrap, {tag: "div", cls:"x-dlg-ft"}, true);
30558         }
30559         if(!this.btnContainer){
30560             var tb = this.footer.createChild({
30561
30562                 cls:"x-dlg-btns x-dlg-btns-"+this.buttonAlign,
30563                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
30564             }, null, true);
30565             this.btnContainer = tb.firstChild.firstChild.firstChild;
30566         }
30567         var bconfig = {
30568             handler: handler,
30569             scope: scope,
30570             minWidth: this.minButtonWidth,
30571             hideParent:true
30572         };
30573         if(typeof config == "string"){
30574             bconfig.text = config;
30575         }else{
30576             if(config.tag){
30577                 bconfig.dhconfig = config;
30578             }else{
30579                 Roo.apply(bconfig, config);
30580             }
30581         }
30582         var fc = false;
30583         if ((typeof(bconfig.position) != 'undefined') && bconfig.position < this.btnContainer.childNodes.length-1) {
30584             bconfig.position = Math.max(0, bconfig.position);
30585             fc = this.btnContainer.childNodes[bconfig.position];
30586         }
30587          
30588         var btn = new Roo.Button(
30589             fc ? 
30590                 this.btnContainer.insertBefore(document.createElement("td"),fc)
30591                 : this.btnContainer.appendChild(document.createElement("td")),
30592             //Roo.get(this.btnContainer).createChild( { tag: 'td'},  fc ),
30593             bconfig
30594         );
30595         this.syncBodyHeight();
30596         if(!this.buttons){
30597             /**
30598              * Array of all the buttons that have been added to this dialog via addButton
30599              * @type Array
30600              */
30601             this.buttons = [];
30602         }
30603         this.buttons.push(btn);
30604         return btn;
30605     },
30606
30607     /**
30608      * Sets the default button to be focused when the dialog is displayed.
30609      * @param {Roo.BasicDialog.Button} btn The button object returned by {@link #addButton}
30610      * @return {Roo.BasicDialog} this
30611      */
30612     setDefaultButton : function(btn){
30613         this.defaultButton = btn;
30614         return this;
30615     },
30616
30617     // private
30618     getHeaderFooterHeight : function(safe){
30619         var height = 0;
30620         if(this.header){
30621            height += this.header.getHeight();
30622         }
30623         if(this.footer){
30624            var fm = this.footer.getMargins();
30625             height += (this.footer.getHeight()+fm.top+fm.bottom);
30626         }
30627         height += this.bwrap.getPadding("tb")+this.bwrap.getBorderWidth("tb");
30628         height += this.centerBg.getPadding("tb");
30629         return height;
30630     },
30631
30632     // private
30633     syncBodyHeight : function()
30634     {
30635         var bd = this.body, // the text
30636             cb = this.centerBg, // wrapper around bottom.. but does not seem to be used..
30637             bw = this.bwrap;
30638         var height = this.size.height - this.getHeaderFooterHeight(false);
30639         bd.setHeight(height-bd.getMargins("tb"));
30640         var hh = this.header.getHeight();
30641         var h = this.size.height-hh;
30642         cb.setHeight(h);
30643         
30644         bw.setLeftTop(cb.getPadding("l"), hh+cb.getPadding("t"));
30645         bw.setHeight(h-cb.getPadding("tb"));
30646         
30647         bw.setWidth(this.el.getWidth(true)-cb.getPadding("lr"));
30648         bd.setWidth(bw.getWidth(true));
30649         if(this.tabs){
30650             this.tabs.syncHeight();
30651             if(Roo.isIE){
30652                 this.tabs.el.repaint();
30653             }
30654         }
30655     },
30656
30657     /**
30658      * Restores the previous state of the dialog if Roo.state is configured.
30659      * @return {Roo.BasicDialog} this
30660      */
30661     restoreState : function(){
30662         var box = Roo.state.Manager.get(this.stateId || (this.el.id + "-state"));
30663         if(box && box.width){
30664             this.xy = [box.x, box.y];
30665             this.resizeTo(box.width, box.height);
30666         }
30667         return this;
30668     },
30669
30670     // private
30671     beforeShow : function(){
30672         this.expand();
30673         if(this.fixedcenter){
30674             this.xy = this.el.getCenterXY(true);
30675         }
30676         if(this.modal){
30677             Roo.get(document.body).addClass("x-body-masked");
30678             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
30679             this.mask.show();
30680         }
30681         this.constrainXY();
30682     },
30683
30684     // private
30685     animShow : function(){
30686         var b = Roo.get(this.animateTarget).getBox();
30687         this.proxy.setSize(b.width, b.height);
30688         this.proxy.setLocation(b.x, b.y);
30689         this.proxy.show();
30690         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height,
30691                     true, .35, this.showEl.createDelegate(this));
30692     },
30693
30694     /**
30695      * Shows the dialog.
30696      * @param {String/HTMLElement/Roo.Element} animateTarget (optional) Reset the animation target
30697      * @return {Roo.BasicDialog} this
30698      */
30699     show : function(animateTarget){
30700         if (this.fireEvent("beforeshow", this) === false){
30701             return;
30702         }
30703         if(this.syncHeightBeforeShow){
30704             this.syncBodyHeight();
30705         }else if(this.firstShow){
30706             this.firstShow = false;
30707             this.syncBodyHeight(); // sync the height on the first show instead of in the constructor
30708         }
30709         this.animateTarget = animateTarget || this.animateTarget;
30710         if(!this.el.isVisible()){
30711             this.beforeShow();
30712             if(this.animateTarget && Roo.get(this.animateTarget)){
30713                 this.animShow();
30714             }else{
30715                 this.showEl();
30716             }
30717         }
30718         return this;
30719     },
30720
30721     // private
30722     showEl : function(){
30723         this.proxy.hide();
30724         this.el.setXY(this.xy);
30725         this.el.show();
30726         this.adjustAssets(true);
30727         this.toFront();
30728         this.focus();
30729         // IE peekaboo bug - fix found by Dave Fenwick
30730         if(Roo.isIE){
30731             this.el.repaint();
30732         }
30733         this.fireEvent("show", this);
30734     },
30735
30736     /**
30737      * Focuses the dialog.  If a defaultButton is set, it will receive focus, otherwise the
30738      * dialog itself will receive focus.
30739      */
30740     focus : function(){
30741         if(this.defaultButton){
30742             this.defaultButton.focus();
30743         }else{
30744             this.focusEl.focus();
30745         }
30746     },
30747
30748     // private
30749     constrainXY : function(){
30750         if(this.constraintoviewport !== false){
30751             if(!this.viewSize){
30752                 if(this.container){
30753                     var s = this.container.getSize();
30754                     this.viewSize = [s.width, s.height];
30755                 }else{
30756                     this.viewSize = [Roo.lib.Dom.getViewWidth(),Roo.lib.Dom.getViewHeight()];
30757                 }
30758             }
30759             var s = Roo.get(this.container||document).getScroll();
30760
30761             var x = this.xy[0], y = this.xy[1];
30762             var w = this.size.width, h = this.size.height;
30763             var vw = this.viewSize[0], vh = this.viewSize[1];
30764             // only move it if it needs it
30765             var moved = false;
30766             // first validate right/bottom
30767             if(x + w > vw+s.left){
30768                 x = vw - w;
30769                 moved = true;
30770             }
30771             if(y + h > vh+s.top){
30772                 y = vh - h;
30773                 moved = true;
30774             }
30775             // then make sure top/left isn't negative
30776             if(x < s.left){
30777                 x = s.left;
30778                 moved = true;
30779             }
30780             if(y < s.top){
30781                 y = s.top;
30782                 moved = true;
30783             }
30784             if(moved){
30785                 // cache xy
30786                 this.xy = [x, y];
30787                 if(this.isVisible()){
30788                     this.el.setLocation(x, y);
30789                     this.adjustAssets();
30790                 }
30791             }
30792         }
30793     },
30794
30795     // private
30796     onDrag : function(){
30797         if(!this.proxyDrag){
30798             this.xy = this.el.getXY();
30799             this.adjustAssets();
30800         }
30801     },
30802
30803     // private
30804     adjustAssets : function(doShow){
30805         var x = this.xy[0], y = this.xy[1];
30806         var w = this.size.width, h = this.size.height;
30807         if(doShow === true){
30808             if(this.shadow){
30809                 this.shadow.show(this.el);
30810             }
30811             if(this.shim){
30812                 this.shim.show();
30813             }
30814         }
30815         if(this.shadow && this.shadow.isVisible()){
30816             this.shadow.show(this.el);
30817         }
30818         if(this.shim && this.shim.isVisible()){
30819             this.shim.setBounds(x, y, w, h);
30820         }
30821     },
30822
30823     // private
30824     adjustViewport : function(w, h){
30825         if(!w || !h){
30826             w = Roo.lib.Dom.getViewWidth();
30827             h = Roo.lib.Dom.getViewHeight();
30828         }
30829         // cache the size
30830         this.viewSize = [w, h];
30831         if(this.modal && this.mask.isVisible()){
30832             this.mask.setSize(w, h); // first make sure the mask isn't causing overflow
30833             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
30834         }
30835         if(this.isVisible()){
30836             this.constrainXY();
30837         }
30838     },
30839
30840     /**
30841      * Destroys this dialog and all its supporting elements (including any tabs, shim,
30842      * shadow, proxy, mask, etc.)  Also removes all event listeners.
30843      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
30844      */
30845     destroy : function(removeEl){
30846         if(this.isVisible()){
30847             this.animateTarget = null;
30848             this.hide();
30849         }
30850         Roo.EventManager.removeResizeListener(this.adjustViewport, this);
30851         if(this.tabs){
30852             this.tabs.destroy(removeEl);
30853         }
30854         Roo.destroy(
30855              this.shim,
30856              this.proxy,
30857              this.resizer,
30858              this.close,
30859              this.mask
30860         );
30861         if(this.dd){
30862             this.dd.unreg();
30863         }
30864         if(this.buttons){
30865            for(var i = 0, len = this.buttons.length; i < len; i++){
30866                this.buttons[i].destroy();
30867            }
30868         }
30869         this.el.removeAllListeners();
30870         if(removeEl === true){
30871             this.el.update("");
30872             this.el.remove();
30873         }
30874         Roo.DialogManager.unregister(this);
30875     },
30876
30877     // private
30878     startMove : function(){
30879         if(this.proxyDrag){
30880             this.proxy.show();
30881         }
30882         if(this.constraintoviewport !== false){
30883             this.dd.constrainTo(document.body, {right: this.shadowOffset, bottom: this.shadowOffset});
30884         }
30885     },
30886
30887     // private
30888     endMove : function(){
30889         if(!this.proxyDrag){
30890             Roo.dd.DD.prototype.endDrag.apply(this.dd, arguments);
30891         }else{
30892             Roo.dd.DDProxy.prototype.endDrag.apply(this.dd, arguments);
30893             this.proxy.hide();
30894         }
30895         this.refreshSize();
30896         this.adjustAssets();
30897         this.focus();
30898         this.fireEvent("move", this, this.xy[0], this.xy[1]);
30899     },
30900
30901     /**
30902      * Brings this dialog to the front of any other visible dialogs
30903      * @return {Roo.BasicDialog} this
30904      */
30905     toFront : function(){
30906         Roo.DialogManager.bringToFront(this);
30907         return this;
30908     },
30909
30910     /**
30911      * Sends this dialog to the back (under) of any other visible dialogs
30912      * @return {Roo.BasicDialog} this
30913      */
30914     toBack : function(){
30915         Roo.DialogManager.sendToBack(this);
30916         return this;
30917     },
30918
30919     /**
30920      * Centers this dialog in the viewport
30921      * @return {Roo.BasicDialog} this
30922      */
30923     center : function(){
30924         var xy = this.el.getCenterXY(true);
30925         this.moveTo(xy[0], xy[1]);
30926         return this;
30927     },
30928
30929     /**
30930      * Moves the dialog's top-left corner to the specified point
30931      * @param {Number} x
30932      * @param {Number} y
30933      * @return {Roo.BasicDialog} this
30934      */
30935     moveTo : function(x, y){
30936         this.xy = [x,y];
30937         if(this.isVisible()){
30938             this.el.setXY(this.xy);
30939             this.adjustAssets();
30940         }
30941         return this;
30942     },
30943
30944     /**
30945      * Aligns the dialog to the specified element
30946      * @param {String/HTMLElement/Roo.Element} element The element to align to.
30947      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details).
30948      * @param {Array} offsets (optional) Offset the positioning by [x, y]
30949      * @return {Roo.BasicDialog} this
30950      */
30951     alignTo : function(element, position, offsets){
30952         this.xy = this.el.getAlignToXY(element, position, offsets);
30953         if(this.isVisible()){
30954             this.el.setXY(this.xy);
30955             this.adjustAssets();
30956         }
30957         return this;
30958     },
30959
30960     /**
30961      * Anchors an element to another element and realigns it when the window is resized.
30962      * @param {String/HTMLElement/Roo.Element} element The element to align to.
30963      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details)
30964      * @param {Array} offsets (optional) Offset the positioning by [x, y]
30965      * @param {Boolean/Number} monitorScroll (optional) true to monitor body scroll and reposition. If this parameter
30966      * is a number, it is used as the buffer delay (defaults to 50ms).
30967      * @return {Roo.BasicDialog} this
30968      */
30969     anchorTo : function(el, alignment, offsets, monitorScroll){
30970         var action = function(){
30971             this.alignTo(el, alignment, offsets);
30972         };
30973         Roo.EventManager.onWindowResize(action, this);
30974         var tm = typeof monitorScroll;
30975         if(tm != 'undefined'){
30976             Roo.EventManager.on(window, 'scroll', action, this,
30977                 {buffer: tm == 'number' ? monitorScroll : 50});
30978         }
30979         action.call(this);
30980         return this;
30981     },
30982
30983     /**
30984      * Returns true if the dialog is visible
30985      * @return {Boolean}
30986      */
30987     isVisible : function(){
30988         return this.el.isVisible();
30989     },
30990
30991     // private
30992     animHide : function(callback){
30993         var b = Roo.get(this.animateTarget).getBox();
30994         this.proxy.show();
30995         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height);
30996         this.el.hide();
30997         this.proxy.setBounds(b.x, b.y, b.width, b.height, true, .35,
30998                     this.hideEl.createDelegate(this, [callback]));
30999     },
31000
31001     /**
31002      * Hides the dialog.
31003      * @param {Function} callback (optional) Function to call when the dialog is hidden
31004      * @return {Roo.BasicDialog} this
31005      */
31006     hide : function(callback){
31007         if (this.fireEvent("beforehide", this) === false){
31008             return;
31009         }
31010         if(this.shadow){
31011             this.shadow.hide();
31012         }
31013         if(this.shim) {
31014           this.shim.hide();
31015         }
31016         // sometimes animateTarget seems to get set.. causing problems...
31017         // this just double checks..
31018         if(this.animateTarget && Roo.get(this.animateTarget)) {
31019            this.animHide(callback);
31020         }else{
31021             this.el.hide();
31022             this.hideEl(callback);
31023         }
31024         return this;
31025     },
31026
31027     // private
31028     hideEl : function(callback){
31029         this.proxy.hide();
31030         if(this.modal){
31031             this.mask.hide();
31032             Roo.get(document.body).removeClass("x-body-masked");
31033         }
31034         this.fireEvent("hide", this);
31035         if(typeof callback == "function"){
31036             callback();
31037         }
31038     },
31039
31040     // private
31041     hideAction : function(){
31042         this.setLeft("-10000px");
31043         this.setTop("-10000px");
31044         this.setStyle("visibility", "hidden");
31045     },
31046
31047     // private
31048     refreshSize : function(){
31049         this.size = this.el.getSize();
31050         this.xy = this.el.getXY();
31051         Roo.state.Manager.set(this.stateId || this.el.id + "-state", this.el.getBox());
31052     },
31053
31054     // private
31055     // z-index is managed by the DialogManager and may be overwritten at any time
31056     setZIndex : function(index){
31057         if(this.modal){
31058             this.mask.setStyle("z-index", index);
31059         }
31060         if(this.shim){
31061             this.shim.setStyle("z-index", ++index);
31062         }
31063         if(this.shadow){
31064             this.shadow.setZIndex(++index);
31065         }
31066         this.el.setStyle("z-index", ++index);
31067         if(this.proxy){
31068             this.proxy.setStyle("z-index", ++index);
31069         }
31070         if(this.resizer){
31071             this.resizer.proxy.setStyle("z-index", ++index);
31072         }
31073
31074         this.lastZIndex = index;
31075     },
31076
31077     /**
31078      * Returns the element for this dialog
31079      * @return {Roo.Element} The underlying dialog Element
31080      */
31081     getEl : function(){
31082         return this.el;
31083     }
31084 });
31085
31086 /**
31087  * @class Roo.DialogManager
31088  * Provides global access to BasicDialogs that have been created and
31089  * support for z-indexing (layering) multiple open dialogs.
31090  */
31091 Roo.DialogManager = function(){
31092     var list = {};
31093     var accessList = [];
31094     var front = null;
31095
31096     // private
31097     var sortDialogs = function(d1, d2){
31098         return (!d1._lastAccess || d1._lastAccess < d2._lastAccess) ? -1 : 1;
31099     };
31100
31101     // private
31102     var orderDialogs = function(){
31103         accessList.sort(sortDialogs);
31104         var seed = Roo.DialogManager.zseed;
31105         for(var i = 0, len = accessList.length; i < len; i++){
31106             var dlg = accessList[i];
31107             if(dlg){
31108                 dlg.setZIndex(seed + (i*10));
31109             }
31110         }
31111     };
31112
31113     return {
31114         /**
31115          * The starting z-index for BasicDialogs (defaults to 9000)
31116          * @type Number The z-index value
31117          */
31118         zseed : 9000,
31119
31120         // private
31121         register : function(dlg){
31122             list[dlg.id] = dlg;
31123             accessList.push(dlg);
31124         },
31125
31126         // private
31127         unregister : function(dlg){
31128             delete list[dlg.id];
31129             var i=0;
31130             var len=0;
31131             if(!accessList.indexOf){
31132                 for(  i = 0, len = accessList.length; i < len; i++){
31133                     if(accessList[i] == dlg){
31134                         accessList.splice(i, 1);
31135                         return;
31136                     }
31137                 }
31138             }else{
31139                  i = accessList.indexOf(dlg);
31140                 if(i != -1){
31141                     accessList.splice(i, 1);
31142                 }
31143             }
31144         },
31145
31146         /**
31147          * Gets a registered dialog by id
31148          * @param {String/Object} id The id of the dialog or a dialog
31149          * @return {Roo.BasicDialog} this
31150          */
31151         get : function(id){
31152             return typeof id == "object" ? id : list[id];
31153         },
31154
31155         /**
31156          * Brings the specified dialog to the front
31157          * @param {String/Object} dlg The id of the dialog or a dialog
31158          * @return {Roo.BasicDialog} this
31159          */
31160         bringToFront : function(dlg){
31161             dlg = this.get(dlg);
31162             if(dlg != front){
31163                 front = dlg;
31164                 dlg._lastAccess = new Date().getTime();
31165                 orderDialogs();
31166             }
31167             return dlg;
31168         },
31169
31170         /**
31171          * Sends the specified dialog to the back
31172          * @param {String/Object} dlg The id of the dialog or a dialog
31173          * @return {Roo.BasicDialog} this
31174          */
31175         sendToBack : function(dlg){
31176             dlg = this.get(dlg);
31177             dlg._lastAccess = -(new Date().getTime());
31178             orderDialogs();
31179             return dlg;
31180         },
31181
31182         /**
31183          * Hides all dialogs
31184          */
31185         hideAll : function(){
31186             for(var id in list){
31187                 if(list[id] && typeof list[id] != "function" && list[id].isVisible()){
31188                     list[id].hide();
31189                 }
31190             }
31191         }
31192     };
31193 }();
31194
31195 /**
31196  * @class Roo.LayoutDialog
31197  * @extends Roo.BasicDialog
31198  * Dialog which provides adjustments for working with a layout in a Dialog.
31199  * Add your necessary layout config options to the dialog's config.<br>
31200  * Example usage (including a nested layout):
31201  * <pre><code>
31202 if(!dialog){
31203     dialog = new Roo.LayoutDialog("download-dlg", {
31204         modal: true,
31205         width:600,
31206         height:450,
31207         shadow:true,
31208         minWidth:500,
31209         minHeight:350,
31210         autoTabs:true,
31211         proxyDrag:true,
31212         // layout config merges with the dialog config
31213         center:{
31214             tabPosition: "top",
31215             alwaysShowTabs: true
31216         }
31217     });
31218     dialog.addKeyListener(27, dialog.hide, dialog);
31219     dialog.setDefaultButton(dialog.addButton("Close", dialog.hide, dialog));
31220     dialog.addButton("Build It!", this.getDownload, this);
31221
31222     // we can even add nested layouts
31223     var innerLayout = new Roo.BorderLayout("dl-inner", {
31224         east: {
31225             initialSize: 200,
31226             autoScroll:true,
31227             split:true
31228         },
31229         center: {
31230             autoScroll:true
31231         }
31232     });
31233     innerLayout.beginUpdate();
31234     innerLayout.add("east", new Roo.ContentPanel("dl-details"));
31235     innerLayout.add("center", new Roo.ContentPanel("selection-panel"));
31236     innerLayout.endUpdate(true);
31237
31238     var layout = dialog.getLayout();
31239     layout.beginUpdate();
31240     layout.add("center", new Roo.ContentPanel("standard-panel",
31241                         {title: "Download the Source", fitToFrame:true}));
31242     layout.add("center", new Roo.NestedLayoutPanel(innerLayout,
31243                {title: "Build your own roo.js"}));
31244     layout.getRegion("center").showPanel(sp);
31245     layout.endUpdate();
31246 }
31247 </code></pre>
31248     * @constructor
31249     * @param {String/HTMLElement/Roo.Element} el The id of or container element, or config
31250     * @param {Object} config configuration options
31251   */
31252 Roo.LayoutDialog = function(el, cfg){
31253     
31254     var config=  cfg;
31255     if (typeof(cfg) == 'undefined') {
31256         config = Roo.apply({}, el);
31257         // not sure why we use documentElement here.. - it should always be body.
31258         // IE7 borks horribly if we use documentElement.
31259         // webkit also does not like documentElement - it creates a body element...
31260         el = Roo.get( document.body || document.documentElement ).createChild();
31261         //config.autoCreate = true;
31262     }
31263     
31264     
31265     config.autoTabs = false;
31266     Roo.LayoutDialog.superclass.constructor.call(this, el, config);
31267     this.body.setStyle({overflow:"hidden", position:"relative"});
31268     this.layout = new Roo.BorderLayout(this.body.dom, config);
31269     this.layout.monitorWindowResize = false;
31270     this.el.addClass("x-dlg-auto-layout");
31271     // fix case when center region overwrites center function
31272     this.center = Roo.BasicDialog.prototype.center;
31273     this.on("show", this.layout.layout, this.layout, true);
31274     if (config.items) {
31275         var xitems = config.items;
31276         delete config.items;
31277         Roo.each(xitems, this.addxtype, this);
31278     }
31279     
31280     
31281 };
31282 Roo.extend(Roo.LayoutDialog, Roo.BasicDialog, {
31283     /**
31284      * Ends update of the layout <strike>and resets display to none</strike>. Use standard beginUpdate/endUpdate on the layout.
31285      * @deprecated
31286      */
31287     endUpdate : function(){
31288         this.layout.endUpdate();
31289     },
31290
31291     /**
31292      * Begins an update of the layout <strike>and sets display to block and visibility to hidden</strike>. Use standard beginUpdate/endUpdate on the layout.
31293      *  @deprecated
31294      */
31295     beginUpdate : function(){
31296         this.layout.beginUpdate();
31297     },
31298
31299     /**
31300      * Get the BorderLayout for this dialog
31301      * @return {Roo.BorderLayout}
31302      */
31303     getLayout : function(){
31304         return this.layout;
31305     },
31306
31307     showEl : function(){
31308         Roo.LayoutDialog.superclass.showEl.apply(this, arguments);
31309         if(Roo.isIE7){
31310             this.layout.layout();
31311         }
31312     },
31313
31314     // private
31315     // Use the syncHeightBeforeShow config option to control this automatically
31316     syncBodyHeight : function(){
31317         Roo.LayoutDialog.superclass.syncBodyHeight.call(this);
31318         if(this.layout){this.layout.layout();}
31319     },
31320     
31321       /**
31322      * Add an xtype element (actually adds to the layout.)
31323      * @return {Object} xdata xtype object data.
31324      */
31325     
31326     addxtype : function(c) {
31327         return this.layout.addxtype(c);
31328     }
31329 });/*
31330  * Based on:
31331  * Ext JS Library 1.1.1
31332  * Copyright(c) 2006-2007, Ext JS, LLC.
31333  *
31334  * Originally Released Under LGPL - original licence link has changed is not relivant.
31335  *
31336  * Fork - LGPL
31337  * <script type="text/javascript">
31338  */
31339  
31340 /**
31341  * @class Roo.MessageBox
31342  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
31343  * Example usage:
31344  *<pre><code>
31345 // Basic alert:
31346 Roo.Msg.alert('Status', 'Changes saved successfully.');
31347
31348 // Prompt for user data:
31349 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
31350     if (btn == 'ok'){
31351         // process text value...
31352     }
31353 });
31354
31355 // Show a dialog using config options:
31356 Roo.Msg.show({
31357    title:'Save Changes?',
31358    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
31359    buttons: Roo.Msg.YESNOCANCEL,
31360    fn: processResult,
31361    animEl: 'elId'
31362 });
31363 </code></pre>
31364  * @singleton
31365  */
31366 Roo.MessageBox = function(){
31367     var dlg, opt, mask, waitTimer;
31368     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
31369     var buttons, activeTextEl, bwidth;
31370
31371     // private
31372     var handleButton = function(button){
31373         dlg.hide();
31374         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
31375     };
31376
31377     // private
31378     var handleHide = function(){
31379         if(opt && opt.cls){
31380             dlg.el.removeClass(opt.cls);
31381         }
31382         if(waitTimer){
31383             Roo.TaskMgr.stop(waitTimer);
31384             waitTimer = null;
31385         }
31386     };
31387
31388     // private
31389     var updateButtons = function(b){
31390         var width = 0;
31391         if(!b){
31392             buttons["ok"].hide();
31393             buttons["cancel"].hide();
31394             buttons["yes"].hide();
31395             buttons["no"].hide();
31396             dlg.footer.dom.style.display = 'none';
31397             return width;
31398         }
31399         dlg.footer.dom.style.display = '';
31400         for(var k in buttons){
31401             if(typeof buttons[k] != "function"){
31402                 if(b[k]){
31403                     buttons[k].show();
31404                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.MessageBox.buttonText[k]);
31405                     width += buttons[k].el.getWidth()+15;
31406                 }else{
31407                     buttons[k].hide();
31408                 }
31409             }
31410         }
31411         return width;
31412     };
31413
31414     // private
31415     var handleEsc = function(d, k, e){
31416         if(opt && opt.closable !== false){
31417             dlg.hide();
31418         }
31419         if(e){
31420             e.stopEvent();
31421         }
31422     };
31423
31424     return {
31425         /**
31426          * Returns a reference to the underlying {@link Roo.BasicDialog} element
31427          * @return {Roo.BasicDialog} The BasicDialog element
31428          */
31429         getDialog : function(){
31430            if(!dlg){
31431                 dlg = new Roo.BasicDialog("x-msg-box", {
31432                     autoCreate : true,
31433                     shadow: true,
31434                     draggable: true,
31435                     resizable:false,
31436                     constraintoviewport:false,
31437                     fixedcenter:true,
31438                     collapsible : false,
31439                     shim:true,
31440                     modal: true,
31441                     width:400, height:100,
31442                     buttonAlign:"center",
31443                     closeClick : function(){
31444                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
31445                             handleButton("no");
31446                         }else{
31447                             handleButton("cancel");
31448                         }
31449                     }
31450                 });
31451                 dlg.on("hide", handleHide);
31452                 mask = dlg.mask;
31453                 dlg.addKeyListener(27, handleEsc);
31454                 buttons = {};
31455                 var bt = this.buttonText;
31456                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
31457                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
31458                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
31459                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
31460                 bodyEl = dlg.body.createChild({
31461
31462                     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>'
31463                 });
31464                 msgEl = bodyEl.dom.firstChild;
31465                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
31466                 textboxEl.enableDisplayMode();
31467                 textboxEl.addKeyListener([10,13], function(){
31468                     if(dlg.isVisible() && opt && opt.buttons){
31469                         if(opt.buttons.ok){
31470                             handleButton("ok");
31471                         }else if(opt.buttons.yes){
31472                             handleButton("yes");
31473                         }
31474                     }
31475                 });
31476                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
31477                 textareaEl.enableDisplayMode();
31478                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
31479                 progressEl.enableDisplayMode();
31480                 var pf = progressEl.dom.firstChild;
31481                 if (pf) {
31482                     pp = Roo.get(pf.firstChild);
31483                     pp.setHeight(pf.offsetHeight);
31484                 }
31485                 
31486             }
31487             return dlg;
31488         },
31489
31490         /**
31491          * Updates the message box body text
31492          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
31493          * the XHTML-compliant non-breaking space character '&amp;#160;')
31494          * @return {Roo.MessageBox} This message box
31495          */
31496         updateText : function(text){
31497             if(!dlg.isVisible() && !opt.width){
31498                 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
31499             }
31500             msgEl.innerHTML = text || '&#160;';
31501       
31502             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
31503             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
31504             var w = Math.max(
31505                     Math.min(opt.width || cw , this.maxWidth), 
31506                     Math.max(opt.minWidth || this.minWidth, bwidth)
31507             );
31508             if(opt.prompt){
31509                 activeTextEl.setWidth(w);
31510             }
31511             if(dlg.isVisible()){
31512                 dlg.fixedcenter = false;
31513             }
31514             // to big, make it scroll. = But as usual stupid IE does not support
31515             // !important..
31516             
31517             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
31518                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
31519                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
31520             } else {
31521                 bodyEl.dom.style.height = '';
31522                 bodyEl.dom.style.overflowY = '';
31523             }
31524             if (cw > w) {
31525                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
31526             } else {
31527                 bodyEl.dom.style.overflowX = '';
31528             }
31529             
31530             dlg.setContentSize(w, bodyEl.getHeight());
31531             if(dlg.isVisible()){
31532                 dlg.fixedcenter = true;
31533             }
31534             return this;
31535         },
31536
31537         /**
31538          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
31539          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
31540          * @param {Number} value Any number between 0 and 1 (e.g., .5)
31541          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
31542          * @return {Roo.MessageBox} This message box
31543          */
31544         updateProgress : function(value, text){
31545             if(text){
31546                 this.updateText(text);
31547             }
31548             if (pp) { // weird bug on my firefox - for some reason this is not defined
31549                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
31550             }
31551             return this;
31552         },        
31553
31554         /**
31555          * Returns true if the message box is currently displayed
31556          * @return {Boolean} True if the message box is visible, else false
31557          */
31558         isVisible : function(){
31559             return dlg && dlg.isVisible();  
31560         },
31561
31562         /**
31563          * Hides the message box if it is displayed
31564          */
31565         hide : function(){
31566             if(this.isVisible()){
31567                 dlg.hide();
31568             }  
31569         },
31570
31571         /**
31572          * Displays a new message box, or reinitializes an existing message box, based on the config options
31573          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
31574          * The following config object properties are supported:
31575          * <pre>
31576 Property    Type             Description
31577 ----------  ---------------  ------------------------------------------------------------------------------------
31578 animEl            String/Element   An id or Element from which the message box should animate as it opens and
31579                                    closes (defaults to undefined)
31580 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
31581                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
31582 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
31583                                    progress and wait dialogs will ignore this property and always hide the
31584                                    close button as they can only be closed programmatically.
31585 cls               String           A custom CSS class to apply to the message box element
31586 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
31587                                    displayed (defaults to 75)
31588 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
31589                                    function will be btn (the name of the button that was clicked, if applicable,
31590                                    e.g. "ok"), and text (the value of the active text field, if applicable).
31591                                    Progress and wait dialogs will ignore this option since they do not respond to
31592                                    user actions and can only be closed programmatically, so any required function
31593                                    should be called by the same code after it closes the dialog.
31594 icon              String           A CSS class that provides a background image to be used as an icon for
31595                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
31596 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
31597 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
31598 modal             Boolean          False to allow user interaction with the page while the message box is
31599                                    displayed (defaults to true)
31600 msg               String           A string that will replace the existing message box body text (defaults
31601                                    to the XHTML-compliant non-breaking space character '&#160;')
31602 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
31603 progress          Boolean          True to display a progress bar (defaults to false)
31604 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
31605 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
31606 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
31607 title             String           The title text
31608 value             String           The string value to set into the active textbox element if displayed
31609 wait              Boolean          True to display a progress bar (defaults to false)
31610 width             Number           The width of the dialog in pixels
31611 </pre>
31612          *
31613          * Example usage:
31614          * <pre><code>
31615 Roo.Msg.show({
31616    title: 'Address',
31617    msg: 'Please enter your address:',
31618    width: 300,
31619    buttons: Roo.MessageBox.OKCANCEL,
31620    multiline: true,
31621    fn: saveAddress,
31622    animEl: 'addAddressBtn'
31623 });
31624 </code></pre>
31625          * @param {Object} config Configuration options
31626          * @return {Roo.MessageBox} This message box
31627          */
31628         show : function(options)
31629         {
31630             
31631             // this causes nightmares if you show one dialog after another
31632             // especially on callbacks..
31633              
31634             if(this.isVisible()){
31635                 
31636                 this.hide();
31637                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
31638                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
31639                 Roo.log("New Dialog Message:" +  options.msg )
31640                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
31641                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
31642                 
31643             }
31644             var d = this.getDialog();
31645             opt = options;
31646             d.setTitle(opt.title || "&#160;");
31647             d.close.setDisplayed(opt.closable !== false);
31648             activeTextEl = textboxEl;
31649             opt.prompt = opt.prompt || (opt.multiline ? true : false);
31650             if(opt.prompt){
31651                 if(opt.multiline){
31652                     textboxEl.hide();
31653                     textareaEl.show();
31654                     textareaEl.setHeight(typeof opt.multiline == "number" ?
31655                         opt.multiline : this.defaultTextHeight);
31656                     activeTextEl = textareaEl;
31657                 }else{
31658                     textboxEl.show();
31659                     textareaEl.hide();
31660                 }
31661             }else{
31662                 textboxEl.hide();
31663                 textareaEl.hide();
31664             }
31665             progressEl.setDisplayed(opt.progress === true);
31666             this.updateProgress(0);
31667             activeTextEl.dom.value = opt.value || "";
31668             if(opt.prompt){
31669                 dlg.setDefaultButton(activeTextEl);
31670             }else{
31671                 var bs = opt.buttons;
31672                 var db = null;
31673                 if(bs && bs.ok){
31674                     db = buttons["ok"];
31675                 }else if(bs && bs.yes){
31676                     db = buttons["yes"];
31677                 }
31678                 dlg.setDefaultButton(db);
31679             }
31680             bwidth = updateButtons(opt.buttons);
31681             this.updateText(opt.msg);
31682             if(opt.cls){
31683                 d.el.addClass(opt.cls);
31684             }
31685             d.proxyDrag = opt.proxyDrag === true;
31686             d.modal = opt.modal !== false;
31687             d.mask = opt.modal !== false ? mask : false;
31688             if(!d.isVisible()){
31689                 // force it to the end of the z-index stack so it gets a cursor in FF
31690                 document.body.appendChild(dlg.el.dom);
31691                 d.animateTarget = null;
31692                 d.show(options.animEl);
31693             }
31694             return this;
31695         },
31696
31697         /**
31698          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
31699          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
31700          * and closing the message box when the process is complete.
31701          * @param {String} title The title bar text
31702          * @param {String} msg The message box body text
31703          * @return {Roo.MessageBox} This message box
31704          */
31705         progress : function(title, msg){
31706             this.show({
31707                 title : title,
31708                 msg : msg,
31709                 buttons: false,
31710                 progress:true,
31711                 closable:false,
31712                 minWidth: this.minProgressWidth,
31713                 modal : true
31714             });
31715             return this;
31716         },
31717
31718         /**
31719          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
31720          * If a callback function is passed it will be called after the user clicks the button, and the
31721          * id of the button that was clicked will be passed as the only parameter to the callback
31722          * (could also be the top-right close button).
31723          * @param {String} title The title bar text
31724          * @param {String} msg The message box body text
31725          * @param {Function} fn (optional) The callback function invoked after the message box is closed
31726          * @param {Object} scope (optional) The scope of the callback function
31727          * @return {Roo.MessageBox} This message box
31728          */
31729         alert : function(title, msg, fn, scope){
31730             this.show({
31731                 title : title,
31732                 msg : msg,
31733                 buttons: this.OK,
31734                 fn: fn,
31735                 scope : scope,
31736                 modal : true
31737             });
31738             return this;
31739         },
31740
31741         /**
31742          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
31743          * interaction while waiting for a long-running process to complete that does not have defined intervals.
31744          * You are responsible for closing the message box when the process is complete.
31745          * @param {String} msg The message box body text
31746          * @param {String} title (optional) The title bar text
31747          * @return {Roo.MessageBox} This message box
31748          */
31749         wait : function(msg, title){
31750             this.show({
31751                 title : title,
31752                 msg : msg,
31753                 buttons: false,
31754                 closable:false,
31755                 progress:true,
31756                 modal:true,
31757                 width:300,
31758                 wait:true
31759             });
31760             waitTimer = Roo.TaskMgr.start({
31761                 run: function(i){
31762                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
31763                 },
31764                 interval: 1000
31765             });
31766             return this;
31767         },
31768
31769         /**
31770          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
31771          * If a callback function is passed it will be called after the user clicks either button, and the id of the
31772          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
31773          * @param {String} title The title bar text
31774          * @param {String} msg The message box body text
31775          * @param {Function} fn (optional) The callback function invoked after the message box is closed
31776          * @param {Object} scope (optional) The scope of the callback function
31777          * @return {Roo.MessageBox} This message box
31778          */
31779         confirm : function(title, msg, fn, scope){
31780             this.show({
31781                 title : title,
31782                 msg : msg,
31783                 buttons: this.YESNO,
31784                 fn: fn,
31785                 scope : scope,
31786                 modal : true
31787             });
31788             return this;
31789         },
31790
31791         /**
31792          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
31793          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
31794          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
31795          * (could also be the top-right close button) and the text that was entered will be passed as the two
31796          * parameters to the callback.
31797          * @param {String} title The title bar text
31798          * @param {String} msg The message box body text
31799          * @param {Function} fn (optional) The callback function invoked after the message box is closed
31800          * @param {Object} scope (optional) The scope of the callback function
31801          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
31802          * property, or the height in pixels to create the textbox (defaults to false / single-line)
31803          * @return {Roo.MessageBox} This message box
31804          */
31805         prompt : function(title, msg, fn, scope, multiline){
31806             this.show({
31807                 title : title,
31808                 msg : msg,
31809                 buttons: this.OKCANCEL,
31810                 fn: fn,
31811                 minWidth:250,
31812                 scope : scope,
31813                 prompt:true,
31814                 multiline: multiline,
31815                 modal : true
31816             });
31817             return this;
31818         },
31819
31820         /**
31821          * Button config that displays a single OK button
31822          * @type Object
31823          */
31824         OK : {ok:true},
31825         /**
31826          * Button config that displays Yes and No buttons
31827          * @type Object
31828          */
31829         YESNO : {yes:true, no:true},
31830         /**
31831          * Button config that displays OK and Cancel buttons
31832          * @type Object
31833          */
31834         OKCANCEL : {ok:true, cancel:true},
31835         /**
31836          * Button config that displays Yes, No and Cancel buttons
31837          * @type Object
31838          */
31839         YESNOCANCEL : {yes:true, no:true, cancel:true},
31840
31841         /**
31842          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
31843          * @type Number
31844          */
31845         defaultTextHeight : 75,
31846         /**
31847          * The maximum width in pixels of the message box (defaults to 600)
31848          * @type Number
31849          */
31850         maxWidth : 600,
31851         /**
31852          * The minimum width in pixels of the message box (defaults to 100)
31853          * @type Number
31854          */
31855         minWidth : 100,
31856         /**
31857          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
31858          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
31859          * @type Number
31860          */
31861         minProgressWidth : 250,
31862         /**
31863          * An object containing the default button text strings that can be overriden for localized language support.
31864          * Supported properties are: ok, cancel, yes and no.
31865          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
31866          * @type Object
31867          */
31868         buttonText : {
31869             ok : "OK",
31870             cancel : "Cancel",
31871             yes : "Yes",
31872             no : "No"
31873         }
31874     };
31875 }();
31876
31877 /**
31878  * Shorthand for {@link Roo.MessageBox}
31879  */
31880 Roo.Msg = Roo.MessageBox;/*
31881  * Based on:
31882  * Ext JS Library 1.1.1
31883  * Copyright(c) 2006-2007, Ext JS, LLC.
31884  *
31885  * Originally Released Under LGPL - original licence link has changed is not relivant.
31886  *
31887  * Fork - LGPL
31888  * <script type="text/javascript">
31889  */
31890 /**
31891  * @class Roo.QuickTips
31892  * Provides attractive and customizable tooltips for any element.
31893  * @singleton
31894  */
31895 Roo.QuickTips = function(){
31896     var el, tipBody, tipBodyText, tipTitle, tm, cfg, close, tagEls = {}, esc, removeCls = null, bdLeft, bdRight;
31897     var ce, bd, xy, dd;
31898     var visible = false, disabled = true, inited = false;
31899     var showProc = 1, hideProc = 1, dismissProc = 1, locks = [];
31900     
31901     var onOver = function(e){
31902         if(disabled){
31903             return;
31904         }
31905         var t = e.getTarget();
31906         if(!t || t.nodeType !== 1 || t == document || t == document.body){
31907             return;
31908         }
31909         if(ce && t == ce.el){
31910             clearTimeout(hideProc);
31911             return;
31912         }
31913         if(t && tagEls[t.id]){
31914             tagEls[t.id].el = t;
31915             showProc = show.defer(tm.showDelay, tm, [tagEls[t.id]]);
31916             return;
31917         }
31918         var ttp, et = Roo.fly(t);
31919         var ns = cfg.namespace;
31920         if(tm.interceptTitles && t.title){
31921             ttp = t.title;
31922             t.qtip = ttp;
31923             t.removeAttribute("title");
31924             e.preventDefault();
31925         }else{
31926             ttp = t.qtip || et.getAttributeNS(ns, cfg.attribute);
31927         }
31928         if(ttp){
31929             showProc = show.defer(tm.showDelay, tm, [{
31930                 el: t, 
31931                 text: ttp, 
31932                 width: et.getAttributeNS(ns, cfg.width),
31933                 autoHide: et.getAttributeNS(ns, cfg.hide) != "user",
31934                 title: et.getAttributeNS(ns, cfg.title),
31935                     cls: et.getAttributeNS(ns, cfg.cls)
31936             }]);
31937         }
31938     };
31939     
31940     var onOut = function(e){
31941         clearTimeout(showProc);
31942         var t = e.getTarget();
31943         if(t && ce && ce.el == t && (tm.autoHide && ce.autoHide !== false)){
31944             hideProc = setTimeout(hide, tm.hideDelay);
31945         }
31946     };
31947     
31948     var onMove = function(e){
31949         if(disabled){
31950             return;
31951         }
31952         xy = e.getXY();
31953         xy[1] += 18;
31954         if(tm.trackMouse && ce){
31955             el.setXY(xy);
31956         }
31957     };
31958     
31959     var onDown = function(e){
31960         clearTimeout(showProc);
31961         clearTimeout(hideProc);
31962         if(!e.within(el)){
31963             if(tm.hideOnClick){
31964                 hide();
31965                 tm.disable();
31966                 tm.enable.defer(100, tm);
31967             }
31968         }
31969     };
31970     
31971     var getPad = function(){
31972         return 2;//bdLeft.getPadding('l')+bdRight.getPadding('r');
31973     };
31974
31975     var show = function(o){
31976         if(disabled){
31977             return;
31978         }
31979         clearTimeout(dismissProc);
31980         ce = o;
31981         if(removeCls){ // in case manually hidden
31982             el.removeClass(removeCls);
31983             removeCls = null;
31984         }
31985         if(ce.cls){
31986             el.addClass(ce.cls);
31987             removeCls = ce.cls;
31988         }
31989         if(ce.title){
31990             tipTitle.update(ce.title);
31991             tipTitle.show();
31992         }else{
31993             tipTitle.update('');
31994             tipTitle.hide();
31995         }
31996         el.dom.style.width  = tm.maxWidth+'px';
31997         //tipBody.dom.style.width = '';
31998         tipBodyText.update(o.text);
31999         var p = getPad(), w = ce.width;
32000         if(!w){
32001             var td = tipBodyText.dom;
32002             var aw = Math.max(td.offsetWidth, td.clientWidth, td.scrollWidth);
32003             if(aw > tm.maxWidth){
32004                 w = tm.maxWidth;
32005             }else if(aw < tm.minWidth){
32006                 w = tm.minWidth;
32007             }else{
32008                 w = aw;
32009             }
32010         }
32011         //tipBody.setWidth(w);
32012         el.setWidth(parseInt(w, 10) + p);
32013         if(ce.autoHide === false){
32014             close.setDisplayed(true);
32015             if(dd){
32016                 dd.unlock();
32017             }
32018         }else{
32019             close.setDisplayed(false);
32020             if(dd){
32021                 dd.lock();
32022             }
32023         }
32024         if(xy){
32025             el.avoidY = xy[1]-18;
32026             el.setXY(xy);
32027         }
32028         if(tm.animate){
32029             el.setOpacity(.1);
32030             el.setStyle("visibility", "visible");
32031             el.fadeIn({callback: afterShow});
32032         }else{
32033             afterShow();
32034         }
32035     };
32036     
32037     var afterShow = function(){
32038         if(ce){
32039             el.show();
32040             esc.enable();
32041             if(tm.autoDismiss && ce.autoHide !== false){
32042                 dismissProc = setTimeout(hide, tm.autoDismissDelay);
32043             }
32044         }
32045     };
32046     
32047     var hide = function(noanim){
32048         clearTimeout(dismissProc);
32049         clearTimeout(hideProc);
32050         ce = null;
32051         if(el.isVisible()){
32052             esc.disable();
32053             if(noanim !== true && tm.animate){
32054                 el.fadeOut({callback: afterHide});
32055             }else{
32056                 afterHide();
32057             } 
32058         }
32059     };
32060     
32061     var afterHide = function(){
32062         el.hide();
32063         if(removeCls){
32064             el.removeClass(removeCls);
32065             removeCls = null;
32066         }
32067     };
32068     
32069     return {
32070         /**
32071         * @cfg {Number} minWidth
32072         * The minimum width of the quick tip (defaults to 40)
32073         */
32074        minWidth : 40,
32075         /**
32076         * @cfg {Number} maxWidth
32077         * The maximum width of the quick tip (defaults to 300)
32078         */
32079        maxWidth : 300,
32080         /**
32081         * @cfg {Boolean} interceptTitles
32082         * True to automatically use the element's DOM title value if available (defaults to false)
32083         */
32084        interceptTitles : false,
32085         /**
32086         * @cfg {Boolean} trackMouse
32087         * True to have the quick tip follow the mouse as it moves over the target element (defaults to false)
32088         */
32089        trackMouse : false,
32090         /**
32091         * @cfg {Boolean} hideOnClick
32092         * True to hide the quick tip if the user clicks anywhere in the document (defaults to true)
32093         */
32094        hideOnClick : true,
32095         /**
32096         * @cfg {Number} showDelay
32097         * Delay in milliseconds before the quick tip displays after the mouse enters the target element (defaults to 500)
32098         */
32099        showDelay : 500,
32100         /**
32101         * @cfg {Number} hideDelay
32102         * Delay in milliseconds before the quick tip hides when autoHide = true (defaults to 200)
32103         */
32104        hideDelay : 200,
32105         /**
32106         * @cfg {Boolean} autoHide
32107         * True to automatically hide the quick tip after the mouse exits the target element (defaults to true).
32108         * Used in conjunction with hideDelay.
32109         */
32110        autoHide : true,
32111         /**
32112         * @cfg {Boolean}
32113         * True to automatically hide the quick tip after a set period of time, regardless of the user's actions
32114         * (defaults to true).  Used in conjunction with autoDismissDelay.
32115         */
32116        autoDismiss : true,
32117         /**
32118         * @cfg {Number}
32119         * Delay in milliseconds before the quick tip hides when autoDismiss = true (defaults to 5000)
32120         */
32121        autoDismissDelay : 5000,
32122        /**
32123         * @cfg {Boolean} animate
32124         * True to turn on fade animation. Defaults to false (ClearType/scrollbar flicker issues in IE7).
32125         */
32126        animate : false,
32127
32128        /**
32129         * @cfg {String} title
32130         * Title text to display (defaults to '').  This can be any valid HTML markup.
32131         */
32132         title: '',
32133        /**
32134         * @cfg {String} text
32135         * Body text to display (defaults to '').  This can be any valid HTML markup.
32136         */
32137         text : '',
32138        /**
32139         * @cfg {String} cls
32140         * A CSS class to apply to the base quick tip element (defaults to '').
32141         */
32142         cls : '',
32143        /**
32144         * @cfg {Number} width
32145         * Width in pixels of the quick tip (defaults to auto).  Width will be ignored if it exceeds the bounds of
32146         * minWidth or maxWidth.
32147         */
32148         width : null,
32149
32150     /**
32151      * Initialize and enable QuickTips for first use.  This should be called once before the first attempt to access
32152      * or display QuickTips in a page.
32153      */
32154        init : function(){
32155           tm = Roo.QuickTips;
32156           cfg = tm.tagConfig;
32157           if(!inited){
32158               if(!Roo.isReady){ // allow calling of init() before onReady
32159                   Roo.onReady(Roo.QuickTips.init, Roo.QuickTips);
32160                   return;
32161               }
32162               el = new Roo.Layer({cls:"x-tip", shadow:"drop", shim: true, constrain:true, shadowOffset:4});
32163               el.fxDefaults = {stopFx: true};
32164               // maximum custom styling
32165               //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>');
32166               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>');              
32167               tipTitle = el.child('h3');
32168               tipTitle.enableDisplayMode("block");
32169               tipBody = el.child('div.x-tip-bd');
32170               tipBodyText = el.child('div.x-tip-bd-inner');
32171               //bdLeft = el.child('div.x-tip-bd-left');
32172               //bdRight = el.child('div.x-tip-bd-right');
32173               close = el.child('div.x-tip-close');
32174               close.enableDisplayMode("block");
32175               close.on("click", hide);
32176               var d = Roo.get(document);
32177               d.on("mousedown", onDown);
32178               d.on("mouseover", onOver);
32179               d.on("mouseout", onOut);
32180               d.on("mousemove", onMove);
32181               esc = d.addKeyListener(27, hide);
32182               esc.disable();
32183               if(Roo.dd.DD){
32184                   dd = el.initDD("default", null, {
32185                       onDrag : function(){
32186                           el.sync();  
32187                       }
32188                   });
32189                   dd.setHandleElId(tipTitle.id);
32190                   dd.lock();
32191               }
32192               inited = true;
32193           }
32194           this.enable(); 
32195        },
32196
32197     /**
32198      * Configures a new quick tip instance and assigns it to a target element.  The following config options
32199      * are supported:
32200      * <pre>
32201 Property    Type                   Description
32202 ----------  ---------------------  ------------------------------------------------------------------------
32203 target      Element/String/Array   An Element, id or array of ids that this quick tip should be tied to
32204      * </ul>
32205      * @param {Object} config The config object
32206      */
32207        register : function(config){
32208            var cs = config instanceof Array ? config : arguments;
32209            for(var i = 0, len = cs.length; i < len; i++) {
32210                var c = cs[i];
32211                var target = c.target;
32212                if(target){
32213                    if(target instanceof Array){
32214                        for(var j = 0, jlen = target.length; j < jlen; j++){
32215                            tagEls[target[j]] = c;
32216                        }
32217                    }else{
32218                        tagEls[typeof target == 'string' ? target : Roo.id(target)] = c;
32219                    }
32220                }
32221            }
32222        },
32223
32224     /**
32225      * Removes this quick tip from its element and destroys it.
32226      * @param {String/HTMLElement/Element} el The element from which the quick tip is to be removed.
32227      */
32228        unregister : function(el){
32229            delete tagEls[Roo.id(el)];
32230        },
32231
32232     /**
32233      * Enable this quick tip.
32234      */
32235        enable : function(){
32236            if(inited && disabled){
32237                locks.pop();
32238                if(locks.length < 1){
32239                    disabled = false;
32240                }
32241            }
32242        },
32243
32244     /**
32245      * Disable this quick tip.
32246      */
32247        disable : function(){
32248           disabled = true;
32249           clearTimeout(showProc);
32250           clearTimeout(hideProc);
32251           clearTimeout(dismissProc);
32252           if(ce){
32253               hide(true);
32254           }
32255           locks.push(1);
32256        },
32257
32258     /**
32259      * Returns true if the quick tip is enabled, else false.
32260      */
32261        isEnabled : function(){
32262             return !disabled;
32263        },
32264
32265         // private
32266        tagConfig : {
32267            namespace : "ext",
32268            attribute : "qtip",
32269            width : "width",
32270            target : "target",
32271            title : "qtitle",
32272            hide : "hide",
32273            cls : "qclass"
32274        }
32275    };
32276 }();
32277
32278 // backwards compat
32279 Roo.QuickTips.tips = Roo.QuickTips.register;/*
32280  * Based on:
32281  * Ext JS Library 1.1.1
32282  * Copyright(c) 2006-2007, Ext JS, LLC.
32283  *
32284  * Originally Released Under LGPL - original licence link has changed is not relivant.
32285  *
32286  * Fork - LGPL
32287  * <script type="text/javascript">
32288  */
32289  
32290
32291 /**
32292  * @class Roo.tree.TreePanel
32293  * @extends Roo.data.Tree
32294
32295  * @cfg {Boolean} rootVisible false to hide the root node (defaults to true)
32296  * @cfg {Boolean} lines false to disable tree lines (defaults to true)
32297  * @cfg {Boolean} enableDD true to enable drag and drop
32298  * @cfg {Boolean} enableDrag true to enable just drag
32299  * @cfg {Boolean} enableDrop true to enable just drop
32300  * @cfg {Object} dragConfig Custom config to pass to the {@link Roo.tree.TreeDragZone} instance
32301  * @cfg {Object} dropConfig Custom config to pass to the {@link Roo.tree.TreeDropZone} instance
32302  * @cfg {String} ddGroup The DD group this TreePanel belongs to
32303  * @cfg {String} ddAppendOnly True if the tree should only allow append drops (use for trees which are sorted)
32304  * @cfg {Boolean} ddScroll true to enable YUI body scrolling
32305  * @cfg {Boolean} containerScroll true to register this container with ScrollManager
32306  * @cfg {Boolean} hlDrop false to disable node highlight on drop (defaults to the value of Roo.enableFx)
32307  * @cfg {String} hlColor The color of the node highlight (defaults to C3DAF9)
32308  * @cfg {Boolean} animate true to enable animated expand/collapse (defaults to the value of Roo.enableFx)
32309  * @cfg {Boolean} singleExpand true if only 1 node per branch may be expanded
32310  * @cfg {Boolean} selModel A tree selection model to use with this TreePanel (defaults to a {@link Roo.tree.DefaultSelectionModel})
32311  * @cfg {Boolean} loader A TreeLoader for use with this TreePanel
32312  * @cfg {Object|Roo.tree.TreeEditor} editor The TreeEditor or xtype data to display when clicked.
32313  * @cfg {String} pathSeparator The token used to separate sub-paths in path strings (defaults to '/')
32314  * @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>
32315  * @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>
32316  * 
32317  * @constructor
32318  * @param {String/HTMLElement/Element} el The container element
32319  * @param {Object} config
32320  */
32321 Roo.tree.TreePanel = function(el, config){
32322     var root = false;
32323     var loader = false;
32324     if (config.root) {
32325         root = config.root;
32326         delete config.root;
32327     }
32328     if (config.loader) {
32329         loader = config.loader;
32330         delete config.loader;
32331     }
32332     
32333     Roo.apply(this, config);
32334     Roo.tree.TreePanel.superclass.constructor.call(this);
32335     this.el = Roo.get(el);
32336     this.el.addClass('x-tree');
32337     //console.log(root);
32338     if (root) {
32339         this.setRootNode( Roo.factory(root, Roo.tree));
32340     }
32341     if (loader) {
32342         this.loader = Roo.factory(loader, Roo.tree);
32343     }
32344    /**
32345     * Read-only. The id of the container element becomes this TreePanel's id.
32346     */
32347     this.id = this.el.id;
32348     this.addEvents({
32349         /**
32350         * @event beforeload
32351         * Fires before a node is loaded, return false to cancel
32352         * @param {Node} node The node being loaded
32353         */
32354         "beforeload" : true,
32355         /**
32356         * @event load
32357         * Fires when a node is loaded
32358         * @param {Node} node The node that was loaded
32359         */
32360         "load" : true,
32361         /**
32362         * @event textchange
32363         * Fires when the text for a node is changed
32364         * @param {Node} node The node
32365         * @param {String} text The new text
32366         * @param {String} oldText The old text
32367         */
32368         "textchange" : true,
32369         /**
32370         * @event beforeexpand
32371         * Fires before a node is expanded, return false to cancel.
32372         * @param {Node} node The node
32373         * @param {Boolean} deep
32374         * @param {Boolean} anim
32375         */
32376         "beforeexpand" : true,
32377         /**
32378         * @event beforecollapse
32379         * Fires before a node is collapsed, return false to cancel.
32380         * @param {Node} node The node
32381         * @param {Boolean} deep
32382         * @param {Boolean} anim
32383         */
32384         "beforecollapse" : true,
32385         /**
32386         * @event expand
32387         * Fires when a node is expanded
32388         * @param {Node} node The node
32389         */
32390         "expand" : true,
32391         /**
32392         * @event disabledchange
32393         * Fires when the disabled status of a node changes
32394         * @param {Node} node The node
32395         * @param {Boolean} disabled
32396         */
32397         "disabledchange" : true,
32398         /**
32399         * @event collapse
32400         * Fires when a node is collapsed
32401         * @param {Node} node The node
32402         */
32403         "collapse" : true,
32404         /**
32405         * @event beforeclick
32406         * Fires before click processing on a node. Return false to cancel the default action.
32407         * @param {Node} node The node
32408         * @param {Roo.EventObject} e The event object
32409         */
32410         "beforeclick":true,
32411         /**
32412         * @event checkchange
32413         * Fires when a node with a checkbox's checked property changes
32414         * @param {Node} this This node
32415         * @param {Boolean} checked
32416         */
32417         "checkchange":true,
32418         /**
32419         * @event click
32420         * Fires when a node is clicked
32421         * @param {Node} node The node
32422         * @param {Roo.EventObject} e The event object
32423         */
32424         "click":true,
32425         /**
32426         * @event dblclick
32427         * Fires when a node is double clicked
32428         * @param {Node} node The node
32429         * @param {Roo.EventObject} e The event object
32430         */
32431         "dblclick":true,
32432         /**
32433         * @event contextmenu
32434         * Fires when a node is right clicked
32435         * @param {Node} node The node
32436         * @param {Roo.EventObject} e The event object
32437         */
32438         "contextmenu":true,
32439         /**
32440         * @event beforechildrenrendered
32441         * Fires right before the child nodes for a node are rendered
32442         * @param {Node} node The node
32443         */
32444         "beforechildrenrendered":true,
32445         /**
32446         * @event startdrag
32447         * Fires when a node starts being dragged
32448         * @param {Roo.tree.TreePanel} this
32449         * @param {Roo.tree.TreeNode} node
32450         * @param {event} e The raw browser event
32451         */ 
32452        "startdrag" : true,
32453        /**
32454         * @event enddrag
32455         * Fires when a drag operation is complete
32456         * @param {Roo.tree.TreePanel} this
32457         * @param {Roo.tree.TreeNode} node
32458         * @param {event} e The raw browser event
32459         */
32460        "enddrag" : true,
32461        /**
32462         * @event dragdrop
32463         * Fires when a dragged node is dropped on a valid DD target
32464         * @param {Roo.tree.TreePanel} this
32465         * @param {Roo.tree.TreeNode} node
32466         * @param {DD} dd The dd it was dropped on
32467         * @param {event} e The raw browser event
32468         */
32469        "dragdrop" : true,
32470        /**
32471         * @event beforenodedrop
32472         * Fires when a DD object is dropped on a node in this tree for preprocessing. Return false to cancel the drop. The dropEvent
32473         * passed to handlers has the following properties:<br />
32474         * <ul style="padding:5px;padding-left:16px;">
32475         * <li>tree - The TreePanel</li>
32476         * <li>target - The node being targeted for the drop</li>
32477         * <li>data - The drag data from the drag source</li>
32478         * <li>point - The point of the drop - append, above or below</li>
32479         * <li>source - The drag source</li>
32480         * <li>rawEvent - Raw mouse event</li>
32481         * <li>dropNode - Drop node(s) provided by the source <b>OR</b> you can supply node(s)
32482         * to be inserted by setting them on this object.</li>
32483         * <li>cancel - Set this to true to cancel the drop.</li>
32484         * </ul>
32485         * @param {Object} dropEvent
32486         */
32487        "beforenodedrop" : true,
32488        /**
32489         * @event nodedrop
32490         * Fires after a DD object is dropped on a node in this tree. The dropEvent
32491         * passed to handlers has the following properties:<br />
32492         * <ul style="padding:5px;padding-left:16px;">
32493         * <li>tree - The TreePanel</li>
32494         * <li>target - The node being targeted for the drop</li>
32495         * <li>data - The drag data from the drag source</li>
32496         * <li>point - The point of the drop - append, above or below</li>
32497         * <li>source - The drag source</li>
32498         * <li>rawEvent - Raw mouse event</li>
32499         * <li>dropNode - Dropped node(s).</li>
32500         * </ul>
32501         * @param {Object} dropEvent
32502         */
32503        "nodedrop" : true,
32504         /**
32505         * @event nodedragover
32506         * Fires when a tree node is being targeted for a drag drop, return false to signal drop not allowed. The dragOverEvent
32507         * passed to handlers has the following properties:<br />
32508         * <ul style="padding:5px;padding-left:16px;">
32509         * <li>tree - The TreePanel</li>
32510         * <li>target - The node being targeted for the drop</li>
32511         * <li>data - The drag data from the drag source</li>
32512         * <li>point - The point of the drop - append, above or below</li>
32513         * <li>source - The drag source</li>
32514         * <li>rawEvent - Raw mouse event</li>
32515         * <li>dropNode - Drop node(s) provided by the source.</li>
32516         * <li>cancel - Set this to true to signal drop not allowed.</li>
32517         * </ul>
32518         * @param {Object} dragOverEvent
32519         */
32520        "nodedragover" : true
32521         
32522     });
32523     if(this.singleExpand){
32524        this.on("beforeexpand", this.restrictExpand, this);
32525     }
32526     if (this.editor) {
32527         this.editor.tree = this;
32528         this.editor = Roo.factory(this.editor, Roo.tree);
32529     }
32530     
32531     if (this.selModel) {
32532         this.selModel = Roo.factory(this.selModel, Roo.tree);
32533     }
32534    
32535 };
32536 Roo.extend(Roo.tree.TreePanel, Roo.data.Tree, {
32537     rootVisible : true,
32538     animate: Roo.enableFx,
32539     lines : true,
32540     enableDD : false,
32541     hlDrop : Roo.enableFx,
32542   
32543     renderer: false,
32544     
32545     rendererTip: false,
32546     // private
32547     restrictExpand : function(node){
32548         var p = node.parentNode;
32549         if(p){
32550             if(p.expandedChild && p.expandedChild.parentNode == p){
32551                 p.expandedChild.collapse();
32552             }
32553             p.expandedChild = node;
32554         }
32555     },
32556
32557     // private override
32558     setRootNode : function(node){
32559         Roo.tree.TreePanel.superclass.setRootNode.call(this, node);
32560         if(!this.rootVisible){
32561             node.ui = new Roo.tree.RootTreeNodeUI(node);
32562         }
32563         return node;
32564     },
32565
32566     /**
32567      * Returns the container element for this TreePanel
32568      */
32569     getEl : function(){
32570         return this.el;
32571     },
32572
32573     /**
32574      * Returns the default TreeLoader for this TreePanel
32575      */
32576     getLoader : function(){
32577         return this.loader;
32578     },
32579
32580     /**
32581      * Expand all nodes
32582      */
32583     expandAll : function(){
32584         this.root.expand(true);
32585     },
32586
32587     /**
32588      * Collapse all nodes
32589      */
32590     collapseAll : function(){
32591         this.root.collapse(true);
32592     },
32593
32594     /**
32595      * Returns the selection model used by this TreePanel
32596      */
32597     getSelectionModel : function(){
32598         if(!this.selModel){
32599             this.selModel = new Roo.tree.DefaultSelectionModel();
32600         }
32601         return this.selModel;
32602     },
32603
32604     /**
32605      * Retrieve an array of checked nodes, or an array of a specific attribute of checked nodes (e.g. "id")
32606      * @param {String} attribute (optional) Defaults to null (return the actual nodes)
32607      * @param {TreeNode} startNode (optional) The node to start from, defaults to the root
32608      * @return {Array}
32609      */
32610     getChecked : function(a, startNode){
32611         startNode = startNode || this.root;
32612         var r = [];
32613         var f = function(){
32614             if(this.attributes.checked){
32615                 r.push(!a ? this : (a == 'id' ? this.id : this.attributes[a]));
32616             }
32617         }
32618         startNode.cascade(f);
32619         return r;
32620     },
32621
32622     /**
32623      * Expands a specified path in this TreePanel. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
32624      * @param {String} path
32625      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
32626      * @param {Function} callback (optional) The callback to call when the expand is complete. The callback will be called with
32627      * (bSuccess, oLastNode) where bSuccess is if the expand was successful and oLastNode is the last node that was expanded.
32628      */
32629     expandPath : function(path, attr, callback){
32630         attr = attr || "id";
32631         var keys = path.split(this.pathSeparator);
32632         var curNode = this.root;
32633         if(curNode.attributes[attr] != keys[1]){ // invalid root
32634             if(callback){
32635                 callback(false, null);
32636             }
32637             return;
32638         }
32639         var index = 1;
32640         var f = function(){
32641             if(++index == keys.length){
32642                 if(callback){
32643                     callback(true, curNode);
32644                 }
32645                 return;
32646             }
32647             var c = curNode.findChild(attr, keys[index]);
32648             if(!c){
32649                 if(callback){
32650                     callback(false, curNode);
32651                 }
32652                 return;
32653             }
32654             curNode = c;
32655             c.expand(false, false, f);
32656         };
32657         curNode.expand(false, false, f);
32658     },
32659
32660     /**
32661      * Selects the node in this tree at the specified path. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
32662      * @param {String} path
32663      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
32664      * @param {Function} callback (optional) The callback to call when the selection is complete. The callback will be called with
32665      * (bSuccess, oSelNode) where bSuccess is if the selection was successful and oSelNode is the selected node.
32666      */
32667     selectPath : function(path, attr, callback){
32668         attr = attr || "id";
32669         var keys = path.split(this.pathSeparator);
32670         var v = keys.pop();
32671         if(keys.length > 0){
32672             var f = function(success, node){
32673                 if(success && node){
32674                     var n = node.findChild(attr, v);
32675                     if(n){
32676                         n.select();
32677                         if(callback){
32678                             callback(true, n);
32679                         }
32680                     }else if(callback){
32681                         callback(false, n);
32682                     }
32683                 }else{
32684                     if(callback){
32685                         callback(false, n);
32686                     }
32687                 }
32688             };
32689             this.expandPath(keys.join(this.pathSeparator), attr, f);
32690         }else{
32691             this.root.select();
32692             if(callback){
32693                 callback(true, this.root);
32694             }
32695         }
32696     },
32697
32698     getTreeEl : function(){
32699         return this.el;
32700     },
32701
32702     /**
32703      * Trigger rendering of this TreePanel
32704      */
32705     render : function(){
32706         if (this.innerCt) {
32707             return this; // stop it rendering more than once!!
32708         }
32709         
32710         this.innerCt = this.el.createChild({tag:"ul",
32711                cls:"x-tree-root-ct " +
32712                (this.lines ? "x-tree-lines" : "x-tree-no-lines")});
32713
32714         if(this.containerScroll){
32715             Roo.dd.ScrollManager.register(this.el);
32716         }
32717         if((this.enableDD || this.enableDrop) && !this.dropZone){
32718            /**
32719             * The dropZone used by this tree if drop is enabled
32720             * @type Roo.tree.TreeDropZone
32721             */
32722              this.dropZone = new Roo.tree.TreeDropZone(this, this.dropConfig || {
32723                ddGroup: this.ddGroup || "TreeDD", appendOnly: this.ddAppendOnly === true
32724            });
32725         }
32726         if((this.enableDD || this.enableDrag) && !this.dragZone){
32727            /**
32728             * The dragZone used by this tree if drag is enabled
32729             * @type Roo.tree.TreeDragZone
32730             */
32731             this.dragZone = new Roo.tree.TreeDragZone(this, this.dragConfig || {
32732                ddGroup: this.ddGroup || "TreeDD",
32733                scroll: this.ddScroll
32734            });
32735         }
32736         this.getSelectionModel().init(this);
32737         if (!this.root) {
32738             Roo.log("ROOT not set in tree");
32739             return this;
32740         }
32741         this.root.render();
32742         if(!this.rootVisible){
32743             this.root.renderChildren();
32744         }
32745         return this;
32746     }
32747 });/*
32748  * Based on:
32749  * Ext JS Library 1.1.1
32750  * Copyright(c) 2006-2007, Ext JS, LLC.
32751  *
32752  * Originally Released Under LGPL - original licence link has changed is not relivant.
32753  *
32754  * Fork - LGPL
32755  * <script type="text/javascript">
32756  */
32757  
32758
32759 /**
32760  * @class Roo.tree.DefaultSelectionModel
32761  * @extends Roo.util.Observable
32762  * The default single selection for a TreePanel.
32763  * @param {Object} cfg Configuration
32764  */
32765 Roo.tree.DefaultSelectionModel = function(cfg){
32766    this.selNode = null;
32767    
32768    
32769    
32770    this.addEvents({
32771        /**
32772         * @event selectionchange
32773         * Fires when the selected node changes
32774         * @param {DefaultSelectionModel} this
32775         * @param {TreeNode} node the new selection
32776         */
32777        "selectionchange" : true,
32778
32779        /**
32780         * @event beforeselect
32781         * Fires before the selected node changes, return false to cancel the change
32782         * @param {DefaultSelectionModel} this
32783         * @param {TreeNode} node the new selection
32784         * @param {TreeNode} node the old selection
32785         */
32786        "beforeselect" : true
32787    });
32788    
32789     Roo.tree.DefaultSelectionModel.superclass.constructor.call(this,cfg);
32790 };
32791
32792 Roo.extend(Roo.tree.DefaultSelectionModel, Roo.util.Observable, {
32793     init : function(tree){
32794         this.tree = tree;
32795         tree.getTreeEl().on("keydown", this.onKeyDown, this);
32796         tree.on("click", this.onNodeClick, this);
32797     },
32798     
32799     onNodeClick : function(node, e){
32800         if (e.ctrlKey && this.selNode == node)  {
32801             this.unselect(node);
32802             return;
32803         }
32804         this.select(node);
32805     },
32806     
32807     /**
32808      * Select a node.
32809      * @param {TreeNode} node The node to select
32810      * @return {TreeNode} The selected node
32811      */
32812     select : function(node){
32813         var last = this.selNode;
32814         if(last != node && this.fireEvent('beforeselect', this, node, last) !== false){
32815             if(last){
32816                 last.ui.onSelectedChange(false);
32817             }
32818             this.selNode = node;
32819             node.ui.onSelectedChange(true);
32820             this.fireEvent("selectionchange", this, node, last);
32821         }
32822         return node;
32823     },
32824     
32825     /**
32826      * Deselect a node.
32827      * @param {TreeNode} node The node to unselect
32828      */
32829     unselect : function(node){
32830         if(this.selNode == node){
32831             this.clearSelections();
32832         }    
32833     },
32834     
32835     /**
32836      * Clear all selections
32837      */
32838     clearSelections : function(){
32839         var n = this.selNode;
32840         if(n){
32841             n.ui.onSelectedChange(false);
32842             this.selNode = null;
32843             this.fireEvent("selectionchange", this, null);
32844         }
32845         return n;
32846     },
32847     
32848     /**
32849      * Get the selected node
32850      * @return {TreeNode} The selected node
32851      */
32852     getSelectedNode : function(){
32853         return this.selNode;    
32854     },
32855     
32856     /**
32857      * Returns true if the node is selected
32858      * @param {TreeNode} node The node to check
32859      * @return {Boolean}
32860      */
32861     isSelected : function(node){
32862         return this.selNode == node;  
32863     },
32864
32865     /**
32866      * Selects the node above the selected node in the tree, intelligently walking the nodes
32867      * @return TreeNode The new selection
32868      */
32869     selectPrevious : function(){
32870         var s = this.selNode || this.lastSelNode;
32871         if(!s){
32872             return null;
32873         }
32874         var ps = s.previousSibling;
32875         if(ps){
32876             if(!ps.isExpanded() || ps.childNodes.length < 1){
32877                 return this.select(ps);
32878             } else{
32879                 var lc = ps.lastChild;
32880                 while(lc && lc.isExpanded() && lc.childNodes.length > 0){
32881                     lc = lc.lastChild;
32882                 }
32883                 return this.select(lc);
32884             }
32885         } else if(s.parentNode && (this.tree.rootVisible || !s.parentNode.isRoot)){
32886             return this.select(s.parentNode);
32887         }
32888         return null;
32889     },
32890
32891     /**
32892      * Selects the node above the selected node in the tree, intelligently walking the nodes
32893      * @return TreeNode The new selection
32894      */
32895     selectNext : function(){
32896         var s = this.selNode || this.lastSelNode;
32897         if(!s){
32898             return null;
32899         }
32900         if(s.firstChild && s.isExpanded()){
32901              return this.select(s.firstChild);
32902          }else if(s.nextSibling){
32903              return this.select(s.nextSibling);
32904          }else if(s.parentNode){
32905             var newS = null;
32906             s.parentNode.bubble(function(){
32907                 if(this.nextSibling){
32908                     newS = this.getOwnerTree().selModel.select(this.nextSibling);
32909                     return false;
32910                 }
32911             });
32912             return newS;
32913          }
32914         return null;
32915     },
32916
32917     onKeyDown : function(e){
32918         var s = this.selNode || this.lastSelNode;
32919         // undesirable, but required
32920         var sm = this;
32921         if(!s){
32922             return;
32923         }
32924         var k = e.getKey();
32925         switch(k){
32926              case e.DOWN:
32927                  e.stopEvent();
32928                  this.selectNext();
32929              break;
32930              case e.UP:
32931                  e.stopEvent();
32932                  this.selectPrevious();
32933              break;
32934              case e.RIGHT:
32935                  e.preventDefault();
32936                  if(s.hasChildNodes()){
32937                      if(!s.isExpanded()){
32938                          s.expand();
32939                      }else if(s.firstChild){
32940                          this.select(s.firstChild, e);
32941                      }
32942                  }
32943              break;
32944              case e.LEFT:
32945                  e.preventDefault();
32946                  if(s.hasChildNodes() && s.isExpanded()){
32947                      s.collapse();
32948                  }else if(s.parentNode && (this.tree.rootVisible || s.parentNode != this.tree.getRootNode())){
32949                      this.select(s.parentNode, e);
32950                  }
32951              break;
32952         };
32953     }
32954 });
32955
32956 /**
32957  * @class Roo.tree.MultiSelectionModel
32958  * @extends Roo.util.Observable
32959  * Multi selection for a TreePanel.
32960  * @param {Object} cfg Configuration
32961  */
32962 Roo.tree.MultiSelectionModel = function(){
32963    this.selNodes = [];
32964    this.selMap = {};
32965    this.addEvents({
32966        /**
32967         * @event selectionchange
32968         * Fires when the selected nodes change
32969         * @param {MultiSelectionModel} this
32970         * @param {Array} nodes Array of the selected nodes
32971         */
32972        "selectionchange" : true
32973    });
32974    Roo.tree.MultiSelectionModel.superclass.constructor.call(this,cfg);
32975    
32976 };
32977
32978 Roo.extend(Roo.tree.MultiSelectionModel, Roo.util.Observable, {
32979     init : function(tree){
32980         this.tree = tree;
32981         tree.getTreeEl().on("keydown", this.onKeyDown, this);
32982         tree.on("click", this.onNodeClick, this);
32983     },
32984     
32985     onNodeClick : function(node, e){
32986         this.select(node, e, e.ctrlKey);
32987     },
32988     
32989     /**
32990      * Select a node.
32991      * @param {TreeNode} node The node to select
32992      * @param {EventObject} e (optional) An event associated with the selection
32993      * @param {Boolean} keepExisting True to retain existing selections
32994      * @return {TreeNode} The selected node
32995      */
32996     select : function(node, e, keepExisting){
32997         if(keepExisting !== true){
32998             this.clearSelections(true);
32999         }
33000         if(this.isSelected(node)){
33001             this.lastSelNode = node;
33002             return node;
33003         }
33004         this.selNodes.push(node);
33005         this.selMap[node.id] = node;
33006         this.lastSelNode = node;
33007         node.ui.onSelectedChange(true);
33008         this.fireEvent("selectionchange", this, this.selNodes);
33009         return node;
33010     },
33011     
33012     /**
33013      * Deselect a node.
33014      * @param {TreeNode} node The node to unselect
33015      */
33016     unselect : function(node){
33017         if(this.selMap[node.id]){
33018             node.ui.onSelectedChange(false);
33019             var sn = this.selNodes;
33020             var index = -1;
33021             if(sn.indexOf){
33022                 index = sn.indexOf(node);
33023             }else{
33024                 for(var i = 0, len = sn.length; i < len; i++){
33025                     if(sn[i] == node){
33026                         index = i;
33027                         break;
33028                     }
33029                 }
33030             }
33031             if(index != -1){
33032                 this.selNodes.splice(index, 1);
33033             }
33034             delete this.selMap[node.id];
33035             this.fireEvent("selectionchange", this, this.selNodes);
33036         }
33037     },
33038     
33039     /**
33040      * Clear all selections
33041      */
33042     clearSelections : function(suppressEvent){
33043         var sn = this.selNodes;
33044         if(sn.length > 0){
33045             for(var i = 0, len = sn.length; i < len; i++){
33046                 sn[i].ui.onSelectedChange(false);
33047             }
33048             this.selNodes = [];
33049             this.selMap = {};
33050             if(suppressEvent !== true){
33051                 this.fireEvent("selectionchange", this, this.selNodes);
33052             }
33053         }
33054     },
33055     
33056     /**
33057      * Returns true if the node is selected
33058      * @param {TreeNode} node The node to check
33059      * @return {Boolean}
33060      */
33061     isSelected : function(node){
33062         return this.selMap[node.id] ? true : false;  
33063     },
33064     
33065     /**
33066      * Returns an array of the selected nodes
33067      * @return {Array}
33068      */
33069     getSelectedNodes : function(){
33070         return this.selNodes;    
33071     },
33072
33073     onKeyDown : Roo.tree.DefaultSelectionModel.prototype.onKeyDown,
33074
33075     selectNext : Roo.tree.DefaultSelectionModel.prototype.selectNext,
33076
33077     selectPrevious : Roo.tree.DefaultSelectionModel.prototype.selectPrevious
33078 });/*
33079  * Based on:
33080  * Ext JS Library 1.1.1
33081  * Copyright(c) 2006-2007, Ext JS, LLC.
33082  *
33083  * Originally Released Under LGPL - original licence link has changed is not relivant.
33084  *
33085  * Fork - LGPL
33086  * <script type="text/javascript">
33087  */
33088  
33089 /**
33090  * @class Roo.tree.TreeNode
33091  * @extends Roo.data.Node
33092  * @cfg {String} text The text for this node
33093  * @cfg {Boolean} expanded true to start the node expanded
33094  * @cfg {Boolean} allowDrag false to make this node undraggable if DD is on (defaults to true)
33095  * @cfg {Boolean} allowDrop false if this node cannot be drop on
33096  * @cfg {Boolean} disabled true to start the node disabled
33097  * @cfg {String} icon The path to an icon for the node. The preferred way to do this
33098  * is to use the cls or iconCls attributes and add the icon via a CSS background image.
33099  * @cfg {String} cls A css class to be added to the node
33100  * @cfg {String} iconCls A css class to be added to the nodes icon element for applying css background images
33101  * @cfg {String} href URL of the link used for the node (defaults to #)
33102  * @cfg {String} hrefTarget target frame for the link
33103  * @cfg {String} qtip An Ext QuickTip for the node
33104  * @cfg {String} qtipCfg An Ext QuickTip config for the node (used instead of qtip)
33105  * @cfg {Boolean} singleClickExpand True for single click expand on this node
33106  * @cfg {Function} uiProvider A UI <b>class</b> to use for this node (defaults to Roo.tree.TreeNodeUI)
33107  * @cfg {Boolean} checked True to render a checked checkbox for this node, false to render an unchecked checkbox
33108  * (defaults to undefined with no checkbox rendered)
33109  * @constructor
33110  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node
33111  */
33112 Roo.tree.TreeNode = function(attributes){
33113     attributes = attributes || {};
33114     if(typeof attributes == "string"){
33115         attributes = {text: attributes};
33116     }
33117     this.childrenRendered = false;
33118     this.rendered = false;
33119     Roo.tree.TreeNode.superclass.constructor.call(this, attributes);
33120     this.expanded = attributes.expanded === true;
33121     this.isTarget = attributes.isTarget !== false;
33122     this.draggable = attributes.draggable !== false && attributes.allowDrag !== false;
33123     this.allowChildren = attributes.allowChildren !== false && attributes.allowDrop !== false;
33124
33125     /**
33126      * Read-only. The text for this node. To change it use setText().
33127      * @type String
33128      */
33129     this.text = attributes.text;
33130     /**
33131      * True if this node is disabled.
33132      * @type Boolean
33133      */
33134     this.disabled = attributes.disabled === true;
33135
33136     this.addEvents({
33137         /**
33138         * @event textchange
33139         * Fires when the text for this node is changed
33140         * @param {Node} this This node
33141         * @param {String} text The new text
33142         * @param {String} oldText The old text
33143         */
33144         "textchange" : true,
33145         /**
33146         * @event beforeexpand
33147         * Fires before this node is expanded, return false to cancel.
33148         * @param {Node} this This node
33149         * @param {Boolean} deep
33150         * @param {Boolean} anim
33151         */
33152         "beforeexpand" : true,
33153         /**
33154         * @event beforecollapse
33155         * Fires before this node is collapsed, return false to cancel.
33156         * @param {Node} this This node
33157         * @param {Boolean} deep
33158         * @param {Boolean} anim
33159         */
33160         "beforecollapse" : true,
33161         /**
33162         * @event expand
33163         * Fires when this node is expanded
33164         * @param {Node} this This node
33165         */
33166         "expand" : true,
33167         /**
33168         * @event disabledchange
33169         * Fires when the disabled status of this node changes
33170         * @param {Node} this This node
33171         * @param {Boolean} disabled
33172         */
33173         "disabledchange" : true,
33174         /**
33175         * @event collapse
33176         * Fires when this node is collapsed
33177         * @param {Node} this This node
33178         */
33179         "collapse" : true,
33180         /**
33181         * @event beforeclick
33182         * Fires before click processing. Return false to cancel the default action.
33183         * @param {Node} this This node
33184         * @param {Roo.EventObject} e The event object
33185         */
33186         "beforeclick":true,
33187         /**
33188         * @event checkchange
33189         * Fires when a node with a checkbox's checked property changes
33190         * @param {Node} this This node
33191         * @param {Boolean} checked
33192         */
33193         "checkchange":true,
33194         /**
33195         * @event click
33196         * Fires when this node is clicked
33197         * @param {Node} this This node
33198         * @param {Roo.EventObject} e The event object
33199         */
33200         "click":true,
33201         /**
33202         * @event dblclick
33203         * Fires when this node is double clicked
33204         * @param {Node} this This node
33205         * @param {Roo.EventObject} e The event object
33206         */
33207         "dblclick":true,
33208         /**
33209         * @event contextmenu
33210         * Fires when this node is right clicked
33211         * @param {Node} this This node
33212         * @param {Roo.EventObject} e The event object
33213         */
33214         "contextmenu":true,
33215         /**
33216         * @event beforechildrenrendered
33217         * Fires right before the child nodes for this node are rendered
33218         * @param {Node} this This node
33219         */
33220         "beforechildrenrendered":true
33221     });
33222
33223     var uiClass = this.attributes.uiProvider || Roo.tree.TreeNodeUI;
33224
33225     /**
33226      * Read-only. The UI for this node
33227      * @type TreeNodeUI
33228      */
33229     this.ui = new uiClass(this);
33230     
33231     // finally support items[]
33232     if (typeof(this.attributes.items) == 'undefined' || !this.attributes.items) {
33233         return;
33234     }
33235     
33236     
33237     Roo.each(this.attributes.items, function(c) {
33238         this.appendChild(Roo.factory(c,Roo.Tree));
33239     }, this);
33240     delete this.attributes.items;
33241     
33242     
33243     
33244 };
33245 Roo.extend(Roo.tree.TreeNode, Roo.data.Node, {
33246     preventHScroll: true,
33247     /**
33248      * Returns true if this node is expanded
33249      * @return {Boolean}
33250      */
33251     isExpanded : function(){
33252         return this.expanded;
33253     },
33254
33255     /**
33256      * Returns the UI object for this node
33257      * @return {TreeNodeUI}
33258      */
33259     getUI : function(){
33260         return this.ui;
33261     },
33262
33263     // private override
33264     setFirstChild : function(node){
33265         var of = this.firstChild;
33266         Roo.tree.TreeNode.superclass.setFirstChild.call(this, node);
33267         if(this.childrenRendered && of && node != of){
33268             of.renderIndent(true, true);
33269         }
33270         if(this.rendered){
33271             this.renderIndent(true, true);
33272         }
33273     },
33274
33275     // private override
33276     setLastChild : function(node){
33277         var ol = this.lastChild;
33278         Roo.tree.TreeNode.superclass.setLastChild.call(this, node);
33279         if(this.childrenRendered && ol && node != ol){
33280             ol.renderIndent(true, true);
33281         }
33282         if(this.rendered){
33283             this.renderIndent(true, true);
33284         }
33285     },
33286
33287     // these methods are overridden to provide lazy rendering support
33288     // private override
33289     appendChild : function()
33290     {
33291         var node = Roo.tree.TreeNode.superclass.appendChild.apply(this, arguments);
33292         if(node && this.childrenRendered){
33293             node.render();
33294         }
33295         this.ui.updateExpandIcon();
33296         return node;
33297     },
33298
33299     // private override
33300     removeChild : function(node){
33301         this.ownerTree.getSelectionModel().unselect(node);
33302         Roo.tree.TreeNode.superclass.removeChild.apply(this, arguments);
33303         // if it's been rendered remove dom node
33304         if(this.childrenRendered){
33305             node.ui.remove();
33306         }
33307         if(this.childNodes.length < 1){
33308             this.collapse(false, false);
33309         }else{
33310             this.ui.updateExpandIcon();
33311         }
33312         if(!this.firstChild) {
33313             this.childrenRendered = false;
33314         }
33315         return node;
33316     },
33317
33318     // private override
33319     insertBefore : function(node, refNode){
33320         var newNode = Roo.tree.TreeNode.superclass.insertBefore.apply(this, arguments);
33321         if(newNode && refNode && this.childrenRendered){
33322             node.render();
33323         }
33324         this.ui.updateExpandIcon();
33325         return newNode;
33326     },
33327
33328     /**
33329      * Sets the text for this node
33330      * @param {String} text
33331      */
33332     setText : function(text){
33333         var oldText = this.text;
33334         this.text = text;
33335         this.attributes.text = text;
33336         if(this.rendered){ // event without subscribing
33337             this.ui.onTextChange(this, text, oldText);
33338         }
33339         this.fireEvent("textchange", this, text, oldText);
33340     },
33341
33342     /**
33343      * Triggers selection of this node
33344      */
33345     select : function(){
33346         this.getOwnerTree().getSelectionModel().select(this);
33347     },
33348
33349     /**
33350      * Triggers deselection of this node
33351      */
33352     unselect : function(){
33353         this.getOwnerTree().getSelectionModel().unselect(this);
33354     },
33355
33356     /**
33357      * Returns true if this node is selected
33358      * @return {Boolean}
33359      */
33360     isSelected : function(){
33361         return this.getOwnerTree().getSelectionModel().isSelected(this);
33362     },
33363
33364     /**
33365      * Expand this node.
33366      * @param {Boolean} deep (optional) True to expand all children as well
33367      * @param {Boolean} anim (optional) false to cancel the default animation
33368      * @param {Function} callback (optional) A callback to be called when
33369      * expanding this node completes (does not wait for deep expand to complete).
33370      * Called with 1 parameter, this node.
33371      */
33372     expand : function(deep, anim, callback){
33373         if(!this.expanded){
33374             if(this.fireEvent("beforeexpand", this, deep, anim) === false){
33375                 return;
33376             }
33377             if(!this.childrenRendered){
33378                 this.renderChildren();
33379             }
33380             this.expanded = true;
33381             if(!this.isHiddenRoot() && (this.getOwnerTree().animate && anim !== false) || anim){
33382                 this.ui.animExpand(function(){
33383                     this.fireEvent("expand", this);
33384                     if(typeof callback == "function"){
33385                         callback(this);
33386                     }
33387                     if(deep === true){
33388                         this.expandChildNodes(true);
33389                     }
33390                 }.createDelegate(this));
33391                 return;
33392             }else{
33393                 this.ui.expand();
33394                 this.fireEvent("expand", this);
33395                 if(typeof callback == "function"){
33396                     callback(this);
33397                 }
33398             }
33399         }else{
33400            if(typeof callback == "function"){
33401                callback(this);
33402            }
33403         }
33404         if(deep === true){
33405             this.expandChildNodes(true);
33406         }
33407     },
33408
33409     isHiddenRoot : function(){
33410         return this.isRoot && !this.getOwnerTree().rootVisible;
33411     },
33412
33413     /**
33414      * Collapse this node.
33415      * @param {Boolean} deep (optional) True to collapse all children as well
33416      * @param {Boolean} anim (optional) false to cancel the default animation
33417      */
33418     collapse : function(deep, anim){
33419         if(this.expanded && !this.isHiddenRoot()){
33420             if(this.fireEvent("beforecollapse", this, deep, anim) === false){
33421                 return;
33422             }
33423             this.expanded = false;
33424             if((this.getOwnerTree().animate && anim !== false) || anim){
33425                 this.ui.animCollapse(function(){
33426                     this.fireEvent("collapse", this);
33427                     if(deep === true){
33428                         this.collapseChildNodes(true);
33429                     }
33430                 }.createDelegate(this));
33431                 return;
33432             }else{
33433                 this.ui.collapse();
33434                 this.fireEvent("collapse", this);
33435             }
33436         }
33437         if(deep === true){
33438             var cs = this.childNodes;
33439             for(var i = 0, len = cs.length; i < len; i++) {
33440                 cs[i].collapse(true, false);
33441             }
33442         }
33443     },
33444
33445     // private
33446     delayedExpand : function(delay){
33447         if(!this.expandProcId){
33448             this.expandProcId = this.expand.defer(delay, this);
33449         }
33450     },
33451
33452     // private
33453     cancelExpand : function(){
33454         if(this.expandProcId){
33455             clearTimeout(this.expandProcId);
33456         }
33457         this.expandProcId = false;
33458     },
33459
33460     /**
33461      * Toggles expanded/collapsed state of the node
33462      */
33463     toggle : function(){
33464         if(this.expanded){
33465             this.collapse();
33466         }else{
33467             this.expand();
33468         }
33469     },
33470
33471     /**
33472      * Ensures all parent nodes are expanded
33473      */
33474     ensureVisible : function(callback){
33475         var tree = this.getOwnerTree();
33476         tree.expandPath(this.parentNode.getPath(), false, function(){
33477             tree.getTreeEl().scrollChildIntoView(this.ui.anchor);
33478             Roo.callback(callback);
33479         }.createDelegate(this));
33480     },
33481
33482     /**
33483      * Expand all child nodes
33484      * @param {Boolean} deep (optional) true if the child nodes should also expand their child nodes
33485      */
33486     expandChildNodes : function(deep){
33487         var cs = this.childNodes;
33488         for(var i = 0, len = cs.length; i < len; i++) {
33489                 cs[i].expand(deep);
33490         }
33491     },
33492
33493     /**
33494      * Collapse all child nodes
33495      * @param {Boolean} deep (optional) true if the child nodes should also collapse their child nodes
33496      */
33497     collapseChildNodes : function(deep){
33498         var cs = this.childNodes;
33499         for(var i = 0, len = cs.length; i < len; i++) {
33500                 cs[i].collapse(deep);
33501         }
33502     },
33503
33504     /**
33505      * Disables this node
33506      */
33507     disable : function(){
33508         this.disabled = true;
33509         this.unselect();
33510         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
33511             this.ui.onDisableChange(this, true);
33512         }
33513         this.fireEvent("disabledchange", this, true);
33514     },
33515
33516     /**
33517      * Enables this node
33518      */
33519     enable : function(){
33520         this.disabled = false;
33521         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
33522             this.ui.onDisableChange(this, false);
33523         }
33524         this.fireEvent("disabledchange", this, false);
33525     },
33526
33527     // private
33528     renderChildren : function(suppressEvent){
33529         if(suppressEvent !== false){
33530             this.fireEvent("beforechildrenrendered", this);
33531         }
33532         var cs = this.childNodes;
33533         for(var i = 0, len = cs.length; i < len; i++){
33534             cs[i].render(true);
33535         }
33536         this.childrenRendered = true;
33537     },
33538
33539     // private
33540     sort : function(fn, scope){
33541         Roo.tree.TreeNode.superclass.sort.apply(this, arguments);
33542         if(this.childrenRendered){
33543             var cs = this.childNodes;
33544             for(var i = 0, len = cs.length; i < len; i++){
33545                 cs[i].render(true);
33546             }
33547         }
33548     },
33549
33550     // private
33551     render : function(bulkRender){
33552         this.ui.render(bulkRender);
33553         if(!this.rendered){
33554             this.rendered = true;
33555             if(this.expanded){
33556                 this.expanded = false;
33557                 this.expand(false, false);
33558             }
33559         }
33560     },
33561
33562     // private
33563     renderIndent : function(deep, refresh){
33564         if(refresh){
33565             this.ui.childIndent = null;
33566         }
33567         this.ui.renderIndent();
33568         if(deep === true && this.childrenRendered){
33569             var cs = this.childNodes;
33570             for(var i = 0, len = cs.length; i < len; i++){
33571                 cs[i].renderIndent(true, refresh);
33572             }
33573         }
33574     }
33575 });/*
33576  * Based on:
33577  * Ext JS Library 1.1.1
33578  * Copyright(c) 2006-2007, Ext JS, LLC.
33579  *
33580  * Originally Released Under LGPL - original licence link has changed is not relivant.
33581  *
33582  * Fork - LGPL
33583  * <script type="text/javascript">
33584  */
33585  
33586 /**
33587  * @class Roo.tree.AsyncTreeNode
33588  * @extends Roo.tree.TreeNode
33589  * @cfg {TreeLoader} loader A TreeLoader to be used by this node (defaults to the loader defined on the tree)
33590  * @constructor
33591  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node 
33592  */
33593  Roo.tree.AsyncTreeNode = function(config){
33594     this.loaded = false;
33595     this.loading = false;
33596     Roo.tree.AsyncTreeNode.superclass.constructor.apply(this, arguments);
33597     /**
33598     * @event beforeload
33599     * Fires before this node is loaded, return false to cancel
33600     * @param {Node} this This node
33601     */
33602     this.addEvents({'beforeload':true, 'load': true});
33603     /**
33604     * @event load
33605     * Fires when this node is loaded
33606     * @param {Node} this This node
33607     */
33608     /**
33609      * The loader used by this node (defaults to using the tree's defined loader)
33610      * @type TreeLoader
33611      * @property loader
33612      */
33613 };
33614 Roo.extend(Roo.tree.AsyncTreeNode, Roo.tree.TreeNode, {
33615     expand : function(deep, anim, callback){
33616         if(this.loading){ // if an async load is already running, waiting til it's done
33617             var timer;
33618             var f = function(){
33619                 if(!this.loading){ // done loading
33620                     clearInterval(timer);
33621                     this.expand(deep, anim, callback);
33622                 }
33623             }.createDelegate(this);
33624             timer = setInterval(f, 200);
33625             return;
33626         }
33627         if(!this.loaded){
33628             if(this.fireEvent("beforeload", this) === false){
33629                 return;
33630             }
33631             this.loading = true;
33632             this.ui.beforeLoad(this);
33633             var loader = this.loader || this.attributes.loader || this.getOwnerTree().getLoader();
33634             if(loader){
33635                 loader.load(this, this.loadComplete.createDelegate(this, [deep, anim, callback]));
33636                 return;
33637             }
33638         }
33639         Roo.tree.AsyncTreeNode.superclass.expand.call(this, deep, anim, callback);
33640     },
33641     
33642     /**
33643      * Returns true if this node is currently loading
33644      * @return {Boolean}
33645      */
33646     isLoading : function(){
33647         return this.loading;  
33648     },
33649     
33650     loadComplete : function(deep, anim, callback){
33651         this.loading = false;
33652         this.loaded = true;
33653         this.ui.afterLoad(this);
33654         this.fireEvent("load", this);
33655         this.expand(deep, anim, callback);
33656     },
33657     
33658     /**
33659      * Returns true if this node has been loaded
33660      * @return {Boolean}
33661      */
33662     isLoaded : function(){
33663         return this.loaded;
33664     },
33665     
33666     hasChildNodes : function(){
33667         if(!this.isLeaf() && !this.loaded){
33668             return true;
33669         }else{
33670             return Roo.tree.AsyncTreeNode.superclass.hasChildNodes.call(this);
33671         }
33672     },
33673
33674     /**
33675      * Trigger a reload for this node
33676      * @param {Function} callback
33677      */
33678     reload : function(callback){
33679         this.collapse(false, false);
33680         while(this.firstChild){
33681             this.removeChild(this.firstChild);
33682         }
33683         this.childrenRendered = false;
33684         this.loaded = false;
33685         if(this.isHiddenRoot()){
33686             this.expanded = false;
33687         }
33688         this.expand(false, false, callback);
33689     }
33690 });/*
33691  * Based on:
33692  * Ext JS Library 1.1.1
33693  * Copyright(c) 2006-2007, Ext JS, LLC.
33694  *
33695  * Originally Released Under LGPL - original licence link has changed is not relivant.
33696  *
33697  * Fork - LGPL
33698  * <script type="text/javascript">
33699  */
33700  
33701 /**
33702  * @class Roo.tree.TreeNodeUI
33703  * @constructor
33704  * @param {Object} node The node to render
33705  * The TreeNode UI implementation is separate from the
33706  * tree implementation. Unless you are customizing the tree UI,
33707  * you should never have to use this directly.
33708  */
33709 Roo.tree.TreeNodeUI = function(node){
33710     this.node = node;
33711     this.rendered = false;
33712     this.animating = false;
33713     this.emptyIcon = Roo.BLANK_IMAGE_URL;
33714 };
33715
33716 Roo.tree.TreeNodeUI.prototype = {
33717     removeChild : function(node){
33718         if(this.rendered){
33719             this.ctNode.removeChild(node.ui.getEl());
33720         }
33721     },
33722
33723     beforeLoad : function(){
33724          this.addClass("x-tree-node-loading");
33725     },
33726
33727     afterLoad : function(){
33728          this.removeClass("x-tree-node-loading");
33729     },
33730
33731     onTextChange : function(node, text, oldText){
33732         if(this.rendered){
33733             this.textNode.innerHTML = text;
33734         }
33735     },
33736
33737     onDisableChange : function(node, state){
33738         this.disabled = state;
33739         if(state){
33740             this.addClass("x-tree-node-disabled");
33741         }else{
33742             this.removeClass("x-tree-node-disabled");
33743         }
33744     },
33745
33746     onSelectedChange : function(state){
33747         if(state){
33748             this.focus();
33749             this.addClass("x-tree-selected");
33750         }else{
33751             //this.blur();
33752             this.removeClass("x-tree-selected");
33753         }
33754     },
33755
33756     onMove : function(tree, node, oldParent, newParent, index, refNode){
33757         this.childIndent = null;
33758         if(this.rendered){
33759             var targetNode = newParent.ui.getContainer();
33760             if(!targetNode){//target not rendered
33761                 this.holder = document.createElement("div");
33762                 this.holder.appendChild(this.wrap);
33763                 return;
33764             }
33765             var insertBefore = refNode ? refNode.ui.getEl() : null;
33766             if(insertBefore){
33767                 targetNode.insertBefore(this.wrap, insertBefore);
33768             }else{
33769                 targetNode.appendChild(this.wrap);
33770             }
33771             this.node.renderIndent(true);
33772         }
33773     },
33774
33775     addClass : function(cls){
33776         if(this.elNode){
33777             Roo.fly(this.elNode).addClass(cls);
33778         }
33779     },
33780
33781     removeClass : function(cls){
33782         if(this.elNode){
33783             Roo.fly(this.elNode).removeClass(cls);
33784         }
33785     },
33786
33787     remove : function(){
33788         if(this.rendered){
33789             this.holder = document.createElement("div");
33790             this.holder.appendChild(this.wrap);
33791         }
33792     },
33793
33794     fireEvent : function(){
33795         return this.node.fireEvent.apply(this.node, arguments);
33796     },
33797
33798     initEvents : function(){
33799         this.node.on("move", this.onMove, this);
33800         var E = Roo.EventManager;
33801         var a = this.anchor;
33802
33803         var el = Roo.fly(a, '_treeui');
33804
33805         if(Roo.isOpera){ // opera render bug ignores the CSS
33806             el.setStyle("text-decoration", "none");
33807         }
33808
33809         el.on("click", this.onClick, this);
33810         el.on("dblclick", this.onDblClick, this);
33811
33812         if(this.checkbox){
33813             Roo.EventManager.on(this.checkbox,
33814                     Roo.isIE ? 'click' : 'change', this.onCheckChange, this);
33815         }
33816
33817         el.on("contextmenu", this.onContextMenu, this);
33818
33819         var icon = Roo.fly(this.iconNode);
33820         icon.on("click", this.onClick, this);
33821         icon.on("dblclick", this.onDblClick, this);
33822         icon.on("contextmenu", this.onContextMenu, this);
33823         E.on(this.ecNode, "click", this.ecClick, this, true);
33824
33825         if(this.node.disabled){
33826             this.addClass("x-tree-node-disabled");
33827         }
33828         if(this.node.hidden){
33829             this.addClass("x-tree-node-disabled");
33830         }
33831         var ot = this.node.getOwnerTree();
33832         var dd = ot.enableDD || ot.enableDrag || ot.enableDrop;
33833         if(dd && (!this.node.isRoot || ot.rootVisible)){
33834             Roo.dd.Registry.register(this.elNode, {
33835                 node: this.node,
33836                 handles: this.getDDHandles(),
33837                 isHandle: false
33838             });
33839         }
33840     },
33841
33842     getDDHandles : function(){
33843         return [this.iconNode, this.textNode];
33844     },
33845
33846     hide : function(){
33847         if(this.rendered){
33848             this.wrap.style.display = "none";
33849         }
33850     },
33851
33852     show : function(){
33853         if(this.rendered){
33854             this.wrap.style.display = "";
33855         }
33856     },
33857
33858     onContextMenu : function(e){
33859         if (this.node.hasListener("contextmenu") || this.node.getOwnerTree().hasListener("contextmenu")) {
33860             e.preventDefault();
33861             this.focus();
33862             this.fireEvent("contextmenu", this.node, e);
33863         }
33864     },
33865
33866     onClick : function(e){
33867         if(this.dropping){
33868             e.stopEvent();
33869             return;
33870         }
33871         if(this.fireEvent("beforeclick", this.node, e) !== false){
33872             if(!this.disabled && this.node.attributes.href){
33873                 this.fireEvent("click", this.node, e);
33874                 return;
33875             }
33876             e.preventDefault();
33877             if(this.disabled){
33878                 return;
33879             }
33880
33881             if(this.node.attributes.singleClickExpand && !this.animating && this.node.hasChildNodes()){
33882                 this.node.toggle();
33883             }
33884
33885             this.fireEvent("click", this.node, e);
33886         }else{
33887             e.stopEvent();
33888         }
33889     },
33890
33891     onDblClick : function(e){
33892         e.preventDefault();
33893         if(this.disabled){
33894             return;
33895         }
33896         if(this.checkbox){
33897             this.toggleCheck();
33898         }
33899         if(!this.animating && this.node.hasChildNodes()){
33900             this.node.toggle();
33901         }
33902         this.fireEvent("dblclick", this.node, e);
33903     },
33904
33905     onCheckChange : function(){
33906         var checked = this.checkbox.checked;
33907         this.node.attributes.checked = checked;
33908         this.fireEvent('checkchange', this.node, checked);
33909     },
33910
33911     ecClick : function(e){
33912         if(!this.animating && this.node.hasChildNodes()){
33913             this.node.toggle();
33914         }
33915     },
33916
33917     startDrop : function(){
33918         this.dropping = true;
33919     },
33920
33921     // delayed drop so the click event doesn't get fired on a drop
33922     endDrop : function(){
33923        setTimeout(function(){
33924            this.dropping = false;
33925        }.createDelegate(this), 50);
33926     },
33927
33928     expand : function(){
33929         this.updateExpandIcon();
33930         this.ctNode.style.display = "";
33931     },
33932
33933     focus : function(){
33934         if(!this.node.preventHScroll){
33935             try{this.anchor.focus();
33936             }catch(e){}
33937         }else if(!Roo.isIE){
33938             try{
33939                 var noscroll = this.node.getOwnerTree().getTreeEl().dom;
33940                 var l = noscroll.scrollLeft;
33941                 this.anchor.focus();
33942                 noscroll.scrollLeft = l;
33943             }catch(e){}
33944         }
33945     },
33946
33947     toggleCheck : function(value){
33948         var cb = this.checkbox;
33949         if(cb){
33950             cb.checked = (value === undefined ? !cb.checked : value);
33951         }
33952     },
33953
33954     blur : function(){
33955         try{
33956             this.anchor.blur();
33957         }catch(e){}
33958     },
33959
33960     animExpand : function(callback){
33961         var ct = Roo.get(this.ctNode);
33962         ct.stopFx();
33963         if(!this.node.hasChildNodes()){
33964             this.updateExpandIcon();
33965             this.ctNode.style.display = "";
33966             Roo.callback(callback);
33967             return;
33968         }
33969         this.animating = true;
33970         this.updateExpandIcon();
33971
33972         ct.slideIn('t', {
33973            callback : function(){
33974                this.animating = false;
33975                Roo.callback(callback);
33976             },
33977             scope: this,
33978             duration: this.node.ownerTree.duration || .25
33979         });
33980     },
33981
33982     highlight : function(){
33983         var tree = this.node.getOwnerTree();
33984         Roo.fly(this.wrap).highlight(
33985             tree.hlColor || "C3DAF9",
33986             {endColor: tree.hlBaseColor}
33987         );
33988     },
33989
33990     collapse : function(){
33991         this.updateExpandIcon();
33992         this.ctNode.style.display = "none";
33993     },
33994
33995     animCollapse : function(callback){
33996         var ct = Roo.get(this.ctNode);
33997         ct.enableDisplayMode('block');
33998         ct.stopFx();
33999
34000         this.animating = true;
34001         this.updateExpandIcon();
34002
34003         ct.slideOut('t', {
34004             callback : function(){
34005                this.animating = false;
34006                Roo.callback(callback);
34007             },
34008             scope: this,
34009             duration: this.node.ownerTree.duration || .25
34010         });
34011     },
34012
34013     getContainer : function(){
34014         return this.ctNode;
34015     },
34016
34017     getEl : function(){
34018         return this.wrap;
34019     },
34020
34021     appendDDGhost : function(ghostNode){
34022         ghostNode.appendChild(this.elNode.cloneNode(true));
34023     },
34024
34025     getDDRepairXY : function(){
34026         return Roo.lib.Dom.getXY(this.iconNode);
34027     },
34028
34029     onRender : function(){
34030         this.render();
34031     },
34032
34033     render : function(bulkRender){
34034         var n = this.node, a = n.attributes;
34035         var targetNode = n.parentNode ?
34036               n.parentNode.ui.getContainer() : n.ownerTree.innerCt.dom;
34037
34038         if(!this.rendered){
34039             this.rendered = true;
34040
34041             this.renderElements(n, a, targetNode, bulkRender);
34042
34043             if(a.qtip){
34044                if(this.textNode.setAttributeNS){
34045                    this.textNode.setAttributeNS("ext", "qtip", a.qtip);
34046                    if(a.qtipTitle){
34047                        this.textNode.setAttributeNS("ext", "qtitle", a.qtipTitle);
34048                    }
34049                }else{
34050                    this.textNode.setAttribute("ext:qtip", a.qtip);
34051                    if(a.qtipTitle){
34052                        this.textNode.setAttribute("ext:qtitle", a.qtipTitle);
34053                    }
34054                }
34055             }else if(a.qtipCfg){
34056                 a.qtipCfg.target = Roo.id(this.textNode);
34057                 Roo.QuickTips.register(a.qtipCfg);
34058             }
34059             this.initEvents();
34060             if(!this.node.expanded){
34061                 this.updateExpandIcon();
34062             }
34063         }else{
34064             if(bulkRender === true) {
34065                 targetNode.appendChild(this.wrap);
34066             }
34067         }
34068     },
34069
34070     renderElements : function(n, a, targetNode, bulkRender)
34071     {
34072         // add some indent caching, this helps performance when rendering a large tree
34073         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
34074         var t = n.getOwnerTree();
34075         var txt = t.renderer ? t.renderer(n.attributes) : Roo.util.Format.htmlEncode(n.text);
34076         if (typeof(n.attributes.html) != 'undefined') {
34077             txt = n.attributes.html;
34078         }
34079         var tip = t.rendererTip ? t.rendererTip(n.attributes) : txt;
34080         var cb = typeof a.checked == 'boolean';
34081         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
34082         var buf = ['<li class="x-tree-node"><div class="x-tree-node-el ', a.cls,'">',
34083             '<span class="x-tree-node-indent">',this.indentMarkup,"</span>",
34084             '<img src="', this.emptyIcon, '" class="x-tree-ec-icon" />',
34085             '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',(a.icon ? " x-tree-node-inline-icon" : ""),(a.iconCls ? " "+a.iconCls : ""),'" unselectable="on" />',
34086             cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + (a.checked ? 'checked="checked" />' : ' />')) : '',
34087             '<a hidefocus="on" href="',href,'" tabIndex="1" ',
34088              a.hrefTarget ? ' target="'+a.hrefTarget+'"' : "", 
34089                 '><span unselectable="on" qtip="' , tip ,'">',txt,"</span></a></div>",
34090             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
34091             "</li>"];
34092
34093         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
34094             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
34095                                 n.nextSibling.ui.getEl(), buf.join(""));
34096         }else{
34097             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
34098         }
34099
34100         this.elNode = this.wrap.childNodes[0];
34101         this.ctNode = this.wrap.childNodes[1];
34102         var cs = this.elNode.childNodes;
34103         this.indentNode = cs[0];
34104         this.ecNode = cs[1];
34105         this.iconNode = cs[2];
34106         var index = 3;
34107         if(cb){
34108             this.checkbox = cs[3];
34109             index++;
34110         }
34111         this.anchor = cs[index];
34112         this.textNode = cs[index].firstChild;
34113     },
34114
34115     getAnchor : function(){
34116         return this.anchor;
34117     },
34118
34119     getTextEl : function(){
34120         return this.textNode;
34121     },
34122
34123     getIconEl : function(){
34124         return this.iconNode;
34125     },
34126
34127     isChecked : function(){
34128         return this.checkbox ? this.checkbox.checked : false;
34129     },
34130
34131     updateExpandIcon : function(){
34132         if(this.rendered){
34133             var n = this.node, c1, c2;
34134             var cls = n.isLast() ? "x-tree-elbow-end" : "x-tree-elbow";
34135             var hasChild = n.hasChildNodes();
34136             if(hasChild){
34137                 if(n.expanded){
34138                     cls += "-minus";
34139                     c1 = "x-tree-node-collapsed";
34140                     c2 = "x-tree-node-expanded";
34141                 }else{
34142                     cls += "-plus";
34143                     c1 = "x-tree-node-expanded";
34144                     c2 = "x-tree-node-collapsed";
34145                 }
34146                 if(this.wasLeaf){
34147                     this.removeClass("x-tree-node-leaf");
34148                     this.wasLeaf = false;
34149                 }
34150                 if(this.c1 != c1 || this.c2 != c2){
34151                     Roo.fly(this.elNode).replaceClass(c1, c2);
34152                     this.c1 = c1; this.c2 = c2;
34153                 }
34154             }else{
34155                 // this changes non-leafs into leafs if they have no children.
34156                 // it's not very rational behaviour..
34157                 
34158                 if(!this.wasLeaf && this.node.leaf){
34159                     Roo.fly(this.elNode).replaceClass("x-tree-node-expanded", "x-tree-node-leaf");
34160                     delete this.c1;
34161                     delete this.c2;
34162                     this.wasLeaf = true;
34163                 }
34164             }
34165             var ecc = "x-tree-ec-icon "+cls;
34166             if(this.ecc != ecc){
34167                 this.ecNode.className = ecc;
34168                 this.ecc = ecc;
34169             }
34170         }
34171     },
34172
34173     getChildIndent : function(){
34174         if(!this.childIndent){
34175             var buf = [];
34176             var p = this.node;
34177             while(p){
34178                 if(!p.isRoot || (p.isRoot && p.ownerTree.rootVisible)){
34179                     if(!p.isLast()) {
34180                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-elbow-line" />');
34181                     } else {
34182                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-icon" />');
34183                     }
34184                 }
34185                 p = p.parentNode;
34186             }
34187             this.childIndent = buf.join("");
34188         }
34189         return this.childIndent;
34190     },
34191
34192     renderIndent : function(){
34193         if(this.rendered){
34194             var indent = "";
34195             var p = this.node.parentNode;
34196             if(p){
34197                 indent = p.ui.getChildIndent();
34198             }
34199             if(this.indentMarkup != indent){ // don't rerender if not required
34200                 this.indentNode.innerHTML = indent;
34201                 this.indentMarkup = indent;
34202             }
34203             this.updateExpandIcon();
34204         }
34205     }
34206 };
34207
34208 Roo.tree.RootTreeNodeUI = function(){
34209     Roo.tree.RootTreeNodeUI.superclass.constructor.apply(this, arguments);
34210 };
34211 Roo.extend(Roo.tree.RootTreeNodeUI, Roo.tree.TreeNodeUI, {
34212     render : function(){
34213         if(!this.rendered){
34214             var targetNode = this.node.ownerTree.innerCt.dom;
34215             this.node.expanded = true;
34216             targetNode.innerHTML = '<div class="x-tree-root-node"></div>';
34217             this.wrap = this.ctNode = targetNode.firstChild;
34218         }
34219     },
34220     collapse : function(){
34221     },
34222     expand : function(){
34223     }
34224 });/*
34225  * Based on:
34226  * Ext JS Library 1.1.1
34227  * Copyright(c) 2006-2007, Ext JS, LLC.
34228  *
34229  * Originally Released Under LGPL - original licence link has changed is not relivant.
34230  *
34231  * Fork - LGPL
34232  * <script type="text/javascript">
34233  */
34234 /**
34235  * @class Roo.tree.TreeLoader
34236  * @extends Roo.util.Observable
34237  * A TreeLoader provides for lazy loading of an {@link Roo.tree.TreeNode}'s child
34238  * nodes from a specified URL. The response must be a javascript Array definition
34239  * who's elements are node definition objects. eg:
34240  * <pre><code>
34241 {  success : true,
34242    data :      [
34243    
34244     { 'id': 1, 'text': 'A folder Node', 'leaf': false },
34245     { 'id': 2, 'text': 'A leaf Node', 'leaf': true }
34246     ]
34247 }
34248
34249
34250 </code></pre>
34251  * <br><br>
34252  * The old style respose with just an array is still supported, but not recommended.
34253  * <br><br>
34254  *
34255  * A server request is sent, and child nodes are loaded only when a node is expanded.
34256  * The loading node's id is passed to the server under the parameter name "node" to
34257  * enable the server to produce the correct child nodes.
34258  * <br><br>
34259  * To pass extra parameters, an event handler may be attached to the "beforeload"
34260  * event, and the parameters specified in the TreeLoader's baseParams property:
34261  * <pre><code>
34262     myTreeLoader.on("beforeload", function(treeLoader, node) {
34263         this.baseParams.category = node.attributes.category;
34264     }, this);
34265 </code></pre><
34266  * This would pass an HTTP parameter called "category" to the server containing
34267  * the value of the Node's "category" attribute.
34268  * @constructor
34269  * Creates a new Treeloader.
34270  * @param {Object} config A config object containing config properties.
34271  */
34272 Roo.tree.TreeLoader = function(config){
34273     this.baseParams = {};
34274     this.requestMethod = "POST";
34275     Roo.apply(this, config);
34276
34277     this.addEvents({
34278     
34279         /**
34280          * @event beforeload
34281          * Fires before a network request is made to retrieve the Json text which specifies a node's children.
34282          * @param {Object} This TreeLoader object.
34283          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
34284          * @param {Object} callback The callback function specified in the {@link #load} call.
34285          */
34286         beforeload : true,
34287         /**
34288          * @event load
34289          * Fires when the node has been successfuly loaded.
34290          * @param {Object} This TreeLoader object.
34291          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
34292          * @param {Object} response The response object containing the data from the server.
34293          */
34294         load : true,
34295         /**
34296          * @event loadexception
34297          * Fires if the network request failed.
34298          * @param {Object} This TreeLoader object.
34299          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
34300          * @param {Object} response The response object containing the data from the server.
34301          */
34302         loadexception : true,
34303         /**
34304          * @event create
34305          * Fires before a node is created, enabling you to return custom Node types 
34306          * @param {Object} This TreeLoader object.
34307          * @param {Object} attr - the data returned from the AJAX call (modify it to suit)
34308          */
34309         create : true
34310     });
34311
34312     Roo.tree.TreeLoader.superclass.constructor.call(this);
34313 };
34314
34315 Roo.extend(Roo.tree.TreeLoader, Roo.util.Observable, {
34316     /**
34317     * @cfg {String} dataUrl The URL from which to request a Json string which
34318     * specifies an array of node definition object representing the child nodes
34319     * to be loaded.
34320     */
34321     /**
34322     * @cfg {String} requestMethod either GET or POST
34323     * defaults to POST (due to BC)
34324     * to be loaded.
34325     */
34326     /**
34327     * @cfg {Object} baseParams (optional) An object containing properties which
34328     * specify HTTP parameters to be passed to each request for child nodes.
34329     */
34330     /**
34331     * @cfg {Object} baseAttrs (optional) An object containing attributes to be added to all nodes
34332     * created by this loader. If the attributes sent by the server have an attribute in this object,
34333     * they take priority.
34334     */
34335     /**
34336     * @cfg {Object} uiProviders (optional) An object containing properties which
34337     * 
34338     * DEPRECATED - use 'create' event handler to modify attributes - which affect creation.
34339     * specify custom {@link Roo.tree.TreeNodeUI} implementations. If the optional
34340     * <i>uiProvider</i> attribute of a returned child node is a string rather
34341     * than a reference to a TreeNodeUI implementation, this that string value
34342     * is used as a property name in the uiProviders object. You can define the provider named
34343     * 'default' , and this will be used for all nodes (if no uiProvider is delivered by the node data)
34344     */
34345     uiProviders : {},
34346
34347     /**
34348     * @cfg {Boolean} clearOnLoad (optional) Default to true. Remove previously existing
34349     * child nodes before loading.
34350     */
34351     clearOnLoad : true,
34352
34353     /**
34354     * @cfg {String} root (optional) Default to false. Use this to read data from an object 
34355     * property on loading, rather than expecting an array. (eg. more compatible to a standard
34356     * Grid query { data : [ .....] }
34357     */
34358     
34359     root : false,
34360      /**
34361     * @cfg {String} queryParam (optional) 
34362     * Name of the query as it will be passed on the querystring (defaults to 'node')
34363     * eg. the request will be ?node=[id]
34364     */
34365     
34366     
34367     queryParam: false,
34368     
34369     /**
34370      * Load an {@link Roo.tree.TreeNode} from the URL specified in the constructor.
34371      * This is called automatically when a node is expanded, but may be used to reload
34372      * a node (or append new children if the {@link #clearOnLoad} option is false.)
34373      * @param {Roo.tree.TreeNode} node
34374      * @param {Function} callback
34375      */
34376     load : function(node, callback){
34377         if(this.clearOnLoad){
34378             while(node.firstChild){
34379                 node.removeChild(node.firstChild);
34380             }
34381         }
34382         if(node.attributes.children){ // preloaded json children
34383             var cs = node.attributes.children;
34384             for(var i = 0, len = cs.length; i < len; i++){
34385                 node.appendChild(this.createNode(cs[i]));
34386             }
34387             if(typeof callback == "function"){
34388                 callback();
34389             }
34390         }else if(this.dataUrl){
34391             this.requestData(node, callback);
34392         }
34393     },
34394
34395     getParams: function(node){
34396         var buf = [], bp = this.baseParams;
34397         for(var key in bp){
34398             if(typeof bp[key] != "function"){
34399                 buf.push(encodeURIComponent(key), "=", encodeURIComponent(bp[key]), "&");
34400             }
34401         }
34402         var n = this.queryParam === false ? 'node' : this.queryParam;
34403         buf.push(n + "=", encodeURIComponent(node.id));
34404         return buf.join("");
34405     },
34406
34407     requestData : function(node, callback){
34408         if(this.fireEvent("beforeload", this, node, callback) !== false){
34409             this.transId = Roo.Ajax.request({
34410                 method:this.requestMethod,
34411                 url: this.dataUrl||this.url,
34412                 success: this.handleResponse,
34413                 failure: this.handleFailure,
34414                 scope: this,
34415                 argument: {callback: callback, node: node},
34416                 params: this.getParams(node)
34417             });
34418         }else{
34419             // if the load is cancelled, make sure we notify
34420             // the node that we are done
34421             if(typeof callback == "function"){
34422                 callback();
34423             }
34424         }
34425     },
34426
34427     isLoading : function(){
34428         return this.transId ? true : false;
34429     },
34430
34431     abort : function(){
34432         if(this.isLoading()){
34433             Roo.Ajax.abort(this.transId);
34434         }
34435     },
34436
34437     // private
34438     createNode : function(attr)
34439     {
34440         // apply baseAttrs, nice idea Corey!
34441         if(this.baseAttrs){
34442             Roo.applyIf(attr, this.baseAttrs);
34443         }
34444         if(this.applyLoader !== false){
34445             attr.loader = this;
34446         }
34447         // uiProvider = depreciated..
34448         
34449         if(typeof(attr.uiProvider) == 'string'){
34450            attr.uiProvider = this.uiProviders[attr.uiProvider] || 
34451                 /**  eval:var:attr */ eval(attr.uiProvider);
34452         }
34453         if(typeof(this.uiProviders['default']) != 'undefined') {
34454             attr.uiProvider = this.uiProviders['default'];
34455         }
34456         
34457         this.fireEvent('create', this, attr);
34458         
34459         attr.leaf  = typeof(attr.leaf) == 'string' ? attr.leaf * 1 : attr.leaf;
34460         return(attr.leaf ?
34461                         new Roo.tree.TreeNode(attr) :
34462                         new Roo.tree.AsyncTreeNode(attr));
34463     },
34464
34465     processResponse : function(response, node, callback)
34466     {
34467         var json = response.responseText;
34468         try {
34469             
34470             var o = Roo.decode(json);
34471             
34472             if (this.root === false && typeof(o.success) != undefined) {
34473                 this.root = 'data'; // the default behaviour for list like data..
34474                 }
34475                 
34476             if (this.root !== false &&  !o.success) {
34477                 // it's a failure condition.
34478                 var a = response.argument;
34479                 this.fireEvent("loadexception", this, a.node, response);
34480                 Roo.log("Load failed - should have a handler really");
34481                 return;
34482             }
34483             
34484             
34485             
34486             if (this.root !== false) {
34487                  o = o[this.root];
34488             }
34489             
34490             for(var i = 0, len = o.length; i < len; i++){
34491                 var n = this.createNode(o[i]);
34492                 if(n){
34493                     node.appendChild(n);
34494                 }
34495             }
34496             if(typeof callback == "function"){
34497                 callback(this, node);
34498             }
34499         }catch(e){
34500             this.handleFailure(response);
34501         }
34502     },
34503
34504     handleResponse : function(response){
34505         this.transId = false;
34506         var a = response.argument;
34507         this.processResponse(response, a.node, a.callback);
34508         this.fireEvent("load", this, a.node, response);
34509     },
34510
34511     handleFailure : function(response)
34512     {
34513         // should handle failure better..
34514         this.transId = false;
34515         var a = response.argument;
34516         this.fireEvent("loadexception", this, a.node, response);
34517         if(typeof a.callback == "function"){
34518             a.callback(this, a.node);
34519         }
34520     }
34521 });/*
34522  * Based on:
34523  * Ext JS Library 1.1.1
34524  * Copyright(c) 2006-2007, Ext JS, LLC.
34525  *
34526  * Originally Released Under LGPL - original licence link has changed is not relivant.
34527  *
34528  * Fork - LGPL
34529  * <script type="text/javascript">
34530  */
34531
34532 /**
34533 * @class Roo.tree.TreeFilter
34534 * Note this class is experimental and doesn't update the indent (lines) or expand collapse icons of the nodes
34535 * @param {TreePanel} tree
34536 * @param {Object} config (optional)
34537  */
34538 Roo.tree.TreeFilter = function(tree, config){
34539     this.tree = tree;
34540     this.filtered = {};
34541     Roo.apply(this, config);
34542 };
34543
34544 Roo.tree.TreeFilter.prototype = {
34545     clearBlank:false,
34546     reverse:false,
34547     autoClear:false,
34548     remove:false,
34549
34550      /**
34551      * Filter the data by a specific attribute.
34552      * @param {String/RegExp} value Either string that the attribute value
34553      * should start with or a RegExp to test against the attribute
34554      * @param {String} attr (optional) The attribute passed in your node's attributes collection. Defaults to "text".
34555      * @param {TreeNode} startNode (optional) The node to start the filter at.
34556      */
34557     filter : function(value, attr, startNode){
34558         attr = attr || "text";
34559         var f;
34560         if(typeof value == "string"){
34561             var vlen = value.length;
34562             // auto clear empty filter
34563             if(vlen == 0 && this.clearBlank){
34564                 this.clear();
34565                 return;
34566             }
34567             value = value.toLowerCase();
34568             f = function(n){
34569                 return n.attributes[attr].substr(0, vlen).toLowerCase() == value;
34570             };
34571         }else if(value.exec){ // regex?
34572             f = function(n){
34573                 return value.test(n.attributes[attr]);
34574             };
34575         }else{
34576             throw 'Illegal filter type, must be string or regex';
34577         }
34578         this.filterBy(f, null, startNode);
34579         },
34580
34581     /**
34582      * Filter by a function. The passed function will be called with each
34583      * node in the tree (or from the startNode). If the function returns true, the node is kept
34584      * otherwise it is filtered. If a node is filtered, its children are also filtered.
34585      * @param {Function} fn The filter function
34586      * @param {Object} scope (optional) The scope of the function (defaults to the current node)
34587      */
34588     filterBy : function(fn, scope, startNode){
34589         startNode = startNode || this.tree.root;
34590         if(this.autoClear){
34591             this.clear();
34592         }
34593         var af = this.filtered, rv = this.reverse;
34594         var f = function(n){
34595             if(n == startNode){
34596                 return true;
34597             }
34598             if(af[n.id]){
34599                 return false;
34600             }
34601             var m = fn.call(scope || n, n);
34602             if(!m || rv){
34603                 af[n.id] = n;
34604                 n.ui.hide();
34605                 return false;
34606             }
34607             return true;
34608         };
34609         startNode.cascade(f);
34610         if(this.remove){
34611            for(var id in af){
34612                if(typeof id != "function"){
34613                    var n = af[id];
34614                    if(n && n.parentNode){
34615                        n.parentNode.removeChild(n);
34616                    }
34617                }
34618            }
34619         }
34620     },
34621
34622     /**
34623      * Clears the current filter. Note: with the "remove" option
34624      * set a filter cannot be cleared.
34625      */
34626     clear : function(){
34627         var t = this.tree;
34628         var af = this.filtered;
34629         for(var id in af){
34630             if(typeof id != "function"){
34631                 var n = af[id];
34632                 if(n){
34633                     n.ui.show();
34634                 }
34635             }
34636         }
34637         this.filtered = {};
34638     }
34639 };
34640 /*
34641  * Based on:
34642  * Ext JS Library 1.1.1
34643  * Copyright(c) 2006-2007, Ext JS, LLC.
34644  *
34645  * Originally Released Under LGPL - original licence link has changed is not relivant.
34646  *
34647  * Fork - LGPL
34648  * <script type="text/javascript">
34649  */
34650  
34651
34652 /**
34653  * @class Roo.tree.TreeSorter
34654  * Provides sorting of nodes in a TreePanel
34655  * 
34656  * @cfg {Boolean} folderSort True to sort leaf nodes under non leaf nodes
34657  * @cfg {String} property The named attribute on the node to sort by (defaults to text)
34658  * @cfg {String} dir The direction to sort (asc or desc) (defaults to asc)
34659  * @cfg {String} leafAttr The attribute used to determine leaf nodes in folder sort (defaults to "leaf")
34660  * @cfg {Boolean} caseSensitive true for case sensitive sort (defaults to false)
34661  * @cfg {Function} sortType A custom "casting" function used to convert node values before sorting
34662  * @constructor
34663  * @param {TreePanel} tree
34664  * @param {Object} config
34665  */
34666 Roo.tree.TreeSorter = function(tree, config){
34667     Roo.apply(this, config);
34668     tree.on("beforechildrenrendered", this.doSort, this);
34669     tree.on("append", this.updateSort, this);
34670     tree.on("insert", this.updateSort, this);
34671     
34672     var dsc = this.dir && this.dir.toLowerCase() == "desc";
34673     var p = this.property || "text";
34674     var sortType = this.sortType;
34675     var fs = this.folderSort;
34676     var cs = this.caseSensitive === true;
34677     var leafAttr = this.leafAttr || 'leaf';
34678
34679     this.sortFn = function(n1, n2){
34680         if(fs){
34681             if(n1.attributes[leafAttr] && !n2.attributes[leafAttr]){
34682                 return 1;
34683             }
34684             if(!n1.attributes[leafAttr] && n2.attributes[leafAttr]){
34685                 return -1;
34686             }
34687         }
34688         var v1 = sortType ? sortType(n1) : (cs ? n1.attributes[p] : n1.attributes[p].toUpperCase());
34689         var v2 = sortType ? sortType(n2) : (cs ? n2.attributes[p] : n2.attributes[p].toUpperCase());
34690         if(v1 < v2){
34691                         return dsc ? +1 : -1;
34692                 }else if(v1 > v2){
34693                         return dsc ? -1 : +1;
34694         }else{
34695                 return 0;
34696         }
34697     };
34698 };
34699
34700 Roo.tree.TreeSorter.prototype = {
34701     doSort : function(node){
34702         node.sort(this.sortFn);
34703     },
34704     
34705     compareNodes : function(n1, n2){
34706         return (n1.text.toUpperCase() > n2.text.toUpperCase() ? 1 : -1);
34707     },
34708     
34709     updateSort : function(tree, node){
34710         if(node.childrenRendered){
34711             this.doSort.defer(1, this, [node]);
34712         }
34713     }
34714 };/*
34715  * Based on:
34716  * Ext JS Library 1.1.1
34717  * Copyright(c) 2006-2007, Ext JS, LLC.
34718  *
34719  * Originally Released Under LGPL - original licence link has changed is not relivant.
34720  *
34721  * Fork - LGPL
34722  * <script type="text/javascript">
34723  */
34724
34725 if(Roo.dd.DropZone){
34726     
34727 Roo.tree.TreeDropZone = function(tree, config){
34728     this.allowParentInsert = false;
34729     this.allowContainerDrop = false;
34730     this.appendOnly = false;
34731     Roo.tree.TreeDropZone.superclass.constructor.call(this, tree.innerCt, config);
34732     this.tree = tree;
34733     this.lastInsertClass = "x-tree-no-status";
34734     this.dragOverData = {};
34735 };
34736
34737 Roo.extend(Roo.tree.TreeDropZone, Roo.dd.DropZone, {
34738     ddGroup : "TreeDD",
34739     scroll:  true,
34740     
34741     expandDelay : 1000,
34742     
34743     expandNode : function(node){
34744         if(node.hasChildNodes() && !node.isExpanded()){
34745             node.expand(false, null, this.triggerCacheRefresh.createDelegate(this));
34746         }
34747     },
34748     
34749     queueExpand : function(node){
34750         this.expandProcId = this.expandNode.defer(this.expandDelay, this, [node]);
34751     },
34752     
34753     cancelExpand : function(){
34754         if(this.expandProcId){
34755             clearTimeout(this.expandProcId);
34756             this.expandProcId = false;
34757         }
34758     },
34759     
34760     isValidDropPoint : function(n, pt, dd, e, data){
34761         if(!n || !data){ return false; }
34762         var targetNode = n.node;
34763         var dropNode = data.node;
34764         // default drop rules
34765         if(!(targetNode && targetNode.isTarget && pt)){
34766             return false;
34767         }
34768         if(pt == "append" && targetNode.allowChildren === false){
34769             return false;
34770         }
34771         if((pt == "above" || pt == "below") && (targetNode.parentNode && targetNode.parentNode.allowChildren === false)){
34772             return false;
34773         }
34774         if(dropNode && (targetNode == dropNode || dropNode.contains(targetNode))){
34775             return false;
34776         }
34777         // reuse the object
34778         var overEvent = this.dragOverData;
34779         overEvent.tree = this.tree;
34780         overEvent.target = targetNode;
34781         overEvent.data = data;
34782         overEvent.point = pt;
34783         overEvent.source = dd;
34784         overEvent.rawEvent = e;
34785         overEvent.dropNode = dropNode;
34786         overEvent.cancel = false;  
34787         var result = this.tree.fireEvent("nodedragover", overEvent);
34788         return overEvent.cancel === false && result !== false;
34789     },
34790     
34791     getDropPoint : function(e, n, dd)
34792     {
34793         var tn = n.node;
34794         if(tn.isRoot){
34795             return tn.allowChildren !== false ? "append" : false; // always append for root
34796         }
34797         var dragEl = n.ddel;
34798         var t = Roo.lib.Dom.getY(dragEl), b = t + dragEl.offsetHeight;
34799         var y = Roo.lib.Event.getPageY(e);
34800         //var noAppend = tn.allowChildren === false || tn.isLeaf();
34801         
34802         // we may drop nodes anywhere, as long as allowChildren has not been set to false..
34803         var noAppend = tn.allowChildren === false;
34804         if(this.appendOnly || tn.parentNode.allowChildren === false){
34805             return noAppend ? false : "append";
34806         }
34807         var noBelow = false;
34808         if(!this.allowParentInsert){
34809             noBelow = tn.hasChildNodes() && tn.isExpanded();
34810         }
34811         var q = (b - t) / (noAppend ? 2 : 3);
34812         if(y >= t && y < (t + q)){
34813             return "above";
34814         }else if(!noBelow && (noAppend || y >= b-q && y <= b)){
34815             return "below";
34816         }else{
34817             return "append";
34818         }
34819     },
34820     
34821     onNodeEnter : function(n, dd, e, data)
34822     {
34823         this.cancelExpand();
34824     },
34825     
34826     onNodeOver : function(n, dd, e, data)
34827     {
34828        
34829         var pt = this.getDropPoint(e, n, dd);
34830         var node = n.node;
34831         
34832         // auto node expand check
34833         if(!this.expandProcId && pt == "append" && node.hasChildNodes() && !n.node.isExpanded()){
34834             this.queueExpand(node);
34835         }else if(pt != "append"){
34836             this.cancelExpand();
34837         }
34838         
34839         // set the insert point style on the target node
34840         var returnCls = this.dropNotAllowed;
34841         if(this.isValidDropPoint(n, pt, dd, e, data)){
34842            if(pt){
34843                var el = n.ddel;
34844                var cls;
34845                if(pt == "above"){
34846                    returnCls = n.node.isFirst() ? "x-tree-drop-ok-above" : "x-tree-drop-ok-between";
34847                    cls = "x-tree-drag-insert-above";
34848                }else if(pt == "below"){
34849                    returnCls = n.node.isLast() ? "x-tree-drop-ok-below" : "x-tree-drop-ok-between";
34850                    cls = "x-tree-drag-insert-below";
34851                }else{
34852                    returnCls = "x-tree-drop-ok-append";
34853                    cls = "x-tree-drag-append";
34854                }
34855                if(this.lastInsertClass != cls){
34856                    Roo.fly(el).replaceClass(this.lastInsertClass, cls);
34857                    this.lastInsertClass = cls;
34858                }
34859            }
34860        }
34861        return returnCls;
34862     },
34863     
34864     onNodeOut : function(n, dd, e, data){
34865         
34866         this.cancelExpand();
34867         this.removeDropIndicators(n);
34868     },
34869     
34870     onNodeDrop : function(n, dd, e, data){
34871         var point = this.getDropPoint(e, n, dd);
34872         var targetNode = n.node;
34873         targetNode.ui.startDrop();
34874         if(!this.isValidDropPoint(n, point, dd, e, data)){
34875             targetNode.ui.endDrop();
34876             return false;
34877         }
34878         // first try to find the drop node
34879         var dropNode = data.node || (dd.getTreeNode ? dd.getTreeNode(data, targetNode, point, e) : null);
34880         var dropEvent = {
34881             tree : this.tree,
34882             target: targetNode,
34883             data: data,
34884             point: point,
34885             source: dd,
34886             rawEvent: e,
34887             dropNode: dropNode,
34888             cancel: !dropNode   
34889         };
34890         var retval = this.tree.fireEvent("beforenodedrop", dropEvent);
34891         if(retval === false || dropEvent.cancel === true || !dropEvent.dropNode){
34892             targetNode.ui.endDrop();
34893             return false;
34894         }
34895         // allow target changing
34896         targetNode = dropEvent.target;
34897         if(point == "append" && !targetNode.isExpanded()){
34898             targetNode.expand(false, null, function(){
34899                 this.completeDrop(dropEvent);
34900             }.createDelegate(this));
34901         }else{
34902             this.completeDrop(dropEvent);
34903         }
34904         return true;
34905     },
34906     
34907     completeDrop : function(de){
34908         var ns = de.dropNode, p = de.point, t = de.target;
34909         if(!(ns instanceof Array)){
34910             ns = [ns];
34911         }
34912         var n;
34913         for(var i = 0, len = ns.length; i < len; i++){
34914             n = ns[i];
34915             if(p == "above"){
34916                 t.parentNode.insertBefore(n, t);
34917             }else if(p == "below"){
34918                 t.parentNode.insertBefore(n, t.nextSibling);
34919             }else{
34920                 t.appendChild(n);
34921             }
34922         }
34923         n.ui.focus();
34924         if(this.tree.hlDrop){
34925             n.ui.highlight();
34926         }
34927         t.ui.endDrop();
34928         this.tree.fireEvent("nodedrop", de);
34929     },
34930     
34931     afterNodeMoved : function(dd, data, e, targetNode, dropNode){
34932         if(this.tree.hlDrop){
34933             dropNode.ui.focus();
34934             dropNode.ui.highlight();
34935         }
34936         this.tree.fireEvent("nodedrop", this.tree, targetNode, data, dd, e);
34937     },
34938     
34939     getTree : function(){
34940         return this.tree;
34941     },
34942     
34943     removeDropIndicators : function(n){
34944         if(n && n.ddel){
34945             var el = n.ddel;
34946             Roo.fly(el).removeClass([
34947                     "x-tree-drag-insert-above",
34948                     "x-tree-drag-insert-below",
34949                     "x-tree-drag-append"]);
34950             this.lastInsertClass = "_noclass";
34951         }
34952     },
34953     
34954     beforeDragDrop : function(target, e, id){
34955         this.cancelExpand();
34956         return true;
34957     },
34958     
34959     afterRepair : function(data){
34960         if(data && Roo.enableFx){
34961             data.node.ui.highlight();
34962         }
34963         this.hideProxy();
34964     } 
34965     
34966 });
34967
34968 }
34969 /*
34970  * Based on:
34971  * Ext JS Library 1.1.1
34972  * Copyright(c) 2006-2007, Ext JS, LLC.
34973  *
34974  * Originally Released Under LGPL - original licence link has changed is not relivant.
34975  *
34976  * Fork - LGPL
34977  * <script type="text/javascript">
34978  */
34979  
34980
34981 if(Roo.dd.DragZone){
34982 Roo.tree.TreeDragZone = function(tree, config){
34983     Roo.tree.TreeDragZone.superclass.constructor.call(this, tree.getTreeEl(), config);
34984     this.tree = tree;
34985 };
34986
34987 Roo.extend(Roo.tree.TreeDragZone, Roo.dd.DragZone, {
34988     ddGroup : "TreeDD",
34989    
34990     onBeforeDrag : function(data, e){
34991         var n = data.node;
34992         return n && n.draggable && !n.disabled;
34993     },
34994      
34995     
34996     onInitDrag : function(e){
34997         var data = this.dragData;
34998         this.tree.getSelectionModel().select(data.node);
34999         this.proxy.update("");
35000         data.node.ui.appendDDGhost(this.proxy.ghost.dom);
35001         this.tree.fireEvent("startdrag", this.tree, data.node, e);
35002     },
35003     
35004     getRepairXY : function(e, data){
35005         return data.node.ui.getDDRepairXY();
35006     },
35007     
35008     onEndDrag : function(data, e){
35009         this.tree.fireEvent("enddrag", this.tree, data.node, e);
35010         
35011         
35012     },
35013     
35014     onValidDrop : function(dd, e, id){
35015         this.tree.fireEvent("dragdrop", this.tree, this.dragData.node, dd, e);
35016         this.hideProxy();
35017     },
35018     
35019     beforeInvalidDrop : function(e, id){
35020         // this scrolls the original position back into view
35021         var sm = this.tree.getSelectionModel();
35022         sm.clearSelections();
35023         sm.select(this.dragData.node);
35024     }
35025 });
35026 }/*
35027  * Based on:
35028  * Ext JS Library 1.1.1
35029  * Copyright(c) 2006-2007, Ext JS, LLC.
35030  *
35031  * Originally Released Under LGPL - original licence link has changed is not relivant.
35032  *
35033  * Fork - LGPL
35034  * <script type="text/javascript">
35035  */
35036 /**
35037  * @class Roo.tree.TreeEditor
35038  * @extends Roo.Editor
35039  * Provides editor functionality for inline tree node editing.  Any valid {@link Roo.form.Field} can be used
35040  * as the editor field.
35041  * @constructor
35042  * @param {Object} config (used to be the tree panel.)
35043  * @param {Object} oldconfig DEPRECIATED Either a prebuilt {@link Roo.form.Field} instance or a Field config object
35044  * 
35045  * @cfg {Roo.tree.TreePanel} tree The tree to bind to.
35046  * @cfg {Roo.form.TextField|Object} field The field configuration
35047  *
35048  * 
35049  */
35050 Roo.tree.TreeEditor = function(config, oldconfig) { // was -- (tree, config){
35051     var tree = config;
35052     var field;
35053     if (oldconfig) { // old style..
35054         field = oldconfig.events ? oldconfig : new Roo.form.TextField(oldconfig);
35055     } else {
35056         // new style..
35057         tree = config.tree;
35058         config.field = config.field  || {};
35059         config.field.xtype = 'TextField';
35060         field = Roo.factory(config.field, Roo.form);
35061     }
35062     config = config || {};
35063     
35064     
35065     this.addEvents({
35066         /**
35067          * @event beforenodeedit
35068          * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
35069          * false from the handler of this event.
35070          * @param {Editor} this
35071          * @param {Roo.tree.Node} node 
35072          */
35073         "beforenodeedit" : true
35074     });
35075     
35076     //Roo.log(config);
35077     Roo.tree.TreeEditor.superclass.constructor.call(this, field, config);
35078
35079     this.tree = tree;
35080
35081     tree.on('beforeclick', this.beforeNodeClick, this);
35082     tree.getTreeEl().on('mousedown', this.hide, this);
35083     this.on('complete', this.updateNode, this);
35084     this.on('beforestartedit', this.fitToTree, this);
35085     this.on('startedit', this.bindScroll, this, {delay:10});
35086     this.on('specialkey', this.onSpecialKey, this);
35087 };
35088
35089 Roo.extend(Roo.tree.TreeEditor, Roo.Editor, {
35090     /**
35091      * @cfg {String} alignment
35092      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "l-l").
35093      */
35094     alignment: "l-l",
35095     // inherit
35096     autoSize: false,
35097     /**
35098      * @cfg {Boolean} hideEl
35099      * True to hide the bound element while the editor is displayed (defaults to false)
35100      */
35101     hideEl : false,
35102     /**
35103      * @cfg {String} cls
35104      * CSS class to apply to the editor (defaults to "x-small-editor x-tree-editor")
35105      */
35106     cls: "x-small-editor x-tree-editor",
35107     /**
35108      * @cfg {Boolean} shim
35109      * True to shim the editor if selects/iframes could be displayed beneath it (defaults to false)
35110      */
35111     shim:false,
35112     // inherit
35113     shadow:"frame",
35114     /**
35115      * @cfg {Number} maxWidth
35116      * The maximum width in pixels of the editor field (defaults to 250).  Note that if the maxWidth would exceed
35117      * the containing tree element's size, it will be automatically limited for you to the container width, taking
35118      * scroll and client offsets into account prior to each edit.
35119      */
35120     maxWidth: 250,
35121
35122     editDelay : 350,
35123
35124     // private
35125     fitToTree : function(ed, el){
35126         var td = this.tree.getTreeEl().dom, nd = el.dom;
35127         if(td.scrollLeft >  nd.offsetLeft){ // ensure the node left point is visible
35128             td.scrollLeft = nd.offsetLeft;
35129         }
35130         var w = Math.min(
35131                 this.maxWidth,
35132                 (td.clientWidth > 20 ? td.clientWidth : td.offsetWidth) - Math.max(0, nd.offsetLeft-td.scrollLeft) - /*cushion*/5);
35133         this.setSize(w, '');
35134         
35135         return this.fireEvent('beforenodeedit', this, this.editNode);
35136         
35137     },
35138
35139     // private
35140     triggerEdit : function(node){
35141         this.completeEdit();
35142         this.editNode = node;
35143         this.startEdit(node.ui.textNode, node.text);
35144     },
35145
35146     // private
35147     bindScroll : function(){
35148         this.tree.getTreeEl().on('scroll', this.cancelEdit, this);
35149     },
35150
35151     // private
35152     beforeNodeClick : function(node, e){
35153         var sinceLast = (this.lastClick ? this.lastClick.getElapsed() : 0);
35154         this.lastClick = new Date();
35155         if(sinceLast > this.editDelay && this.tree.getSelectionModel().isSelected(node)){
35156             e.stopEvent();
35157             this.triggerEdit(node);
35158             return false;
35159         }
35160         return true;
35161     },
35162
35163     // private
35164     updateNode : function(ed, value){
35165         this.tree.getTreeEl().un('scroll', this.cancelEdit, this);
35166         this.editNode.setText(value);
35167     },
35168
35169     // private
35170     onHide : function(){
35171         Roo.tree.TreeEditor.superclass.onHide.call(this);
35172         if(this.editNode){
35173             this.editNode.ui.focus();
35174         }
35175     },
35176
35177     // private
35178     onSpecialKey : function(field, e){
35179         var k = e.getKey();
35180         if(k == e.ESC){
35181             e.stopEvent();
35182             this.cancelEdit();
35183         }else if(k == e.ENTER && !e.hasModifier()){
35184             e.stopEvent();
35185             this.completeEdit();
35186         }
35187     }
35188 });//<Script type="text/javascript">
35189 /*
35190  * Based on:
35191  * Ext JS Library 1.1.1
35192  * Copyright(c) 2006-2007, Ext JS, LLC.
35193  *
35194  * Originally Released Under LGPL - original licence link has changed is not relivant.
35195  *
35196  * Fork - LGPL
35197  * <script type="text/javascript">
35198  */
35199  
35200 /**
35201  * Not documented??? - probably should be...
35202  */
35203
35204 Roo.tree.ColumnNodeUI = Roo.extend(Roo.tree.TreeNodeUI, {
35205     //focus: Roo.emptyFn, // prevent odd scrolling behavior
35206     
35207     renderElements : function(n, a, targetNode, bulkRender){
35208         //consel.log("renderElements?");
35209         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
35210
35211         var t = n.getOwnerTree();
35212         var tid = Pman.Tab.Document_TypesTree.tree.el.id;
35213         
35214         var cols = t.columns;
35215         var bw = t.borderWidth;
35216         var c = cols[0];
35217         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
35218          var cb = typeof a.checked == "boolean";
35219         var tx = String.format('{0}',n.text || (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
35220         var colcls = 'x-t-' + tid + '-c0';
35221         var buf = [
35222             '<li class="x-tree-node">',
35223             
35224                 
35225                 '<div class="x-tree-node-el ', a.cls,'">',
35226                     // extran...
35227                     '<div class="x-tree-col ', colcls, '" style="width:', c.width-bw, 'px;">',
35228                 
35229                 
35230                         '<span class="x-tree-node-indent">',this.indentMarkup,'</span>',
35231                         '<img src="', this.emptyIcon, '" class="x-tree-ec-icon  " />',
35232                         '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',
35233                            (a.icon ? ' x-tree-node-inline-icon' : ''),
35234                            (a.iconCls ? ' '+a.iconCls : ''),
35235                            '" unselectable="on" />',
35236                         (cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + 
35237                              (a.checked ? 'checked="checked" />' : ' />')) : ''),
35238                              
35239                         '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
35240                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>',
35241                             '<span unselectable="on" qtip="' + tx + '">',
35242                              tx,
35243                              '</span></a>' ,
35244                     '</div>',
35245                      '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
35246                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>'
35247                  ];
35248         for(var i = 1, len = cols.length; i < len; i++){
35249             c = cols[i];
35250             colcls = 'x-t-' + tid + '-c' +i;
35251             tx = String.format('{0}', (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
35252             buf.push('<div class="x-tree-col ', colcls, ' ' ,(c.cls?c.cls:''),'" style="width:',c.width-bw,'px;">',
35253                         '<div class="x-tree-col-text" qtip="' + tx +'">',tx,"</div>",
35254                       "</div>");
35255          }
35256          
35257          buf.push(
35258             '</a>',
35259             '<div class="x-clear"></div></div>',
35260             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
35261             "</li>");
35262         
35263         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
35264             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
35265                                 n.nextSibling.ui.getEl(), buf.join(""));
35266         }else{
35267             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
35268         }
35269         var el = this.wrap.firstChild;
35270         this.elRow = el;
35271         this.elNode = el.firstChild;
35272         this.ranchor = el.childNodes[1];
35273         this.ctNode = this.wrap.childNodes[1];
35274         var cs = el.firstChild.childNodes;
35275         this.indentNode = cs[0];
35276         this.ecNode = cs[1];
35277         this.iconNode = cs[2];
35278         var index = 3;
35279         if(cb){
35280             this.checkbox = cs[3];
35281             index++;
35282         }
35283         this.anchor = cs[index];
35284         
35285         this.textNode = cs[index].firstChild;
35286         
35287         //el.on("click", this.onClick, this);
35288         //el.on("dblclick", this.onDblClick, this);
35289         
35290         
35291        // console.log(this);
35292     },
35293     initEvents : function(){
35294         Roo.tree.ColumnNodeUI.superclass.initEvents.call(this);
35295         
35296             
35297         var a = this.ranchor;
35298
35299         var el = Roo.get(a);
35300
35301         if(Roo.isOpera){ // opera render bug ignores the CSS
35302             el.setStyle("text-decoration", "none");
35303         }
35304
35305         el.on("click", this.onClick, this);
35306         el.on("dblclick", this.onDblClick, this);
35307         el.on("contextmenu", this.onContextMenu, this);
35308         
35309     },
35310     
35311     /*onSelectedChange : function(state){
35312         if(state){
35313             this.focus();
35314             this.addClass("x-tree-selected");
35315         }else{
35316             //this.blur();
35317             this.removeClass("x-tree-selected");
35318         }
35319     },*/
35320     addClass : function(cls){
35321         if(this.elRow){
35322             Roo.fly(this.elRow).addClass(cls);
35323         }
35324         
35325     },
35326     
35327     
35328     removeClass : function(cls){
35329         if(this.elRow){
35330             Roo.fly(this.elRow).removeClass(cls);
35331         }
35332     }
35333
35334     
35335     
35336 });//<Script type="text/javascript">
35337
35338 /*
35339  * Based on:
35340  * Ext JS Library 1.1.1
35341  * Copyright(c) 2006-2007, Ext JS, LLC.
35342  *
35343  * Originally Released Under LGPL - original licence link has changed is not relivant.
35344  *
35345  * Fork - LGPL
35346  * <script type="text/javascript">
35347  */
35348  
35349
35350 /**
35351  * @class Roo.tree.ColumnTree
35352  * @extends Roo.data.TreePanel
35353  * @cfg {Object} columns  Including width, header, renderer, cls, dataIndex 
35354  * @cfg {int} borderWidth  compined right/left border allowance
35355  * @constructor
35356  * @param {String/HTMLElement/Element} el The container element
35357  * @param {Object} config
35358  */
35359 Roo.tree.ColumnTree =  function(el, config)
35360 {
35361    Roo.tree.ColumnTree.superclass.constructor.call(this, el , config);
35362    this.addEvents({
35363         /**
35364         * @event resize
35365         * Fire this event on a container when it resizes
35366         * @param {int} w Width
35367         * @param {int} h Height
35368         */
35369        "resize" : true
35370     });
35371     this.on('resize', this.onResize, this);
35372 };
35373
35374 Roo.extend(Roo.tree.ColumnTree, Roo.tree.TreePanel, {
35375     //lines:false,
35376     
35377     
35378     borderWidth: Roo.isBorderBox ? 0 : 2, 
35379     headEls : false,
35380     
35381     render : function(){
35382         // add the header.....
35383        
35384         Roo.tree.ColumnTree.superclass.render.apply(this);
35385         
35386         this.el.addClass('x-column-tree');
35387         
35388         this.headers = this.el.createChild(
35389             {cls:'x-tree-headers'},this.innerCt.dom);
35390    
35391         var cols = this.columns, c;
35392         var totalWidth = 0;
35393         this.headEls = [];
35394         var  len = cols.length;
35395         for(var i = 0; i < len; i++){
35396              c = cols[i];
35397              totalWidth += c.width;
35398             this.headEls.push(this.headers.createChild({
35399                  cls:'x-tree-hd ' + (c.cls?c.cls+'-hd':''),
35400                  cn: {
35401                      cls:'x-tree-hd-text',
35402                      html: c.header
35403                  },
35404                  style:'width:'+(c.width-this.borderWidth)+'px;'
35405              }));
35406         }
35407         this.headers.createChild({cls:'x-clear'});
35408         // prevent floats from wrapping when clipped
35409         this.headers.setWidth(totalWidth);
35410         //this.innerCt.setWidth(totalWidth);
35411         this.innerCt.setStyle({ overflow: 'auto' });
35412         this.onResize(this.width, this.height);
35413              
35414         
35415     },
35416     onResize : function(w,h)
35417     {
35418         this.height = h;
35419         this.width = w;
35420         // resize cols..
35421         this.innerCt.setWidth(this.width);
35422         this.innerCt.setHeight(this.height-20);
35423         
35424         // headers...
35425         var cols = this.columns, c;
35426         var totalWidth = 0;
35427         var expEl = false;
35428         var len = cols.length;
35429         for(var i = 0; i < len; i++){
35430             c = cols[i];
35431             if (this.autoExpandColumn !== false && c.dataIndex == this.autoExpandColumn) {
35432                 // it's the expander..
35433                 expEl  = this.headEls[i];
35434                 continue;
35435             }
35436             totalWidth += c.width;
35437             
35438         }
35439         if (expEl) {
35440             expEl.setWidth(  ((w - totalWidth)-this.borderWidth - 20));
35441         }
35442         this.headers.setWidth(w-20);
35443
35444         
35445         
35446         
35447     }
35448 });
35449 /*
35450  * Based on:
35451  * Ext JS Library 1.1.1
35452  * Copyright(c) 2006-2007, Ext JS, LLC.
35453  *
35454  * Originally Released Under LGPL - original licence link has changed is not relivant.
35455  *
35456  * Fork - LGPL
35457  * <script type="text/javascript">
35458  */
35459  
35460 /**
35461  * @class Roo.menu.Menu
35462  * @extends Roo.util.Observable
35463  * A menu object.  This is the container to which you add all other menu items.  Menu can also serve a as a base class
35464  * when you want a specialzed menu based off of another component (like {@link Roo.menu.DateMenu} for example).
35465  * @constructor
35466  * Creates a new Menu
35467  * @param {Object} config Configuration options
35468  */
35469 Roo.menu.Menu = function(config){
35470     Roo.apply(this, config);
35471     this.id = this.id || Roo.id();
35472     this.addEvents({
35473         /**
35474          * @event beforeshow
35475          * Fires before this menu is displayed
35476          * @param {Roo.menu.Menu} this
35477          */
35478         beforeshow : true,
35479         /**
35480          * @event beforehide
35481          * Fires before this menu is hidden
35482          * @param {Roo.menu.Menu} this
35483          */
35484         beforehide : true,
35485         /**
35486          * @event show
35487          * Fires after this menu is displayed
35488          * @param {Roo.menu.Menu} this
35489          */
35490         show : true,
35491         /**
35492          * @event hide
35493          * Fires after this menu is hidden
35494          * @param {Roo.menu.Menu} this
35495          */
35496         hide : true,
35497         /**
35498          * @event click
35499          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
35500          * @param {Roo.menu.Menu} this
35501          * @param {Roo.menu.Item} menuItem The menu item that was clicked
35502          * @param {Roo.EventObject} e
35503          */
35504         click : true,
35505         /**
35506          * @event mouseover
35507          * Fires when the mouse is hovering over this menu
35508          * @param {Roo.menu.Menu} this
35509          * @param {Roo.EventObject} e
35510          * @param {Roo.menu.Item} menuItem The menu item that was clicked
35511          */
35512         mouseover : true,
35513         /**
35514          * @event mouseout
35515          * Fires when the mouse exits this menu
35516          * @param {Roo.menu.Menu} this
35517          * @param {Roo.EventObject} e
35518          * @param {Roo.menu.Item} menuItem The menu item that was clicked
35519          */
35520         mouseout : true,
35521         /**
35522          * @event itemclick
35523          * Fires when a menu item contained in this menu is clicked
35524          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
35525          * @param {Roo.EventObject} e
35526          */
35527         itemclick: true
35528     });
35529     if (this.registerMenu) {
35530         Roo.menu.MenuMgr.register(this);
35531     }
35532     
35533     var mis = this.items;
35534     this.items = new Roo.util.MixedCollection();
35535     if(mis){
35536         this.add.apply(this, mis);
35537     }
35538 };
35539
35540 Roo.extend(Roo.menu.Menu, Roo.util.Observable, {
35541     /**
35542      * @cfg {Number} minWidth The minimum width of the menu in pixels (defaults to 120)
35543      */
35544     minWidth : 120,
35545     /**
35546      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop"
35547      * for bottom-right shadow (defaults to "sides")
35548      */
35549     shadow : "sides",
35550     /**
35551      * @cfg {String} subMenuAlign The {@link Roo.Element#alignTo} anchor position value to use for submenus of
35552      * this menu (defaults to "tl-tr?")
35553      */
35554     subMenuAlign : "tl-tr?",
35555     /**
35556      * @cfg {String} defaultAlign The default {@link Roo.Element#alignTo) anchor position value for this menu
35557      * relative to its element of origin (defaults to "tl-bl?")
35558      */
35559     defaultAlign : "tl-bl?",
35560     /**
35561      * @cfg {Boolean} allowOtherMenus True to allow multiple menus to be displayed at the same time (defaults to false)
35562      */
35563     allowOtherMenus : false,
35564     /**
35565      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
35566      */
35567     registerMenu : true,
35568
35569     hidden:true,
35570
35571     // private
35572     render : function(){
35573         if(this.el){
35574             return;
35575         }
35576         var el = this.el = new Roo.Layer({
35577             cls: "x-menu",
35578             shadow:this.shadow,
35579             constrain: false,
35580             parentEl: this.parentEl || document.body,
35581             zindex:15000
35582         });
35583
35584         this.keyNav = new Roo.menu.MenuNav(this);
35585
35586         if(this.plain){
35587             el.addClass("x-menu-plain");
35588         }
35589         if(this.cls){
35590             el.addClass(this.cls);
35591         }
35592         // generic focus element
35593         this.focusEl = el.createChild({
35594             tag: "a", cls: "x-menu-focus", href: "#", onclick: "return false;", tabIndex:"-1"
35595         });
35596         var ul = el.createChild({tag: "ul", cls: "x-menu-list"});
35597         ul.on(Roo.isTouch ? 'touchstart' : 'click'   , this.onClick, this);
35598         
35599         ul.on("mouseover", this.onMouseOver, this);
35600         ul.on("mouseout", this.onMouseOut, this);
35601         this.items.each(function(item){
35602             if (item.hidden) {
35603                 return;
35604             }
35605             
35606             var li = document.createElement("li");
35607             li.className = "x-menu-list-item";
35608             ul.dom.appendChild(li);
35609             item.render(li, this);
35610         }, this);
35611         this.ul = ul;
35612         this.autoWidth();
35613     },
35614
35615     // private
35616     autoWidth : function(){
35617         var el = this.el, ul = this.ul;
35618         if(!el){
35619             return;
35620         }
35621         var w = this.width;
35622         if(w){
35623             el.setWidth(w);
35624         }else if(Roo.isIE){
35625             el.setWidth(this.minWidth);
35626             var t = el.dom.offsetWidth; // force recalc
35627             el.setWidth(ul.getWidth()+el.getFrameWidth("lr"));
35628         }
35629     },
35630
35631     // private
35632     delayAutoWidth : function(){
35633         if(this.rendered){
35634             if(!this.awTask){
35635                 this.awTask = new Roo.util.DelayedTask(this.autoWidth, this);
35636             }
35637             this.awTask.delay(20);
35638         }
35639     },
35640
35641     // private
35642     findTargetItem : function(e){
35643         var t = e.getTarget(".x-menu-list-item", this.ul,  true);
35644         if(t && t.menuItemId){
35645             return this.items.get(t.menuItemId);
35646         }
35647     },
35648
35649     // private
35650     onClick : function(e){
35651         Roo.log("menu.onClick");
35652         var t = this.findTargetItem(e);
35653         if(!t){
35654             return;
35655         }
35656         Roo.log(e);
35657         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
35658             if(t == this.activeItem && t.shouldDeactivate(e)){
35659                 this.activeItem.deactivate();
35660                 delete this.activeItem;
35661                 return;
35662             }
35663             if(t.canActivate){
35664                 this.setActiveItem(t, true);
35665             }
35666             return;
35667             
35668             
35669         }
35670         
35671         t.onClick(e);
35672         this.fireEvent("click", this, t, e);
35673     },
35674
35675     // private
35676     setActiveItem : function(item, autoExpand){
35677         if(item != this.activeItem){
35678             if(this.activeItem){
35679                 this.activeItem.deactivate();
35680             }
35681             this.activeItem = item;
35682             item.activate(autoExpand);
35683         }else if(autoExpand){
35684             item.expandMenu();
35685         }
35686     },
35687
35688     // private
35689     tryActivate : function(start, step){
35690         var items = this.items;
35691         for(var i = start, len = items.length; i >= 0 && i < len; i+= step){
35692             var item = items.get(i);
35693             if(!item.disabled && item.canActivate){
35694                 this.setActiveItem(item, false);
35695                 return item;
35696             }
35697         }
35698         return false;
35699     },
35700
35701     // private
35702     onMouseOver : function(e){
35703         var t;
35704         if(t = this.findTargetItem(e)){
35705             if(t.canActivate && !t.disabled){
35706                 this.setActiveItem(t, true);
35707             }
35708         }
35709         this.fireEvent("mouseover", this, e, t);
35710     },
35711
35712     // private
35713     onMouseOut : function(e){
35714         var t;
35715         if(t = this.findTargetItem(e)){
35716             if(t == this.activeItem && t.shouldDeactivate(e)){
35717                 this.activeItem.deactivate();
35718                 delete this.activeItem;
35719             }
35720         }
35721         this.fireEvent("mouseout", this, e, t);
35722     },
35723
35724     /**
35725      * Read-only.  Returns true if the menu is currently displayed, else false.
35726      * @type Boolean
35727      */
35728     isVisible : function(){
35729         return this.el && !this.hidden;
35730     },
35731
35732     /**
35733      * Displays this menu relative to another element
35734      * @param {String/HTMLElement/Roo.Element} element The element to align to
35735      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
35736      * the element (defaults to this.defaultAlign)
35737      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
35738      */
35739     show : function(el, pos, parentMenu){
35740         this.parentMenu = parentMenu;
35741         if(!this.el){
35742             this.render();
35743         }
35744         this.fireEvent("beforeshow", this);
35745         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
35746     },
35747
35748     /**
35749      * Displays this menu at a specific xy position
35750      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
35751      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
35752      */
35753     showAt : function(xy, parentMenu, /* private: */_e){
35754         this.parentMenu = parentMenu;
35755         if(!this.el){
35756             this.render();
35757         }
35758         if(_e !== false){
35759             this.fireEvent("beforeshow", this);
35760             xy = this.el.adjustForConstraints(xy);
35761         }
35762         this.el.setXY(xy);
35763         this.el.show();
35764         this.hidden = false;
35765         this.focus();
35766         this.fireEvent("show", this);
35767     },
35768
35769     focus : function(){
35770         if(!this.hidden){
35771             this.doFocus.defer(50, this);
35772         }
35773     },
35774
35775     doFocus : function(){
35776         if(!this.hidden){
35777             this.focusEl.focus();
35778         }
35779     },
35780
35781     /**
35782      * Hides this menu and optionally all parent menus
35783      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
35784      */
35785     hide : function(deep){
35786         if(this.el && this.isVisible()){
35787             this.fireEvent("beforehide", this);
35788             if(this.activeItem){
35789                 this.activeItem.deactivate();
35790                 this.activeItem = null;
35791             }
35792             this.el.hide();
35793             this.hidden = true;
35794             this.fireEvent("hide", this);
35795         }
35796         if(deep === true && this.parentMenu){
35797             this.parentMenu.hide(true);
35798         }
35799     },
35800
35801     /**
35802      * Addds one or more items of any type supported by the Menu class, or that can be converted into menu items.
35803      * Any of the following are valid:
35804      * <ul>
35805      * <li>Any menu item object based on {@link Roo.menu.Item}</li>
35806      * <li>An HTMLElement object which will be converted to a menu item</li>
35807      * <li>A menu item config object that will be created as a new menu item</li>
35808      * <li>A string, which can either be '-' or 'separator' to add a menu separator, otherwise
35809      * it will be converted into a {@link Roo.menu.TextItem} and added</li>
35810      * </ul>
35811      * Usage:
35812      * <pre><code>
35813 // Create the menu
35814 var menu = new Roo.menu.Menu();
35815
35816 // Create a menu item to add by reference
35817 var menuItem = new Roo.menu.Item({ text: 'New Item!' });
35818
35819 // Add a bunch of items at once using different methods.
35820 // Only the last item added will be returned.
35821 var item = menu.add(
35822     menuItem,                // add existing item by ref
35823     'Dynamic Item',          // new TextItem
35824     '-',                     // new separator
35825     { text: 'Config Item' }  // new item by config
35826 );
35827 </code></pre>
35828      * @param {Mixed} args One or more menu items, menu item configs or other objects that can be converted to menu items
35829      * @return {Roo.menu.Item} The menu item that was added, or the last one if multiple items were added
35830      */
35831     add : function(){
35832         var a = arguments, l = a.length, item;
35833         for(var i = 0; i < l; i++){
35834             var el = a[i];
35835             if ((typeof(el) == "object") && el.xtype && el.xns) {
35836                 el = Roo.factory(el, Roo.menu);
35837             }
35838             
35839             if(el.render){ // some kind of Item
35840                 item = this.addItem(el);
35841             }else if(typeof el == "string"){ // string
35842                 if(el == "separator" || el == "-"){
35843                     item = this.addSeparator();
35844                 }else{
35845                     item = this.addText(el);
35846                 }
35847             }else if(el.tagName || el.el){ // element
35848                 item = this.addElement(el);
35849             }else if(typeof el == "object"){ // must be menu item config?
35850                 item = this.addMenuItem(el);
35851             }
35852         }
35853         return item;
35854     },
35855
35856     /**
35857      * Returns this menu's underlying {@link Roo.Element} object
35858      * @return {Roo.Element} The element
35859      */
35860     getEl : function(){
35861         if(!this.el){
35862             this.render();
35863         }
35864         return this.el;
35865     },
35866
35867     /**
35868      * Adds a separator bar to the menu
35869      * @return {Roo.menu.Item} The menu item that was added
35870      */
35871     addSeparator : function(){
35872         return this.addItem(new Roo.menu.Separator());
35873     },
35874
35875     /**
35876      * Adds an {@link Roo.Element} object to the menu
35877      * @param {String/HTMLElement/Roo.Element} el The element or DOM node to add, or its id
35878      * @return {Roo.menu.Item} The menu item that was added
35879      */
35880     addElement : function(el){
35881         return this.addItem(new Roo.menu.BaseItem(el));
35882     },
35883
35884     /**
35885      * Adds an existing object based on {@link Roo.menu.Item} to the menu
35886      * @param {Roo.menu.Item} item The menu item to add
35887      * @return {Roo.menu.Item} The menu item that was added
35888      */
35889     addItem : function(item){
35890         this.items.add(item);
35891         if(this.ul){
35892             var li = document.createElement("li");
35893             li.className = "x-menu-list-item";
35894             this.ul.dom.appendChild(li);
35895             item.render(li, this);
35896             this.delayAutoWidth();
35897         }
35898         return item;
35899     },
35900
35901     /**
35902      * Creates a new {@link Roo.menu.Item} based an the supplied config object and adds it to the menu
35903      * @param {Object} config A MenuItem config object
35904      * @return {Roo.menu.Item} The menu item that was added
35905      */
35906     addMenuItem : function(config){
35907         if(!(config instanceof Roo.menu.Item)){
35908             if(typeof config.checked == "boolean"){ // must be check menu item config?
35909                 config = new Roo.menu.CheckItem(config);
35910             }else{
35911                 config = new Roo.menu.Item(config);
35912             }
35913         }
35914         return this.addItem(config);
35915     },
35916
35917     /**
35918      * Creates a new {@link Roo.menu.TextItem} with the supplied text and adds it to the menu
35919      * @param {String} text The text to display in the menu item
35920      * @return {Roo.menu.Item} The menu item that was added
35921      */
35922     addText : function(text){
35923         return this.addItem(new Roo.menu.TextItem({ text : text }));
35924     },
35925
35926     /**
35927      * Inserts an existing object based on {@link Roo.menu.Item} to the menu at a specified index
35928      * @param {Number} index The index in the menu's list of current items where the new item should be inserted
35929      * @param {Roo.menu.Item} item The menu item to add
35930      * @return {Roo.menu.Item} The menu item that was added
35931      */
35932     insert : function(index, item){
35933         this.items.insert(index, item);
35934         if(this.ul){
35935             var li = document.createElement("li");
35936             li.className = "x-menu-list-item";
35937             this.ul.dom.insertBefore(li, this.ul.dom.childNodes[index]);
35938             item.render(li, this);
35939             this.delayAutoWidth();
35940         }
35941         return item;
35942     },
35943
35944     /**
35945      * Removes an {@link Roo.menu.Item} from the menu and destroys the object
35946      * @param {Roo.menu.Item} item The menu item to remove
35947      */
35948     remove : function(item){
35949         this.items.removeKey(item.id);
35950         item.destroy();
35951     },
35952
35953     /**
35954      * Removes and destroys all items in the menu
35955      */
35956     removeAll : function(){
35957         var f;
35958         while(f = this.items.first()){
35959             this.remove(f);
35960         }
35961     }
35962 });
35963
35964 // MenuNav is a private utility class used internally by the Menu
35965 Roo.menu.MenuNav = function(menu){
35966     Roo.menu.MenuNav.superclass.constructor.call(this, menu.el);
35967     this.scope = this.menu = menu;
35968 };
35969
35970 Roo.extend(Roo.menu.MenuNav, Roo.KeyNav, {
35971     doRelay : function(e, h){
35972         var k = e.getKey();
35973         if(!this.menu.activeItem && e.isNavKeyPress() && k != e.SPACE && k != e.RETURN){
35974             this.menu.tryActivate(0, 1);
35975             return false;
35976         }
35977         return h.call(this.scope || this, e, this.menu);
35978     },
35979
35980     up : function(e, m){
35981         if(!m.tryActivate(m.items.indexOf(m.activeItem)-1, -1)){
35982             m.tryActivate(m.items.length-1, -1);
35983         }
35984     },
35985
35986     down : function(e, m){
35987         if(!m.tryActivate(m.items.indexOf(m.activeItem)+1, 1)){
35988             m.tryActivate(0, 1);
35989         }
35990     },
35991
35992     right : function(e, m){
35993         if(m.activeItem){
35994             m.activeItem.expandMenu(true);
35995         }
35996     },
35997
35998     left : function(e, m){
35999         m.hide();
36000         if(m.parentMenu && m.parentMenu.activeItem){
36001             m.parentMenu.activeItem.activate();
36002         }
36003     },
36004
36005     enter : function(e, m){
36006         if(m.activeItem){
36007             e.stopPropagation();
36008             m.activeItem.onClick(e);
36009             m.fireEvent("click", this, m.activeItem);
36010             return true;
36011         }
36012     }
36013 });/*
36014  * Based on:
36015  * Ext JS Library 1.1.1
36016  * Copyright(c) 2006-2007, Ext JS, LLC.
36017  *
36018  * Originally Released Under LGPL - original licence link has changed is not relivant.
36019  *
36020  * Fork - LGPL
36021  * <script type="text/javascript">
36022  */
36023  
36024 /**
36025  * @class Roo.menu.MenuMgr
36026  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
36027  * @singleton
36028  */
36029 Roo.menu.MenuMgr = function(){
36030    var menus, active, groups = {}, attached = false, lastShow = new Date();
36031
36032    // private - called when first menu is created
36033    function init(){
36034        menus = {};
36035        active = new Roo.util.MixedCollection();
36036        Roo.get(document).addKeyListener(27, function(){
36037            if(active.length > 0){
36038                hideAll();
36039            }
36040        });
36041    }
36042
36043    // private
36044    function hideAll(){
36045        if(active && active.length > 0){
36046            var c = active.clone();
36047            c.each(function(m){
36048                m.hide();
36049            });
36050        }
36051    }
36052
36053    // private
36054    function onHide(m){
36055        active.remove(m);
36056        if(active.length < 1){
36057            Roo.get(document).un("mousedown", onMouseDown);
36058            attached = false;
36059        }
36060    }
36061
36062    // private
36063    function onShow(m){
36064        var last = active.last();
36065        lastShow = new Date();
36066        active.add(m);
36067        if(!attached){
36068            Roo.get(document).on("mousedown", onMouseDown);
36069            attached = true;
36070        }
36071        if(m.parentMenu){
36072           m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
36073           m.parentMenu.activeChild = m;
36074        }else if(last && last.isVisible()){
36075           m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
36076        }
36077    }
36078
36079    // private
36080    function onBeforeHide(m){
36081        if(m.activeChild){
36082            m.activeChild.hide();
36083        }
36084        if(m.autoHideTimer){
36085            clearTimeout(m.autoHideTimer);
36086            delete m.autoHideTimer;
36087        }
36088    }
36089
36090    // private
36091    function onBeforeShow(m){
36092        var pm = m.parentMenu;
36093        if(!pm && !m.allowOtherMenus){
36094            hideAll();
36095        }else if(pm && pm.activeChild && active != m){
36096            pm.activeChild.hide();
36097        }
36098    }
36099
36100    // private
36101    function onMouseDown(e){
36102        if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".x-menu")){
36103            hideAll();
36104        }
36105    }
36106
36107    // private
36108    function onBeforeCheck(mi, state){
36109        if(state){
36110            var g = groups[mi.group];
36111            for(var i = 0, l = g.length; i < l; i++){
36112                if(g[i] != mi){
36113                    g[i].setChecked(false);
36114                }
36115            }
36116        }
36117    }
36118
36119    return {
36120
36121        /**
36122         * Hides all menus that are currently visible
36123         */
36124        hideAll : function(){
36125             hideAll();  
36126        },
36127
36128        // private
36129        register : function(menu){
36130            if(!menus){
36131                init();
36132            }
36133            menus[menu.id] = menu;
36134            menu.on("beforehide", onBeforeHide);
36135            menu.on("hide", onHide);
36136            menu.on("beforeshow", onBeforeShow);
36137            menu.on("show", onShow);
36138            var g = menu.group;
36139            if(g && menu.events["checkchange"]){
36140                if(!groups[g]){
36141                    groups[g] = [];
36142                }
36143                groups[g].push(menu);
36144                menu.on("checkchange", onCheck);
36145            }
36146        },
36147
36148         /**
36149          * Returns a {@link Roo.menu.Menu} object
36150          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
36151          * be used to generate and return a new Menu instance.
36152          */
36153        get : function(menu){
36154            if(typeof menu == "string"){ // menu id
36155                return menus[menu];
36156            }else if(menu.events){  // menu instance
36157                return menu;
36158            }else if(typeof menu.length == 'number'){ // array of menu items?
36159                return new Roo.menu.Menu({items:menu});
36160            }else{ // otherwise, must be a config
36161                return new Roo.menu.Menu(menu);
36162            }
36163        },
36164
36165        // private
36166        unregister : function(menu){
36167            delete menus[menu.id];
36168            menu.un("beforehide", onBeforeHide);
36169            menu.un("hide", onHide);
36170            menu.un("beforeshow", onBeforeShow);
36171            menu.un("show", onShow);
36172            var g = menu.group;
36173            if(g && menu.events["checkchange"]){
36174                groups[g].remove(menu);
36175                menu.un("checkchange", onCheck);
36176            }
36177        },
36178
36179        // private
36180        registerCheckable : function(menuItem){
36181            var g = menuItem.group;
36182            if(g){
36183                if(!groups[g]){
36184                    groups[g] = [];
36185                }
36186                groups[g].push(menuItem);
36187                menuItem.on("beforecheckchange", onBeforeCheck);
36188            }
36189        },
36190
36191        // private
36192        unregisterCheckable : function(menuItem){
36193            var g = menuItem.group;
36194            if(g){
36195                groups[g].remove(menuItem);
36196                menuItem.un("beforecheckchange", onBeforeCheck);
36197            }
36198        }
36199    };
36200 }();/*
36201  * Based on:
36202  * Ext JS Library 1.1.1
36203  * Copyright(c) 2006-2007, Ext JS, LLC.
36204  *
36205  * Originally Released Under LGPL - original licence link has changed is not relivant.
36206  *
36207  * Fork - LGPL
36208  * <script type="text/javascript">
36209  */
36210  
36211
36212 /**
36213  * @class Roo.menu.BaseItem
36214  * @extends Roo.Component
36215  * The base class for all items that render into menus.  BaseItem provides default rendering, activated state
36216  * management and base configuration options shared by all menu components.
36217  * @constructor
36218  * Creates a new BaseItem
36219  * @param {Object} config Configuration options
36220  */
36221 Roo.menu.BaseItem = function(config){
36222     Roo.menu.BaseItem.superclass.constructor.call(this, config);
36223
36224     this.addEvents({
36225         /**
36226          * @event click
36227          * Fires when this item is clicked
36228          * @param {Roo.menu.BaseItem} this
36229          * @param {Roo.EventObject} e
36230          */
36231         click: true,
36232         /**
36233          * @event activate
36234          * Fires when this item is activated
36235          * @param {Roo.menu.BaseItem} this
36236          */
36237         activate : true,
36238         /**
36239          * @event deactivate
36240          * Fires when this item is deactivated
36241          * @param {Roo.menu.BaseItem} this
36242          */
36243         deactivate : true
36244     });
36245
36246     if(this.handler){
36247         this.on("click", this.handler, this.scope, true);
36248     }
36249 };
36250
36251 Roo.extend(Roo.menu.BaseItem, Roo.Component, {
36252     /**
36253      * @cfg {Function} handler
36254      * A function that will handle the click event of this menu item (defaults to undefined)
36255      */
36256     /**
36257      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to false)
36258      */
36259     canActivate : false,
36260     
36261      /**
36262      * @cfg {Boolean} hidden True to prevent creation of this menu item (defaults to false)
36263      */
36264     hidden: false,
36265     
36266     /**
36267      * @cfg {String} activeClass The CSS class to use when the item becomes activated (defaults to "x-menu-item-active")
36268      */
36269     activeClass : "x-menu-item-active",
36270     /**
36271      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to true)
36272      */
36273     hideOnClick : true,
36274     /**
36275      * @cfg {Number} hideDelay Length of time in milliseconds to wait before hiding after a click (defaults to 100)
36276      */
36277     hideDelay : 100,
36278
36279     // private
36280     ctype: "Roo.menu.BaseItem",
36281
36282     // private
36283     actionMode : "container",
36284
36285     // private
36286     render : function(container, parentMenu){
36287         this.parentMenu = parentMenu;
36288         Roo.menu.BaseItem.superclass.render.call(this, container);
36289         this.container.menuItemId = this.id;
36290     },
36291
36292     // private
36293     onRender : function(container, position){
36294         this.el = Roo.get(this.el);
36295         container.dom.appendChild(this.el.dom);
36296     },
36297
36298     // private
36299     onClick : function(e){
36300         if(!this.disabled && this.fireEvent("click", this, e) !== false
36301                 && this.parentMenu.fireEvent("itemclick", this, e) !== false){
36302             this.handleClick(e);
36303         }else{
36304             e.stopEvent();
36305         }
36306     },
36307
36308     // private
36309     activate : function(){
36310         if(this.disabled){
36311             return false;
36312         }
36313         var li = this.container;
36314         li.addClass(this.activeClass);
36315         this.region = li.getRegion().adjust(2, 2, -2, -2);
36316         this.fireEvent("activate", this);
36317         return true;
36318     },
36319
36320     // private
36321     deactivate : function(){
36322         this.container.removeClass(this.activeClass);
36323         this.fireEvent("deactivate", this);
36324     },
36325
36326     // private
36327     shouldDeactivate : function(e){
36328         return !this.region || !this.region.contains(e.getPoint());
36329     },
36330
36331     // private
36332     handleClick : function(e){
36333         if(this.hideOnClick){
36334             this.parentMenu.hide.defer(this.hideDelay, this.parentMenu, [true]);
36335         }
36336     },
36337
36338     // private
36339     expandMenu : function(autoActivate){
36340         // do nothing
36341     },
36342
36343     // private
36344     hideMenu : function(){
36345         // do nothing
36346     }
36347 });/*
36348  * Based on:
36349  * Ext JS Library 1.1.1
36350  * Copyright(c) 2006-2007, Ext JS, LLC.
36351  *
36352  * Originally Released Under LGPL - original licence link has changed is not relivant.
36353  *
36354  * Fork - LGPL
36355  * <script type="text/javascript">
36356  */
36357  
36358 /**
36359  * @class Roo.menu.Adapter
36360  * @extends Roo.menu.BaseItem
36361  * 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.
36362  * It provides basic rendering, activation management and enable/disable logic required to work in menus.
36363  * @constructor
36364  * Creates a new Adapter
36365  * @param {Object} config Configuration options
36366  */
36367 Roo.menu.Adapter = function(component, config){
36368     Roo.menu.Adapter.superclass.constructor.call(this, config);
36369     this.component = component;
36370 };
36371 Roo.extend(Roo.menu.Adapter, Roo.menu.BaseItem, {
36372     // private
36373     canActivate : true,
36374
36375     // private
36376     onRender : function(container, position){
36377         this.component.render(container);
36378         this.el = this.component.getEl();
36379     },
36380
36381     // private
36382     activate : function(){
36383         if(this.disabled){
36384             return false;
36385         }
36386         this.component.focus();
36387         this.fireEvent("activate", this);
36388         return true;
36389     },
36390
36391     // private
36392     deactivate : function(){
36393         this.fireEvent("deactivate", this);
36394     },
36395
36396     // private
36397     disable : function(){
36398         this.component.disable();
36399         Roo.menu.Adapter.superclass.disable.call(this);
36400     },
36401
36402     // private
36403     enable : function(){
36404         this.component.enable();
36405         Roo.menu.Adapter.superclass.enable.call(this);
36406     }
36407 });/*
36408  * Based on:
36409  * Ext JS Library 1.1.1
36410  * Copyright(c) 2006-2007, Ext JS, LLC.
36411  *
36412  * Originally Released Under LGPL - original licence link has changed is not relivant.
36413  *
36414  * Fork - LGPL
36415  * <script type="text/javascript">
36416  */
36417
36418 /**
36419  * @class Roo.menu.TextItem
36420  * @extends Roo.menu.BaseItem
36421  * Adds a static text string to a menu, usually used as either a heading or group separator.
36422  * Note: old style constructor with text is still supported.
36423  * 
36424  * @constructor
36425  * Creates a new TextItem
36426  * @param {Object} cfg Configuration
36427  */
36428 Roo.menu.TextItem = function(cfg){
36429     if (typeof(cfg) == 'string') {
36430         this.text = cfg;
36431     } else {
36432         Roo.apply(this,cfg);
36433     }
36434     
36435     Roo.menu.TextItem.superclass.constructor.call(this);
36436 };
36437
36438 Roo.extend(Roo.menu.TextItem, Roo.menu.BaseItem, {
36439     /**
36440      * @cfg {Boolean} text Text to show on item.
36441      */
36442     text : '',
36443     
36444     /**
36445      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
36446      */
36447     hideOnClick : false,
36448     /**
36449      * @cfg {String} itemCls The default CSS class to use for text items (defaults to "x-menu-text")
36450      */
36451     itemCls : "x-menu-text",
36452
36453     // private
36454     onRender : function(){
36455         var s = document.createElement("span");
36456         s.className = this.itemCls;
36457         s.innerHTML = this.text;
36458         this.el = s;
36459         Roo.menu.TextItem.superclass.onRender.apply(this, arguments);
36460     }
36461 });/*
36462  * Based on:
36463  * Ext JS Library 1.1.1
36464  * Copyright(c) 2006-2007, Ext JS, LLC.
36465  *
36466  * Originally Released Under LGPL - original licence link has changed is not relivant.
36467  *
36468  * Fork - LGPL
36469  * <script type="text/javascript">
36470  */
36471
36472 /**
36473  * @class Roo.menu.Separator
36474  * @extends Roo.menu.BaseItem
36475  * Adds a separator bar to a menu, used to divide logical groups of menu items. Generally you will
36476  * add one of these by using "-" in you call to add() or in your items config rather than creating one directly.
36477  * @constructor
36478  * @param {Object} config Configuration options
36479  */
36480 Roo.menu.Separator = function(config){
36481     Roo.menu.Separator.superclass.constructor.call(this, config);
36482 };
36483
36484 Roo.extend(Roo.menu.Separator, Roo.menu.BaseItem, {
36485     /**
36486      * @cfg {String} itemCls The default CSS class to use for separators (defaults to "x-menu-sep")
36487      */
36488     itemCls : "x-menu-sep",
36489     /**
36490      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
36491      */
36492     hideOnClick : false,
36493
36494     // private
36495     onRender : function(li){
36496         var s = document.createElement("span");
36497         s.className = this.itemCls;
36498         s.innerHTML = "&#160;";
36499         this.el = s;
36500         li.addClass("x-menu-sep-li");
36501         Roo.menu.Separator.superclass.onRender.apply(this, arguments);
36502     }
36503 });/*
36504  * Based on:
36505  * Ext JS Library 1.1.1
36506  * Copyright(c) 2006-2007, Ext JS, LLC.
36507  *
36508  * Originally Released Under LGPL - original licence link has changed is not relivant.
36509  *
36510  * Fork - LGPL
36511  * <script type="text/javascript">
36512  */
36513 /**
36514  * @class Roo.menu.Item
36515  * @extends Roo.menu.BaseItem
36516  * A base class for all menu items that require menu-related functionality (like sub-menus) and are not static
36517  * display items.  Item extends the base functionality of {@link Roo.menu.BaseItem} by adding menu-specific
36518  * activation and click handling.
36519  * @constructor
36520  * Creates a new Item
36521  * @param {Object} config Configuration options
36522  */
36523 Roo.menu.Item = function(config){
36524     Roo.menu.Item.superclass.constructor.call(this, config);
36525     if(this.menu){
36526         this.menu = Roo.menu.MenuMgr.get(this.menu);
36527     }
36528 };
36529 Roo.extend(Roo.menu.Item, Roo.menu.BaseItem, {
36530     
36531     /**
36532      * @cfg {String} text
36533      * The text to show on the menu item.
36534      */
36535     text: '',
36536      /**
36537      * @cfg {String} HTML to render in menu
36538      * The text to show on the menu item (HTML version).
36539      */
36540     html: '',
36541     /**
36542      * @cfg {String} icon
36543      * The path to an icon to display in this menu item (defaults to Roo.BLANK_IMAGE_URL)
36544      */
36545     icon: undefined,
36546     /**
36547      * @cfg {String} itemCls The default CSS class to use for menu items (defaults to "x-menu-item")
36548      */
36549     itemCls : "x-menu-item",
36550     /**
36551      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to true)
36552      */
36553     canActivate : true,
36554     /**
36555      * @cfg {Number} showDelay Length of time in milliseconds to wait before showing this item (defaults to 200)
36556      */
36557     showDelay: 200,
36558     // doc'd in BaseItem
36559     hideDelay: 200,
36560
36561     // private
36562     ctype: "Roo.menu.Item",
36563     
36564     // private
36565     onRender : function(container, position){
36566         var el = document.createElement("a");
36567         el.hideFocus = true;
36568         el.unselectable = "on";
36569         el.href = this.href || "#";
36570         if(this.hrefTarget){
36571             el.target = this.hrefTarget;
36572         }
36573         el.className = this.itemCls + (this.menu ?  " x-menu-item-arrow" : "") + (this.cls ?  " " + this.cls : "");
36574         
36575         var html = this.html.length ? this.html  : String.format('{0}',this.text);
36576         
36577         el.innerHTML = String.format(
36578                 '<img src="{0}" class="x-menu-item-icon {1}" />' + html,
36579                 this.icon || Roo.BLANK_IMAGE_URL, this.iconCls || '');
36580         this.el = el;
36581         Roo.menu.Item.superclass.onRender.call(this, container, position);
36582     },
36583
36584     /**
36585      * Sets the text to display in this menu item
36586      * @param {String} text The text to display
36587      * @param {Boolean} isHTML true to indicate text is pure html.
36588      */
36589     setText : function(text, isHTML){
36590         if (isHTML) {
36591             this.html = text;
36592         } else {
36593             this.text = text;
36594             this.html = '';
36595         }
36596         if(this.rendered){
36597             var html = this.html.length ? this.html  : String.format('{0}',this.text);
36598      
36599             this.el.update(String.format(
36600                 '<img src="{0}" class="x-menu-item-icon {2}">' + html,
36601                 this.icon || Roo.BLANK_IMAGE_URL, this.text, this.iconCls || ''));
36602             this.parentMenu.autoWidth();
36603         }
36604     },
36605
36606     // private
36607     handleClick : function(e){
36608         if(!this.href){ // if no link defined, stop the event automatically
36609             e.stopEvent();
36610         }
36611         Roo.menu.Item.superclass.handleClick.apply(this, arguments);
36612     },
36613
36614     // private
36615     activate : function(autoExpand){
36616         if(Roo.menu.Item.superclass.activate.apply(this, arguments)){
36617             this.focus();
36618             if(autoExpand){
36619                 this.expandMenu();
36620             }
36621         }
36622         return true;
36623     },
36624
36625     // private
36626     shouldDeactivate : function(e){
36627         if(Roo.menu.Item.superclass.shouldDeactivate.call(this, e)){
36628             if(this.menu && this.menu.isVisible()){
36629                 return !this.menu.getEl().getRegion().contains(e.getPoint());
36630             }
36631             return true;
36632         }
36633         return false;
36634     },
36635
36636     // private
36637     deactivate : function(){
36638         Roo.menu.Item.superclass.deactivate.apply(this, arguments);
36639         this.hideMenu();
36640     },
36641
36642     // private
36643     expandMenu : function(autoActivate){
36644         if(!this.disabled && this.menu){
36645             clearTimeout(this.hideTimer);
36646             delete this.hideTimer;
36647             if(!this.menu.isVisible() && !this.showTimer){
36648                 this.showTimer = this.deferExpand.defer(this.showDelay, this, [autoActivate]);
36649             }else if (this.menu.isVisible() && autoActivate){
36650                 this.menu.tryActivate(0, 1);
36651             }
36652         }
36653     },
36654
36655     // private
36656     deferExpand : function(autoActivate){
36657         delete this.showTimer;
36658         this.menu.show(this.container, this.parentMenu.subMenuAlign || "tl-tr?", this.parentMenu);
36659         if(autoActivate){
36660             this.menu.tryActivate(0, 1);
36661         }
36662     },
36663
36664     // private
36665     hideMenu : function(){
36666         clearTimeout(this.showTimer);
36667         delete this.showTimer;
36668         if(!this.hideTimer && this.menu && this.menu.isVisible()){
36669             this.hideTimer = this.deferHide.defer(this.hideDelay, this);
36670         }
36671     },
36672
36673     // private
36674     deferHide : function(){
36675         delete this.hideTimer;
36676         this.menu.hide();
36677     }
36678 });/*
36679  * Based on:
36680  * Ext JS Library 1.1.1
36681  * Copyright(c) 2006-2007, Ext JS, LLC.
36682  *
36683  * Originally Released Under LGPL - original licence link has changed is not relivant.
36684  *
36685  * Fork - LGPL
36686  * <script type="text/javascript">
36687  */
36688  
36689 /**
36690  * @class Roo.menu.CheckItem
36691  * @extends Roo.menu.Item
36692  * Adds a menu item that contains a checkbox by default, but can also be part of a radio group.
36693  * @constructor
36694  * Creates a new CheckItem
36695  * @param {Object} config Configuration options
36696  */
36697 Roo.menu.CheckItem = function(config){
36698     Roo.menu.CheckItem.superclass.constructor.call(this, config);
36699     this.addEvents({
36700         /**
36701          * @event beforecheckchange
36702          * Fires before the checked value is set, providing an opportunity to cancel if needed
36703          * @param {Roo.menu.CheckItem} this
36704          * @param {Boolean} checked The new checked value that will be set
36705          */
36706         "beforecheckchange" : true,
36707         /**
36708          * @event checkchange
36709          * Fires after the checked value has been set
36710          * @param {Roo.menu.CheckItem} this
36711          * @param {Boolean} checked The checked value that was set
36712          */
36713         "checkchange" : true
36714     });
36715     if(this.checkHandler){
36716         this.on('checkchange', this.checkHandler, this.scope);
36717     }
36718 };
36719 Roo.extend(Roo.menu.CheckItem, Roo.menu.Item, {
36720     /**
36721      * @cfg {String} group
36722      * All check items with the same group name will automatically be grouped into a single-select
36723      * radio button group (defaults to '')
36724      */
36725     /**
36726      * @cfg {String} itemCls The default CSS class to use for check items (defaults to "x-menu-item x-menu-check-item")
36727      */
36728     itemCls : "x-menu-item x-menu-check-item",
36729     /**
36730      * @cfg {String} groupClass The default CSS class to use for radio group check items (defaults to "x-menu-group-item")
36731      */
36732     groupClass : "x-menu-group-item",
36733
36734     /**
36735      * @cfg {Boolean} checked True to initialize this checkbox as checked (defaults to false).  Note that
36736      * if this checkbox is part of a radio group (group = true) only the last item in the group that is
36737      * initialized with checked = true will be rendered as checked.
36738      */
36739     checked: false,
36740
36741     // private
36742     ctype: "Roo.menu.CheckItem",
36743
36744     // private
36745     onRender : function(c){
36746         Roo.menu.CheckItem.superclass.onRender.apply(this, arguments);
36747         if(this.group){
36748             this.el.addClass(this.groupClass);
36749         }
36750         Roo.menu.MenuMgr.registerCheckable(this);
36751         if(this.checked){
36752             this.checked = false;
36753             this.setChecked(true, true);
36754         }
36755     },
36756
36757     // private
36758     destroy : function(){
36759         if(this.rendered){
36760             Roo.menu.MenuMgr.unregisterCheckable(this);
36761         }
36762         Roo.menu.CheckItem.superclass.destroy.apply(this, arguments);
36763     },
36764
36765     /**
36766      * Set the checked state of this item
36767      * @param {Boolean} checked The new checked value
36768      * @param {Boolean} suppressEvent (optional) True to prevent the checkchange event from firing (defaults to false)
36769      */
36770     setChecked : function(state, suppressEvent){
36771         if(this.checked != state && this.fireEvent("beforecheckchange", this, state) !== false){
36772             if(this.container){
36773                 this.container[state ? "addClass" : "removeClass"]("x-menu-item-checked");
36774             }
36775             this.checked = state;
36776             if(suppressEvent !== true){
36777                 this.fireEvent("checkchange", this, state);
36778             }
36779         }
36780     },
36781
36782     // private
36783     handleClick : function(e){
36784        if(!this.disabled && !(this.checked && this.group)){// disable unselect on radio item
36785            this.setChecked(!this.checked);
36786        }
36787        Roo.menu.CheckItem.superclass.handleClick.apply(this, arguments);
36788     }
36789 });/*
36790  * Based on:
36791  * Ext JS Library 1.1.1
36792  * Copyright(c) 2006-2007, Ext JS, LLC.
36793  *
36794  * Originally Released Under LGPL - original licence link has changed is not relivant.
36795  *
36796  * Fork - LGPL
36797  * <script type="text/javascript">
36798  */
36799  
36800 /**
36801  * @class Roo.menu.DateItem
36802  * @extends Roo.menu.Adapter
36803  * A menu item that wraps the {@link Roo.DatPicker} component.
36804  * @constructor
36805  * Creates a new DateItem
36806  * @param {Object} config Configuration options
36807  */
36808 Roo.menu.DateItem = function(config){
36809     Roo.menu.DateItem.superclass.constructor.call(this, new Roo.DatePicker(config), config);
36810     /** The Roo.DatePicker object @type Roo.DatePicker */
36811     this.picker = this.component;
36812     this.addEvents({select: true});
36813     
36814     this.picker.on("render", function(picker){
36815         picker.getEl().swallowEvent("click");
36816         picker.container.addClass("x-menu-date-item");
36817     });
36818
36819     this.picker.on("select", this.onSelect, this);
36820 };
36821
36822 Roo.extend(Roo.menu.DateItem, Roo.menu.Adapter, {
36823     // private
36824     onSelect : function(picker, date){
36825         this.fireEvent("select", this, date, picker);
36826         Roo.menu.DateItem.superclass.handleClick.call(this);
36827     }
36828 });/*
36829  * Based on:
36830  * Ext JS Library 1.1.1
36831  * Copyright(c) 2006-2007, Ext JS, LLC.
36832  *
36833  * Originally Released Under LGPL - original licence link has changed is not relivant.
36834  *
36835  * Fork - LGPL
36836  * <script type="text/javascript">
36837  */
36838  
36839 /**
36840  * @class Roo.menu.ColorItem
36841  * @extends Roo.menu.Adapter
36842  * A menu item that wraps the {@link Roo.ColorPalette} component.
36843  * @constructor
36844  * Creates a new ColorItem
36845  * @param {Object} config Configuration options
36846  */
36847 Roo.menu.ColorItem = function(config){
36848     Roo.menu.ColorItem.superclass.constructor.call(this, new Roo.ColorPalette(config), config);
36849     /** The Roo.ColorPalette object @type Roo.ColorPalette */
36850     this.palette = this.component;
36851     this.relayEvents(this.palette, ["select"]);
36852     if(this.selectHandler){
36853         this.on('select', this.selectHandler, this.scope);
36854     }
36855 };
36856 Roo.extend(Roo.menu.ColorItem, Roo.menu.Adapter);/*
36857  * Based on:
36858  * Ext JS Library 1.1.1
36859  * Copyright(c) 2006-2007, Ext JS, LLC.
36860  *
36861  * Originally Released Under LGPL - original licence link has changed is not relivant.
36862  *
36863  * Fork - LGPL
36864  * <script type="text/javascript">
36865  */
36866  
36867
36868 /**
36869  * @class Roo.menu.DateMenu
36870  * @extends Roo.menu.Menu
36871  * A menu containing a {@link Roo.menu.DateItem} component (which provides a date picker).
36872  * @constructor
36873  * Creates a new DateMenu
36874  * @param {Object} config Configuration options
36875  */
36876 Roo.menu.DateMenu = function(config){
36877     Roo.menu.DateMenu.superclass.constructor.call(this, config);
36878     this.plain = true;
36879     var di = new Roo.menu.DateItem(config);
36880     this.add(di);
36881     /**
36882      * The {@link Roo.DatePicker} instance for this DateMenu
36883      * @type DatePicker
36884      */
36885     this.picker = di.picker;
36886     /**
36887      * @event select
36888      * @param {DatePicker} picker
36889      * @param {Date} date
36890      */
36891     this.relayEvents(di, ["select"]);
36892     this.on('beforeshow', function(){
36893         if(this.picker){
36894             this.picker.hideMonthPicker(false);
36895         }
36896     }, this);
36897 };
36898 Roo.extend(Roo.menu.DateMenu, Roo.menu.Menu, {
36899     cls:'x-date-menu'
36900 });/*
36901  * Based on:
36902  * Ext JS Library 1.1.1
36903  * Copyright(c) 2006-2007, Ext JS, LLC.
36904  *
36905  * Originally Released Under LGPL - original licence link has changed is not relivant.
36906  *
36907  * Fork - LGPL
36908  * <script type="text/javascript">
36909  */
36910  
36911
36912 /**
36913  * @class Roo.menu.ColorMenu
36914  * @extends Roo.menu.Menu
36915  * A menu containing a {@link Roo.menu.ColorItem} component (which provides a basic color picker).
36916  * @constructor
36917  * Creates a new ColorMenu
36918  * @param {Object} config Configuration options
36919  */
36920 Roo.menu.ColorMenu = function(config){
36921     Roo.menu.ColorMenu.superclass.constructor.call(this, config);
36922     this.plain = true;
36923     var ci = new Roo.menu.ColorItem(config);
36924     this.add(ci);
36925     /**
36926      * The {@link Roo.ColorPalette} instance for this ColorMenu
36927      * @type ColorPalette
36928      */
36929     this.palette = ci.palette;
36930     /**
36931      * @event select
36932      * @param {ColorPalette} palette
36933      * @param {String} color
36934      */
36935     this.relayEvents(ci, ["select"]);
36936 };
36937 Roo.extend(Roo.menu.ColorMenu, Roo.menu.Menu);/*
36938  * Based on:
36939  * Ext JS Library 1.1.1
36940  * Copyright(c) 2006-2007, Ext JS, LLC.
36941  *
36942  * Originally Released Under LGPL - original licence link has changed is not relivant.
36943  *
36944  * Fork - LGPL
36945  * <script type="text/javascript">
36946  */
36947  
36948 /**
36949  * @class Roo.form.Field
36950  * @extends Roo.BoxComponent
36951  * Base class for form fields that provides default event handling, sizing, value handling and other functionality.
36952  * @constructor
36953  * Creates a new Field
36954  * @param {Object} config Configuration options
36955  */
36956 Roo.form.Field = function(config){
36957     Roo.form.Field.superclass.constructor.call(this, config);
36958 };
36959
36960 Roo.extend(Roo.form.Field, Roo.BoxComponent,  {
36961     /**
36962      * @cfg {String} fieldLabel Label to use when rendering a form.
36963      */
36964        /**
36965      * @cfg {String} qtip Mouse over tip
36966      */
36967      
36968     /**
36969      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
36970      */
36971     invalidClass : "x-form-invalid",
36972     /**
36973      * @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")
36974      */
36975     invalidText : "The value in this field is invalid",
36976     /**
36977      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
36978      */
36979     focusClass : "x-form-focus",
36980     /**
36981      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
36982       automatic validation (defaults to "keyup").
36983      */
36984     validationEvent : "keyup",
36985     /**
36986      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
36987      */
36988     validateOnBlur : true,
36989     /**
36990      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
36991      */
36992     validationDelay : 250,
36993     /**
36994      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
36995      * {tag: "input", type: "text", size: "20", autocomplete: "off"})
36996      */
36997     defaultAutoCreate : {tag: "input", type: "text", size: "20", autocomplete: "off"},
36998     /**
36999      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field")
37000      */
37001     fieldClass : "x-form-field",
37002     /**
37003      * @cfg {String} msgTarget The location where error text should display.  Should be one of the following values (defaults to 'qtip'):
37004      *<pre>
37005 Value         Description
37006 -----------   ----------------------------------------------------------------------
37007 qtip          Display a quick tip when the user hovers over the field
37008 title         Display a default browser title attribute popup
37009 under         Add a block div beneath the field containing the error text
37010 side          Add an error icon to the right of the field with a popup on hover
37011 [element id]  Add the error text directly to the innerHTML of the specified element
37012 </pre>
37013      */
37014     msgTarget : 'qtip',
37015     /**
37016      * @cfg {String} msgFx <b>Experimental</b> The effect used when displaying a validation message under the field (defaults to 'normal').
37017      */
37018     msgFx : 'normal',
37019
37020     /**
37021      * @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.
37022      */
37023     readOnly : false,
37024
37025     /**
37026      * @cfg {Boolean} disabled True to disable the field (defaults to false).
37027      */
37028     disabled : false,
37029
37030     /**
37031      * @cfg {String} inputType The type attribute for input fields -- e.g. radio, text, password (defaults to "text").
37032      */
37033     inputType : undefined,
37034     
37035     /**
37036      * @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).
37037          */
37038         tabIndex : undefined,
37039         
37040     // private
37041     isFormField : true,
37042
37043     // private
37044     hasFocus : false,
37045     /**
37046      * @property {Roo.Element} fieldEl
37047      * Element Containing the rendered Field (with label etc.)
37048      */
37049     /**
37050      * @cfg {Mixed} value A value to initialize this field with.
37051      */
37052     value : undefined,
37053
37054     /**
37055      * @cfg {String} name The field's HTML name attribute.
37056      */
37057     /**
37058      * @cfg {String} cls A CSS class to apply to the field's underlying element.
37059      */
37060
37061         // private ??
37062         initComponent : function(){
37063         Roo.form.Field.superclass.initComponent.call(this);
37064         this.addEvents({
37065             /**
37066              * @event focus
37067              * Fires when this field receives input focus.
37068              * @param {Roo.form.Field} this
37069              */
37070             focus : true,
37071             /**
37072              * @event blur
37073              * Fires when this field loses input focus.
37074              * @param {Roo.form.Field} this
37075              */
37076             blur : true,
37077             /**
37078              * @event specialkey
37079              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
37080              * {@link Roo.EventObject#getKey} to determine which key was pressed.
37081              * @param {Roo.form.Field} this
37082              * @param {Roo.EventObject} e The event object
37083              */
37084             specialkey : true,
37085             /**
37086              * @event change
37087              * Fires just before the field blurs if the field value has changed.
37088              * @param {Roo.form.Field} this
37089              * @param {Mixed} newValue The new value
37090              * @param {Mixed} oldValue The original value
37091              */
37092             change : true,
37093             /**
37094              * @event invalid
37095              * Fires after the field has been marked as invalid.
37096              * @param {Roo.form.Field} this
37097              * @param {String} msg The validation message
37098              */
37099             invalid : true,
37100             /**
37101              * @event valid
37102              * Fires after the field has been validated with no errors.
37103              * @param {Roo.form.Field} this
37104              */
37105             valid : true,
37106              /**
37107              * @event keyup
37108              * Fires after the key up
37109              * @param {Roo.form.Field} this
37110              * @param {Roo.EventObject}  e The event Object
37111              */
37112             keyup : true
37113         });
37114     },
37115
37116     /**
37117      * Returns the name attribute of the field if available
37118      * @return {String} name The field name
37119      */
37120     getName: function(){
37121          return this.rendered && this.el.dom.name ? this.el.dom.name : (this.hiddenName || '');
37122     },
37123
37124     // private
37125     onRender : function(ct, position){
37126         Roo.form.Field.superclass.onRender.call(this, ct, position);
37127         if(!this.el){
37128             var cfg = this.getAutoCreate();
37129             if(!cfg.name){
37130                 cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
37131             }
37132             if (!cfg.name.length) {
37133                 delete cfg.name;
37134             }
37135             if(this.inputType){
37136                 cfg.type = this.inputType;
37137             }
37138             this.el = ct.createChild(cfg, position);
37139         }
37140         var type = this.el.dom.type;
37141         if(type){
37142             if(type == 'password'){
37143                 type = 'text';
37144             }
37145             this.el.addClass('x-form-'+type);
37146         }
37147         if(this.readOnly){
37148             this.el.dom.readOnly = true;
37149         }
37150         if(this.tabIndex !== undefined){
37151             this.el.dom.setAttribute('tabIndex', this.tabIndex);
37152         }
37153
37154         this.el.addClass([this.fieldClass, this.cls]);
37155         this.initValue();
37156     },
37157
37158     /**
37159      * Apply the behaviors of this component to an existing element. <b>This is used instead of render().</b>
37160      * @param {String/HTMLElement/Element} el The id of the node, a DOM node or an existing Element
37161      * @return {Roo.form.Field} this
37162      */
37163     applyTo : function(target){
37164         this.allowDomMove = false;
37165         this.el = Roo.get(target);
37166         this.render(this.el.dom.parentNode);
37167         return this;
37168     },
37169
37170     // private
37171     initValue : function(){
37172         if(this.value !== undefined){
37173             this.setValue(this.value);
37174         }else if(this.el.dom.value.length > 0){
37175             this.setValue(this.el.dom.value);
37176         }
37177     },
37178
37179     /**
37180      * Returns true if this field has been changed since it was originally loaded and is not disabled.
37181      */
37182     isDirty : function() {
37183         if(this.disabled) {
37184             return false;
37185         }
37186         return String(this.getValue()) !== String(this.originalValue);
37187     },
37188
37189     // private
37190     afterRender : function(){
37191         Roo.form.Field.superclass.afterRender.call(this);
37192         this.initEvents();
37193     },
37194
37195     // private
37196     fireKey : function(e){
37197         //Roo.log('field ' + e.getKey());
37198         if(e.isNavKeyPress()){
37199             this.fireEvent("specialkey", this, e);
37200         }
37201     },
37202
37203     /**
37204      * Resets the current field value to the originally loaded value and clears any validation messages
37205      */
37206     reset : function(){
37207         this.setValue(this.resetValue);
37208         this.clearInvalid();
37209     },
37210
37211     // private
37212     initEvents : function(){
37213         // safari killled keypress - so keydown is now used..
37214         this.el.on("keydown" , this.fireKey,  this);
37215         this.el.on("focus", this.onFocus,  this);
37216         this.el.on("blur", this.onBlur,  this);
37217         this.el.relayEvent('keyup', this);
37218
37219         // reference to original value for reset
37220         this.originalValue = this.getValue();
37221         this.resetValue =  this.getValue();
37222     },
37223
37224     // private
37225     onFocus : function(){
37226         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
37227             this.el.addClass(this.focusClass);
37228         }
37229         if(!this.hasFocus){
37230             this.hasFocus = true;
37231             this.startValue = this.getValue();
37232             this.fireEvent("focus", this);
37233         }
37234     },
37235
37236     beforeBlur : Roo.emptyFn,
37237
37238     // private
37239     onBlur : function(){
37240         this.beforeBlur();
37241         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
37242             this.el.removeClass(this.focusClass);
37243         }
37244         this.hasFocus = false;
37245         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
37246             this.validate();
37247         }
37248         var v = this.getValue();
37249         if(String(v) !== String(this.startValue)){
37250             this.fireEvent('change', this, v, this.startValue);
37251         }
37252         this.fireEvent("blur", this);
37253     },
37254
37255     /**
37256      * Returns whether or not the field value is currently valid
37257      * @param {Boolean} preventMark True to disable marking the field invalid
37258      * @return {Boolean} True if the value is valid, else false
37259      */
37260     isValid : function(preventMark){
37261         if(this.disabled){
37262             return true;
37263         }
37264         var restore = this.preventMark;
37265         this.preventMark = preventMark === true;
37266         var v = this.validateValue(this.processValue(this.getRawValue()));
37267         this.preventMark = restore;
37268         return v;
37269     },
37270
37271     /**
37272      * Validates the field value
37273      * @return {Boolean} True if the value is valid, else false
37274      */
37275     validate : function(){
37276         if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
37277             this.clearInvalid();
37278             return true;
37279         }
37280         return false;
37281     },
37282
37283     processValue : function(value){
37284         return value;
37285     },
37286
37287     // private
37288     // Subclasses should provide the validation implementation by overriding this
37289     validateValue : function(value){
37290         return true;
37291     },
37292
37293     /**
37294      * Mark this field as invalid
37295      * @param {String} msg The validation message
37296      */
37297     markInvalid : function(msg){
37298         if(!this.rendered || this.preventMark){ // not rendered
37299             return;
37300         }
37301         
37302         var obj = (typeof(this.combo) != 'undefined') ? this.combo : this; // fix the combox array!!
37303         
37304         obj.el.addClass(this.invalidClass);
37305         msg = msg || this.invalidText;
37306         switch(this.msgTarget){
37307             case 'qtip':
37308                 obj.el.dom.qtip = msg;
37309                 obj.el.dom.qclass = 'x-form-invalid-tip';
37310                 if(Roo.QuickTips){ // fix for floating editors interacting with DND
37311                     Roo.QuickTips.enable();
37312                 }
37313                 break;
37314             case 'title':
37315                 this.el.dom.title = msg;
37316                 break;
37317             case 'under':
37318                 if(!this.errorEl){
37319                     var elp = this.el.findParent('.x-form-element', 5, true);
37320                     this.errorEl = elp.createChild({cls:'x-form-invalid-msg'});
37321                     this.errorEl.setWidth(elp.getWidth(true)-20);
37322                 }
37323                 this.errorEl.update(msg);
37324                 Roo.form.Field.msgFx[this.msgFx].show(this.errorEl, this);
37325                 break;
37326             case 'side':
37327                 if(!this.errorIcon){
37328                     var elp = this.el.findParent('.x-form-element', 5, true);
37329                     this.errorIcon = elp.createChild({cls:'x-form-invalid-icon'});
37330                 }
37331                 this.alignErrorIcon();
37332                 this.errorIcon.dom.qtip = msg;
37333                 this.errorIcon.dom.qclass = 'x-form-invalid-tip';
37334                 this.errorIcon.show();
37335                 this.on('resize', this.alignErrorIcon, this);
37336                 break;
37337             default:
37338                 var t = Roo.getDom(this.msgTarget);
37339                 t.innerHTML = msg;
37340                 t.style.display = this.msgDisplay;
37341                 break;
37342         }
37343         this.fireEvent('invalid', this, msg);
37344     },
37345
37346     // private
37347     alignErrorIcon : function(){
37348         this.errorIcon.alignTo(this.el, 'tl-tr', [2, 0]);
37349     },
37350
37351     /**
37352      * Clear any invalid styles/messages for this field
37353      */
37354     clearInvalid : function(){
37355         if(!this.rendered || this.preventMark){ // not rendered
37356             return;
37357         }
37358         var obj = (typeof(this.combo) != 'undefined') ? this.combo : this; // fix the combox array!!
37359         
37360         obj.el.removeClass(this.invalidClass);
37361         switch(this.msgTarget){
37362             case 'qtip':
37363                 obj.el.dom.qtip = '';
37364                 break;
37365             case 'title':
37366                 this.el.dom.title = '';
37367                 break;
37368             case 'under':
37369                 if(this.errorEl){
37370                     Roo.form.Field.msgFx[this.msgFx].hide(this.errorEl, this);
37371                 }
37372                 break;
37373             case 'side':
37374                 if(this.errorIcon){
37375                     this.errorIcon.dom.qtip = '';
37376                     this.errorIcon.hide();
37377                     this.un('resize', this.alignErrorIcon, this);
37378                 }
37379                 break;
37380             default:
37381                 var t = Roo.getDom(this.msgTarget);
37382                 t.innerHTML = '';
37383                 t.style.display = 'none';
37384                 break;
37385         }
37386         this.fireEvent('valid', this);
37387     },
37388
37389     /**
37390      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
37391      * @return {Mixed} value The field value
37392      */
37393     getRawValue : function(){
37394         var v = this.el.getValue();
37395         
37396         return v;
37397     },
37398
37399     /**
37400      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
37401      * @return {Mixed} value The field value
37402      */
37403     getValue : function(){
37404         var v = this.el.getValue();
37405          
37406         return v;
37407     },
37408
37409     /**
37410      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
37411      * @param {Mixed} value The value to set
37412      */
37413     setRawValue : function(v){
37414         return this.el.dom.value = (v === null || v === undefined ? '' : v);
37415     },
37416
37417     /**
37418      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
37419      * @param {Mixed} value The value to set
37420      */
37421     setValue : function(v){
37422         this.value = v;
37423         if(this.rendered){
37424             this.el.dom.value = (v === null || v === undefined ? '' : v);
37425              this.validate();
37426         }
37427     },
37428
37429     adjustSize : function(w, h){
37430         var s = Roo.form.Field.superclass.adjustSize.call(this, w, h);
37431         s.width = this.adjustWidth(this.el.dom.tagName, s.width);
37432         return s;
37433     },
37434
37435     adjustWidth : function(tag, w){
37436         tag = tag.toLowerCase();
37437         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
37438             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
37439                 if(tag == 'input'){
37440                     return w + 2;
37441                 }
37442                 if(tag == 'textarea'){
37443                     return w-2;
37444                 }
37445             }else if(Roo.isOpera){
37446                 if(tag == 'input'){
37447                     return w + 2;
37448                 }
37449                 if(tag == 'textarea'){
37450                     return w-2;
37451                 }
37452             }
37453         }
37454         return w;
37455     }
37456 });
37457
37458
37459 // anything other than normal should be considered experimental
37460 Roo.form.Field.msgFx = {
37461     normal : {
37462         show: function(msgEl, f){
37463             msgEl.setDisplayed('block');
37464         },
37465
37466         hide : function(msgEl, f){
37467             msgEl.setDisplayed(false).update('');
37468         }
37469     },
37470
37471     slide : {
37472         show: function(msgEl, f){
37473             msgEl.slideIn('t', {stopFx:true});
37474         },
37475
37476         hide : function(msgEl, f){
37477             msgEl.slideOut('t', {stopFx:true,useDisplay:true});
37478         }
37479     },
37480
37481     slideRight : {
37482         show: function(msgEl, f){
37483             msgEl.fixDisplay();
37484             msgEl.alignTo(f.el, 'tl-tr');
37485             msgEl.slideIn('l', {stopFx:true});
37486         },
37487
37488         hide : function(msgEl, f){
37489             msgEl.slideOut('l', {stopFx:true,useDisplay:true});
37490         }
37491     }
37492 };/*
37493  * Based on:
37494  * Ext JS Library 1.1.1
37495  * Copyright(c) 2006-2007, Ext JS, LLC.
37496  *
37497  * Originally Released Under LGPL - original licence link has changed is not relivant.
37498  *
37499  * Fork - LGPL
37500  * <script type="text/javascript">
37501  */
37502  
37503
37504 /**
37505  * @class Roo.form.TextField
37506  * @extends Roo.form.Field
37507  * Basic text field.  Can be used as a direct replacement for traditional text inputs, or as the base
37508  * class for more sophisticated input controls (like {@link Roo.form.TextArea} and {@link Roo.form.ComboBox}).
37509  * @constructor
37510  * Creates a new TextField
37511  * @param {Object} config Configuration options
37512  */
37513 Roo.form.TextField = function(config){
37514     Roo.form.TextField.superclass.constructor.call(this, config);
37515     this.addEvents({
37516         /**
37517          * @event autosize
37518          * Fires when the autosize function is triggered.  The field may or may not have actually changed size
37519          * according to the default logic, but this event provides a hook for the developer to apply additional
37520          * logic at runtime to resize the field if needed.
37521              * @param {Roo.form.Field} this This text field
37522              * @param {Number} width The new field width
37523              */
37524         autosize : true
37525     });
37526 };
37527
37528 Roo.extend(Roo.form.TextField, Roo.form.Field,  {
37529     /**
37530      * @cfg {Boolean} grow True if this field should automatically grow and shrink to its content
37531      */
37532     grow : false,
37533     /**
37534      * @cfg {Number} growMin The minimum width to allow when grow = true (defaults to 30)
37535      */
37536     growMin : 30,
37537     /**
37538      * @cfg {Number} growMax The maximum width to allow when grow = true (defaults to 800)
37539      */
37540     growMax : 800,
37541     /**
37542      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
37543      */
37544     vtype : null,
37545     /**
37546      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
37547      */
37548     maskRe : null,
37549     /**
37550      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
37551      */
37552     disableKeyFilter : false,
37553     /**
37554      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
37555      */
37556     allowBlank : true,
37557     /**
37558      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
37559      */
37560     minLength : 0,
37561     /**
37562      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
37563      */
37564     maxLength : Number.MAX_VALUE,
37565     /**
37566      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
37567      */
37568     minLengthText : "The minimum length for this field is {0}",
37569     /**
37570      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
37571      */
37572     maxLengthText : "The maximum length for this field is {0}",
37573     /**
37574      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
37575      */
37576     selectOnFocus : false,
37577     /**
37578      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
37579      */
37580     blankText : "This field is required",
37581     /**
37582      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
37583      * If available, this function will be called only after the basic validators all return true, and will be passed the
37584      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
37585      */
37586     validator : null,
37587     /**
37588      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
37589      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
37590      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
37591      */
37592     regex : null,
37593     /**
37594      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
37595      */
37596     regexText : "",
37597     /**
37598      * @cfg {String} emptyText The default text to display in an empty field - placeholder... (defaults to null).
37599      */
37600     emptyText : null,
37601    
37602
37603     // private
37604     initEvents : function()
37605     {
37606         if (this.emptyText) {
37607             this.el.attr('placeholder', this.emptyText);
37608         }
37609         
37610         Roo.form.TextField.superclass.initEvents.call(this);
37611         if(this.validationEvent == 'keyup'){
37612             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
37613             this.el.on('keyup', this.filterValidation, this);
37614         }
37615         else if(this.validationEvent !== false){
37616             this.el.on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
37617         }
37618         
37619         if(this.selectOnFocus){
37620             this.on("focus", this.preFocus, this);
37621             
37622         }
37623         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
37624             this.el.on("keypress", this.filterKeys, this);
37625         }
37626         if(this.grow){
37627             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
37628             this.el.on("click", this.autoSize,  this);
37629         }
37630         if(this.el.is('input[type=password]') && Roo.isSafari){
37631             this.el.on('keydown', this.SafariOnKeyDown, this);
37632         }
37633     },
37634
37635     processValue : function(value){
37636         if(this.stripCharsRe){
37637             var newValue = value.replace(this.stripCharsRe, '');
37638             if(newValue !== value){
37639                 this.setRawValue(newValue);
37640                 return newValue;
37641             }
37642         }
37643         return value;
37644     },
37645
37646     filterValidation : function(e){
37647         if(!e.isNavKeyPress()){
37648             this.validationTask.delay(this.validationDelay);
37649         }
37650     },
37651
37652     // private
37653     onKeyUp : function(e){
37654         if(!e.isNavKeyPress()){
37655             this.autoSize();
37656         }
37657     },
37658
37659     /**
37660      * Resets the current field value to the originally-loaded value and clears any validation messages.
37661      *  
37662      */
37663     reset : function(){
37664         Roo.form.TextField.superclass.reset.call(this);
37665        
37666     },
37667
37668     
37669     // private
37670     preFocus : function(){
37671         
37672         if(this.selectOnFocus){
37673             this.el.dom.select();
37674         }
37675     },
37676
37677     
37678     // private
37679     filterKeys : function(e){
37680         var k = e.getKey();
37681         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
37682             return;
37683         }
37684         var c = e.getCharCode(), cc = String.fromCharCode(c);
37685         if(Roo.isIE && (e.isSpecialKey() || !cc)){
37686             return;
37687         }
37688         if(!this.maskRe.test(cc)){
37689             e.stopEvent();
37690         }
37691     },
37692
37693     setValue : function(v){
37694         
37695         Roo.form.TextField.superclass.setValue.apply(this, arguments);
37696         
37697         this.autoSize();
37698     },
37699
37700     /**
37701      * Validates a value according to the field's validation rules and marks the field as invalid
37702      * if the validation fails
37703      * @param {Mixed} value The value to validate
37704      * @return {Boolean} True if the value is valid, else false
37705      */
37706     validateValue : function(value){
37707         if(value.length < 1)  { // if it's blank
37708              if(this.allowBlank){
37709                 this.clearInvalid();
37710                 return true;
37711              }else{
37712                 this.markInvalid(this.blankText);
37713                 return false;
37714              }
37715         }
37716         if(value.length < this.minLength){
37717             this.markInvalid(String.format(this.minLengthText, this.minLength));
37718             return false;
37719         }
37720         if(value.length > this.maxLength){
37721             this.markInvalid(String.format(this.maxLengthText, this.maxLength));
37722             return false;
37723         }
37724         if(this.vtype){
37725             var vt = Roo.form.VTypes;
37726             if(!vt[this.vtype](value, this)){
37727                 this.markInvalid(this.vtypeText || vt[this.vtype +'Text']);
37728                 return false;
37729             }
37730         }
37731         if(typeof this.validator == "function"){
37732             var msg = this.validator(value);
37733             if(msg !== true){
37734                 this.markInvalid(msg);
37735                 return false;
37736             }
37737         }
37738         if(this.regex && !this.regex.test(value)){
37739             this.markInvalid(this.regexText);
37740             return false;
37741         }
37742         return true;
37743     },
37744
37745     /**
37746      * Selects text in this field
37747      * @param {Number} start (optional) The index where the selection should start (defaults to 0)
37748      * @param {Number} end (optional) The index where the selection should end (defaults to the text length)
37749      */
37750     selectText : function(start, end){
37751         var v = this.getRawValue();
37752         if(v.length > 0){
37753             start = start === undefined ? 0 : start;
37754             end = end === undefined ? v.length : end;
37755             var d = this.el.dom;
37756             if(d.setSelectionRange){
37757                 d.setSelectionRange(start, end);
37758             }else if(d.createTextRange){
37759                 var range = d.createTextRange();
37760                 range.moveStart("character", start);
37761                 range.moveEnd("character", v.length-end);
37762                 range.select();
37763             }
37764         }
37765     },
37766
37767     /**
37768      * Automatically grows the field to accomodate the width of the text up to the maximum field width allowed.
37769      * This only takes effect if grow = true, and fires the autosize event.
37770      */
37771     autoSize : function(){
37772         if(!this.grow || !this.rendered){
37773             return;
37774         }
37775         if(!this.metrics){
37776             this.metrics = Roo.util.TextMetrics.createInstance(this.el);
37777         }
37778         var el = this.el;
37779         var v = el.dom.value;
37780         var d = document.createElement('div');
37781         d.appendChild(document.createTextNode(v));
37782         v = d.innerHTML;
37783         d = null;
37784         v += "&#160;";
37785         var w = Math.min(this.growMax, Math.max(this.metrics.getWidth(v) + /* add extra padding */ 10, this.growMin));
37786         this.el.setWidth(w);
37787         this.fireEvent("autosize", this, w);
37788     },
37789     
37790     // private
37791     SafariOnKeyDown : function(event)
37792     {
37793         // this is a workaround for a password hang bug on chrome/ webkit.
37794         
37795         var isSelectAll = false;
37796         
37797         if(this.el.dom.selectionEnd > 0){
37798             isSelectAll = (this.el.dom.selectionEnd - this.el.dom.selectionStart - this.getValue().length == 0) ? true : false;
37799         }
37800         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
37801             event.preventDefault();
37802             this.setValue('');
37803             return;
37804         }
37805         
37806         if(isSelectAll){ // backspace and delete key
37807             
37808             event.preventDefault();
37809             // this is very hacky as keydown always get's upper case.
37810             //
37811             var cc = String.fromCharCode(event.getCharCode());
37812             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
37813             
37814         }
37815         
37816         
37817     }
37818 });/*
37819  * Based on:
37820  * Ext JS Library 1.1.1
37821  * Copyright(c) 2006-2007, Ext JS, LLC.
37822  *
37823  * Originally Released Under LGPL - original licence link has changed is not relivant.
37824  *
37825  * Fork - LGPL
37826  * <script type="text/javascript">
37827  */
37828  
37829 /**
37830  * @class Roo.form.Hidden
37831  * @extends Roo.form.TextField
37832  * Simple Hidden element used on forms 
37833  * 
37834  * usage: form.add(new Roo.form.HiddenField({ 'name' : 'test1' }));
37835  * 
37836  * @constructor
37837  * Creates a new Hidden form element.
37838  * @param {Object} config Configuration options
37839  */
37840
37841
37842
37843 // easy hidden field...
37844 Roo.form.Hidden = function(config){
37845     Roo.form.Hidden.superclass.constructor.call(this, config);
37846 };
37847   
37848 Roo.extend(Roo.form.Hidden, Roo.form.TextField, {
37849     fieldLabel:      '',
37850     inputType:      'hidden',
37851     width:          50,
37852     allowBlank:     true,
37853     labelSeparator: '',
37854     hidden:         true,
37855     itemCls :       'x-form-item-display-none'
37856
37857
37858 });
37859
37860
37861 /*
37862  * Based on:
37863  * Ext JS Library 1.1.1
37864  * Copyright(c) 2006-2007, Ext JS, LLC.
37865  *
37866  * Originally Released Under LGPL - original licence link has changed is not relivant.
37867  *
37868  * Fork - LGPL
37869  * <script type="text/javascript">
37870  */
37871  
37872 /**
37873  * @class Roo.form.TriggerField
37874  * @extends Roo.form.TextField
37875  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
37876  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
37877  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
37878  * for which you can provide a custom implementation.  For example:
37879  * <pre><code>
37880 var trigger = new Roo.form.TriggerField();
37881 trigger.onTriggerClick = myTriggerFn;
37882 trigger.applyTo('my-field');
37883 </code></pre>
37884  *
37885  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
37886  * {@link Roo.form.DateField} and {@link Roo.form.ComboBox} are perfect examples of this.
37887  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
37888  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
37889  * @constructor
37890  * Create a new TriggerField.
37891  * @param {Object} config Configuration options (valid {@Roo.form.TextField} config options will also be applied
37892  * to the base TextField)
37893  */
37894 Roo.form.TriggerField = function(config){
37895     this.mimicing = false;
37896     Roo.form.TriggerField.superclass.constructor.call(this, config);
37897 };
37898
37899 Roo.extend(Roo.form.TriggerField, Roo.form.TextField,  {
37900     /**
37901      * @cfg {String} triggerClass A CSS class to apply to the trigger
37902      */
37903     /**
37904      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
37905      * {tag: "input", type: "text", size: "16", autocomplete: "off"})
37906      */
37907     defaultAutoCreate : {tag: "input", type: "text", size: "16", autocomplete: "off"},
37908     /**
37909      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
37910      */
37911     hideTrigger:false,
37912
37913     /** @cfg {Boolean} grow @hide */
37914     /** @cfg {Number} growMin @hide */
37915     /** @cfg {Number} growMax @hide */
37916
37917     /**
37918      * @hide 
37919      * @method
37920      */
37921     autoSize: Roo.emptyFn,
37922     // private
37923     monitorTab : true,
37924     // private
37925     deferHeight : true,
37926
37927     
37928     actionMode : 'wrap',
37929     // private
37930     onResize : function(w, h){
37931         Roo.form.TriggerField.superclass.onResize.apply(this, arguments);
37932         if(typeof w == 'number'){
37933             var x = w - this.trigger.getWidth();
37934             this.el.setWidth(this.adjustWidth('input', x));
37935             this.trigger.setStyle('left', x+'px');
37936         }
37937     },
37938
37939     // private
37940     adjustSize : Roo.BoxComponent.prototype.adjustSize,
37941
37942     // private
37943     getResizeEl : function(){
37944         return this.wrap;
37945     },
37946
37947     // private
37948     getPositionEl : function(){
37949         return this.wrap;
37950     },
37951
37952     // private
37953     alignErrorIcon : function(){
37954         this.errorIcon.alignTo(this.wrap, 'tl-tr', [2, 0]);
37955     },
37956
37957     // private
37958     onRender : function(ct, position){
37959         Roo.form.TriggerField.superclass.onRender.call(this, ct, position);
37960         this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
37961         this.trigger = this.wrap.createChild(this.triggerConfig ||
37962                 {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.triggerClass});
37963         if(this.hideTrigger){
37964             this.trigger.setDisplayed(false);
37965         }
37966         this.initTrigger();
37967         if(!this.width){
37968             this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
37969         }
37970     },
37971
37972     // private
37973     initTrigger : function(){
37974         this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
37975         this.trigger.addClassOnOver('x-form-trigger-over');
37976         this.trigger.addClassOnClick('x-form-trigger-click');
37977     },
37978
37979     // private
37980     onDestroy : function(){
37981         if(this.trigger){
37982             this.trigger.removeAllListeners();
37983             this.trigger.remove();
37984         }
37985         if(this.wrap){
37986             this.wrap.remove();
37987         }
37988         Roo.form.TriggerField.superclass.onDestroy.call(this);
37989     },
37990
37991     // private
37992     onFocus : function(){
37993         Roo.form.TriggerField.superclass.onFocus.call(this);
37994         if(!this.mimicing){
37995             this.wrap.addClass('x-trigger-wrap-focus');
37996             this.mimicing = true;
37997             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
37998             if(this.monitorTab){
37999                 this.el.on("keydown", this.checkTab, this);
38000             }
38001         }
38002     },
38003
38004     // private
38005     checkTab : function(e){
38006         if(e.getKey() == e.TAB){
38007             this.triggerBlur();
38008         }
38009     },
38010
38011     // private
38012     onBlur : function(){
38013         // do nothing
38014     },
38015
38016     // private
38017     mimicBlur : function(e, t){
38018         if(!this.wrap.contains(t) && this.validateBlur()){
38019             this.triggerBlur();
38020         }
38021     },
38022
38023     // private
38024     triggerBlur : function(){
38025         this.mimicing = false;
38026         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
38027         if(this.monitorTab){
38028             this.el.un("keydown", this.checkTab, this);
38029         }
38030         this.wrap.removeClass('x-trigger-wrap-focus');
38031         Roo.form.TriggerField.superclass.onBlur.call(this);
38032     },
38033
38034     // private
38035     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
38036     validateBlur : function(e, t){
38037         return true;
38038     },
38039
38040     // private
38041     onDisable : function(){
38042         Roo.form.TriggerField.superclass.onDisable.call(this);
38043         if(this.wrap){
38044             this.wrap.addClass('x-item-disabled');
38045         }
38046     },
38047
38048     // private
38049     onEnable : function(){
38050         Roo.form.TriggerField.superclass.onEnable.call(this);
38051         if(this.wrap){
38052             this.wrap.removeClass('x-item-disabled');
38053         }
38054     },
38055
38056     // private
38057     onShow : function(){
38058         var ae = this.getActionEl();
38059         
38060         if(ae){
38061             ae.dom.style.display = '';
38062             ae.dom.style.visibility = 'visible';
38063         }
38064     },
38065
38066     // private
38067     
38068     onHide : function(){
38069         var ae = this.getActionEl();
38070         ae.dom.style.display = 'none';
38071     },
38072
38073     /**
38074      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
38075      * by an implementing function.
38076      * @method
38077      * @param {EventObject} e
38078      */
38079     onTriggerClick : Roo.emptyFn
38080 });
38081
38082 // TwinTriggerField is not a public class to be used directly.  It is meant as an abstract base class
38083 // to be extended by an implementing class.  For an example of implementing this class, see the custom
38084 // SearchField implementation here: http://extjs.com/deploy/ext/examples/form/custom.html
38085 Roo.form.TwinTriggerField = Roo.extend(Roo.form.TriggerField, {
38086     initComponent : function(){
38087         Roo.form.TwinTriggerField.superclass.initComponent.call(this);
38088
38089         this.triggerConfig = {
38090             tag:'span', cls:'x-form-twin-triggers', cn:[
38091             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger1Class},
38092             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger2Class}
38093         ]};
38094     },
38095
38096     getTrigger : function(index){
38097         return this.triggers[index];
38098     },
38099
38100     initTrigger : function(){
38101         var ts = this.trigger.select('.x-form-trigger', true);
38102         this.wrap.setStyle('overflow', 'hidden');
38103         var triggerField = this;
38104         ts.each(function(t, all, index){
38105             t.hide = function(){
38106                 var w = triggerField.wrap.getWidth();
38107                 this.dom.style.display = 'none';
38108                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
38109             };
38110             t.show = function(){
38111                 var w = triggerField.wrap.getWidth();
38112                 this.dom.style.display = '';
38113                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
38114             };
38115             var triggerIndex = 'Trigger'+(index+1);
38116
38117             if(this['hide'+triggerIndex]){
38118                 t.dom.style.display = 'none';
38119             }
38120             t.on("click", this['on'+triggerIndex+'Click'], this, {preventDefault:true});
38121             t.addClassOnOver('x-form-trigger-over');
38122             t.addClassOnClick('x-form-trigger-click');
38123         }, this);
38124         this.triggers = ts.elements;
38125     },
38126
38127     onTrigger1Click : Roo.emptyFn,
38128     onTrigger2Click : Roo.emptyFn
38129 });/*
38130  * Based on:
38131  * Ext JS Library 1.1.1
38132  * Copyright(c) 2006-2007, Ext JS, LLC.
38133  *
38134  * Originally Released Under LGPL - original licence link has changed is not relivant.
38135  *
38136  * Fork - LGPL
38137  * <script type="text/javascript">
38138  */
38139  
38140 /**
38141  * @class Roo.form.TextArea
38142  * @extends Roo.form.TextField
38143  * Multiline text field.  Can be used as a direct replacement for traditional textarea fields, plus adds
38144  * support for auto-sizing.
38145  * @constructor
38146  * Creates a new TextArea
38147  * @param {Object} config Configuration options
38148  */
38149 Roo.form.TextArea = function(config){
38150     Roo.form.TextArea.superclass.constructor.call(this, config);
38151     // these are provided exchanges for backwards compat
38152     // minHeight/maxHeight were replaced by growMin/growMax to be
38153     // compatible with TextField growing config values
38154     if(this.minHeight !== undefined){
38155         this.growMin = this.minHeight;
38156     }
38157     if(this.maxHeight !== undefined){
38158         this.growMax = this.maxHeight;
38159     }
38160 };
38161
38162 Roo.extend(Roo.form.TextArea, Roo.form.TextField,  {
38163     /**
38164      * @cfg {Number} growMin The minimum height to allow when grow = true (defaults to 60)
38165      */
38166     growMin : 60,
38167     /**
38168      * @cfg {Number} growMax The maximum height to allow when grow = true (defaults to 1000)
38169      */
38170     growMax: 1000,
38171     /**
38172      * @cfg {Boolean} preventScrollbars True to prevent scrollbars from appearing regardless of how much text is
38173      * in the field (equivalent to setting overflow: hidden, defaults to false)
38174      */
38175     preventScrollbars: false,
38176     /**
38177      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
38178      * {tag: "textarea", style: "width:300px;height:60px;", autocomplete: "off"})
38179      */
38180
38181     // private
38182     onRender : function(ct, position){
38183         if(!this.el){
38184             this.defaultAutoCreate = {
38185                 tag: "textarea",
38186                 style:"width:300px;height:60px;",
38187                 autocomplete: "off"
38188             };
38189         }
38190         Roo.form.TextArea.superclass.onRender.call(this, ct, position);
38191         if(this.grow){
38192             this.textSizeEl = Roo.DomHelper.append(document.body, {
38193                 tag: "pre", cls: "x-form-grow-sizer"
38194             });
38195             if(this.preventScrollbars){
38196                 this.el.setStyle("overflow", "hidden");
38197             }
38198             this.el.setHeight(this.growMin);
38199         }
38200     },
38201
38202     onDestroy : function(){
38203         if(this.textSizeEl){
38204             this.textSizeEl.parentNode.removeChild(this.textSizeEl);
38205         }
38206         Roo.form.TextArea.superclass.onDestroy.call(this);
38207     },
38208
38209     // private
38210     onKeyUp : function(e){
38211         if(!e.isNavKeyPress() || e.getKey() == e.ENTER){
38212             this.autoSize();
38213         }
38214     },
38215
38216     /**
38217      * Automatically grows the field to accomodate the height of the text up to the maximum field height allowed.
38218      * This only takes effect if grow = true, and fires the autosize event if the height changes.
38219      */
38220     autoSize : function(){
38221         if(!this.grow || !this.textSizeEl){
38222             return;
38223         }
38224         var el = this.el;
38225         var v = el.dom.value;
38226         var ts = this.textSizeEl;
38227
38228         ts.innerHTML = '';
38229         ts.appendChild(document.createTextNode(v));
38230         v = ts.innerHTML;
38231
38232         Roo.fly(ts).setWidth(this.el.getWidth());
38233         if(v.length < 1){
38234             v = "&#160;&#160;";
38235         }else{
38236             if(Roo.isIE){
38237                 v = v.replace(/\n/g, '<p>&#160;</p>');
38238             }
38239             v += "&#160;\n&#160;";
38240         }
38241         ts.innerHTML = v;
38242         var h = Math.min(this.growMax, Math.max(ts.offsetHeight, this.growMin));
38243         if(h != this.lastHeight){
38244             this.lastHeight = h;
38245             this.el.setHeight(h);
38246             this.fireEvent("autosize", this, h);
38247         }
38248     }
38249 });/*
38250  * Based on:
38251  * Ext JS Library 1.1.1
38252  * Copyright(c) 2006-2007, Ext JS, LLC.
38253  *
38254  * Originally Released Under LGPL - original licence link has changed is not relivant.
38255  *
38256  * Fork - LGPL
38257  * <script type="text/javascript">
38258  */
38259  
38260
38261 /**
38262  * @class Roo.form.NumberField
38263  * @extends Roo.form.TextField
38264  * Numeric text field that provides automatic keystroke filtering and numeric validation.
38265  * @constructor
38266  * Creates a new NumberField
38267  * @param {Object} config Configuration options
38268  */
38269 Roo.form.NumberField = function(config){
38270     Roo.form.NumberField.superclass.constructor.call(this, config);
38271 };
38272
38273 Roo.extend(Roo.form.NumberField, Roo.form.TextField,  {
38274     /**
38275      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field x-form-num-field")
38276      */
38277     fieldClass: "x-form-field x-form-num-field",
38278     /**
38279      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
38280      */
38281     allowDecimals : true,
38282     /**
38283      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
38284      */
38285     decimalSeparator : ".",
38286     /**
38287      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
38288      */
38289     decimalPrecision : 2,
38290     /**
38291      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
38292      */
38293     allowNegative : true,
38294     /**
38295      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
38296      */
38297     minValue : Number.NEGATIVE_INFINITY,
38298     /**
38299      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
38300      */
38301     maxValue : Number.MAX_VALUE,
38302     /**
38303      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
38304      */
38305     minText : "The minimum value for this field is {0}",
38306     /**
38307      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
38308      */
38309     maxText : "The maximum value for this field is {0}",
38310     /**
38311      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
38312      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
38313      */
38314     nanText : "{0} is not a valid number",
38315
38316     // private
38317     initEvents : function(){
38318         Roo.form.NumberField.superclass.initEvents.call(this);
38319         var allowed = "0123456789";
38320         if(this.allowDecimals){
38321             allowed += this.decimalSeparator;
38322         }
38323         if(this.allowNegative){
38324             allowed += "-";
38325         }
38326         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
38327         var keyPress = function(e){
38328             var k = e.getKey();
38329             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
38330                 return;
38331             }
38332             var c = e.getCharCode();
38333             if(allowed.indexOf(String.fromCharCode(c)) === -1){
38334                 e.stopEvent();
38335             }
38336         };
38337         this.el.on("keypress", keyPress, this);
38338     },
38339
38340     // private
38341     validateValue : function(value){
38342         if(!Roo.form.NumberField.superclass.validateValue.call(this, value)){
38343             return false;
38344         }
38345         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
38346              return true;
38347         }
38348         var num = this.parseValue(value);
38349         if(isNaN(num)){
38350             this.markInvalid(String.format(this.nanText, value));
38351             return false;
38352         }
38353         if(num < this.minValue){
38354             this.markInvalid(String.format(this.minText, this.minValue));
38355             return false;
38356         }
38357         if(num > this.maxValue){
38358             this.markInvalid(String.format(this.maxText, this.maxValue));
38359             return false;
38360         }
38361         return true;
38362     },
38363
38364     getValue : function(){
38365         return this.fixPrecision(this.parseValue(Roo.form.NumberField.superclass.getValue.call(this)));
38366     },
38367
38368     // private
38369     parseValue : function(value){
38370         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
38371         return isNaN(value) ? '' : value;
38372     },
38373
38374     // private
38375     fixPrecision : function(value){
38376         var nan = isNaN(value);
38377         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
38378             return nan ? '' : value;
38379         }
38380         return parseFloat(value).toFixed(this.decimalPrecision);
38381     },
38382
38383     setValue : function(v){
38384         v = this.fixPrecision(v);
38385         Roo.form.NumberField.superclass.setValue.call(this, String(v).replace(".", this.decimalSeparator));
38386     },
38387
38388     // private
38389     decimalPrecisionFcn : function(v){
38390         return Math.floor(v);
38391     },
38392
38393     beforeBlur : function(){
38394         var v = this.parseValue(this.getRawValue());
38395         if(v){
38396             this.setValue(v);
38397         }
38398     }
38399 });/*
38400  * Based on:
38401  * Ext JS Library 1.1.1
38402  * Copyright(c) 2006-2007, Ext JS, LLC.
38403  *
38404  * Originally Released Under LGPL - original licence link has changed is not relivant.
38405  *
38406  * Fork - LGPL
38407  * <script type="text/javascript">
38408  */
38409  
38410 /**
38411  * @class Roo.form.DateField
38412  * @extends Roo.form.TriggerField
38413  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
38414 * @constructor
38415 * Create a new DateField
38416 * @param {Object} config
38417  */
38418 Roo.form.DateField = function(config){
38419     Roo.form.DateField.superclass.constructor.call(this, config);
38420     
38421       this.addEvents({
38422          
38423         /**
38424          * @event select
38425          * Fires when a date is selected
38426              * @param {Roo.form.DateField} combo This combo box
38427              * @param {Date} date The date selected
38428              */
38429         'select' : true
38430          
38431     });
38432     
38433     
38434     if(typeof this.minValue == "string") this.minValue = this.parseDate(this.minValue);
38435     if(typeof this.maxValue == "string") this.maxValue = this.parseDate(this.maxValue);
38436     this.ddMatch = null;
38437     if(this.disabledDates){
38438         var dd = this.disabledDates;
38439         var re = "(?:";
38440         for(var i = 0; i < dd.length; i++){
38441             re += dd[i];
38442             if(i != dd.length-1) re += "|";
38443         }
38444         this.ddMatch = new RegExp(re + ")");
38445     }
38446 };
38447
38448 Roo.extend(Roo.form.DateField, Roo.form.TriggerField,  {
38449     /**
38450      * @cfg {String} format
38451      * The default date format string which can be overriden for localization support.  The format must be
38452      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
38453      */
38454     format : "m/d/y",
38455     /**
38456      * @cfg {String} altFormats
38457      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
38458      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
38459      */
38460     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
38461     /**
38462      * @cfg {Array} disabledDays
38463      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
38464      */
38465     disabledDays : null,
38466     /**
38467      * @cfg {String} disabledDaysText
38468      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
38469      */
38470     disabledDaysText : "Disabled",
38471     /**
38472      * @cfg {Array} disabledDates
38473      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
38474      * expression so they are very powerful. Some examples:
38475      * <ul>
38476      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
38477      * <li>["03/08", "09/16"] would disable those days for every year</li>
38478      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
38479      * <li>["03/../2006"] would disable every day in March 2006</li>
38480      * <li>["^03"] would disable every day in every March</li>
38481      * </ul>
38482      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
38483      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
38484      */
38485     disabledDates : null,
38486     /**
38487      * @cfg {String} disabledDatesText
38488      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
38489      */
38490     disabledDatesText : "Disabled",
38491     /**
38492      * @cfg {Date/String} minValue
38493      * The minimum allowed date. Can be either a Javascript date object or a string date in a
38494      * valid format (defaults to null).
38495      */
38496     minValue : null,
38497     /**
38498      * @cfg {Date/String} maxValue
38499      * The maximum allowed date. Can be either a Javascript date object or a string date in a
38500      * valid format (defaults to null).
38501      */
38502     maxValue : null,
38503     /**
38504      * @cfg {String} minText
38505      * The error text to display when the date in the cell is before minValue (defaults to
38506      * 'The date in this field must be after {minValue}').
38507      */
38508     minText : "The date in this field must be equal to or after {0}",
38509     /**
38510      * @cfg {String} maxText
38511      * The error text to display when the date in the cell is after maxValue (defaults to
38512      * 'The date in this field must be before {maxValue}').
38513      */
38514     maxText : "The date in this field must be equal to or before {0}",
38515     /**
38516      * @cfg {String} invalidText
38517      * The error text to display when the date in the field is invalid (defaults to
38518      * '{value} is not a valid date - it must be in the format {format}').
38519      */
38520     invalidText : "{0} is not a valid date - it must be in the format {1}",
38521     /**
38522      * @cfg {String} triggerClass
38523      * An additional CSS class used to style the trigger button.  The trigger will always get the
38524      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
38525      * which displays a calendar icon).
38526      */
38527     triggerClass : 'x-form-date-trigger',
38528     
38529
38530     /**
38531      * @cfg {Boolean} useIso
38532      * if enabled, then the date field will use a hidden field to store the 
38533      * real value as iso formated date. default (false)
38534      */ 
38535     useIso : false,
38536     /**
38537      * @cfg {String/Object} autoCreate
38538      * A DomHelper element spec, or true for a default element spec (defaults to
38539      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
38540      */ 
38541     // private
38542     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "off"},
38543     
38544     // private
38545     hiddenField: false,
38546     
38547     onRender : function(ct, position)
38548     {
38549         Roo.form.DateField.superclass.onRender.call(this, ct, position);
38550         if (this.useIso) {
38551             //this.el.dom.removeAttribute('name'); 
38552             Roo.log("Changing name?");
38553             this.el.dom.setAttribute('name', this.name + '____hidden___' ); 
38554             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
38555                     'before', true);
38556             this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
38557             // prevent input submission
38558             this.hiddenName = this.name;
38559         }
38560             
38561             
38562     },
38563     
38564     // private
38565     validateValue : function(value)
38566     {
38567         value = this.formatDate(value);
38568         if(!Roo.form.DateField.superclass.validateValue.call(this, value)){
38569             Roo.log('super failed');
38570             return false;
38571         }
38572         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
38573              return true;
38574         }
38575         var svalue = value;
38576         value = this.parseDate(value);
38577         if(!value){
38578             Roo.log('parse date failed' + svalue);
38579             this.markInvalid(String.format(this.invalidText, svalue, this.format));
38580             return false;
38581         }
38582         var time = value.getTime();
38583         if(this.minValue && time < this.minValue.getTime()){
38584             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
38585             return false;
38586         }
38587         if(this.maxValue && time > this.maxValue.getTime()){
38588             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
38589             return false;
38590         }
38591         if(this.disabledDays){
38592             var day = value.getDay();
38593             for(var i = 0; i < this.disabledDays.length; i++) {
38594                 if(day === this.disabledDays[i]){
38595                     this.markInvalid(this.disabledDaysText);
38596                     return false;
38597                 }
38598             }
38599         }
38600         var fvalue = this.formatDate(value);
38601         if(this.ddMatch && this.ddMatch.test(fvalue)){
38602             this.markInvalid(String.format(this.disabledDatesText, fvalue));
38603             return false;
38604         }
38605         return true;
38606     },
38607
38608     // private
38609     // Provides logic to override the default TriggerField.validateBlur which just returns true
38610     validateBlur : function(){
38611         return !this.menu || !this.menu.isVisible();
38612     },
38613     
38614     getName: function()
38615     {
38616         // returns hidden if it's set..
38617         if (!this.rendered) {return ''};
38618         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
38619         
38620     },
38621
38622     /**
38623      * Returns the current date value of the date field.
38624      * @return {Date} The date value
38625      */
38626     getValue : function(){
38627         
38628         return  this.hiddenField ?
38629                 this.hiddenField.value :
38630                 this.parseDate(Roo.form.DateField.superclass.getValue.call(this)) || "";
38631     },
38632
38633     /**
38634      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
38635      * date, using DateField.format as the date format, according to the same rules as {@link Date#parseDate}
38636      * (the default format used is "m/d/y").
38637      * <br />Usage:
38638      * <pre><code>
38639 //All of these calls set the same date value (May 4, 2006)
38640
38641 //Pass a date object:
38642 var dt = new Date('5/4/06');
38643 dateField.setValue(dt);
38644
38645 //Pass a date string (default format):
38646 dateField.setValue('5/4/06');
38647
38648 //Pass a date string (custom format):
38649 dateField.format = 'Y-m-d';
38650 dateField.setValue('2006-5-4');
38651 </code></pre>
38652      * @param {String/Date} date The date or valid date string
38653      */
38654     setValue : function(date){
38655         if (this.hiddenField) {
38656             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
38657         }
38658         Roo.form.DateField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
38659         // make sure the value field is always stored as a date..
38660         this.value = this.parseDate(date);
38661         
38662         
38663     },
38664
38665     // private
38666     parseDate : function(value){
38667         if(!value || value instanceof Date){
38668             return value;
38669         }
38670         var v = Date.parseDate(value, this.format);
38671          if (!v && this.useIso) {
38672             v = Date.parseDate(value, 'Y-m-d');
38673         }
38674         if(!v && this.altFormats){
38675             if(!this.altFormatsArray){
38676                 this.altFormatsArray = this.altFormats.split("|");
38677             }
38678             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
38679                 v = Date.parseDate(value, this.altFormatsArray[i]);
38680             }
38681         }
38682         return v;
38683     },
38684
38685     // private
38686     formatDate : function(date, fmt){
38687         return (!date || !(date instanceof Date)) ?
38688                date : date.dateFormat(fmt || this.format);
38689     },
38690
38691     // private
38692     menuListeners : {
38693         select: function(m, d){
38694             
38695             this.setValue(d);
38696             this.fireEvent('select', this, d);
38697         },
38698         show : function(){ // retain focus styling
38699             this.onFocus();
38700         },
38701         hide : function(){
38702             this.focus.defer(10, this);
38703             var ml = this.menuListeners;
38704             this.menu.un("select", ml.select,  this);
38705             this.menu.un("show", ml.show,  this);
38706             this.menu.un("hide", ml.hide,  this);
38707         }
38708     },
38709
38710     // private
38711     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
38712     onTriggerClick : function(){
38713         if(this.disabled){
38714             return;
38715         }
38716         if(this.menu == null){
38717             this.menu = new Roo.menu.DateMenu();
38718         }
38719         Roo.apply(this.menu.picker,  {
38720             showClear: this.allowBlank,
38721             minDate : this.minValue,
38722             maxDate : this.maxValue,
38723             disabledDatesRE : this.ddMatch,
38724             disabledDatesText : this.disabledDatesText,
38725             disabledDays : this.disabledDays,
38726             disabledDaysText : this.disabledDaysText,
38727             format : this.useIso ? 'Y-m-d' : this.format,
38728             minText : String.format(this.minText, this.formatDate(this.minValue)),
38729             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
38730         });
38731         this.menu.on(Roo.apply({}, this.menuListeners, {
38732             scope:this
38733         }));
38734         this.menu.picker.setValue(this.getValue() || new Date());
38735         this.menu.show(this.el, "tl-bl?");
38736     },
38737
38738     beforeBlur : function(){
38739         var v = this.parseDate(this.getRawValue());
38740         if(v){
38741             this.setValue(v);
38742         }
38743     },
38744
38745     /*@
38746      * overide
38747      * 
38748      */
38749     isDirty : function() {
38750         if(this.disabled) {
38751             return false;
38752         }
38753         
38754         if(typeof(this.startValue) === 'undefined'){
38755             return false;
38756         }
38757         
38758         return String(this.getValue()) !== String(this.startValue);
38759         
38760     }
38761 });/*
38762  * Based on:
38763  * Ext JS Library 1.1.1
38764  * Copyright(c) 2006-2007, Ext JS, LLC.
38765  *
38766  * Originally Released Under LGPL - original licence link has changed is not relivant.
38767  *
38768  * Fork - LGPL
38769  * <script type="text/javascript">
38770  */
38771  
38772 /**
38773  * @class Roo.form.MonthField
38774  * @extends Roo.form.TriggerField
38775  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
38776 * @constructor
38777 * Create a new MonthField
38778 * @param {Object} config
38779  */
38780 Roo.form.MonthField = function(config){
38781     
38782     Roo.form.MonthField.superclass.constructor.call(this, config);
38783     
38784       this.addEvents({
38785          
38786         /**
38787          * @event select
38788          * Fires when a date is selected
38789              * @param {Roo.form.MonthFieeld} combo This combo box
38790              * @param {Date} date The date selected
38791              */
38792         'select' : true
38793          
38794     });
38795     
38796     
38797     if(typeof this.minValue == "string") this.minValue = this.parseDate(this.minValue);
38798     if(typeof this.maxValue == "string") this.maxValue = this.parseDate(this.maxValue);
38799     this.ddMatch = null;
38800     if(this.disabledDates){
38801         var dd = this.disabledDates;
38802         var re = "(?:";
38803         for(var i = 0; i < dd.length; i++){
38804             re += dd[i];
38805             if(i != dd.length-1) re += "|";
38806         }
38807         this.ddMatch = new RegExp(re + ")");
38808     }
38809 };
38810
38811 Roo.extend(Roo.form.MonthField, Roo.form.TriggerField,  {
38812     /**
38813      * @cfg {String} format
38814      * The default date format string which can be overriden for localization support.  The format must be
38815      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
38816      */
38817     format : "M Y",
38818     /**
38819      * @cfg {String} altFormats
38820      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
38821      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
38822      */
38823     altFormats : "M Y|m/Y|m-y|m-Y|my|mY",
38824     /**
38825      * @cfg {Array} disabledDays
38826      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
38827      */
38828     disabledDays : [0,1,2,3,4,5,6],
38829     /**
38830      * @cfg {String} disabledDaysText
38831      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
38832      */
38833     disabledDaysText : "Disabled",
38834     /**
38835      * @cfg {Array} disabledDates
38836      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
38837      * expression so they are very powerful. Some examples:
38838      * <ul>
38839      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
38840      * <li>["03/08", "09/16"] would disable those days for every year</li>
38841      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
38842      * <li>["03/../2006"] would disable every day in March 2006</li>
38843      * <li>["^03"] would disable every day in every March</li>
38844      * </ul>
38845      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
38846      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
38847      */
38848     disabledDates : null,
38849     /**
38850      * @cfg {String} disabledDatesText
38851      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
38852      */
38853     disabledDatesText : "Disabled",
38854     /**
38855      * @cfg {Date/String} minValue
38856      * The minimum allowed date. Can be either a Javascript date object or a string date in a
38857      * valid format (defaults to null).
38858      */
38859     minValue : null,
38860     /**
38861      * @cfg {Date/String} maxValue
38862      * The maximum allowed date. Can be either a Javascript date object or a string date in a
38863      * valid format (defaults to null).
38864      */
38865     maxValue : null,
38866     /**
38867      * @cfg {String} minText
38868      * The error text to display when the date in the cell is before minValue (defaults to
38869      * 'The date in this field must be after {minValue}').
38870      */
38871     minText : "The date in this field must be equal to or after {0}",
38872     /**
38873      * @cfg {String} maxTextf
38874      * The error text to display when the date in the cell is after maxValue (defaults to
38875      * 'The date in this field must be before {maxValue}').
38876      */
38877     maxText : "The date in this field must be equal to or before {0}",
38878     /**
38879      * @cfg {String} invalidText
38880      * The error text to display when the date in the field is invalid (defaults to
38881      * '{value} is not a valid date - it must be in the format {format}').
38882      */
38883     invalidText : "{0} is not a valid date - it must be in the format {1}",
38884     /**
38885      * @cfg {String} triggerClass
38886      * An additional CSS class used to style the trigger button.  The trigger will always get the
38887      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
38888      * which displays a calendar icon).
38889      */
38890     triggerClass : 'x-form-date-trigger',
38891     
38892
38893     /**
38894      * @cfg {Boolean} useIso
38895      * if enabled, then the date field will use a hidden field to store the 
38896      * real value as iso formated date. default (true)
38897      */ 
38898     useIso : true,
38899     /**
38900      * @cfg {String/Object} autoCreate
38901      * A DomHelper element spec, or true for a default element spec (defaults to
38902      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
38903      */ 
38904     // private
38905     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "off"},
38906     
38907     // private
38908     hiddenField: false,
38909     
38910     hideMonthPicker : false,
38911     
38912     onRender : function(ct, position)
38913     {
38914         Roo.form.MonthField.superclass.onRender.call(this, ct, position);
38915         if (this.useIso) {
38916             this.el.dom.removeAttribute('name'); 
38917             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
38918                     'before', true);
38919             this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
38920             // prevent input submission
38921             this.hiddenName = this.name;
38922         }
38923             
38924             
38925     },
38926     
38927     // private
38928     validateValue : function(value)
38929     {
38930         value = this.formatDate(value);
38931         if(!Roo.form.MonthField.superclass.validateValue.call(this, value)){
38932             return false;
38933         }
38934         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
38935              return true;
38936         }
38937         var svalue = value;
38938         value = this.parseDate(value);
38939         if(!value){
38940             this.markInvalid(String.format(this.invalidText, svalue, this.format));
38941             return false;
38942         }
38943         var time = value.getTime();
38944         if(this.minValue && time < this.minValue.getTime()){
38945             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
38946             return false;
38947         }
38948         if(this.maxValue && time > this.maxValue.getTime()){
38949             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
38950             return false;
38951         }
38952         /*if(this.disabledDays){
38953             var day = value.getDay();
38954             for(var i = 0; i < this.disabledDays.length; i++) {
38955                 if(day === this.disabledDays[i]){
38956                     this.markInvalid(this.disabledDaysText);
38957                     return false;
38958                 }
38959             }
38960         }
38961         */
38962         var fvalue = this.formatDate(value);
38963         /*if(this.ddMatch && this.ddMatch.test(fvalue)){
38964             this.markInvalid(String.format(this.disabledDatesText, fvalue));
38965             return false;
38966         }
38967         */
38968         return true;
38969     },
38970
38971     // private
38972     // Provides logic to override the default TriggerField.validateBlur which just returns true
38973     validateBlur : function(){
38974         return !this.menu || !this.menu.isVisible();
38975     },
38976
38977     /**
38978      * Returns the current date value of the date field.
38979      * @return {Date} The date value
38980      */
38981     getValue : function(){
38982         
38983         
38984         
38985         return  this.hiddenField ?
38986                 this.hiddenField.value :
38987                 this.parseDate(Roo.form.MonthField.superclass.getValue.call(this)) || "";
38988     },
38989
38990     /**
38991      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
38992      * date, using MonthField.format as the date format, according to the same rules as {@link Date#parseDate}
38993      * (the default format used is "m/d/y").
38994      * <br />Usage:
38995      * <pre><code>
38996 //All of these calls set the same date value (May 4, 2006)
38997
38998 //Pass a date object:
38999 var dt = new Date('5/4/06');
39000 monthField.setValue(dt);
39001
39002 //Pass a date string (default format):
39003 monthField.setValue('5/4/06');
39004
39005 //Pass a date string (custom format):
39006 monthField.format = 'Y-m-d';
39007 monthField.setValue('2006-5-4');
39008 </code></pre>
39009      * @param {String/Date} date The date or valid date string
39010      */
39011     setValue : function(date){
39012         Roo.log('month setValue' + date);
39013         // can only be first of month..
39014         
39015         var val = this.parseDate(date);
39016         
39017         if (this.hiddenField) {
39018             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
39019         }
39020         Roo.form.MonthField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
39021         this.value = this.parseDate(date);
39022     },
39023
39024     // private
39025     parseDate : function(value){
39026         if(!value || value instanceof Date){
39027             value = value ? Date.parseDate(value.format('Y-m') + '-01', 'Y-m-d') : null;
39028             return value;
39029         }
39030         var v = Date.parseDate(value, this.format);
39031         if (!v && this.useIso) {
39032             v = Date.parseDate(value, 'Y-m-d');
39033         }
39034         if (v) {
39035             // 
39036             v = Date.parseDate(v.format('Y-m') +'-01', 'Y-m-d');
39037         }
39038         
39039         
39040         if(!v && this.altFormats){
39041             if(!this.altFormatsArray){
39042                 this.altFormatsArray = this.altFormats.split("|");
39043             }
39044             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
39045                 v = Date.parseDate(value, this.altFormatsArray[i]);
39046             }
39047         }
39048         return v;
39049     },
39050
39051     // private
39052     formatDate : function(date, fmt){
39053         return (!date || !(date instanceof Date)) ?
39054                date : date.dateFormat(fmt || this.format);
39055     },
39056
39057     // private
39058     menuListeners : {
39059         select: function(m, d){
39060             this.setValue(d);
39061             this.fireEvent('select', this, d);
39062         },
39063         show : function(){ // retain focus styling
39064             this.onFocus();
39065         },
39066         hide : function(){
39067             this.focus.defer(10, this);
39068             var ml = this.menuListeners;
39069             this.menu.un("select", ml.select,  this);
39070             this.menu.un("show", ml.show,  this);
39071             this.menu.un("hide", ml.hide,  this);
39072         }
39073     },
39074     // private
39075     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
39076     onTriggerClick : function(){
39077         if(this.disabled){
39078             return;
39079         }
39080         if(this.menu == null){
39081             this.menu = new Roo.menu.DateMenu();
39082            
39083         }
39084         
39085         Roo.apply(this.menu.picker,  {
39086             
39087             showClear: this.allowBlank,
39088             minDate : this.minValue,
39089             maxDate : this.maxValue,
39090             disabledDatesRE : this.ddMatch,
39091             disabledDatesText : this.disabledDatesText,
39092             
39093             format : this.useIso ? 'Y-m-d' : this.format,
39094             minText : String.format(this.minText, this.formatDate(this.minValue)),
39095             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
39096             
39097         });
39098          this.menu.on(Roo.apply({}, this.menuListeners, {
39099             scope:this
39100         }));
39101        
39102         
39103         var m = this.menu;
39104         var p = m.picker;
39105         
39106         // hide month picker get's called when we called by 'before hide';
39107         
39108         var ignorehide = true;
39109         p.hideMonthPicker  = function(disableAnim){
39110             if (ignorehide) {
39111                 return;
39112             }
39113              if(this.monthPicker){
39114                 Roo.log("hideMonthPicker called");
39115                 if(disableAnim === true){
39116                     this.monthPicker.hide();
39117                 }else{
39118                     this.monthPicker.slideOut('t', {duration:.2});
39119                     p.setValue(new Date(m.picker.mpSelYear, m.picker.mpSelMonth, 1));
39120                     p.fireEvent("select", this, this.value);
39121                     m.hide();
39122                 }
39123             }
39124         }
39125         
39126         Roo.log('picker set value');
39127         Roo.log(this.getValue());
39128         p.setValue(this.getValue() ? this.parseDate(this.getValue()) : new Date());
39129         m.show(this.el, 'tl-bl?');
39130         ignorehide  = false;
39131         // this will trigger hideMonthPicker..
39132         
39133         
39134         // hidden the day picker
39135         Roo.select('.x-date-picker table', true).first().dom.style.visibility = "hidden";
39136         
39137         
39138         
39139       
39140         
39141         p.showMonthPicker.defer(100, p);
39142     
39143         
39144        
39145     },
39146
39147     beforeBlur : function(){
39148         var v = this.parseDate(this.getRawValue());
39149         if(v){
39150             this.setValue(v);
39151         }
39152     }
39153
39154     /** @cfg {Boolean} grow @hide */
39155     /** @cfg {Number} growMin @hide */
39156     /** @cfg {Number} growMax @hide */
39157     /**
39158      * @hide
39159      * @method autoSize
39160      */
39161 });/*
39162  * Based on:
39163  * Ext JS Library 1.1.1
39164  * Copyright(c) 2006-2007, Ext JS, LLC.
39165  *
39166  * Originally Released Under LGPL - original licence link has changed is not relivant.
39167  *
39168  * Fork - LGPL
39169  * <script type="text/javascript">
39170  */
39171  
39172
39173 /**
39174  * @class Roo.form.ComboBox
39175  * @extends Roo.form.TriggerField
39176  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
39177  * @constructor
39178  * Create a new ComboBox.
39179  * @param {Object} config Configuration options
39180  */
39181 Roo.form.ComboBox = function(config){
39182     Roo.form.ComboBox.superclass.constructor.call(this, config);
39183     this.addEvents({
39184         /**
39185          * @event expand
39186          * Fires when the dropdown list is expanded
39187              * @param {Roo.form.ComboBox} combo This combo box
39188              */
39189         'expand' : true,
39190         /**
39191          * @event collapse
39192          * Fires when the dropdown list is collapsed
39193              * @param {Roo.form.ComboBox} combo This combo box
39194              */
39195         'collapse' : true,
39196         /**
39197          * @event beforeselect
39198          * Fires before a list item is selected. Return false to cancel the selection.
39199              * @param {Roo.form.ComboBox} combo This combo box
39200              * @param {Roo.data.Record} record The data record returned from the underlying store
39201              * @param {Number} index The index of the selected item in the dropdown list
39202              */
39203         'beforeselect' : true,
39204         /**
39205          * @event select
39206          * Fires when a list item is selected
39207              * @param {Roo.form.ComboBox} combo This combo box
39208              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
39209              * @param {Number} index The index of the selected item in the dropdown list
39210              */
39211         'select' : true,
39212         /**
39213          * @event beforequery
39214          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
39215          * The event object passed has these properties:
39216              * @param {Roo.form.ComboBox} combo This combo box
39217              * @param {String} query The query
39218              * @param {Boolean} forceAll true to force "all" query
39219              * @param {Boolean} cancel true to cancel the query
39220              * @param {Object} e The query event object
39221              */
39222         'beforequery': true,
39223          /**
39224          * @event add
39225          * Fires when the 'add' icon is pressed (add a listener to enable add button)
39226              * @param {Roo.form.ComboBox} combo This combo box
39227              */
39228         'add' : true,
39229         /**
39230          * @event edit
39231          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
39232              * @param {Roo.form.ComboBox} combo This combo box
39233              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
39234              */
39235         'edit' : true
39236         
39237         
39238     });
39239     if(this.transform){
39240         this.allowDomMove = false;
39241         var s = Roo.getDom(this.transform);
39242         if(!this.hiddenName){
39243             this.hiddenName = s.name;
39244         }
39245         if(!this.store){
39246             this.mode = 'local';
39247             var d = [], opts = s.options;
39248             for(var i = 0, len = opts.length;i < len; i++){
39249                 var o = opts[i];
39250                 var value = (Roo.isIE ? o.getAttributeNode('value').specified : o.hasAttribute('value')) ? o.value : o.text;
39251                 if(o.selected) {
39252                     this.value = value;
39253                 }
39254                 d.push([value, o.text]);
39255             }
39256             this.store = new Roo.data.SimpleStore({
39257                 'id': 0,
39258                 fields: ['value', 'text'],
39259                 data : d
39260             });
39261             this.valueField = 'value';
39262             this.displayField = 'text';
39263         }
39264         s.name = Roo.id(); // wipe out the name in case somewhere else they have a reference
39265         if(!this.lazyRender){
39266             this.target = true;
39267             this.el = Roo.DomHelper.insertBefore(s, this.autoCreate || this.defaultAutoCreate);
39268             s.parentNode.removeChild(s); // remove it
39269             this.render(this.el.parentNode);
39270         }else{
39271             s.parentNode.removeChild(s); // remove it
39272         }
39273
39274     }
39275     if (this.store) {
39276         this.store = Roo.factory(this.store, Roo.data);
39277     }
39278     
39279     this.selectedIndex = -1;
39280     if(this.mode == 'local'){
39281         if(config.queryDelay === undefined){
39282             this.queryDelay = 10;
39283         }
39284         if(config.minChars === undefined){
39285             this.minChars = 0;
39286         }
39287     }
39288 };
39289
39290 Roo.extend(Roo.form.ComboBox, Roo.form.TriggerField, {
39291     /**
39292      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
39293      */
39294     /**
39295      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
39296      * rendering into an Roo.Editor, defaults to false)
39297      */
39298     /**
39299      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
39300      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
39301      */
39302     /**
39303      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
39304      */
39305     /**
39306      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
39307      * the dropdown list (defaults to undefined, with no header element)
39308      */
39309
39310      /**
39311      * @cfg {String/Roo.Template} tpl The template to use to render the output
39312      */
39313      
39314     // private
39315     defaultAutoCreate : {tag: "input", type: "text", size: "24", autocomplete: "off"},
39316     /**
39317      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
39318      */
39319     listWidth: undefined,
39320     /**
39321      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
39322      * mode = 'remote' or 'text' if mode = 'local')
39323      */
39324     displayField: undefined,
39325     /**
39326      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
39327      * mode = 'remote' or 'value' if mode = 'local'). 
39328      * Note: use of a valueField requires the user make a selection
39329      * in order for a value to be mapped.
39330      */
39331     valueField: undefined,
39332     
39333     
39334     /**
39335      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
39336      * field's data value (defaults to the underlying DOM element's name)
39337      */
39338     hiddenName: undefined,
39339     /**
39340      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
39341      */
39342     listClass: '',
39343     /**
39344      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
39345      */
39346     selectedClass: 'x-combo-selected',
39347     /**
39348      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
39349      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
39350      * which displays a downward arrow icon).
39351      */
39352     triggerClass : 'x-form-arrow-trigger',
39353     /**
39354      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
39355      */
39356     shadow:'sides',
39357     /**
39358      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
39359      * anchor positions (defaults to 'tl-bl')
39360      */
39361     listAlign: 'tl-bl?',
39362     /**
39363      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
39364      */
39365     maxHeight: 300,
39366     /**
39367      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
39368      * query specified by the allQuery config option (defaults to 'query')
39369      */
39370     triggerAction: 'query',
39371     /**
39372      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
39373      * (defaults to 4, does not apply if editable = false)
39374      */
39375     minChars : 4,
39376     /**
39377      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
39378      * delay (typeAheadDelay) if it matches a known value (defaults to false)
39379      */
39380     typeAhead: false,
39381     /**
39382      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
39383      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
39384      */
39385     queryDelay: 500,
39386     /**
39387      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
39388      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
39389      */
39390     pageSize: 0,
39391     /**
39392      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
39393      * when editable = true (defaults to false)
39394      */
39395     selectOnFocus:false,
39396     /**
39397      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
39398      */
39399     queryParam: 'query',
39400     /**
39401      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
39402      * when mode = 'remote' (defaults to 'Loading...')
39403      */
39404     loadingText: 'Loading...',
39405     /**
39406      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
39407      */
39408     resizable: false,
39409     /**
39410      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
39411      */
39412     handleHeight : 8,
39413     /**
39414      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
39415      * traditional select (defaults to true)
39416      */
39417     editable: true,
39418     /**
39419      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
39420      */
39421     allQuery: '',
39422     /**
39423      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
39424      */
39425     mode: 'remote',
39426     /**
39427      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
39428      * listWidth has a higher value)
39429      */
39430     minListWidth : 70,
39431     /**
39432      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
39433      * allow the user to set arbitrary text into the field (defaults to false)
39434      */
39435     forceSelection:false,
39436     /**
39437      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
39438      * if typeAhead = true (defaults to 250)
39439      */
39440     typeAheadDelay : 250,
39441     /**
39442      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
39443      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
39444      */
39445     valueNotFoundText : undefined,
39446     /**
39447      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
39448      */
39449     blockFocus : false,
39450     
39451     /**
39452      * @cfg {Boolean} disableClear Disable showing of clear button.
39453      */
39454     disableClear : false,
39455     /**
39456      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
39457      */
39458     alwaysQuery : false,
39459     
39460     //private
39461     addicon : false,
39462     editicon: false,
39463     
39464     // element that contains real text value.. (when hidden is used..)
39465      
39466     // private
39467     onRender : function(ct, position){
39468         Roo.form.ComboBox.superclass.onRender.call(this, ct, position);
39469         if(this.hiddenName){
39470             this.hiddenField = this.el.insertSibling({tag:'input', type:'hidden', name: this.hiddenName, id:  (this.hiddenId||this.hiddenName)},
39471                     'before', true);
39472             this.hiddenField.value =
39473                 this.hiddenValue !== undefined ? this.hiddenValue :
39474                 this.value !== undefined ? this.value : '';
39475
39476             // prevent input submission
39477             this.el.dom.removeAttribute('name');
39478              
39479              
39480         }
39481         if(Roo.isGecko){
39482             this.el.dom.setAttribute('autocomplete', 'off');
39483         }
39484
39485         var cls = 'x-combo-list';
39486
39487         this.list = new Roo.Layer({
39488             shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
39489         });
39490
39491         var lw = this.listWidth || Math.max(this.wrap.getWidth(), this.minListWidth);
39492         this.list.setWidth(lw);
39493         this.list.swallowEvent('mousewheel');
39494         this.assetHeight = 0;
39495
39496         if(this.title){
39497             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
39498             this.assetHeight += this.header.getHeight();
39499         }
39500
39501         this.innerList = this.list.createChild({cls:cls+'-inner'});
39502         this.innerList.on('mouseover', this.onViewOver, this);
39503         this.innerList.on('mousemove', this.onViewMove, this);
39504         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
39505         
39506         if(this.allowBlank && !this.pageSize && !this.disableClear){
39507             this.footer = this.list.createChild({cls:cls+'-ft'});
39508             this.pageTb = new Roo.Toolbar(this.footer);
39509            
39510         }
39511         if(this.pageSize){
39512             this.footer = this.list.createChild({cls:cls+'-ft'});
39513             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
39514                     {pageSize: this.pageSize});
39515             
39516         }
39517         
39518         if (this.pageTb && this.allowBlank && !this.disableClear) {
39519             var _this = this;
39520             this.pageTb.add(new Roo.Toolbar.Fill(), {
39521                 cls: 'x-btn-icon x-btn-clear',
39522                 text: '&#160;',
39523                 handler: function()
39524                 {
39525                     _this.collapse();
39526                     _this.clearValue();
39527                     _this.onSelect(false, -1);
39528                 }
39529             });
39530         }
39531         if (this.footer) {
39532             this.assetHeight += this.footer.getHeight();
39533         }
39534         
39535
39536         if(!this.tpl){
39537             this.tpl = '<div class="'+cls+'-item">{' + this.displayField + '}</div>';
39538         }
39539
39540         this.view = new Roo.View(this.innerList, this.tpl, {
39541             singleSelect:true, store: this.store, selectedClass: this.selectedClass
39542         });
39543
39544         this.view.on('click', this.onViewClick, this);
39545
39546         this.store.on('beforeload', this.onBeforeLoad, this);
39547         this.store.on('load', this.onLoad, this);
39548         this.store.on('loadexception', this.onLoadException, this);
39549
39550         if(this.resizable){
39551             this.resizer = new Roo.Resizable(this.list,  {
39552                pinned:true, handles:'se'
39553             });
39554             this.resizer.on('resize', function(r, w, h){
39555                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
39556                 this.listWidth = w;
39557                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
39558                 this.restrictHeight();
39559             }, this);
39560             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
39561         }
39562         if(!this.editable){
39563             this.editable = true;
39564             this.setEditable(false);
39565         }  
39566         
39567         
39568         if (typeof(this.events.add.listeners) != 'undefined') {
39569             
39570             this.addicon = this.wrap.createChild(
39571                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
39572        
39573             this.addicon.on('click', function(e) {
39574                 this.fireEvent('add', this);
39575             }, this);
39576         }
39577         if (typeof(this.events.edit.listeners) != 'undefined') {
39578             
39579             this.editicon = this.wrap.createChild(
39580                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
39581             if (this.addicon) {
39582                 this.editicon.setStyle('margin-left', '40px');
39583             }
39584             this.editicon.on('click', function(e) {
39585                 
39586                 // we fire even  if inothing is selected..
39587                 this.fireEvent('edit', this, this.lastData );
39588                 
39589             }, this);
39590         }
39591         
39592         
39593         
39594     },
39595
39596     // private
39597     initEvents : function(){
39598         Roo.form.ComboBox.superclass.initEvents.call(this);
39599
39600         this.keyNav = new Roo.KeyNav(this.el, {
39601             "up" : function(e){
39602                 this.inKeyMode = true;
39603                 this.selectPrev();
39604             },
39605
39606             "down" : function(e){
39607                 if(!this.isExpanded()){
39608                     this.onTriggerClick();
39609                 }else{
39610                     this.inKeyMode = true;
39611                     this.selectNext();
39612                 }
39613             },
39614
39615             "enter" : function(e){
39616                 this.onViewClick();
39617                 //return true;
39618             },
39619
39620             "esc" : function(e){
39621                 this.collapse();
39622             },
39623
39624             "tab" : function(e){
39625                 this.onViewClick(false);
39626                 this.fireEvent("specialkey", this, e);
39627                 return true;
39628             },
39629
39630             scope : this,
39631
39632             doRelay : function(foo, bar, hname){
39633                 if(hname == 'down' || this.scope.isExpanded()){
39634                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
39635                 }
39636                 return true;
39637             },
39638
39639             forceKeyDown: true
39640         });
39641         this.queryDelay = Math.max(this.queryDelay || 10,
39642                 this.mode == 'local' ? 10 : 250);
39643         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
39644         if(this.typeAhead){
39645             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
39646         }
39647         if(this.editable !== false){
39648             this.el.on("keyup", this.onKeyUp, this);
39649         }
39650         if(this.forceSelection){
39651             this.on('blur', this.doForce, this);
39652         }
39653     },
39654
39655     onDestroy : function(){
39656         if(this.view){
39657             this.view.setStore(null);
39658             this.view.el.removeAllListeners();
39659             this.view.el.remove();
39660             this.view.purgeListeners();
39661         }
39662         if(this.list){
39663             this.list.destroy();
39664         }
39665         if(this.store){
39666             this.store.un('beforeload', this.onBeforeLoad, this);
39667             this.store.un('load', this.onLoad, this);
39668             this.store.un('loadexception', this.onLoadException, this);
39669         }
39670         Roo.form.ComboBox.superclass.onDestroy.call(this);
39671     },
39672
39673     // private
39674     fireKey : function(e){
39675         if(e.isNavKeyPress() && !this.list.isVisible()){
39676             this.fireEvent("specialkey", this, e);
39677         }
39678     },
39679
39680     // private
39681     onResize: function(w, h){
39682         Roo.form.ComboBox.superclass.onResize.apply(this, arguments);
39683         
39684         if(typeof w != 'number'){
39685             // we do not handle it!?!?
39686             return;
39687         }
39688         var tw = this.trigger.getWidth();
39689         tw += this.addicon ? this.addicon.getWidth() : 0;
39690         tw += this.editicon ? this.editicon.getWidth() : 0;
39691         var x = w - tw;
39692         this.el.setWidth( this.adjustWidth('input', x));
39693             
39694         this.trigger.setStyle('left', x+'px');
39695         
39696         if(this.list && this.listWidth === undefined){
39697             var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
39698             this.list.setWidth(lw);
39699             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
39700         }
39701         
39702     
39703         
39704     },
39705
39706     /**
39707      * Allow or prevent the user from directly editing the field text.  If false is passed,
39708      * the user will only be able to select from the items defined in the dropdown list.  This method
39709      * is the runtime equivalent of setting the 'editable' config option at config time.
39710      * @param {Boolean} value True to allow the user to directly edit the field text
39711      */
39712     setEditable : function(value){
39713         if(value == this.editable){
39714             return;
39715         }
39716         this.editable = value;
39717         if(!value){
39718             this.el.dom.setAttribute('readOnly', true);
39719             this.el.on('mousedown', this.onTriggerClick,  this);
39720             this.el.addClass('x-combo-noedit');
39721         }else{
39722             this.el.dom.setAttribute('readOnly', false);
39723             this.el.un('mousedown', this.onTriggerClick,  this);
39724             this.el.removeClass('x-combo-noedit');
39725         }
39726     },
39727
39728     // private
39729     onBeforeLoad : function(){
39730         if(!this.hasFocus){
39731             return;
39732         }
39733         this.innerList.update(this.loadingText ?
39734                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
39735         this.restrictHeight();
39736         this.selectedIndex = -1;
39737     },
39738
39739     // private
39740     onLoad : function(){
39741         if(!this.hasFocus){
39742             return;
39743         }
39744         if(this.store.getCount() > 0){
39745             this.expand();
39746             this.restrictHeight();
39747             if(this.lastQuery == this.allQuery){
39748                 if(this.editable){
39749                     this.el.dom.select();
39750                 }
39751                 if(!this.selectByValue(this.value, true)){
39752                     this.select(0, true);
39753                 }
39754             }else{
39755                 this.selectNext();
39756                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
39757                     this.taTask.delay(this.typeAheadDelay);
39758                 }
39759             }
39760         }else{
39761             this.onEmptyResults();
39762         }
39763         //this.el.focus();
39764     },
39765     // private
39766     onLoadException : function()
39767     {
39768         this.collapse();
39769         Roo.log(this.store.reader.jsonData);
39770         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
39771             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
39772         }
39773         
39774         
39775     },
39776     // private
39777     onTypeAhead : function(){
39778         if(this.store.getCount() > 0){
39779             var r = this.store.getAt(0);
39780             var newValue = r.data[this.displayField];
39781             var len = newValue.length;
39782             var selStart = this.getRawValue().length;
39783             if(selStart != len){
39784                 this.setRawValue(newValue);
39785                 this.selectText(selStart, newValue.length);
39786             }
39787         }
39788     },
39789
39790     // private
39791     onSelect : function(record, index){
39792         if(this.fireEvent('beforeselect', this, record, index) !== false){
39793             this.setFromData(index > -1 ? record.data : false);
39794             this.collapse();
39795             this.fireEvent('select', this, record, index);
39796         }
39797     },
39798
39799     /**
39800      * Returns the currently selected field value or empty string if no value is set.
39801      * @return {String} value The selected value
39802      */
39803     getValue : function(){
39804         if(this.valueField){
39805             return typeof this.value != 'undefined' ? this.value : '';
39806         }else{
39807             return Roo.form.ComboBox.superclass.getValue.call(this);
39808         }
39809     },
39810
39811     /**
39812      * Clears any text/value currently set in the field
39813      */
39814     clearValue : function(){
39815         if(this.hiddenField){
39816             this.hiddenField.value = '';
39817         }
39818         this.value = '';
39819         this.setRawValue('');
39820         this.lastSelectionText = '';
39821         
39822     },
39823
39824     /**
39825      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
39826      * will be displayed in the field.  If the value does not match the data value of an existing item,
39827      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
39828      * Otherwise the field will be blank (although the value will still be set).
39829      * @param {String} value The value to match
39830      */
39831     setValue : function(v){
39832         var text = v;
39833         if(this.valueField){
39834             var r = this.findRecord(this.valueField, v);
39835             if(r){
39836                 text = r.data[this.displayField];
39837             }else if(this.valueNotFoundText !== undefined){
39838                 text = this.valueNotFoundText;
39839             }
39840         }
39841         this.lastSelectionText = text;
39842         if(this.hiddenField){
39843             this.hiddenField.value = v;
39844         }
39845         Roo.form.ComboBox.superclass.setValue.call(this, text);
39846         this.value = v;
39847     },
39848     /**
39849      * @property {Object} the last set data for the element
39850      */
39851     
39852     lastData : false,
39853     /**
39854      * Sets the value of the field based on a object which is related to the record format for the store.
39855      * @param {Object} value the value to set as. or false on reset?
39856      */
39857     setFromData : function(o){
39858         var dv = ''; // display value
39859         var vv = ''; // value value..
39860         this.lastData = o;
39861         if (this.displayField) {
39862             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
39863         } else {
39864             // this is an error condition!!!
39865             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
39866         }
39867         
39868         if(this.valueField){
39869             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
39870         }
39871         if(this.hiddenField){
39872             this.hiddenField.value = vv;
39873             
39874             this.lastSelectionText = dv;
39875             Roo.form.ComboBox.superclass.setValue.call(this, dv);
39876             this.value = vv;
39877             return;
39878         }
39879         // no hidden field.. - we store the value in 'value', but still display
39880         // display field!!!!
39881         this.lastSelectionText = dv;
39882         Roo.form.ComboBox.superclass.setValue.call(this, dv);
39883         this.value = vv;
39884         
39885         
39886     },
39887     // private
39888     reset : function(){
39889         // overridden so that last data is reset..
39890         this.setValue(this.resetValue);
39891         this.clearInvalid();
39892         this.lastData = false;
39893         if (this.view) {
39894             this.view.clearSelections();
39895         }
39896     },
39897     // private
39898     findRecord : function(prop, value){
39899         var record;
39900         if(this.store.getCount() > 0){
39901             this.store.each(function(r){
39902                 if(r.data[prop] == value){
39903                     record = r;
39904                     return false;
39905                 }
39906                 return true;
39907             });
39908         }
39909         return record;
39910     },
39911     
39912     getName: function()
39913     {
39914         // returns hidden if it's set..
39915         if (!this.rendered) {return ''};
39916         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
39917         
39918     },
39919     // private
39920     onViewMove : function(e, t){
39921         this.inKeyMode = false;
39922     },
39923
39924     // private
39925     onViewOver : function(e, t){
39926         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
39927             return;
39928         }
39929         var item = this.view.findItemFromChild(t);
39930         if(item){
39931             var index = this.view.indexOf(item);
39932             this.select(index, false);
39933         }
39934     },
39935
39936     // private
39937     onViewClick : function(doFocus)
39938     {
39939         var index = this.view.getSelectedIndexes()[0];
39940         var r = this.store.getAt(index);
39941         if(r){
39942             this.onSelect(r, index);
39943         }
39944         if(doFocus !== false && !this.blockFocus){
39945             this.el.focus();
39946         }
39947     },
39948
39949     // private
39950     restrictHeight : function(){
39951         this.innerList.dom.style.height = '';
39952         var inner = this.innerList.dom;
39953         var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
39954         this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
39955         this.list.beginUpdate();
39956         this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
39957         this.list.alignTo(this.el, this.listAlign);
39958         this.list.endUpdate();
39959     },
39960
39961     // private
39962     onEmptyResults : function(){
39963         this.collapse();
39964     },
39965
39966     /**
39967      * Returns true if the dropdown list is expanded, else false.
39968      */
39969     isExpanded : function(){
39970         return this.list.isVisible();
39971     },
39972
39973     /**
39974      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
39975      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
39976      * @param {String} value The data value of the item to select
39977      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
39978      * selected item if it is not currently in view (defaults to true)
39979      * @return {Boolean} True if the value matched an item in the list, else false
39980      */
39981     selectByValue : function(v, scrollIntoView){
39982         if(v !== undefined && v !== null){
39983             var r = this.findRecord(this.valueField || this.displayField, v);
39984             if(r){
39985                 this.select(this.store.indexOf(r), scrollIntoView);
39986                 return true;
39987             }
39988         }
39989         return false;
39990     },
39991
39992     /**
39993      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
39994      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
39995      * @param {Number} index The zero-based index of the list item to select
39996      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
39997      * selected item if it is not currently in view (defaults to true)
39998      */
39999     select : function(index, scrollIntoView){
40000         this.selectedIndex = index;
40001         this.view.select(index);
40002         if(scrollIntoView !== false){
40003             var el = this.view.getNode(index);
40004             if(el){
40005                 this.innerList.scrollChildIntoView(el, false);
40006             }
40007         }
40008     },
40009
40010     // private
40011     selectNext : function(){
40012         var ct = this.store.getCount();
40013         if(ct > 0){
40014             if(this.selectedIndex == -1){
40015                 this.select(0);
40016             }else if(this.selectedIndex < ct-1){
40017                 this.select(this.selectedIndex+1);
40018             }
40019         }
40020     },
40021
40022     // private
40023     selectPrev : function(){
40024         var ct = this.store.getCount();
40025         if(ct > 0){
40026             if(this.selectedIndex == -1){
40027                 this.select(0);
40028             }else if(this.selectedIndex != 0){
40029                 this.select(this.selectedIndex-1);
40030             }
40031         }
40032     },
40033
40034     // private
40035     onKeyUp : function(e){
40036         if(this.editable !== false && !e.isSpecialKey()){
40037             this.lastKey = e.getKey();
40038             this.dqTask.delay(this.queryDelay);
40039         }
40040     },
40041
40042     // private
40043     validateBlur : function(){
40044         return !this.list || !this.list.isVisible();   
40045     },
40046
40047     // private
40048     initQuery : function(){
40049         this.doQuery(this.getRawValue());
40050     },
40051
40052     // private
40053     doForce : function(){
40054         if(this.el.dom.value.length > 0){
40055             this.el.dom.value =
40056                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
40057              
40058         }
40059     },
40060
40061     /**
40062      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
40063      * query allowing the query action to be canceled if needed.
40064      * @param {String} query The SQL query to execute
40065      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
40066      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
40067      * saved in the current store (defaults to false)
40068      */
40069     doQuery : function(q, forceAll){
40070         if(q === undefined || q === null){
40071             q = '';
40072         }
40073         var qe = {
40074             query: q,
40075             forceAll: forceAll,
40076             combo: this,
40077             cancel:false
40078         };
40079         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
40080             return false;
40081         }
40082         q = qe.query;
40083         forceAll = qe.forceAll;
40084         if(forceAll === true || (q.length >= this.minChars)){
40085             if(this.lastQuery != q || this.alwaysQuery){
40086                 this.lastQuery = q;
40087                 if(this.mode == 'local'){
40088                     this.selectedIndex = -1;
40089                     if(forceAll){
40090                         this.store.clearFilter();
40091                     }else{
40092                         this.store.filter(this.displayField, q);
40093                     }
40094                     this.onLoad();
40095                 }else{
40096                     this.store.baseParams[this.queryParam] = q;
40097                     this.store.load({
40098                         params: this.getParams(q)
40099                     });
40100                     this.expand();
40101                 }
40102             }else{
40103                 this.selectedIndex = -1;
40104                 this.onLoad();   
40105             }
40106         }
40107     },
40108
40109     // private
40110     getParams : function(q){
40111         var p = {};
40112         //p[this.queryParam] = q;
40113         if(this.pageSize){
40114             p.start = 0;
40115             p.limit = this.pageSize;
40116         }
40117         return p;
40118     },
40119
40120     /**
40121      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
40122      */
40123     collapse : function(){
40124         if(!this.isExpanded()){
40125             return;
40126         }
40127         this.list.hide();
40128         Roo.get(document).un('mousedown', this.collapseIf, this);
40129         Roo.get(document).un('mousewheel', this.collapseIf, this);
40130         if (!this.editable) {
40131             Roo.get(document).un('keydown', this.listKeyPress, this);
40132         }
40133         this.fireEvent('collapse', this);
40134     },
40135
40136     // private
40137     collapseIf : function(e){
40138         if(!e.within(this.wrap) && !e.within(this.list)){
40139             this.collapse();
40140         }
40141     },
40142
40143     /**
40144      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
40145      */
40146     expand : function(){
40147         if(this.isExpanded() || !this.hasFocus){
40148             return;
40149         }
40150         this.list.alignTo(this.el, this.listAlign);
40151         this.list.show();
40152         Roo.get(document).on('mousedown', this.collapseIf, this);
40153         Roo.get(document).on('mousewheel', this.collapseIf, this);
40154         if (!this.editable) {
40155             Roo.get(document).on('keydown', this.listKeyPress, this);
40156         }
40157         
40158         this.fireEvent('expand', this);
40159     },
40160
40161     // private
40162     // Implements the default empty TriggerField.onTriggerClick function
40163     onTriggerClick : function(){
40164         if(this.disabled){
40165             return;
40166         }
40167         if(this.isExpanded()){
40168             this.collapse();
40169             if (!this.blockFocus) {
40170                 this.el.focus();
40171             }
40172             
40173         }else {
40174             this.hasFocus = true;
40175             if(this.triggerAction == 'all') {
40176                 this.doQuery(this.allQuery, true);
40177             } else {
40178                 this.doQuery(this.getRawValue());
40179             }
40180             if (!this.blockFocus) {
40181                 this.el.focus();
40182             }
40183         }
40184     },
40185     listKeyPress : function(e)
40186     {
40187         //Roo.log('listkeypress');
40188         // scroll to first matching element based on key pres..
40189         if (e.isSpecialKey()) {
40190             return false;
40191         }
40192         var k = String.fromCharCode(e.getKey()).toUpperCase();
40193         //Roo.log(k);
40194         var match  = false;
40195         var csel = this.view.getSelectedNodes();
40196         var cselitem = false;
40197         if (csel.length) {
40198             var ix = this.view.indexOf(csel[0]);
40199             cselitem  = this.store.getAt(ix);
40200             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
40201                 cselitem = false;
40202             }
40203             
40204         }
40205         
40206         this.store.each(function(v) { 
40207             if (cselitem) {
40208                 // start at existing selection.
40209                 if (cselitem.id == v.id) {
40210                     cselitem = false;
40211                 }
40212                 return;
40213             }
40214                 
40215             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
40216                 match = this.store.indexOf(v);
40217                 return false;
40218             }
40219         }, this);
40220         
40221         if (match === false) {
40222             return true; // no more action?
40223         }
40224         // scroll to?
40225         this.view.select(match);
40226         var sn = Roo.get(this.view.getSelectedNodes()[0])
40227         sn.scrollIntoView(sn.dom.parentNode, false);
40228     }
40229
40230     /** 
40231     * @cfg {Boolean} grow 
40232     * @hide 
40233     */
40234     /** 
40235     * @cfg {Number} growMin 
40236     * @hide 
40237     */
40238     /** 
40239     * @cfg {Number} growMax 
40240     * @hide 
40241     */
40242     /**
40243      * @hide
40244      * @method autoSize
40245      */
40246 });/*
40247  * Copyright(c) 2010-2012, Roo J Solutions Limited
40248  *
40249  * Licence LGPL
40250  *
40251  */
40252
40253 /**
40254  * @class Roo.form.ComboBoxArray
40255  * @extends Roo.form.TextField
40256  * A facebook style adder... for lists of email / people / countries  etc...
40257  * pick multiple items from a combo box, and shows each one.
40258  *
40259  *  Fred [x]  Brian [x]  [Pick another |v]
40260  *
40261  *
40262  *  For this to work: it needs various extra information
40263  *    - normal combo problay has
40264  *      name, hiddenName
40265  *    + displayField, valueField
40266  *
40267  *    For our purpose...
40268  *
40269  *
40270  *   If we change from 'extends' to wrapping...
40271  *   
40272  *  
40273  *
40274  
40275  
40276  * @constructor
40277  * Create a new ComboBoxArray.
40278  * @param {Object} config Configuration options
40279  */
40280  
40281
40282 Roo.form.ComboBoxArray = function(config)
40283 {
40284     this.addEvents({
40285         /**
40286          * @event remove
40287          * Fires when remove the value from the list
40288              * @param {Roo.form.ComboBoxArray} _self This combo box array
40289              * @param {Roo.form.ComboBoxArray.Item} item removed item
40290              */
40291         'remove' : true
40292         
40293         
40294     });
40295     
40296     Roo.form.ComboBoxArray.superclass.constructor.call(this, config);
40297     
40298     this.items = new Roo.util.MixedCollection(false);
40299     
40300     // construct the child combo...
40301     
40302     
40303     
40304     
40305    
40306     
40307 }
40308
40309  
40310 Roo.extend(Roo.form.ComboBoxArray, Roo.form.TextField,
40311
40312     /**
40313      * @cfg {Roo.form.Combo} combo The combo box that is wrapped
40314      */
40315     
40316     lastData : false,
40317     
40318     // behavies liek a hiddne field
40319     inputType:      'hidden',
40320     /**
40321      * @cfg {Number} width The width of the box that displays the selected element
40322      */ 
40323     width:          300,
40324
40325     
40326     
40327     /**
40328      * @cfg {String} name    The name of the visable items on this form (eg. titles not ids)
40329      */
40330     name : false,
40331     /**
40332      * @cfg {String} hiddenName    The hidden name of the field, often contains an comma seperated list of names
40333      */
40334     hiddenName : false,
40335     
40336     
40337     // private the array of items that are displayed..
40338     items  : false,
40339     // private - the hidden field el.
40340     hiddenEl : false,
40341     // private - the filed el..
40342     el : false,
40343     
40344     //validateValue : function() { return true; }, // all values are ok!
40345     //onAddClick: function() { },
40346     
40347     onRender : function(ct, position) 
40348     {
40349         
40350         // create the standard hidden element
40351         //Roo.form.ComboBoxArray.superclass.onRender.call(this, ct, position);
40352         
40353         
40354         // give fake names to child combo;
40355         this.combo.hiddenName = this.hiddenName ? (this.hiddenName+'-subcombo') : this.hiddenName;
40356         this.combo.name = this.name? (this.name+'-subcombo') : this.name;
40357         
40358         this.combo = Roo.factory(this.combo, Roo.form);
40359         this.combo.onRender(ct, position);
40360         if (typeof(this.combo.width) != 'undefined') {
40361             this.combo.onResize(this.combo.width,0);
40362         }
40363         
40364         this.combo.initEvents();
40365         
40366         // assigned so form know we need to do this..
40367         this.store          = this.combo.store;
40368         this.valueField     = this.combo.valueField;
40369         this.displayField   = this.combo.displayField ;
40370         
40371         
40372         this.combo.wrap.addClass('x-cbarray-grp');
40373         
40374         var cbwrap = this.combo.wrap.createChild(
40375             {tag: 'div', cls: 'x-cbarray-cb'},
40376             this.combo.el.dom
40377         );
40378         
40379              
40380         this.hiddenEl = this.combo.wrap.createChild({
40381             tag: 'input',  type:'hidden' , name: this.hiddenName, value : ''
40382         });
40383         this.el = this.combo.wrap.createChild({
40384             tag: 'input',  type:'hidden' , name: this.name, value : ''
40385         });
40386          //   this.el.dom.removeAttribute("name");
40387         
40388         
40389         this.outerWrap = this.combo.wrap;
40390         this.wrap = cbwrap;
40391         
40392         this.outerWrap.setWidth(this.width);
40393         this.outerWrap.dom.removeChild(this.el.dom);
40394         
40395         this.wrap.dom.appendChild(this.el.dom);
40396         this.outerWrap.dom.removeChild(this.combo.trigger.dom);
40397         this.combo.wrap.dom.appendChild(this.combo.trigger.dom);
40398         
40399         this.combo.trigger.setStyle('position','relative');
40400         this.combo.trigger.setStyle('left', '0px');
40401         this.combo.trigger.setStyle('top', '2px');
40402         
40403         this.combo.el.setStyle('vertical-align', 'text-bottom');
40404         
40405         //this.trigger.setStyle('vertical-align', 'top');
40406         
40407         // this should use the code from combo really... on('add' ....)
40408         if (this.adder) {
40409             
40410         
40411             this.adder = this.outerWrap.createChild(
40412                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-adder', style: 'margin-left:2px'});  
40413             var _t = this;
40414             this.adder.on('click', function(e) {
40415                 _t.fireEvent('adderclick', this, e);
40416             }, _t);
40417         }
40418         //var _t = this;
40419         //this.adder.on('click', this.onAddClick, _t);
40420         
40421         
40422         this.combo.on('select', function(cb, rec, ix) {
40423             this.addItem(rec.data);
40424             
40425             cb.setValue('');
40426             cb.el.dom.value = '';
40427             //cb.lastData = rec.data;
40428             // add to list
40429             
40430         }, this);
40431         
40432         
40433     },
40434     
40435     
40436     getName: function()
40437     {
40438         // returns hidden if it's set..
40439         if (!this.rendered) {return ''};
40440         return  this.hiddenName ? this.hiddenName : this.name;
40441         
40442     },
40443     
40444     
40445     onResize: function(w, h){
40446         
40447         return;
40448         // not sure if this is needed..
40449         //this.combo.onResize(w,h);
40450         
40451         if(typeof w != 'number'){
40452             // we do not handle it!?!?
40453             return;
40454         }
40455         var tw = this.combo.trigger.getWidth();
40456         tw += this.addicon ? this.addicon.getWidth() : 0;
40457         tw += this.editicon ? this.editicon.getWidth() : 0;
40458         var x = w - tw;
40459         this.combo.el.setWidth( this.combo.adjustWidth('input', x));
40460             
40461         this.combo.trigger.setStyle('left', '0px');
40462         
40463         if(this.list && this.listWidth === undefined){
40464             var lw = Math.max(x + this.combo.trigger.getWidth(), this.combo.minListWidth);
40465             this.list.setWidth(lw);
40466             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
40467         }
40468         
40469     
40470         
40471     },
40472     
40473     addItem: function(rec)
40474     {
40475         var valueField = this.combo.valueField;
40476         var displayField = this.combo.displayField;
40477         if (this.items.indexOfKey(rec[valueField]) > -1) {
40478             //console.log("GOT " + rec.data.id);
40479             return;
40480         }
40481         
40482         var x = new Roo.form.ComboBoxArray.Item({
40483             //id : rec[this.idField],
40484             data : rec,
40485             displayField : displayField ,
40486             tipField : displayField ,
40487             cb : this
40488         });
40489         // use the 
40490         this.items.add(rec[valueField],x);
40491         // add it before the element..
40492         this.updateHiddenEl();
40493         x.render(this.outerWrap, this.wrap.dom);
40494         // add the image handler..
40495     },
40496     
40497     updateHiddenEl : function()
40498     {
40499         this.validate();
40500         if (!this.hiddenEl) {
40501             return;
40502         }
40503         var ar = [];
40504         var idField = this.combo.valueField;
40505         
40506         this.items.each(function(f) {
40507             ar.push(f.data[idField]);
40508            
40509         });
40510         this.hiddenEl.dom.value = ar.join(',');
40511         this.validate();
40512     },
40513     
40514     reset : function()
40515     {
40516         //Roo.form.ComboBoxArray.superclass.reset.call(this); 
40517         this.items.each(function(f) {
40518            f.remove(); 
40519         });
40520         this.el.dom.value = '';
40521         if (this.hiddenEl) {
40522             this.hiddenEl.dom.value = '';
40523         }
40524         
40525     },
40526     getValue: function()
40527     {
40528         return this.hiddenEl ? this.hiddenEl.dom.value : '';
40529     },
40530     setValue: function(v) // not a valid action - must use addItems..
40531     {
40532          
40533         this.reset();
40534         
40535         
40536         
40537         if (this.store.isLocal && (typeof(v) == 'string')) {
40538             // then we can use the store to find the values..
40539             // comma seperated at present.. this needs to allow JSON based encoding..
40540             this.hiddenEl.value  = v;
40541             var v_ar = [];
40542             Roo.each(v.split(','), function(k) {
40543                 Roo.log("CHECK " + this.valueField + ',' + k);
40544                 var li = this.store.query(this.valueField, k);
40545                 if (!li.length) {
40546                     return;
40547                 }
40548                 var add = {};
40549                 add[this.valueField] = k;
40550                 add[this.displayField] = li.item(0).data[this.displayField];
40551                 
40552                 this.addItem(add);
40553             }, this) 
40554              
40555         }
40556         if (typeof(v) == 'object') {
40557             // then let's assume it's an array of objects..
40558             Roo.each(v, function(l) {
40559                 this.addItem(l);
40560             }, this);
40561              
40562         }
40563         
40564         
40565     },
40566     setFromData: function(v)
40567     {
40568         // this recieves an object, if setValues is called.
40569         this.reset();
40570         this.el.dom.value = v[this.displayField];
40571         this.hiddenEl.dom.value = v[this.valueField];
40572         if (typeof(v[this.valueField]) != 'string' || !v[this.valueField].length) {
40573             return;
40574         }
40575         var kv = v[this.valueField];
40576         var dv = v[this.displayField];
40577         kv = typeof(kv) != 'string' ? '' : kv;
40578         dv = typeof(dv) != 'string' ? '' : dv;
40579         
40580         
40581         var keys = kv.split(',');
40582         var display = dv.split(',');
40583         for (var i = 0 ; i < keys.length; i++) {
40584             
40585             add = {};
40586             add[this.valueField] = keys[i];
40587             add[this.displayField] = display[i];
40588             this.addItem(add);
40589         }
40590       
40591         
40592     },
40593     
40594     /**
40595      * Validates the combox array value
40596      * @return {Boolean} True if the value is valid, else false
40597      */
40598     validate : function(){
40599         if(this.disabled || this.validateValue(this.processValue(this.getValue()))){
40600             this.clearInvalid();
40601             return true;
40602         }
40603         return false;
40604     },
40605     
40606     validateValue : function(value){
40607         return Roo.form.ComboBoxArray.superclass.validateValue.call(this, this.getValue());
40608         
40609     },
40610     
40611     /*@
40612      * overide
40613      * 
40614      */
40615     isDirty : function() {
40616         if(this.disabled) {
40617             return false;
40618         }
40619         
40620         try {
40621             var d = Roo.decode(String(this.originalValue));
40622         } catch (e) {
40623             return String(this.getValue()) !== String(this.originalValue);
40624         }
40625         
40626         var originalValue = [];
40627         
40628         for (var i = 0; i < d.length; i++){
40629             originalValue.push(d[i][this.valueField]);
40630         }
40631         
40632         return String(this.getValue()) !== String(originalValue.join(','));
40633         
40634     }
40635     
40636 });
40637
40638
40639
40640 /**
40641  * @class Roo.form.ComboBoxArray.Item
40642  * @extends Roo.BoxComponent
40643  * A selected item in the list
40644  *  Fred [x]  Brian [x]  [Pick another |v]
40645  * 
40646  * @constructor
40647  * Create a new item.
40648  * @param {Object} config Configuration options
40649  */
40650  
40651 Roo.form.ComboBoxArray.Item = function(config) {
40652     config.id = Roo.id();
40653     Roo.form.ComboBoxArray.Item.superclass.constructor.call(this, config);
40654 }
40655
40656 Roo.extend(Roo.form.ComboBoxArray.Item, Roo.BoxComponent, {
40657     data : {},
40658     cb: false,
40659     displayField : false,
40660     tipField : false,
40661     
40662     
40663     defaultAutoCreate : {
40664         tag: 'div',
40665         cls: 'x-cbarray-item',
40666         cn : [ 
40667             { tag: 'div' },
40668             {
40669                 tag: 'img',
40670                 width:16,
40671                 height : 16,
40672                 src : Roo.BLANK_IMAGE_URL ,
40673                 align: 'center'
40674             }
40675         ]
40676         
40677     },
40678     
40679  
40680     onRender : function(ct, position)
40681     {
40682         Roo.form.Field.superclass.onRender.call(this, ct, position);
40683         
40684         if(!this.el){
40685             var cfg = this.getAutoCreate();
40686             this.el = ct.createChild(cfg, position);
40687         }
40688         
40689         this.el.child('img').dom.setAttribute('src', Roo.BLANK_IMAGE_URL);
40690         
40691         this.el.child('div').dom.innerHTML = this.cb.renderer ? 
40692             this.cb.renderer(this.data) :
40693             String.format('{0}',this.data[this.displayField]);
40694         
40695             
40696         this.el.child('div').dom.setAttribute('qtip',
40697                         String.format('{0}',this.data[this.tipField])
40698         );
40699         
40700         this.el.child('img').on('click', this.remove, this);
40701         
40702     },
40703    
40704     remove : function()
40705     {
40706         this.cb.items.remove(this);
40707         this.el.child('img').un('click', this.remove, this);
40708         this.el.remove();
40709         this.cb.updateHiddenEl();
40710         
40711         this.cb.fireEvent('remove', this.cb, this);
40712     }
40713 });/*
40714  * Based on:
40715  * Ext JS Library 1.1.1
40716  * Copyright(c) 2006-2007, Ext JS, LLC.
40717  *
40718  * Originally Released Under LGPL - original licence link has changed is not relivant.
40719  *
40720  * Fork - LGPL
40721  * <script type="text/javascript">
40722  */
40723 /**
40724  * @class Roo.form.Checkbox
40725  * @extends Roo.form.Field
40726  * Single checkbox field.  Can be used as a direct replacement for traditional checkbox fields.
40727  * @constructor
40728  * Creates a new Checkbox
40729  * @param {Object} config Configuration options
40730  */
40731 Roo.form.Checkbox = function(config){
40732     Roo.form.Checkbox.superclass.constructor.call(this, config);
40733     this.addEvents({
40734         /**
40735          * @event check
40736          * Fires when the checkbox is checked or unchecked.
40737              * @param {Roo.form.Checkbox} this This checkbox
40738              * @param {Boolean} checked The new checked value
40739              */
40740         check : true
40741     });
40742 };
40743
40744 Roo.extend(Roo.form.Checkbox, Roo.form.Field,  {
40745     /**
40746      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
40747      */
40748     focusClass : undefined,
40749     /**
40750      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
40751      */
40752     fieldClass: "x-form-field",
40753     /**
40754      * @cfg {Boolean} checked True if the the checkbox should render already checked (defaults to false)
40755      */
40756     checked: false,
40757     /**
40758      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
40759      * {tag: "input", type: "checkbox", autocomplete: "off"})
40760      */
40761     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "off"},
40762     /**
40763      * @cfg {String} boxLabel The text that appears beside the checkbox
40764      */
40765     boxLabel : "",
40766     /**
40767      * @cfg {String} inputValue The value that should go into the generated input element's value attribute
40768      */  
40769     inputValue : '1',
40770     /**
40771      * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
40772      */
40773      valueOff: '0', // value when not checked..
40774
40775     actionMode : 'viewEl', 
40776     //
40777     // private
40778     itemCls : 'x-menu-check-item x-form-item',
40779     groupClass : 'x-menu-group-item',
40780     inputType : 'hidden',
40781     
40782     
40783     inSetChecked: false, // check that we are not calling self...
40784     
40785     inputElement: false, // real input element?
40786     basedOn: false, // ????
40787     
40788     isFormField: true, // not sure where this is needed!!!!
40789
40790     onResize : function(){
40791         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
40792         if(!this.boxLabel){
40793             this.el.alignTo(this.wrap, 'c-c');
40794         }
40795     },
40796
40797     initEvents : function(){
40798         Roo.form.Checkbox.superclass.initEvents.call(this);
40799         this.el.on("click", this.onClick,  this);
40800         this.el.on("change", this.onClick,  this);
40801     },
40802
40803
40804     getResizeEl : function(){
40805         return this.wrap;
40806     },
40807
40808     getPositionEl : function(){
40809         return this.wrap;
40810     },
40811
40812     // private
40813     onRender : function(ct, position){
40814         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
40815         /*
40816         if(this.inputValue !== undefined){
40817             this.el.dom.value = this.inputValue;
40818         }
40819         */
40820         //this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
40821         this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
40822         var viewEl = this.wrap.createChild({ 
40823             tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
40824         this.viewEl = viewEl;   
40825         this.wrap.on('click', this.onClick,  this); 
40826         
40827         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
40828         this.el.on('propertychange', this.setFromHidden,  this);  //ie
40829         
40830         
40831         
40832         if(this.boxLabel){
40833             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
40834         //    viewEl.on('click', this.onClick,  this); 
40835         }
40836         //if(this.checked){
40837             this.setChecked(this.checked);
40838         //}else{
40839             //this.checked = this.el.dom;
40840         //}
40841
40842     },
40843
40844     // private
40845     initValue : Roo.emptyFn,
40846
40847     /**
40848      * Returns the checked state of the checkbox.
40849      * @return {Boolean} True if checked, else false
40850      */
40851     getValue : function(){
40852         if(this.el){
40853             return String(this.el.dom.value) == String(this.inputValue ) ? this.inputValue : this.valueOff;
40854         }
40855         return this.valueOff;
40856         
40857     },
40858
40859         // private
40860     onClick : function(){ 
40861         this.setChecked(!this.checked);
40862
40863         //if(this.el.dom.checked != this.checked){
40864         //    this.setValue(this.el.dom.checked);
40865        // }
40866     },
40867
40868     /**
40869      * Sets the checked state of the checkbox.
40870      * On is always based on a string comparison between inputValue and the param.
40871      * @param {Boolean/String} value - the value to set 
40872      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
40873      */
40874     setValue : function(v,suppressEvent){
40875         
40876         
40877         //this.checked = (v === true || v === 'true' || v == '1' || String(v).toLowerCase() == 'on');
40878         //if(this.el && this.el.dom){
40879         //    this.el.dom.checked = this.checked;
40880         //    this.el.dom.defaultChecked = this.checked;
40881         //}
40882         this.setChecked(String(v) === String(this.inputValue), suppressEvent);
40883         //this.fireEvent("check", this, this.checked);
40884     },
40885     // private..
40886     setChecked : function(state,suppressEvent)
40887     {
40888         if (this.inSetChecked) {
40889             this.checked = state;
40890             return;
40891         }
40892         
40893     
40894         if(this.wrap){
40895             this.wrap[state ? 'addClass' : 'removeClass']('x-menu-item-checked');
40896         }
40897         this.checked = state;
40898         if(suppressEvent !== true){
40899             this.fireEvent('check', this, state);
40900         }
40901         this.inSetChecked = true;
40902         this.el.dom.value = state ? this.inputValue : this.valueOff;
40903         this.inSetChecked = false;
40904         
40905     },
40906     // handle setting of hidden value by some other method!!?!?
40907     setFromHidden: function()
40908     {
40909         if(!this.el){
40910             return;
40911         }
40912         //console.log("SET FROM HIDDEN");
40913         //alert('setFrom hidden');
40914         this.setValue(this.el.dom.value);
40915     },
40916     
40917     onDestroy : function()
40918     {
40919         if(this.viewEl){
40920             Roo.get(this.viewEl).remove();
40921         }
40922          
40923         Roo.form.Checkbox.superclass.onDestroy.call(this);
40924     }
40925
40926 });/*
40927  * Based on:
40928  * Ext JS Library 1.1.1
40929  * Copyright(c) 2006-2007, Ext JS, LLC.
40930  *
40931  * Originally Released Under LGPL - original licence link has changed is not relivant.
40932  *
40933  * Fork - LGPL
40934  * <script type="text/javascript">
40935  */
40936  
40937 /**
40938  * @class Roo.form.Radio
40939  * @extends Roo.form.Checkbox
40940  * Single radio field.  Same as Checkbox, but provided as a convenience for automatically setting the input type.
40941  * Radio grouping is handled automatically by the browser if you give each radio in a group the same name.
40942  * @constructor
40943  * Creates a new Radio
40944  * @param {Object} config Configuration options
40945  */
40946 Roo.form.Radio = function(){
40947     Roo.form.Radio.superclass.constructor.apply(this, arguments);
40948 };
40949 Roo.extend(Roo.form.Radio, Roo.form.Checkbox, {
40950     inputType: 'radio',
40951
40952     /**
40953      * If this radio is part of a group, it will return the selected value
40954      * @return {String}
40955      */
40956     getGroupValue : function(){
40957         return this.el.up('form').child('input[name='+this.el.dom.name+']:checked', true).value;
40958     },
40959     
40960     
40961     onRender : function(ct, position){
40962         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
40963         
40964         if(this.inputValue !== undefined){
40965             this.el.dom.value = this.inputValue;
40966         }
40967          
40968         this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
40969         //this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
40970         //var viewEl = this.wrap.createChild({ 
40971         //    tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
40972         //this.viewEl = viewEl;   
40973         //this.wrap.on('click', this.onClick,  this); 
40974         
40975         //this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
40976         //this.el.on('propertychange', this.setFromHidden,  this);  //ie
40977         
40978         
40979         
40980         if(this.boxLabel){
40981             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
40982         //    viewEl.on('click', this.onClick,  this); 
40983         }
40984          if(this.checked){
40985             this.el.dom.checked =   'checked' ;
40986         }
40987          
40988     } 
40989     
40990     
40991 });//<script type="text/javascript">
40992
40993 /*
40994  * Based  Ext JS Library 1.1.1
40995  * Copyright(c) 2006-2007, Ext JS, LLC.
40996  * LGPL
40997  *
40998  */
40999  
41000 /**
41001  * @class Roo.HtmlEditorCore
41002  * @extends Roo.Component
41003  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
41004  *
41005  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
41006  */
41007
41008 Roo.HtmlEditorCore = function(config){
41009     
41010     
41011     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
41012     this.addEvents({
41013         /**
41014          * @event initialize
41015          * Fires when the editor is fully initialized (including the iframe)
41016          * @param {Roo.HtmlEditorCore} this
41017          */
41018         initialize: true,
41019         /**
41020          * @event activate
41021          * Fires when the editor is first receives the focus. Any insertion must wait
41022          * until after this event.
41023          * @param {Roo.HtmlEditorCore} this
41024          */
41025         activate: true,
41026          /**
41027          * @event beforesync
41028          * Fires before the textarea is updated with content from the editor iframe. Return false
41029          * to cancel the sync.
41030          * @param {Roo.HtmlEditorCore} this
41031          * @param {String} html
41032          */
41033         beforesync: true,
41034          /**
41035          * @event beforepush
41036          * Fires before the iframe editor is updated with content from the textarea. Return false
41037          * to cancel the push.
41038          * @param {Roo.HtmlEditorCore} this
41039          * @param {String} html
41040          */
41041         beforepush: true,
41042          /**
41043          * @event sync
41044          * Fires when the textarea is updated with content from the editor iframe.
41045          * @param {Roo.HtmlEditorCore} this
41046          * @param {String} html
41047          */
41048         sync: true,
41049          /**
41050          * @event push
41051          * Fires when the iframe editor is updated with content from the textarea.
41052          * @param {Roo.HtmlEditorCore} this
41053          * @param {String} html
41054          */
41055         push: true,
41056         
41057         /**
41058          * @event editorevent
41059          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
41060          * @param {Roo.HtmlEditorCore} this
41061          */
41062         editorevent: true
41063     });
41064      
41065 };
41066
41067
41068 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
41069
41070
41071      /**
41072      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
41073      */
41074     
41075     owner : false,
41076     
41077      /**
41078      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
41079      *                        Roo.resizable.
41080      */
41081     resizable : false,
41082      /**
41083      * @cfg {Number} height (in pixels)
41084      */   
41085     height: 300,
41086    /**
41087      * @cfg {Number} width (in pixels)
41088      */   
41089     width: 500,
41090     
41091     /**
41092      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
41093      * 
41094      */
41095     stylesheets: false,
41096     
41097     // id of frame..
41098     frameId: false,
41099     
41100     // private properties
41101     validationEvent : false,
41102     deferHeight: true,
41103     initialized : false,
41104     activated : false,
41105     sourceEditMode : false,
41106     onFocus : Roo.emptyFn,
41107     iframePad:3,
41108     hideMode:'offsets',
41109     
41110     clearUp: true,
41111     
41112      
41113     
41114
41115     /**
41116      * Protected method that will not generally be called directly. It
41117      * is called when the editor initializes the iframe with HTML contents. Override this method if you
41118      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
41119      */
41120     getDocMarkup : function(){
41121         // body styles..
41122         var st = '';
41123         Roo.log(this.stylesheets);
41124         
41125         // inherit styels from page...?? 
41126         if (this.stylesheets === false) {
41127             
41128             Roo.get(document.head).select('style').each(function(node) {
41129                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
41130             });
41131             
41132             Roo.get(document.head).select('link').each(function(node) { 
41133                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
41134             });
41135             
41136         } else if (!this.stylesheets.length) {
41137                 // simple..
41138                 st = '<style type="text/css">' +
41139                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
41140                    '</style>';
41141         } else {
41142             Roo.each(this.stylesheets, function(s) {
41143                 st += '<link rel="stylesheet" type="text/css" href="' + s +'" />'
41144             });
41145             
41146         }
41147         
41148         st +=  '<style type="text/css">' +
41149             'IMG { cursor: pointer } ' +
41150         '</style>';
41151
41152         
41153         return '<html><head>' + st  +
41154             //<style type="text/css">' +
41155             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
41156             //'</style>' +
41157             ' </head><body class="roo-htmleditor-body"></body></html>';
41158     },
41159
41160     // private
41161     onRender : function(ct, position)
41162     {
41163         var _t = this;
41164         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
41165         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
41166         
41167         
41168         this.el.dom.style.border = '0 none';
41169         this.el.dom.setAttribute('tabIndex', -1);
41170         this.el.addClass('x-hidden hide');
41171         
41172         
41173         
41174         if(Roo.isIE){ // fix IE 1px bogus margin
41175             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
41176         }
41177        
41178         
41179         this.frameId = Roo.id();
41180         
41181          
41182         
41183         var iframe = this.owner.wrap.createChild({
41184             tag: 'iframe',
41185             cls: 'form-control', // bootstrap..
41186             id: this.frameId,
41187             name: this.frameId,
41188             frameBorder : 'no',
41189             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
41190         }, this.el
41191         );
41192         
41193         
41194         this.iframe = iframe.dom;
41195
41196          this.assignDocWin();
41197         
41198         this.doc.designMode = 'on';
41199        
41200         this.doc.open();
41201         this.doc.write(this.getDocMarkup());
41202         this.doc.close();
41203
41204         
41205         var task = { // must defer to wait for browser to be ready
41206             run : function(){
41207                 //console.log("run task?" + this.doc.readyState);
41208                 this.assignDocWin();
41209                 if(this.doc.body || this.doc.readyState == 'complete'){
41210                     try {
41211                         this.doc.designMode="on";
41212                     } catch (e) {
41213                         return;
41214                     }
41215                     Roo.TaskMgr.stop(task);
41216                     this.initEditor.defer(10, this);
41217                 }
41218             },
41219             interval : 10,
41220             duration: 10000,
41221             scope: this
41222         };
41223         Roo.TaskMgr.start(task);
41224
41225         
41226          
41227     },
41228
41229     // private
41230     onResize : function(w, h)
41231     {
41232          Roo.log('resize: ' +w + ',' + h );
41233         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
41234         if(!this.iframe){
41235             return;
41236         }
41237         if(typeof w == 'number'){
41238             
41239             this.iframe.style.width = w + 'px';
41240         }
41241         if(typeof h == 'number'){
41242             
41243             this.iframe.style.height = h + 'px';
41244             if(this.doc){
41245                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
41246             }
41247         }
41248         
41249     },
41250
41251     /**
41252      * Toggles the editor between standard and source edit mode.
41253      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
41254      */
41255     toggleSourceEdit : function(sourceEditMode){
41256         
41257         this.sourceEditMode = sourceEditMode === true;
41258         
41259         if(this.sourceEditMode){
41260  
41261             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
41262             
41263         }else{
41264             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
41265             //this.iframe.className = '';
41266             this.deferFocus();
41267         }
41268         //this.setSize(this.owner.wrap.getSize());
41269         //this.fireEvent('editmodechange', this, this.sourceEditMode);
41270     },
41271
41272     
41273   
41274
41275     /**
41276      * Protected method that will not generally be called directly. If you need/want
41277      * custom HTML cleanup, this is the method you should override.
41278      * @param {String} html The HTML to be cleaned
41279      * return {String} The cleaned HTML
41280      */
41281     cleanHtml : function(html){
41282         html = String(html);
41283         if(html.length > 5){
41284             if(Roo.isSafari){ // strip safari nonsense
41285                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
41286             }
41287         }
41288         if(html == '&nbsp;'){
41289             html = '';
41290         }
41291         return html;
41292     },
41293
41294     /**
41295      * HTML Editor -> Textarea
41296      * Protected method that will not generally be called directly. Syncs the contents
41297      * of the editor iframe with the textarea.
41298      */
41299     syncValue : function(){
41300         if(this.initialized){
41301             var bd = (this.doc.body || this.doc.documentElement);
41302             //this.cleanUpPaste(); -- this is done else where and causes havoc..
41303             var html = bd.innerHTML;
41304             if(Roo.isSafari){
41305                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
41306                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
41307                 if(m && m[1]){
41308                     html = '<div style="'+m[0]+'">' + html + '</div>';
41309                 }
41310             }
41311             html = this.cleanHtml(html);
41312             // fix up the special chars.. normaly like back quotes in word...
41313             // however we do not want to do this with chinese..
41314             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
41315                 var cc = b.charCodeAt();
41316                 if (
41317                     (cc >= 0x4E00 && cc < 0xA000 ) ||
41318                     (cc >= 0x3400 && cc < 0x4E00 ) ||
41319                     (cc >= 0xf900 && cc < 0xfb00 )
41320                 ) {
41321                         return b;
41322                 }
41323                 return "&#"+cc+";" 
41324             });
41325             if(this.owner.fireEvent('beforesync', this, html) !== false){
41326                 this.el.dom.value = html;
41327                 this.owner.fireEvent('sync', this, html);
41328             }
41329         }
41330     },
41331
41332     /**
41333      * Protected method that will not generally be called directly. Pushes the value of the textarea
41334      * into the iframe editor.
41335      */
41336     pushValue : function(){
41337         if(this.initialized){
41338             var v = this.el.dom.value.trim();
41339             
41340 //            if(v.length < 1){
41341 //                v = '&#160;';
41342 //            }
41343             
41344             if(this.owner.fireEvent('beforepush', this, v) !== false){
41345                 var d = (this.doc.body || this.doc.documentElement);
41346                 d.innerHTML = v;
41347                 this.cleanUpPaste();
41348                 this.el.dom.value = d.innerHTML;
41349                 this.owner.fireEvent('push', this, v);
41350             }
41351         }
41352     },
41353
41354     // private
41355     deferFocus : function(){
41356         this.focus.defer(10, this);
41357     },
41358
41359     // doc'ed in Field
41360     focus : function(){
41361         if(this.win && !this.sourceEditMode){
41362             this.win.focus();
41363         }else{
41364             this.el.focus();
41365         }
41366     },
41367     
41368     assignDocWin: function()
41369     {
41370         var iframe = this.iframe;
41371         
41372          if(Roo.isIE){
41373             this.doc = iframe.contentWindow.document;
41374             this.win = iframe.contentWindow;
41375         } else {
41376             if (!Roo.get(this.frameId)) {
41377                 return;
41378             }
41379             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
41380             this.win = Roo.get(this.frameId).dom.contentWindow;
41381         }
41382     },
41383     
41384     // private
41385     initEditor : function(){
41386         //console.log("INIT EDITOR");
41387         this.assignDocWin();
41388         
41389         
41390         
41391         this.doc.designMode="on";
41392         this.doc.open();
41393         this.doc.write(this.getDocMarkup());
41394         this.doc.close();
41395         
41396         var dbody = (this.doc.body || this.doc.documentElement);
41397         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
41398         // this copies styles from the containing element into thsi one..
41399         // not sure why we need all of this..
41400         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
41401         
41402         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
41403         //ss['background-attachment'] = 'fixed'; // w3c
41404         dbody.bgProperties = 'fixed'; // ie
41405         //Roo.DomHelper.applyStyles(dbody, ss);
41406         Roo.EventManager.on(this.doc, {
41407             //'mousedown': this.onEditorEvent,
41408             'mouseup': this.onEditorEvent,
41409             'dblclick': this.onEditorEvent,
41410             'click': this.onEditorEvent,
41411             'keyup': this.onEditorEvent,
41412             buffer:100,
41413             scope: this
41414         });
41415         if(Roo.isGecko){
41416             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
41417         }
41418         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
41419             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
41420         }
41421         this.initialized = true;
41422
41423         this.owner.fireEvent('initialize', this);
41424         this.pushValue();
41425     },
41426
41427     // private
41428     onDestroy : function(){
41429         
41430         
41431         
41432         if(this.rendered){
41433             
41434             //for (var i =0; i < this.toolbars.length;i++) {
41435             //    // fixme - ask toolbars for heights?
41436             //    this.toolbars[i].onDestroy();
41437            // }
41438             
41439             //this.wrap.dom.innerHTML = '';
41440             //this.wrap.remove();
41441         }
41442     },
41443
41444     // private
41445     onFirstFocus : function(){
41446         
41447         this.assignDocWin();
41448         
41449         
41450         this.activated = true;
41451          
41452     
41453         if(Roo.isGecko){ // prevent silly gecko errors
41454             this.win.focus();
41455             var s = this.win.getSelection();
41456             if(!s.focusNode || s.focusNode.nodeType != 3){
41457                 var r = s.getRangeAt(0);
41458                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
41459                 r.collapse(true);
41460                 this.deferFocus();
41461             }
41462             try{
41463                 this.execCmd('useCSS', true);
41464                 this.execCmd('styleWithCSS', false);
41465             }catch(e){}
41466         }
41467         this.owner.fireEvent('activate', this);
41468     },
41469
41470     // private
41471     adjustFont: function(btn){
41472         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
41473         //if(Roo.isSafari){ // safari
41474         //    adjust *= 2;
41475        // }
41476         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
41477         if(Roo.isSafari){ // safari
41478             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
41479             v =  (v < 10) ? 10 : v;
41480             v =  (v > 48) ? 48 : v;
41481             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
41482             
41483         }
41484         
41485         
41486         v = Math.max(1, v+adjust);
41487         
41488         this.execCmd('FontSize', v  );
41489     },
41490
41491     onEditorEvent : function(e){
41492         this.owner.fireEvent('editorevent', this, e);
41493       //  this.updateToolbar();
41494         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
41495     },
41496
41497     insertTag : function(tg)
41498     {
41499         // could be a bit smarter... -> wrap the current selected tRoo..
41500         if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
41501             
41502             range = this.createRange(this.getSelection());
41503             var wrappingNode = this.doc.createElement(tg.toLowerCase());
41504             wrappingNode.appendChild(range.extractContents());
41505             range.insertNode(wrappingNode);
41506
41507             return;
41508             
41509             
41510             
41511         }
41512         this.execCmd("formatblock",   tg);
41513         
41514     },
41515     
41516     insertText : function(txt)
41517     {
41518         
41519         
41520         var range = this.createRange();
41521         range.deleteContents();
41522                //alert(Sender.getAttribute('label'));
41523                
41524         range.insertNode(this.doc.createTextNode(txt));
41525     } ,
41526     
41527      
41528
41529     /**
41530      * Executes a Midas editor command on the editor document and performs necessary focus and
41531      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
41532      * @param {String} cmd The Midas command
41533      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
41534      */
41535     relayCmd : function(cmd, value){
41536         this.win.focus();
41537         this.execCmd(cmd, value);
41538         this.owner.fireEvent('editorevent', this);
41539         //this.updateToolbar();
41540         this.owner.deferFocus();
41541     },
41542
41543     /**
41544      * Executes a Midas editor command directly on the editor document.
41545      * For visual commands, you should use {@link #relayCmd} instead.
41546      * <b>This should only be called after the editor is initialized.</b>
41547      * @param {String} cmd The Midas command
41548      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
41549      */
41550     execCmd : function(cmd, value){
41551         this.doc.execCommand(cmd, false, value === undefined ? null : value);
41552         this.syncValue();
41553     },
41554  
41555  
41556    
41557     /**
41558      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
41559      * to insert tRoo.
41560      * @param {String} text | dom node.. 
41561      */
41562     insertAtCursor : function(text)
41563     {
41564         
41565         
41566         
41567         if(!this.activated){
41568             return;
41569         }
41570         /*
41571         if(Roo.isIE){
41572             this.win.focus();
41573             var r = this.doc.selection.createRange();
41574             if(r){
41575                 r.collapse(true);
41576                 r.pasteHTML(text);
41577                 this.syncValue();
41578                 this.deferFocus();
41579             
41580             }
41581             return;
41582         }
41583         */
41584         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
41585             this.win.focus();
41586             
41587             
41588             // from jquery ui (MIT licenced)
41589             var range, node;
41590             var win = this.win;
41591             
41592             if (win.getSelection && win.getSelection().getRangeAt) {
41593                 range = win.getSelection().getRangeAt(0);
41594                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
41595                 range.insertNode(node);
41596             } else if (win.document.selection && win.document.selection.createRange) {
41597                 // no firefox support
41598                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
41599                 win.document.selection.createRange().pasteHTML(txt);
41600             } else {
41601                 // no firefox support
41602                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
41603                 this.execCmd('InsertHTML', txt);
41604             } 
41605             
41606             this.syncValue();
41607             
41608             this.deferFocus();
41609         }
41610     },
41611  // private
41612     mozKeyPress : function(e){
41613         if(e.ctrlKey){
41614             var c = e.getCharCode(), cmd;
41615           
41616             if(c > 0){
41617                 c = String.fromCharCode(c).toLowerCase();
41618                 switch(c){
41619                     case 'b':
41620                         cmd = 'bold';
41621                         break;
41622                     case 'i':
41623                         cmd = 'italic';
41624                         break;
41625                     
41626                     case 'u':
41627                         cmd = 'underline';
41628                         break;
41629                     
41630                     case 'v':
41631                         this.cleanUpPaste.defer(100, this);
41632                         return;
41633                         
41634                 }
41635                 if(cmd){
41636                     this.win.focus();
41637                     this.execCmd(cmd);
41638                     this.deferFocus();
41639                     e.preventDefault();
41640                 }
41641                 
41642             }
41643         }
41644     },
41645
41646     // private
41647     fixKeys : function(){ // load time branching for fastest keydown performance
41648         if(Roo.isIE){
41649             return function(e){
41650                 var k = e.getKey(), r;
41651                 if(k == e.TAB){
41652                     e.stopEvent();
41653                     r = this.doc.selection.createRange();
41654                     if(r){
41655                         r.collapse(true);
41656                         r.pasteHTML('&#160;&#160;&#160;&#160;');
41657                         this.deferFocus();
41658                     }
41659                     return;
41660                 }
41661                 
41662                 if(k == e.ENTER){
41663                     r = this.doc.selection.createRange();
41664                     if(r){
41665                         var target = r.parentElement();
41666                         if(!target || target.tagName.toLowerCase() != 'li'){
41667                             e.stopEvent();
41668                             r.pasteHTML('<br />');
41669                             r.collapse(false);
41670                             r.select();
41671                         }
41672                     }
41673                 }
41674                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
41675                     this.cleanUpPaste.defer(100, this);
41676                     return;
41677                 }
41678                 
41679                 
41680             };
41681         }else if(Roo.isOpera){
41682             return function(e){
41683                 var k = e.getKey();
41684                 if(k == e.TAB){
41685                     e.stopEvent();
41686                     this.win.focus();
41687                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
41688                     this.deferFocus();
41689                 }
41690                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
41691                     this.cleanUpPaste.defer(100, this);
41692                     return;
41693                 }
41694                 
41695             };
41696         }else if(Roo.isSafari){
41697             return function(e){
41698                 var k = e.getKey();
41699                 
41700                 if(k == e.TAB){
41701                     e.stopEvent();
41702                     this.execCmd('InsertText','\t');
41703                     this.deferFocus();
41704                     return;
41705                 }
41706                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
41707                     this.cleanUpPaste.defer(100, this);
41708                     return;
41709                 }
41710                 
41711              };
41712         }
41713     }(),
41714     
41715     getAllAncestors: function()
41716     {
41717         var p = this.getSelectedNode();
41718         var a = [];
41719         if (!p) {
41720             a.push(p); // push blank onto stack..
41721             p = this.getParentElement();
41722         }
41723         
41724         
41725         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
41726             a.push(p);
41727             p = p.parentNode;
41728         }
41729         a.push(this.doc.body);
41730         return a;
41731     },
41732     lastSel : false,
41733     lastSelNode : false,
41734     
41735     
41736     getSelection : function() 
41737     {
41738         this.assignDocWin();
41739         return Roo.isIE ? this.doc.selection : this.win.getSelection();
41740     },
41741     
41742     getSelectedNode: function() 
41743     {
41744         // this may only work on Gecko!!!
41745         
41746         // should we cache this!!!!
41747         
41748         
41749         
41750          
41751         var range = this.createRange(this.getSelection()).cloneRange();
41752         
41753         if (Roo.isIE) {
41754             var parent = range.parentElement();
41755             while (true) {
41756                 var testRange = range.duplicate();
41757                 testRange.moveToElementText(parent);
41758                 if (testRange.inRange(range)) {
41759                     break;
41760                 }
41761                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
41762                     break;
41763                 }
41764                 parent = parent.parentElement;
41765             }
41766             return parent;
41767         }
41768         
41769         // is ancestor a text element.
41770         var ac =  range.commonAncestorContainer;
41771         if (ac.nodeType == 3) {
41772             ac = ac.parentNode;
41773         }
41774         
41775         var ar = ac.childNodes;
41776          
41777         var nodes = [];
41778         var other_nodes = [];
41779         var has_other_nodes = false;
41780         for (var i=0;i<ar.length;i++) {
41781             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
41782                 continue;
41783             }
41784             // fullly contained node.
41785             
41786             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
41787                 nodes.push(ar[i]);
41788                 continue;
41789             }
41790             
41791             // probably selected..
41792             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
41793                 other_nodes.push(ar[i]);
41794                 continue;
41795             }
41796             // outer..
41797             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
41798                 continue;
41799             }
41800             
41801             
41802             has_other_nodes = true;
41803         }
41804         if (!nodes.length && other_nodes.length) {
41805             nodes= other_nodes;
41806         }
41807         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
41808             return false;
41809         }
41810         
41811         return nodes[0];
41812     },
41813     createRange: function(sel)
41814     {
41815         // this has strange effects when using with 
41816         // top toolbar - not sure if it's a great idea.
41817         //this.editor.contentWindow.focus();
41818         if (typeof sel != "undefined") {
41819             try {
41820                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
41821             } catch(e) {
41822                 return this.doc.createRange();
41823             }
41824         } else {
41825             return this.doc.createRange();
41826         }
41827     },
41828     getParentElement: function()
41829     {
41830         
41831         this.assignDocWin();
41832         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
41833         
41834         var range = this.createRange(sel);
41835          
41836         try {
41837             var p = range.commonAncestorContainer;
41838             while (p.nodeType == 3) { // text node
41839                 p = p.parentNode;
41840             }
41841             return p;
41842         } catch (e) {
41843             return null;
41844         }
41845     
41846     },
41847     /***
41848      *
41849      * Range intersection.. the hard stuff...
41850      *  '-1' = before
41851      *  '0' = hits..
41852      *  '1' = after.
41853      *         [ -- selected range --- ]
41854      *   [fail]                        [fail]
41855      *
41856      *    basically..
41857      *      if end is before start or  hits it. fail.
41858      *      if start is after end or hits it fail.
41859      *
41860      *   if either hits (but other is outside. - then it's not 
41861      *   
41862      *    
41863      **/
41864     
41865     
41866     // @see http://www.thismuchiknow.co.uk/?p=64.
41867     rangeIntersectsNode : function(range, node)
41868     {
41869         var nodeRange = node.ownerDocument.createRange();
41870         try {
41871             nodeRange.selectNode(node);
41872         } catch (e) {
41873             nodeRange.selectNodeContents(node);
41874         }
41875     
41876         var rangeStartRange = range.cloneRange();
41877         rangeStartRange.collapse(true);
41878     
41879         var rangeEndRange = range.cloneRange();
41880         rangeEndRange.collapse(false);
41881     
41882         var nodeStartRange = nodeRange.cloneRange();
41883         nodeStartRange.collapse(true);
41884     
41885         var nodeEndRange = nodeRange.cloneRange();
41886         nodeEndRange.collapse(false);
41887     
41888         return rangeStartRange.compareBoundaryPoints(
41889                  Range.START_TO_START, nodeEndRange) == -1 &&
41890                rangeEndRange.compareBoundaryPoints(
41891                  Range.START_TO_START, nodeStartRange) == 1;
41892         
41893          
41894     },
41895     rangeCompareNode : function(range, node)
41896     {
41897         var nodeRange = node.ownerDocument.createRange();
41898         try {
41899             nodeRange.selectNode(node);
41900         } catch (e) {
41901             nodeRange.selectNodeContents(node);
41902         }
41903         
41904         
41905         range.collapse(true);
41906     
41907         nodeRange.collapse(true);
41908      
41909         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
41910         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
41911          
41912         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
41913         
41914         var nodeIsBefore   =  ss == 1;
41915         var nodeIsAfter    = ee == -1;
41916         
41917         if (nodeIsBefore && nodeIsAfter)
41918             return 0; // outer
41919         if (!nodeIsBefore && nodeIsAfter)
41920             return 1; //right trailed.
41921         
41922         if (nodeIsBefore && !nodeIsAfter)
41923             return 2;  // left trailed.
41924         // fully contined.
41925         return 3;
41926     },
41927
41928     // private? - in a new class?
41929     cleanUpPaste :  function()
41930     {
41931         // cleans up the whole document..
41932         Roo.log('cleanuppaste');
41933         
41934         this.cleanUpChildren(this.doc.body);
41935         var clean = this.cleanWordChars(this.doc.body.innerHTML);
41936         if (clean != this.doc.body.innerHTML) {
41937             this.doc.body.innerHTML = clean;
41938         }
41939         
41940     },
41941     
41942     cleanWordChars : function(input) {// change the chars to hex code
41943         var he = Roo.HtmlEditorCore;
41944         
41945         var output = input;
41946         Roo.each(he.swapCodes, function(sw) { 
41947             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
41948             
41949             output = output.replace(swapper, sw[1]);
41950         });
41951         
41952         return output;
41953     },
41954     
41955     
41956     cleanUpChildren : function (n)
41957     {
41958         if (!n.childNodes.length) {
41959             return;
41960         }
41961         for (var i = n.childNodes.length-1; i > -1 ; i--) {
41962            this.cleanUpChild(n.childNodes[i]);
41963         }
41964     },
41965     
41966     
41967         
41968     
41969     cleanUpChild : function (node)
41970     {
41971         var ed = this;
41972         //console.log(node);
41973         if (node.nodeName == "#text") {
41974             // clean up silly Windows -- stuff?
41975             return; 
41976         }
41977         if (node.nodeName == "#comment") {
41978             node.parentNode.removeChild(node);
41979             // clean up silly Windows -- stuff?
41980             return; 
41981         }
41982         
41983         if (Roo.HtmlEditorCore.black.indexOf(node.tagName.toLowerCase()) > -1 && this.clearUp) {
41984             // remove node.
41985             node.parentNode.removeChild(node);
41986             return;
41987             
41988         }
41989         
41990         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
41991         
41992         // remove <a name=....> as rendering on yahoo mailer is borked with this.
41993         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
41994         
41995         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
41996         //    remove_keep_children = true;
41997         //}
41998         
41999         if (remove_keep_children) {
42000             this.cleanUpChildren(node);
42001             // inserts everything just before this node...
42002             while (node.childNodes.length) {
42003                 var cn = node.childNodes[0];
42004                 node.removeChild(cn);
42005                 node.parentNode.insertBefore(cn, node);
42006             }
42007             node.parentNode.removeChild(node);
42008             return;
42009         }
42010         
42011         if (!node.attributes || !node.attributes.length) {
42012             this.cleanUpChildren(node);
42013             return;
42014         }
42015         
42016         function cleanAttr(n,v)
42017         {
42018             
42019             if (v.match(/^\./) || v.match(/^\//)) {
42020                 return;
42021             }
42022             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
42023                 return;
42024             }
42025             if (v.match(/^#/)) {
42026                 return;
42027             }
42028 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
42029             node.removeAttribute(n);
42030             
42031         }
42032         
42033         function cleanStyle(n,v)
42034         {
42035             if (v.match(/expression/)) { //XSS?? should we even bother..
42036                 node.removeAttribute(n);
42037                 return;
42038             }
42039             var cwhite = typeof(ed.cwhite) == 'undefined' ? Roo.HtmlEditorCore.cwhite : ed.cwhite;
42040             var cblack = typeof(ed.cblack) == 'undefined' ? Roo.HtmlEditorCore.cblack : ed.cblack;
42041             
42042             
42043             var parts = v.split(/;/);
42044             var clean = [];
42045             
42046             Roo.each(parts, function(p) {
42047                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
42048                 if (!p.length) {
42049                     return true;
42050                 }
42051                 var l = p.split(':').shift().replace(/\s+/g,'');
42052                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
42053                 
42054                 if ( cblack.indexOf(l) > -1) {
42055 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
42056                     //node.removeAttribute(n);
42057                     return true;
42058                 }
42059                 //Roo.log()
42060                 // only allow 'c whitelisted system attributes'
42061                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
42062 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
42063                     //node.removeAttribute(n);
42064                     return true;
42065                 }
42066                 
42067                 
42068                  
42069                 
42070                 clean.push(p);
42071                 return true;
42072             });
42073             if (clean.length) { 
42074                 node.setAttribute(n, clean.join(';'));
42075             } else {
42076                 node.removeAttribute(n);
42077             }
42078             
42079         }
42080         
42081         
42082         for (var i = node.attributes.length-1; i > -1 ; i--) {
42083             var a = node.attributes[i];
42084             //console.log(a);
42085             
42086             if (a.name.toLowerCase().substr(0,2)=='on')  {
42087                 node.removeAttribute(a.name);
42088                 continue;
42089             }
42090             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
42091                 node.removeAttribute(a.name);
42092                 continue;
42093             }
42094             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
42095                 cleanAttr(a.name,a.value); // fixme..
42096                 continue;
42097             }
42098             if (a.name == 'style') {
42099                 cleanStyle(a.name,a.value);
42100                 continue;
42101             }
42102             /// clean up MS crap..
42103             // tecnically this should be a list of valid class'es..
42104             
42105             
42106             if (a.name == 'class') {
42107                 if (a.value.match(/^Mso/)) {
42108                     node.className = '';
42109                 }
42110                 
42111                 if (a.value.match(/body/)) {
42112                     node.className = '';
42113                 }
42114                 continue;
42115             }
42116             
42117             // style cleanup!?
42118             // class cleanup?
42119             
42120         }
42121         
42122         
42123         this.cleanUpChildren(node);
42124         
42125         
42126     },
42127     /**
42128      * Clean up MS wordisms...
42129      */
42130     cleanWord : function(node)
42131     {
42132         var _t = this;
42133         var cleanWordChildren = function()
42134         {
42135             if (!node.childNodes.length) {
42136                 return;
42137             }
42138             for (var i = node.childNodes.length-1; i > -1 ; i--) {
42139                _t.cleanWord(node.childNodes[i]);
42140             }
42141         }
42142         
42143         
42144         if (!node) {
42145             this.cleanWord(this.doc.body);
42146             return;
42147         }
42148         if (node.nodeName == "#text") {
42149             // clean up silly Windows -- stuff?
42150             return; 
42151         }
42152         if (node.nodeName == "#comment") {
42153             node.parentNode.removeChild(node);
42154             // clean up silly Windows -- stuff?
42155             return; 
42156         }
42157         
42158         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
42159             node.parentNode.removeChild(node);
42160             return;
42161         }
42162         
42163         // remove - but keep children..
42164         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
42165             while (node.childNodes.length) {
42166                 var cn = node.childNodes[0];
42167                 node.removeChild(cn);
42168                 node.parentNode.insertBefore(cn, node);
42169             }
42170             node.parentNode.removeChild(node);
42171             cleanWordChildren();
42172             return;
42173         }
42174         // clean styles
42175         if (node.className.length) {
42176             
42177             var cn = node.className.split(/\W+/);
42178             var cna = [];
42179             Roo.each(cn, function(cls) {
42180                 if (cls.match(/Mso[a-zA-Z]+/)) {
42181                     return;
42182                 }
42183                 cna.push(cls);
42184             });
42185             node.className = cna.length ? cna.join(' ') : '';
42186             if (!cna.length) {
42187                 node.removeAttribute("class");
42188             }
42189         }
42190         
42191         if (node.hasAttribute("lang")) {
42192             node.removeAttribute("lang");
42193         }
42194         
42195         if (node.hasAttribute("style")) {
42196             
42197             var styles = node.getAttribute("style").split(";");
42198             var nstyle = [];
42199             Roo.each(styles, function(s) {
42200                 if (!s.match(/:/)) {
42201                     return;
42202                 }
42203                 var kv = s.split(":");
42204                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
42205                     return;
42206                 }
42207                 // what ever is left... we allow.
42208                 nstyle.push(s);
42209             });
42210             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
42211             if (!nstyle.length) {
42212                 node.removeAttribute('style');
42213             }
42214         }
42215         
42216         cleanWordChildren();
42217         
42218         
42219     },
42220     domToHTML : function(currentElement, depth, nopadtext) {
42221         
42222             depth = depth || 0;
42223             nopadtext = nopadtext || false;
42224         
42225             if (!currentElement) {
42226                 return this.domToHTML(this.doc.body);
42227             }
42228             
42229             //Roo.log(currentElement);
42230             var j;
42231             var allText = false;
42232             var nodeName = currentElement.nodeName;
42233             var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
42234             
42235             if  (nodeName == '#text') {
42236                 return currentElement.nodeValue;
42237             }
42238             
42239             
42240             var ret = '';
42241             if (nodeName != 'BODY') {
42242                  
42243                 var i = 0;
42244                 // Prints the node tagName, such as <A>, <IMG>, etc
42245                 if (tagName) {
42246                     var attr = [];
42247                     for(i = 0; i < currentElement.attributes.length;i++) {
42248                         // quoting?
42249                         var aname = currentElement.attributes.item(i).name;
42250                         if (!currentElement.attributes.item(i).value.length) {
42251                             continue;
42252                         }
42253                         attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
42254                     }
42255                     
42256                     ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
42257                 } 
42258                 else {
42259                     
42260                     // eack
42261                 }
42262             } else {
42263                 tagName = false;
42264             }
42265             if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
42266                 return ret;
42267             }
42268             if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
42269                 nopadtext = true;
42270             }
42271             
42272             
42273             // Traverse the tree
42274             i = 0;
42275             var currentElementChild = currentElement.childNodes.item(i);
42276             var allText = true;
42277             var innerHTML  = '';
42278             lastnode = '';
42279             while (currentElementChild) {
42280                 // Formatting code (indent the tree so it looks nice on the screen)
42281                 var nopad = nopadtext;
42282                 if (lastnode == 'SPAN') {
42283                     nopad  = true;
42284                 }
42285                 // text
42286                 if  (currentElementChild.nodeName == '#text') {
42287                     var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
42288                     if (!nopad && toadd.length > 80) {
42289                         innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
42290                     }
42291                     innerHTML  += toadd;
42292                     
42293                     i++;
42294                     currentElementChild = currentElement.childNodes.item(i);
42295                     lastNode = '';
42296                     continue;
42297                 }
42298                 allText = false;
42299                 
42300                 innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
42301                     
42302                 // Recursively traverse the tree structure of the child node
42303                 innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
42304                 lastnode = currentElementChild.nodeName;
42305                 i++;
42306                 currentElementChild=currentElement.childNodes.item(i);
42307             }
42308             
42309             ret += innerHTML;
42310             
42311             if (!allText) {
42312                     // The remaining code is mostly for formatting the tree
42313                 ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
42314             }
42315             
42316             
42317             if (tagName) {
42318                 ret+= "</"+tagName+">";
42319             }
42320             return ret;
42321             
42322         }
42323     
42324     // hide stuff that is not compatible
42325     /**
42326      * @event blur
42327      * @hide
42328      */
42329     /**
42330      * @event change
42331      * @hide
42332      */
42333     /**
42334      * @event focus
42335      * @hide
42336      */
42337     /**
42338      * @event specialkey
42339      * @hide
42340      */
42341     /**
42342      * @cfg {String} fieldClass @hide
42343      */
42344     /**
42345      * @cfg {String} focusClass @hide
42346      */
42347     /**
42348      * @cfg {String} autoCreate @hide
42349      */
42350     /**
42351      * @cfg {String} inputType @hide
42352      */
42353     /**
42354      * @cfg {String} invalidClass @hide
42355      */
42356     /**
42357      * @cfg {String} invalidText @hide
42358      */
42359     /**
42360      * @cfg {String} msgFx @hide
42361      */
42362     /**
42363      * @cfg {String} validateOnBlur @hide
42364      */
42365 });
42366
42367 Roo.HtmlEditorCore.white = [
42368         'area', 'br', 'img', 'input', 'hr', 'wbr',
42369         
42370        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
42371        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
42372        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
42373        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
42374        'table',   'ul',         'xmp', 
42375        
42376        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
42377       'thead',   'tr', 
42378      
42379       'dir', 'menu', 'ol', 'ul', 'dl',
42380        
42381       'embed',  'object'
42382 ];
42383
42384
42385 Roo.HtmlEditorCore.black = [
42386     //    'embed',  'object', // enable - backend responsiblity to clean thiese
42387         'applet', // 
42388         'base',   'basefont', 'bgsound', 'blink',  'body', 
42389         'frame',  'frameset', 'head',    'html',   'ilayer', 
42390         'iframe', 'layer',  'link',     'meta',    'object',   
42391         'script', 'style' ,'title',  'xml' // clean later..
42392 ];
42393 Roo.HtmlEditorCore.clean = [
42394     'script', 'style', 'title', 'xml'
42395 ];
42396 Roo.HtmlEditorCore.remove = [
42397     'font'
42398 ];
42399 // attributes..
42400
42401 Roo.HtmlEditorCore.ablack = [
42402     'on'
42403 ];
42404     
42405 Roo.HtmlEditorCore.aclean = [ 
42406     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
42407 ];
42408
42409 // protocols..
42410 Roo.HtmlEditorCore.pwhite= [
42411         'http',  'https',  'mailto'
42412 ];
42413
42414 // white listed style attributes.
42415 Roo.HtmlEditorCore.cwhite= [
42416       //  'text-align', /// default is to allow most things..
42417       
42418          
42419 //        'font-size'//??
42420 ];
42421
42422 // black listed style attributes.
42423 Roo.HtmlEditorCore.cblack= [
42424       //  'font-size' -- this can be set by the project 
42425 ];
42426
42427
42428 Roo.HtmlEditorCore.swapCodes   =[ 
42429     [    8211, "--" ], 
42430     [    8212, "--" ], 
42431     [    8216,  "'" ],  
42432     [    8217, "'" ],  
42433     [    8220, '"' ],  
42434     [    8221, '"' ],  
42435     [    8226, "*" ],  
42436     [    8230, "..." ]
42437 ]; 
42438
42439     //<script type="text/javascript">
42440
42441 /*
42442  * Ext JS Library 1.1.1
42443  * Copyright(c) 2006-2007, Ext JS, LLC.
42444  * Licence LGPL
42445  * 
42446  */
42447  
42448  
42449 Roo.form.HtmlEditor = function(config){
42450     
42451     
42452     
42453     Roo.form.HtmlEditor.superclass.constructor.call(this, config);
42454     
42455     if (!this.toolbars) {
42456         this.toolbars = [];
42457     }
42458     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
42459     
42460     
42461 };
42462
42463 /**
42464  * @class Roo.form.HtmlEditor
42465  * @extends Roo.form.Field
42466  * Provides a lightweight HTML Editor component.
42467  *
42468  * This has been tested on Fireforx / Chrome.. IE may not be so great..
42469  * 
42470  * <br><br><b>Note: The focus/blur and validation marking functionality inherited from Ext.form.Field is NOT
42471  * supported by this editor.</b><br/><br/>
42472  * An Editor is a sensitive component that can't be used in all spots standard fields can be used. Putting an Editor within
42473  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
42474  */
42475 Roo.extend(Roo.form.HtmlEditor, Roo.form.Field, {
42476     /**
42477      * @cfg {Boolean} clearUp
42478      */
42479     clearUp : true,
42480       /**
42481      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
42482      */
42483     toolbars : false,
42484    
42485      /**
42486      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
42487      *                        Roo.resizable.
42488      */
42489     resizable : false,
42490      /**
42491      * @cfg {Number} height (in pixels)
42492      */   
42493     height: 300,
42494    /**
42495      * @cfg {Number} width (in pixels)
42496      */   
42497     width: 500,
42498     
42499     /**
42500      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
42501      * 
42502      */
42503     stylesheets: false,
42504     
42505     // id of frame..
42506     frameId: false,
42507     
42508     // private properties
42509     validationEvent : false,
42510     deferHeight: true,
42511     initialized : false,
42512     activated : false,
42513     
42514     onFocus : Roo.emptyFn,
42515     iframePad:3,
42516     hideMode:'offsets',
42517     
42518     defaultAutoCreate : { // modified by initCompnoent..
42519         tag: "textarea",
42520         style:"width:500px;height:300px;",
42521         autocomplete: "off"
42522     },
42523
42524     // private
42525     initComponent : function(){
42526         this.addEvents({
42527             /**
42528              * @event initialize
42529              * Fires when the editor is fully initialized (including the iframe)
42530              * @param {HtmlEditor} this
42531              */
42532             initialize: true,
42533             /**
42534              * @event activate
42535              * Fires when the editor is first receives the focus. Any insertion must wait
42536              * until after this event.
42537              * @param {HtmlEditor} this
42538              */
42539             activate: true,
42540              /**
42541              * @event beforesync
42542              * Fires before the textarea is updated with content from the editor iframe. Return false
42543              * to cancel the sync.
42544              * @param {HtmlEditor} this
42545              * @param {String} html
42546              */
42547             beforesync: true,
42548              /**
42549              * @event beforepush
42550              * Fires before the iframe editor is updated with content from the textarea. Return false
42551              * to cancel the push.
42552              * @param {HtmlEditor} this
42553              * @param {String} html
42554              */
42555             beforepush: true,
42556              /**
42557              * @event sync
42558              * Fires when the textarea is updated with content from the editor iframe.
42559              * @param {HtmlEditor} this
42560              * @param {String} html
42561              */
42562             sync: true,
42563              /**
42564              * @event push
42565              * Fires when the iframe editor is updated with content from the textarea.
42566              * @param {HtmlEditor} this
42567              * @param {String} html
42568              */
42569             push: true,
42570              /**
42571              * @event editmodechange
42572              * Fires when the editor switches edit modes
42573              * @param {HtmlEditor} this
42574              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
42575              */
42576             editmodechange: true,
42577             /**
42578              * @event editorevent
42579              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
42580              * @param {HtmlEditor} this
42581              */
42582             editorevent: true,
42583             /**
42584              * @event firstfocus
42585              * Fires when on first focus - needed by toolbars..
42586              * @param {HtmlEditor} this
42587              */
42588             firstfocus: true,
42589             /**
42590              * @event autosave
42591              * Auto save the htmlEditor value as a file into Events
42592              * @param {HtmlEditor} this
42593              */
42594             autosave: true,
42595             /**
42596              * @event savedpreview
42597              * preview the saved version of htmlEditor
42598              * @param {HtmlEditor} this
42599              */
42600             savedpreview: true
42601         });
42602         this.defaultAutoCreate =  {
42603             tag: "textarea",
42604             style:'width: ' + this.width + 'px;height: ' + this.height + 'px;',
42605             autocomplete: "off"
42606         };
42607     },
42608
42609     /**
42610      * Protected method that will not generally be called directly. It
42611      * is called when the editor creates its toolbar. Override this method if you need to
42612      * add custom toolbar buttons.
42613      * @param {HtmlEditor} editor
42614      */
42615     createToolbar : function(editor){
42616         Roo.log("create toolbars");
42617         if (!editor.toolbars || !editor.toolbars.length) {
42618             editor.toolbars = [ new Roo.form.HtmlEditor.ToolbarStandard() ]; // can be empty?
42619         }
42620         
42621         for (var i =0 ; i < editor.toolbars.length;i++) {
42622             editor.toolbars[i] = Roo.factory(
42623                     typeof(editor.toolbars[i]) == 'string' ?
42624                         { xtype: editor.toolbars[i]} : editor.toolbars[i],
42625                 Roo.form.HtmlEditor);
42626             editor.toolbars[i].init(editor);
42627         }
42628          
42629         
42630     },
42631
42632      
42633     // private
42634     onRender : function(ct, position)
42635     {
42636         var _t = this;
42637         Roo.form.HtmlEditor.superclass.onRender.call(this, ct, position);
42638         
42639         this.wrap = this.el.wrap({
42640             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
42641         });
42642         
42643         this.editorcore.onRender(ct, position);
42644          
42645         if (this.resizable) {
42646             this.resizeEl = new Roo.Resizable(this.wrap, {
42647                 pinned : true,
42648                 wrap: true,
42649                 dynamic : true,
42650                 minHeight : this.height,
42651                 height: this.height,
42652                 handles : this.resizable,
42653                 width: this.width,
42654                 listeners : {
42655                     resize : function(r, w, h) {
42656                         _t.onResize(w,h); // -something
42657                     }
42658                 }
42659             });
42660             
42661         }
42662         this.createToolbar(this);
42663        
42664         
42665         if(!this.width){
42666             this.setSize(this.wrap.getSize());
42667         }
42668         if (this.resizeEl) {
42669             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
42670             // should trigger onReize..
42671         }
42672         
42673 //        if(this.autosave && this.w){
42674 //            this.autoSaveFn = setInterval(this.autosave, 1000);
42675 //        }
42676     },
42677
42678     // private
42679     onResize : function(w, h)
42680     {
42681         //Roo.log('resize: ' +w + ',' + h );
42682         Roo.form.HtmlEditor.superclass.onResize.apply(this, arguments);
42683         var ew = false;
42684         var eh = false;
42685         
42686         if(this.el ){
42687             if(typeof w == 'number'){
42688                 var aw = w - this.wrap.getFrameWidth('lr');
42689                 this.el.setWidth(this.adjustWidth('textarea', aw));
42690                 ew = aw;
42691             }
42692             if(typeof h == 'number'){
42693                 var tbh = 0;
42694                 for (var i =0; i < this.toolbars.length;i++) {
42695                     // fixme - ask toolbars for heights?
42696                     tbh += this.toolbars[i].tb.el.getHeight();
42697                     if (this.toolbars[i].footer) {
42698                         tbh += this.toolbars[i].footer.el.getHeight();
42699                     }
42700                 }
42701                 
42702                 
42703                 
42704                 
42705                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
42706                 ah -= 5; // knock a few pixes off for look..
42707                 this.el.setHeight(this.adjustWidth('textarea', ah));
42708                 var eh = ah;
42709             }
42710         }
42711         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
42712         this.editorcore.onResize(ew,eh);
42713         
42714     },
42715
42716     /**
42717      * Toggles the editor between standard and source edit mode.
42718      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
42719      */
42720     toggleSourceEdit : function(sourceEditMode)
42721     {
42722         this.editorcore.toggleSourceEdit(sourceEditMode);
42723         
42724         if(this.editorcore.sourceEditMode){
42725             Roo.log('editor - showing textarea');
42726             
42727 //            Roo.log('in');
42728 //            Roo.log(this.syncValue());
42729             this.editorcore.syncValue();
42730             this.el.removeClass('x-hidden');
42731             this.el.dom.removeAttribute('tabIndex');
42732             this.el.focus();
42733         }else{
42734             Roo.log('editor - hiding textarea');
42735 //            Roo.log('out')
42736 //            Roo.log(this.pushValue()); 
42737             this.editorcore.pushValue();
42738             
42739             this.el.addClass('x-hidden');
42740             this.el.dom.setAttribute('tabIndex', -1);
42741             //this.deferFocus();
42742         }
42743          
42744         this.setSize(this.wrap.getSize());
42745         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
42746     },
42747  
42748     // private (for BoxComponent)
42749     adjustSize : Roo.BoxComponent.prototype.adjustSize,
42750
42751     // private (for BoxComponent)
42752     getResizeEl : function(){
42753         return this.wrap;
42754     },
42755
42756     // private (for BoxComponent)
42757     getPositionEl : function(){
42758         return this.wrap;
42759     },
42760
42761     // private
42762     initEvents : function(){
42763         this.originalValue = this.getValue();
42764     },
42765
42766     /**
42767      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
42768      * @method
42769      */
42770     markInvalid : Roo.emptyFn,
42771     /**
42772      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
42773      * @method
42774      */
42775     clearInvalid : Roo.emptyFn,
42776
42777     setValue : function(v){
42778         Roo.form.HtmlEditor.superclass.setValue.call(this, v);
42779         this.editorcore.pushValue();
42780     },
42781
42782      
42783     // private
42784     deferFocus : function(){
42785         this.focus.defer(10, this);
42786     },
42787
42788     // doc'ed in Field
42789     focus : function(){
42790         this.editorcore.focus();
42791         
42792     },
42793       
42794
42795     // private
42796     onDestroy : function(){
42797         
42798         
42799         
42800         if(this.rendered){
42801             
42802             for (var i =0; i < this.toolbars.length;i++) {
42803                 // fixme - ask toolbars for heights?
42804                 this.toolbars[i].onDestroy();
42805             }
42806             
42807             this.wrap.dom.innerHTML = '';
42808             this.wrap.remove();
42809         }
42810     },
42811
42812     // private
42813     onFirstFocus : function(){
42814         //Roo.log("onFirstFocus");
42815         this.editorcore.onFirstFocus();
42816          for (var i =0; i < this.toolbars.length;i++) {
42817             this.toolbars[i].onFirstFocus();
42818         }
42819         
42820     },
42821     
42822     // private
42823     syncValue : function()
42824     {
42825         this.editorcore.syncValue();
42826     },
42827     
42828     pushValue : function()
42829     {
42830         this.editorcore.pushValue();
42831     }
42832      
42833     
42834     // hide stuff that is not compatible
42835     /**
42836      * @event blur
42837      * @hide
42838      */
42839     /**
42840      * @event change
42841      * @hide
42842      */
42843     /**
42844      * @event focus
42845      * @hide
42846      */
42847     /**
42848      * @event specialkey
42849      * @hide
42850      */
42851     /**
42852      * @cfg {String} fieldClass @hide
42853      */
42854     /**
42855      * @cfg {String} focusClass @hide
42856      */
42857     /**
42858      * @cfg {String} autoCreate @hide
42859      */
42860     /**
42861      * @cfg {String} inputType @hide
42862      */
42863     /**
42864      * @cfg {String} invalidClass @hide
42865      */
42866     /**
42867      * @cfg {String} invalidText @hide
42868      */
42869     /**
42870      * @cfg {String} msgFx @hide
42871      */
42872     /**
42873      * @cfg {String} validateOnBlur @hide
42874      */
42875 });
42876  
42877     // <script type="text/javascript">
42878 /*
42879  * Based on
42880  * Ext JS Library 1.1.1
42881  * Copyright(c) 2006-2007, Ext JS, LLC.
42882  *  
42883  
42884  */
42885
42886 /**
42887  * @class Roo.form.HtmlEditorToolbar1
42888  * Basic Toolbar
42889  * 
42890  * Usage:
42891  *
42892  new Roo.form.HtmlEditor({
42893     ....
42894     toolbars : [
42895         new Roo.form.HtmlEditorToolbar1({
42896             disable : { fonts: 1 , format: 1, ..., ... , ...],
42897             btns : [ .... ]
42898         })
42899     }
42900      
42901  * 
42902  * @cfg {Object} disable List of elements to disable..
42903  * @cfg {Array} btns List of additional buttons.
42904  * 
42905  * 
42906  * NEEDS Extra CSS? 
42907  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
42908  */
42909  
42910 Roo.form.HtmlEditor.ToolbarStandard = function(config)
42911 {
42912     
42913     Roo.apply(this, config);
42914     
42915     // default disabled, based on 'good practice'..
42916     this.disable = this.disable || {};
42917     Roo.applyIf(this.disable, {
42918         fontSize : true,
42919         colors : true,
42920         specialElements : true
42921     });
42922     
42923     
42924     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
42925     // dont call parent... till later.
42926 }
42927
42928 Roo.apply(Roo.form.HtmlEditor.ToolbarStandard.prototype,  {
42929     
42930     tb: false,
42931     
42932     rendered: false,
42933     
42934     editor : false,
42935     editorcore : false,
42936     /**
42937      * @cfg {Object} disable  List of toolbar elements to disable
42938          
42939      */
42940     disable : false,
42941     
42942     
42943      /**
42944      * @cfg {String} createLinkText The default text for the create link prompt
42945      */
42946     createLinkText : 'Please enter the URL for the link:',
42947     /**
42948      * @cfg {String} defaultLinkValue The default value for the create link prompt (defaults to http:/ /)
42949      */
42950     defaultLinkValue : 'http:/'+'/',
42951    
42952     
42953       /**
42954      * @cfg {Array} fontFamilies An array of available font families
42955      */
42956     fontFamilies : [
42957         'Arial',
42958         'Courier New',
42959         'Tahoma',
42960         'Times New Roman',
42961         'Verdana'
42962     ],
42963     
42964     specialChars : [
42965            "&#169;",
42966           "&#174;",     
42967           "&#8482;",    
42968           "&#163;" ,    
42969          // "&#8212;",    
42970           "&#8230;",    
42971           "&#247;" ,    
42972         //  "&#225;" ,     ?? a acute?
42973            "&#8364;"    , //Euro
42974        //   "&#8220;"    ,
42975         //  "&#8221;"    ,
42976         //  "&#8226;"    ,
42977           "&#176;"  //   , // degrees
42978
42979          // "&#233;"     , // e ecute
42980          // "&#250;"     , // u ecute?
42981     ],
42982     
42983     specialElements : [
42984         {
42985             text: "Insert Table",
42986             xtype: 'MenuItem',
42987             xns : Roo.Menu,
42988             ihtml :  '<table><tr><td>Cell</td></tr></table>' 
42989                 
42990         },
42991         {    
42992             text: "Insert Image",
42993             xtype: 'MenuItem',
42994             xns : Roo.Menu,
42995             ihtml : '<img src="about:blank"/>'
42996             
42997         }
42998         
42999          
43000     ],
43001     
43002     
43003     inputElements : [ 
43004             "form", "input:text", "input:hidden", "input:checkbox", "input:radio", "input:password", 
43005             "input:submit", "input:button", "select", "textarea", "label" ],
43006     formats : [
43007         ["p"] ,  
43008         ["h1"],["h2"],["h3"],["h4"],["h5"],["h6"], 
43009         ["pre"],[ "code"], 
43010         ["abbr"],[ "acronym"],[ "address"],[ "cite"],[ "samp"],[ "var"],
43011         ['div'],['span']
43012     ],
43013     
43014     cleanStyles : [
43015         "font-size"
43016     ],
43017      /**
43018      * @cfg {String} defaultFont default font to use.
43019      */
43020     defaultFont: 'tahoma',
43021    
43022     fontSelect : false,
43023     
43024     
43025     formatCombo : false,
43026     
43027     init : function(editor)
43028     {
43029         this.editor = editor;
43030         this.editorcore = editor.editorcore ? editor.editorcore : editor;
43031         var editorcore = this.editorcore;
43032         
43033         var _t = this;
43034         
43035         var fid = editorcore.frameId;
43036         var etb = this;
43037         function btn(id, toggle, handler){
43038             var xid = fid + '-'+ id ;
43039             return {
43040                 id : xid,
43041                 cmd : id,
43042                 cls : 'x-btn-icon x-edit-'+id,
43043                 enableToggle:toggle !== false,
43044                 scope: _t, // was editor...
43045                 handler:handler||_t.relayBtnCmd,
43046                 clickEvent:'mousedown',
43047                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
43048                 tabIndex:-1
43049             };
43050         }
43051         
43052         
43053         
43054         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
43055         this.tb = tb;
43056          // stop form submits
43057         tb.el.on('click', function(e){
43058             e.preventDefault(); // what does this do?
43059         });
43060
43061         if(!this.disable.font) { // && !Roo.isSafari){
43062             /* why no safari for fonts 
43063             editor.fontSelect = tb.el.createChild({
43064                 tag:'select',
43065                 tabIndex: -1,
43066                 cls:'x-font-select',
43067                 html: this.createFontOptions()
43068             });
43069             
43070             editor.fontSelect.on('change', function(){
43071                 var font = editor.fontSelect.dom.value;
43072                 editor.relayCmd('fontname', font);
43073                 editor.deferFocus();
43074             }, editor);
43075             
43076             tb.add(
43077                 editor.fontSelect.dom,
43078                 '-'
43079             );
43080             */
43081             
43082         };
43083         if(!this.disable.formats){
43084             this.formatCombo = new Roo.form.ComboBox({
43085                 store: new Roo.data.SimpleStore({
43086                     id : 'tag',
43087                     fields: ['tag'],
43088                     data : this.formats // from states.js
43089                 }),
43090                 blockFocus : true,
43091                 name : '',
43092                 //autoCreate : {tag: "div",  size: "20"},
43093                 displayField:'tag',
43094                 typeAhead: false,
43095                 mode: 'local',
43096                 editable : false,
43097                 triggerAction: 'all',
43098                 emptyText:'Add tag',
43099                 selectOnFocus:true,
43100                 width:135,
43101                 listeners : {
43102                     'select': function(c, r, i) {
43103                         editorcore.insertTag(r.get('tag'));
43104                         editor.focus();
43105                     }
43106                 }
43107
43108             });
43109             tb.addField(this.formatCombo);
43110             
43111         }
43112         
43113         if(!this.disable.format){
43114             tb.add(
43115                 btn('bold'),
43116                 btn('italic'),
43117                 btn('underline')
43118             );
43119         };
43120         if(!this.disable.fontSize){
43121             tb.add(
43122                 '-',
43123                 
43124                 
43125                 btn('increasefontsize', false, editorcore.adjustFont),
43126                 btn('decreasefontsize', false, editorcore.adjustFont)
43127             );
43128         };
43129         
43130         
43131         if(!this.disable.colors){
43132             tb.add(
43133                 '-', {
43134                     id:editorcore.frameId +'-forecolor',
43135                     cls:'x-btn-icon x-edit-forecolor',
43136                     clickEvent:'mousedown',
43137                     tooltip: this.buttonTips['forecolor'] || undefined,
43138                     tabIndex:-1,
43139                     menu : new Roo.menu.ColorMenu({
43140                         allowReselect: true,
43141                         focus: Roo.emptyFn,
43142                         value:'000000',
43143                         plain:true,
43144                         selectHandler: function(cp, color){
43145                             editorcore.execCmd('forecolor', Roo.isSafari || Roo.isIE ? '#'+color : color);
43146                             editor.deferFocus();
43147                         },
43148                         scope: editorcore,
43149                         clickEvent:'mousedown'
43150                     })
43151                 }, {
43152                     id:editorcore.frameId +'backcolor',
43153                     cls:'x-btn-icon x-edit-backcolor',
43154                     clickEvent:'mousedown',
43155                     tooltip: this.buttonTips['backcolor'] || undefined,
43156                     tabIndex:-1,
43157                     menu : new Roo.menu.ColorMenu({
43158                         focus: Roo.emptyFn,
43159                         value:'FFFFFF',
43160                         plain:true,
43161                         allowReselect: true,
43162                         selectHandler: function(cp, color){
43163                             if(Roo.isGecko){
43164                                 editorcore.execCmd('useCSS', false);
43165                                 editorcore.execCmd('hilitecolor', color);
43166                                 editorcore.execCmd('useCSS', true);
43167                                 editor.deferFocus();
43168                             }else{
43169                                 editorcore.execCmd(Roo.isOpera ? 'hilitecolor' : 'backcolor', 
43170                                     Roo.isSafari || Roo.isIE ? '#'+color : color);
43171                                 editor.deferFocus();
43172                             }
43173                         },
43174                         scope:editorcore,
43175                         clickEvent:'mousedown'
43176                     })
43177                 }
43178             );
43179         };
43180         // now add all the items...
43181         
43182
43183         if(!this.disable.alignments){
43184             tb.add(
43185                 '-',
43186                 btn('justifyleft'),
43187                 btn('justifycenter'),
43188                 btn('justifyright')
43189             );
43190         };
43191
43192         //if(!Roo.isSafari){
43193             if(!this.disable.links){
43194                 tb.add(
43195                     '-',
43196                     btn('createlink', false, this.createLink)    /// MOVE TO HERE?!!?!?!?!
43197                 );
43198             };
43199
43200             if(!this.disable.lists){
43201                 tb.add(
43202                     '-',
43203                     btn('insertorderedlist'),
43204                     btn('insertunorderedlist')
43205                 );
43206             }
43207             if(!this.disable.sourceEdit){
43208                 tb.add(
43209                     '-',
43210                     btn('sourceedit', true, function(btn){
43211                         Roo.log(this);
43212                         this.toggleSourceEdit(btn.pressed);
43213                     })
43214                 );
43215             }
43216         //}
43217         
43218         var smenu = { };
43219         // special menu.. - needs to be tidied up..
43220         if (!this.disable.special) {
43221             smenu = {
43222                 text: "&#169;",
43223                 cls: 'x-edit-none',
43224                 
43225                 menu : {
43226                     items : []
43227                 }
43228             };
43229             for (var i =0; i < this.specialChars.length; i++) {
43230                 smenu.menu.items.push({
43231                     
43232                     html: this.specialChars[i],
43233                     handler: function(a,b) {
43234                         editorcore.insertAtCursor(String.fromCharCode(a.html.replace('&#','').replace(';', '')));
43235                         //editor.insertAtCursor(a.html);
43236                         
43237                     },
43238                     tabIndex:-1
43239                 });
43240             }
43241             
43242             
43243             tb.add(smenu);
43244             
43245             
43246         }
43247         
43248         var cmenu = { };
43249         if (!this.disable.cleanStyles) {
43250             cmenu = {
43251                 cls: 'x-btn-icon x-btn-clear',
43252                 
43253                 menu : {
43254                     items : []
43255                 }
43256             };
43257             for (var i =0; i < this.cleanStyles.length; i++) {
43258                 cmenu.menu.items.push({
43259                     actiontype : this.cleanStyles[i],
43260                     html: 'Remove ' + this.cleanStyles[i],
43261                     handler: function(a,b) {
43262                         Roo.log(a);
43263                         Roo.log(b);
43264                         var c = Roo.get(editorcore.doc.body);
43265                         c.select('[style]').each(function(s) {
43266                             s.dom.style.removeProperty(a.actiontype);
43267                         });
43268                         editorcore.syncValue();
43269                     },
43270                     tabIndex:-1
43271                 });
43272             }
43273             cmenu.menu.items.push({
43274                 actiontype : 'word',
43275                 html: 'Remove MS Word Formating',
43276                 handler: function(a,b) {
43277                     editorcore.cleanWord();
43278                     editorcore.syncValue();
43279                 },
43280                 tabIndex:-1
43281             });
43282             
43283             cmenu.menu.items.push({
43284                 actiontype : 'all',
43285                 html: 'Remove All Styles',
43286                 handler: function(a,b) {
43287                     
43288                     var c = Roo.get(editorcore.doc.body);
43289                     c.select('[style]').each(function(s) {
43290                         s.dom.removeAttribute('style');
43291                     });
43292                     editorcore.syncValue();
43293                 },
43294                 tabIndex:-1
43295             });
43296              cmenu.menu.items.push({
43297                 actiontype : 'word',
43298                 html: 'Tidy HTML Source',
43299                 handler: function(a,b) {
43300                     editorcore.doc.body.innerHTML = editorcore.domToHTML();
43301                     editorcore.syncValue();
43302                 },
43303                 tabIndex:-1
43304             });
43305             
43306             
43307             tb.add(cmenu);
43308         }
43309          
43310         if (!this.disable.specialElements) {
43311             var semenu = {
43312                 text: "Other;",
43313                 cls: 'x-edit-none',
43314                 menu : {
43315                     items : []
43316                 }
43317             };
43318             for (var i =0; i < this.specialElements.length; i++) {
43319                 semenu.menu.items.push(
43320                     Roo.apply({ 
43321                         handler: function(a,b) {
43322                             editor.insertAtCursor(this.ihtml);
43323                         }
43324                     }, this.specialElements[i])
43325                 );
43326                     
43327             }
43328             
43329             tb.add(semenu);
43330             
43331             
43332         }
43333          
43334         
43335         if (this.btns) {
43336             for(var i =0; i< this.btns.length;i++) {
43337                 var b = Roo.factory(this.btns[i],Roo.form);
43338                 b.cls =  'x-edit-none';
43339                 b.scope = editorcore;
43340                 tb.add(b);
43341             }
43342         
43343         }
43344         
43345         
43346         
43347         // disable everything...
43348         
43349         this.tb.items.each(function(item){
43350            if(item.id != editorcore.frameId+ '-sourceedit'){
43351                 item.disable();
43352             }
43353         });
43354         this.rendered = true;
43355         
43356         // the all the btns;
43357         editor.on('editorevent', this.updateToolbar, this);
43358         // other toolbars need to implement this..
43359         //editor.on('editmodechange', this.updateToolbar, this);
43360     },
43361     
43362     
43363     relayBtnCmd : function(btn) {
43364         this.editorcore.relayCmd(btn.cmd);
43365     },
43366     // private used internally
43367     createLink : function(){
43368         Roo.log("create link?");
43369         var url = prompt(this.createLinkText, this.defaultLinkValue);
43370         if(url && url != 'http:/'+'/'){
43371             this.editorcore.relayCmd('createlink', url);
43372         }
43373     },
43374
43375     
43376     /**
43377      * Protected method that will not generally be called directly. It triggers
43378      * a toolbar update by reading the markup state of the current selection in the editor.
43379      */
43380     updateToolbar: function(){
43381
43382         if(!this.editorcore.activated){
43383             this.editor.onFirstFocus();
43384             return;
43385         }
43386
43387         var btns = this.tb.items.map, 
43388             doc = this.editorcore.doc,
43389             frameId = this.editorcore.frameId;
43390
43391         if(!this.disable.font && !Roo.isSafari){
43392             /*
43393             var name = (doc.queryCommandValue('FontName')||this.editor.defaultFont).toLowerCase();
43394             if(name != this.fontSelect.dom.value){
43395                 this.fontSelect.dom.value = name;
43396             }
43397             */
43398         }
43399         if(!this.disable.format){
43400             btns[frameId + '-bold'].toggle(doc.queryCommandState('bold'));
43401             btns[frameId + '-italic'].toggle(doc.queryCommandState('italic'));
43402             btns[frameId + '-underline'].toggle(doc.queryCommandState('underline'));
43403         }
43404         if(!this.disable.alignments){
43405             btns[frameId + '-justifyleft'].toggle(doc.queryCommandState('justifyleft'));
43406             btns[frameId + '-justifycenter'].toggle(doc.queryCommandState('justifycenter'));
43407             btns[frameId + '-justifyright'].toggle(doc.queryCommandState('justifyright'));
43408         }
43409         if(!Roo.isSafari && !this.disable.lists){
43410             btns[frameId + '-insertorderedlist'].toggle(doc.queryCommandState('insertorderedlist'));
43411             btns[frameId + '-insertunorderedlist'].toggle(doc.queryCommandState('insertunorderedlist'));
43412         }
43413         
43414         var ans = this.editorcore.getAllAncestors();
43415         if (this.formatCombo) {
43416             
43417             
43418             var store = this.formatCombo.store;
43419             this.formatCombo.setValue("");
43420             for (var i =0; i < ans.length;i++) {
43421                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
43422                     // select it..
43423                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
43424                     break;
43425                 }
43426             }
43427         }
43428         
43429         
43430         
43431         // hides menus... - so this cant be on a menu...
43432         Roo.menu.MenuMgr.hideAll();
43433
43434         //this.editorsyncValue();
43435     },
43436    
43437     
43438     createFontOptions : function(){
43439         var buf = [], fs = this.fontFamilies, ff, lc;
43440         
43441         
43442         
43443         for(var i = 0, len = fs.length; i< len; i++){
43444             ff = fs[i];
43445             lc = ff.toLowerCase();
43446             buf.push(
43447                 '<option value="',lc,'" style="font-family:',ff,';"',
43448                     (this.defaultFont == lc ? ' selected="true">' : '>'),
43449                     ff,
43450                 '</option>'
43451             );
43452         }
43453         return buf.join('');
43454     },
43455     
43456     toggleSourceEdit : function(sourceEditMode){
43457         
43458         Roo.log("toolbar toogle");
43459         if(sourceEditMode === undefined){
43460             sourceEditMode = !this.sourceEditMode;
43461         }
43462         this.sourceEditMode = sourceEditMode === true;
43463         var btn = this.tb.items.get(this.editorcore.frameId +'-sourceedit');
43464         // just toggle the button?
43465         if(btn.pressed !== this.sourceEditMode){
43466             btn.toggle(this.sourceEditMode);
43467             return;
43468         }
43469         
43470         if(sourceEditMode){
43471             Roo.log("disabling buttons");
43472             this.tb.items.each(function(item){
43473                 if(item.cmd != 'sourceedit'){
43474                     item.disable();
43475                 }
43476             });
43477           
43478         }else{
43479             Roo.log("enabling buttons");
43480             if(this.editorcore.initialized){
43481                 this.tb.items.each(function(item){
43482                     item.enable();
43483                 });
43484             }
43485             
43486         }
43487         Roo.log("calling toggole on editor");
43488         // tell the editor that it's been pressed..
43489         this.editor.toggleSourceEdit(sourceEditMode);
43490        
43491     },
43492      /**
43493      * Object collection of toolbar tooltips for the buttons in the editor. The key
43494      * is the command id associated with that button and the value is a valid QuickTips object.
43495      * For example:
43496 <pre><code>
43497 {
43498     bold : {
43499         title: 'Bold (Ctrl+B)',
43500         text: 'Make the selected text bold.',
43501         cls: 'x-html-editor-tip'
43502     },
43503     italic : {
43504         title: 'Italic (Ctrl+I)',
43505         text: 'Make the selected text italic.',
43506         cls: 'x-html-editor-tip'
43507     },
43508     ...
43509 </code></pre>
43510     * @type Object
43511      */
43512     buttonTips : {
43513         bold : {
43514             title: 'Bold (Ctrl+B)',
43515             text: 'Make the selected text bold.',
43516             cls: 'x-html-editor-tip'
43517         },
43518         italic : {
43519             title: 'Italic (Ctrl+I)',
43520             text: 'Make the selected text italic.',
43521             cls: 'x-html-editor-tip'
43522         },
43523         underline : {
43524             title: 'Underline (Ctrl+U)',
43525             text: 'Underline the selected text.',
43526             cls: 'x-html-editor-tip'
43527         },
43528         increasefontsize : {
43529             title: 'Grow Text',
43530             text: 'Increase the font size.',
43531             cls: 'x-html-editor-tip'
43532         },
43533         decreasefontsize : {
43534             title: 'Shrink Text',
43535             text: 'Decrease the font size.',
43536             cls: 'x-html-editor-tip'
43537         },
43538         backcolor : {
43539             title: 'Text Highlight Color',
43540             text: 'Change the background color of the selected text.',
43541             cls: 'x-html-editor-tip'
43542         },
43543         forecolor : {
43544             title: 'Font Color',
43545             text: 'Change the color of the selected text.',
43546             cls: 'x-html-editor-tip'
43547         },
43548         justifyleft : {
43549             title: 'Align Text Left',
43550             text: 'Align text to the left.',
43551             cls: 'x-html-editor-tip'
43552         },
43553         justifycenter : {
43554             title: 'Center Text',
43555             text: 'Center text in the editor.',
43556             cls: 'x-html-editor-tip'
43557         },
43558         justifyright : {
43559             title: 'Align Text Right',
43560             text: 'Align text to the right.',
43561             cls: 'x-html-editor-tip'
43562         },
43563         insertunorderedlist : {
43564             title: 'Bullet List',
43565             text: 'Start a bulleted list.',
43566             cls: 'x-html-editor-tip'
43567         },
43568         insertorderedlist : {
43569             title: 'Numbered List',
43570             text: 'Start a numbered list.',
43571             cls: 'x-html-editor-tip'
43572         },
43573         createlink : {
43574             title: 'Hyperlink',
43575             text: 'Make the selected text a hyperlink.',
43576             cls: 'x-html-editor-tip'
43577         },
43578         sourceedit : {
43579             title: 'Source Edit',
43580             text: 'Switch to source editing mode.',
43581             cls: 'x-html-editor-tip'
43582         }
43583     },
43584     // private
43585     onDestroy : function(){
43586         if(this.rendered){
43587             
43588             this.tb.items.each(function(item){
43589                 if(item.menu){
43590                     item.menu.removeAll();
43591                     if(item.menu.el){
43592                         item.menu.el.destroy();
43593                     }
43594                 }
43595                 item.destroy();
43596             });
43597              
43598         }
43599     },
43600     onFirstFocus: function() {
43601         this.tb.items.each(function(item){
43602            item.enable();
43603         });
43604     }
43605 });
43606
43607
43608
43609
43610 // <script type="text/javascript">
43611 /*
43612  * Based on
43613  * Ext JS Library 1.1.1
43614  * Copyright(c) 2006-2007, Ext JS, LLC.
43615  *  
43616  
43617  */
43618
43619  
43620 /**
43621  * @class Roo.form.HtmlEditor.ToolbarContext
43622  * Context Toolbar
43623  * 
43624  * Usage:
43625  *
43626  new Roo.form.HtmlEditor({
43627     ....
43628     toolbars : [
43629         { xtype: 'ToolbarStandard', styles : {} }
43630         { xtype: 'ToolbarContext', disable : {} }
43631     ]
43632 })
43633
43634      
43635  * 
43636  * @config : {Object} disable List of elements to disable.. (not done yet.)
43637  * @config : {Object} styles  Map of styles available.
43638  * 
43639  */
43640
43641 Roo.form.HtmlEditor.ToolbarContext = function(config)
43642 {
43643     
43644     Roo.apply(this, config);
43645     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
43646     // dont call parent... till later.
43647     this.styles = this.styles || {};
43648 }
43649
43650  
43651
43652 Roo.form.HtmlEditor.ToolbarContext.types = {
43653     'IMG' : {
43654         width : {
43655             title: "Width",
43656             width: 40
43657         },
43658         height:  {
43659             title: "Height",
43660             width: 40
43661         },
43662         align: {
43663             title: "Align",
43664             opts : [ [""],[ "left"],[ "right"],[ "center"],[ "top"]],
43665             width : 80
43666             
43667         },
43668         border: {
43669             title: "Border",
43670             width: 40
43671         },
43672         alt: {
43673             title: "Alt",
43674             width: 120
43675         },
43676         src : {
43677             title: "Src",
43678             width: 220
43679         }
43680         
43681     },
43682     'A' : {
43683         name : {
43684             title: "Name",
43685             width: 50
43686         },
43687         target:  {
43688             title: "Target",
43689             width: 120
43690         },
43691         href:  {
43692             title: "Href",
43693             width: 220
43694         } // border?
43695         
43696     },
43697     'TABLE' : {
43698         rows : {
43699             title: "Rows",
43700             width: 20
43701         },
43702         cols : {
43703             title: "Cols",
43704             width: 20
43705         },
43706         width : {
43707             title: "Width",
43708             width: 40
43709         },
43710         height : {
43711             title: "Height",
43712             width: 40
43713         },
43714         border : {
43715             title: "Border",
43716             width: 20
43717         }
43718     },
43719     'TD' : {
43720         width : {
43721             title: "Width",
43722             width: 40
43723         },
43724         height : {
43725             title: "Height",
43726             width: 40
43727         },   
43728         align: {
43729             title: "Align",
43730             opts : [[""],[ "left"],[ "center"],[ "right"],[ "justify"],[ "char"]],
43731             width: 80
43732         },
43733         valign: {
43734             title: "Valign",
43735             opts : [[""],[ "top"],[ "middle"],[ "bottom"],[ "baseline"]],
43736             width: 80
43737         },
43738         colspan: {
43739             title: "Colspan",
43740             width: 20
43741             
43742         },
43743          'font-family'  : {
43744             title : "Font",
43745             style : 'fontFamily',
43746             displayField: 'display',
43747             optname : 'font-family',
43748             width: 140
43749         }
43750     },
43751     'INPUT' : {
43752         name : {
43753             title: "name",
43754             width: 120
43755         },
43756         value : {
43757             title: "Value",
43758             width: 120
43759         },
43760         width : {
43761             title: "Width",
43762             width: 40
43763         }
43764     },
43765     'LABEL' : {
43766         'for' : {
43767             title: "For",
43768             width: 120
43769         }
43770     },
43771     'TEXTAREA' : {
43772           name : {
43773             title: "name",
43774             width: 120
43775         },
43776         rows : {
43777             title: "Rows",
43778             width: 20
43779         },
43780         cols : {
43781             title: "Cols",
43782             width: 20
43783         }
43784     },
43785     'SELECT' : {
43786         name : {
43787             title: "name",
43788             width: 120
43789         },
43790         selectoptions : {
43791             title: "Options",
43792             width: 200
43793         }
43794     },
43795     
43796     // should we really allow this??
43797     // should this just be 
43798     'BODY' : {
43799         title : {
43800             title: "Title",
43801             width: 200,
43802             disabled : true
43803         }
43804     },
43805     'SPAN' : {
43806         'font-family'  : {
43807             title : "Font",
43808             style : 'fontFamily',
43809             displayField: 'display',
43810             optname : 'font-family',
43811             width: 140
43812         }
43813     },
43814     'DIV' : {
43815         'font-family'  : {
43816             title : "Font",
43817             style : 'fontFamily',
43818             displayField: 'display',
43819             optname : 'font-family',
43820             width: 140
43821         }
43822     },
43823      'P' : {
43824         'font-family'  : {
43825             title : "Font",
43826             style : 'fontFamily',
43827             displayField: 'display',
43828             optname : 'font-family',
43829             width: 140
43830         }
43831     },
43832     
43833     '*' : {
43834         // empty..
43835     }
43836
43837 };
43838
43839 // this should be configurable.. - you can either set it up using stores, or modify options somehwere..
43840 Roo.form.HtmlEditor.ToolbarContext.stores = false;
43841
43842 Roo.form.HtmlEditor.ToolbarContext.options = {
43843         'font-family'  : [ 
43844                 [ 'Helvetica,Arial,sans-serif', 'Helvetica'],
43845                 [ 'Courier New', 'Courier New'],
43846                 [ 'Tahoma', 'Tahoma'],
43847                 [ 'Times New Roman,serif', 'Times'],
43848                 [ 'Verdana','Verdana' ]
43849         ]
43850 };
43851
43852 // fixme - these need to be configurable..
43853  
43854
43855 Roo.form.HtmlEditor.ToolbarContext.types
43856
43857
43858 Roo.apply(Roo.form.HtmlEditor.ToolbarContext.prototype,  {
43859     
43860     tb: false,
43861     
43862     rendered: false,
43863     
43864     editor : false,
43865     editorcore : false,
43866     /**
43867      * @cfg {Object} disable  List of toolbar elements to disable
43868          
43869      */
43870     disable : false,
43871     /**
43872      * @cfg {Object} styles List of styles 
43873      *    eg. { '*' : [ 'headline' ] , 'TD' : [ 'underline', 'double-underline' ] } 
43874      *
43875      * These must be defined in the page, so they get rendered correctly..
43876      * .headline { }
43877      * TD.underline { }
43878      * 
43879      */
43880     styles : false,
43881     
43882     options: false,
43883     
43884     toolbars : false,
43885     
43886     init : function(editor)
43887     {
43888         this.editor = editor;
43889         this.editorcore = editor.editorcore ? editor.editorcore : editor;
43890         var editorcore = this.editorcore;
43891         
43892         var fid = editorcore.frameId;
43893         var etb = this;
43894         function btn(id, toggle, handler){
43895             var xid = fid + '-'+ id ;
43896             return {
43897                 id : xid,
43898                 cmd : id,
43899                 cls : 'x-btn-icon x-edit-'+id,
43900                 enableToggle:toggle !== false,
43901                 scope: editorcore, // was editor...
43902                 handler:handler||editorcore.relayBtnCmd,
43903                 clickEvent:'mousedown',
43904                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
43905                 tabIndex:-1
43906             };
43907         }
43908         // create a new element.
43909         var wdiv = editor.wrap.createChild({
43910                 tag: 'div'
43911             }, editor.wrap.dom.firstChild.nextSibling, true);
43912         
43913         // can we do this more than once??
43914         
43915          // stop form submits
43916       
43917  
43918         // disable everything...
43919         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
43920         this.toolbars = {};
43921            
43922         for (var i in  ty) {
43923           
43924             this.toolbars[i] = this.buildToolbar(ty[i],i);
43925         }
43926         this.tb = this.toolbars.BODY;
43927         this.tb.el.show();
43928         this.buildFooter();
43929         this.footer.show();
43930         editor.on('hide', function( ) { this.footer.hide() }, this);
43931         editor.on('show', function( ) { this.footer.show() }, this);
43932         
43933          
43934         this.rendered = true;
43935         
43936         // the all the btns;
43937         editor.on('editorevent', this.updateToolbar, this);
43938         // other toolbars need to implement this..
43939         //editor.on('editmodechange', this.updateToolbar, this);
43940     },
43941     
43942     
43943     
43944     /**
43945      * Protected method that will not generally be called directly. It triggers
43946      * a toolbar update by reading the markup state of the current selection in the editor.
43947      */
43948     updateToolbar: function(editor,ev,sel){
43949
43950         //Roo.log(ev);
43951         // capture mouse up - this is handy for selecting images..
43952         // perhaps should go somewhere else...
43953         if(!this.editorcore.activated){
43954              this.editor.onFirstFocus();
43955             return;
43956         }
43957         
43958         // http://developer.yahoo.com/yui/docs/simple-editor.js.html
43959         // selectNode - might want to handle IE?
43960         if (ev &&
43961             (ev.type == 'mouseup' || ev.type == 'click' ) &&
43962             ev.target && ev.target.tagName == 'IMG') {
43963             // they have click on an image...
43964             // let's see if we can change the selection...
43965             sel = ev.target;
43966          
43967               var nodeRange = sel.ownerDocument.createRange();
43968             try {
43969                 nodeRange.selectNode(sel);
43970             } catch (e) {
43971                 nodeRange.selectNodeContents(sel);
43972             }
43973             //nodeRange.collapse(true);
43974             var s = this.editorcore.win.getSelection();
43975             s.removeAllRanges();
43976             s.addRange(nodeRange);
43977         }  
43978         
43979       
43980         var updateFooter = sel ? false : true;
43981         
43982         
43983         var ans = this.editorcore.getAllAncestors();
43984         
43985         // pick
43986         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
43987         
43988         if (!sel) { 
43989             sel = ans.length ? (ans[0] ?  ans[0]  : ans[1]) : this.editorcore.doc.body;
43990             sel = sel ? sel : this.editorcore.doc.body;
43991             sel = sel.tagName.length ? sel : this.editorcore.doc.body;
43992             
43993         }
43994         // pick a menu that exists..
43995         var tn = sel.tagName.toUpperCase();
43996         //sel = typeof(ty[tn]) != 'undefined' ? sel : this.editor.doc.body;
43997         
43998         tn = sel.tagName.toUpperCase();
43999         
44000         var lastSel = this.tb.selectedNode
44001         
44002         this.tb.selectedNode = sel;
44003         
44004         // if current menu does not match..
44005         if ((this.tb.name != tn) || (lastSel != this.tb.selectedNode)) {
44006                 
44007             this.tb.el.hide();
44008             ///console.log("show: " + tn);
44009             this.tb =  typeof(ty[tn]) != 'undefined' ? this.toolbars[tn] : this.toolbars['*'];
44010             this.tb.el.show();
44011             // update name
44012             this.tb.items.first().el.innerHTML = tn + ':&nbsp;';
44013             
44014             
44015             // update attributes
44016             if (this.tb.fields) {
44017                 this.tb.fields.each(function(e) {
44018                     if (e.stylename) {
44019                         e.setValue(sel.style[e.stylename]);
44020                         return;
44021                     } 
44022                    e.setValue(sel.getAttribute(e.attrname));
44023                 });
44024             }
44025             
44026             var hasStyles = false;
44027             for(var i in this.styles) {
44028                 hasStyles = true;
44029                 break;
44030             }
44031             
44032             // update styles
44033             if (hasStyles) { 
44034                 var st = this.tb.fields.item(0);
44035                 
44036                 st.store.removeAll();
44037                
44038                 
44039                 var cn = sel.className.split(/\s+/);
44040                 
44041                 var avs = [];
44042                 if (this.styles['*']) {
44043                     
44044                     Roo.each(this.styles['*'], function(v) {
44045                         avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
44046                     });
44047                 }
44048                 if (this.styles[tn]) { 
44049                     Roo.each(this.styles[tn], function(v) {
44050                         avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
44051                     });
44052                 }
44053                 
44054                 st.store.loadData(avs);
44055                 st.collapse();
44056                 st.setValue(cn);
44057             }
44058             // flag our selected Node.
44059             this.tb.selectedNode = sel;
44060            
44061            
44062             Roo.menu.MenuMgr.hideAll();
44063
44064         }
44065         
44066         if (!updateFooter) {
44067             //this.footDisp.dom.innerHTML = ''; 
44068             return;
44069         }
44070         // update the footer
44071         //
44072         var html = '';
44073         
44074         this.footerEls = ans.reverse();
44075         Roo.each(this.footerEls, function(a,i) {
44076             if (!a) { return; }
44077             html += html.length ? ' &gt; '  :  '';
44078             
44079             html += '<span class="x-ed-loc-' + i + '">' + a.tagName + '</span>';
44080             
44081         });
44082        
44083         // 
44084         var sz = this.footDisp.up('td').getSize();
44085         this.footDisp.dom.style.width = (sz.width -10) + 'px';
44086         this.footDisp.dom.style.marginLeft = '5px';
44087         
44088         this.footDisp.dom.style.overflow = 'hidden';
44089         
44090         this.footDisp.dom.innerHTML = html;
44091             
44092         //this.editorsyncValue();
44093     },
44094      
44095     
44096    
44097        
44098     // private
44099     onDestroy : function(){
44100         if(this.rendered){
44101             
44102             this.tb.items.each(function(item){
44103                 if(item.menu){
44104                     item.menu.removeAll();
44105                     if(item.menu.el){
44106                         item.menu.el.destroy();
44107                     }
44108                 }
44109                 item.destroy();
44110             });
44111              
44112         }
44113     },
44114     onFirstFocus: function() {
44115         // need to do this for all the toolbars..
44116         this.tb.items.each(function(item){
44117            item.enable();
44118         });
44119     },
44120     buildToolbar: function(tlist, nm)
44121     {
44122         var editor = this.editor;
44123         var editorcore = this.editorcore;
44124          // create a new element.
44125         var wdiv = editor.wrap.createChild({
44126                 tag: 'div'
44127             }, editor.wrap.dom.firstChild.nextSibling, true);
44128         
44129        
44130         var tb = new Roo.Toolbar(wdiv);
44131         // add the name..
44132         
44133         tb.add(nm+ ":&nbsp;");
44134         
44135         var styles = [];
44136         for(var i in this.styles) {
44137             styles.push(i);
44138         }
44139         
44140         // styles...
44141         if (styles && styles.length) {
44142             
44143             // this needs a multi-select checkbox...
44144             tb.addField( new Roo.form.ComboBox({
44145                 store: new Roo.data.SimpleStore({
44146                     id : 'val',
44147                     fields: ['val', 'selected'],
44148                     data : [] 
44149                 }),
44150                 name : '-roo-edit-className',
44151                 attrname : 'className',
44152                 displayField: 'val',
44153                 typeAhead: false,
44154                 mode: 'local',
44155                 editable : false,
44156                 triggerAction: 'all',
44157                 emptyText:'Select Style',
44158                 selectOnFocus:true,
44159                 width: 130,
44160                 listeners : {
44161                     'select': function(c, r, i) {
44162                         // initial support only for on class per el..
44163                         tb.selectedNode.className =  r ? r.get('val') : '';
44164                         editorcore.syncValue();
44165                     }
44166                 }
44167     
44168             }));
44169         }
44170         
44171         var tbc = Roo.form.HtmlEditor.ToolbarContext;
44172         var tbops = tbc.options;
44173         
44174         for (var i in tlist) {
44175             
44176             var item = tlist[i];
44177             tb.add(item.title + ":&nbsp;");
44178             
44179             
44180             //optname == used so you can configure the options available..
44181             var opts = item.opts ? item.opts : false;
44182             if (item.optname) {
44183                 opts = tbops[item.optname];
44184            
44185             }
44186             
44187             if (opts) {
44188                 // opts == pulldown..
44189                 tb.addField( new Roo.form.ComboBox({
44190                     store:   typeof(tbc.stores[i]) != 'undefined' ?  Roo.factory(tbc.stores[i],Roo.data) : new Roo.data.SimpleStore({
44191                         id : 'val',
44192                         fields: ['val', 'display'],
44193                         data : opts  
44194                     }),
44195                     name : '-roo-edit-' + i,
44196                     attrname : i,
44197                     stylename : item.style ? item.style : false,
44198                     displayField: item.displayField ? item.displayField : 'val',
44199                     valueField :  'val',
44200                     typeAhead: false,
44201                     mode: typeof(tbc.stores[i]) != 'undefined'  ? 'remote' : 'local',
44202                     editable : false,
44203                     triggerAction: 'all',
44204                     emptyText:'Select',
44205                     selectOnFocus:true,
44206                     width: item.width ? item.width  : 130,
44207                     listeners : {
44208                         'select': function(c, r, i) {
44209                             if (c.stylename) {
44210                                 tb.selectedNode.style[c.stylename] =  r.get('val');
44211                                 return;
44212                             }
44213                             tb.selectedNode.setAttribute(c.attrname, r.get('val'));
44214                         }
44215                     }
44216
44217                 }));
44218                 continue;
44219                     
44220                  
44221                 
44222                 tb.addField( new Roo.form.TextField({
44223                     name: i,
44224                     width: 100,
44225                     //allowBlank:false,
44226                     value: ''
44227                 }));
44228                 continue;
44229             }
44230             tb.addField( new Roo.form.TextField({
44231                 name: '-roo-edit-' + i,
44232                 attrname : i,
44233                 
44234                 width: item.width,
44235                 //allowBlank:true,
44236                 value: '',
44237                 listeners: {
44238                     'change' : function(f, nv, ov) {
44239                         tb.selectedNode.setAttribute(f.attrname, nv);
44240                     }
44241                 }
44242             }));
44243              
44244         }
44245         tb.addFill();
44246         var _this = this;
44247         tb.addButton( {
44248             text: 'Remove Tag',
44249     
44250             listeners : {
44251                 click : function ()
44252                 {
44253                     // remove
44254                     // undo does not work.
44255                      
44256                     var sn = tb.selectedNode;
44257                     
44258                     var pn = sn.parentNode;
44259                     
44260                     var stn =  sn.childNodes[0];
44261                     var en = sn.childNodes[sn.childNodes.length - 1 ];
44262                     while (sn.childNodes.length) {
44263                         var node = sn.childNodes[0];
44264                         sn.removeChild(node);
44265                         //Roo.log(node);
44266                         pn.insertBefore(node, sn);
44267                         
44268                     }
44269                     pn.removeChild(sn);
44270                     var range = editorcore.createRange();
44271         
44272                     range.setStart(stn,0);
44273                     range.setEnd(en,0); //????
44274                     //range.selectNode(sel);
44275                     
44276                     
44277                     var selection = editorcore.getSelection();
44278                     selection.removeAllRanges();
44279                     selection.addRange(range);
44280                     
44281                     
44282                     
44283                     //_this.updateToolbar(null, null, pn);
44284                     _this.updateToolbar(null, null, null);
44285                     _this.footDisp.dom.innerHTML = ''; 
44286                 }
44287             }
44288             
44289                     
44290                 
44291             
44292         });
44293         
44294         
44295         tb.el.on('click', function(e){
44296             e.preventDefault(); // what does this do?
44297         });
44298         tb.el.setVisibilityMode( Roo.Element.DISPLAY);
44299         tb.el.hide();
44300         tb.name = nm;
44301         // dont need to disable them... as they will get hidden
44302         return tb;
44303          
44304         
44305     },
44306     buildFooter : function()
44307     {
44308         
44309         var fel = this.editor.wrap.createChild();
44310         this.footer = new Roo.Toolbar(fel);
44311         // toolbar has scrolly on left / right?
44312         var footDisp= new Roo.Toolbar.Fill();
44313         var _t = this;
44314         this.footer.add(
44315             {
44316                 text : '&lt;',
44317                 xtype: 'Button',
44318                 handler : function() {
44319                     _t.footDisp.scrollTo('left',0,true)
44320                 }
44321             }
44322         );
44323         this.footer.add( footDisp );
44324         this.footer.add( 
44325             {
44326                 text : '&gt;',
44327                 xtype: 'Button',
44328                 handler : function() {
44329                     // no animation..
44330                     _t.footDisp.select('span').last().scrollIntoView(_t.footDisp,true);
44331                 }
44332             }
44333         );
44334         var fel = Roo.get(footDisp.el);
44335         fel.addClass('x-editor-context');
44336         this.footDispWrap = fel; 
44337         this.footDispWrap.overflow  = 'hidden';
44338         
44339         this.footDisp = fel.createChild();
44340         this.footDispWrap.on('click', this.onContextClick, this)
44341         
44342         
44343     },
44344     onContextClick : function (ev,dom)
44345     {
44346         ev.preventDefault();
44347         var  cn = dom.className;
44348         //Roo.log(cn);
44349         if (!cn.match(/x-ed-loc-/)) {
44350             return;
44351         }
44352         var n = cn.split('-').pop();
44353         var ans = this.footerEls;
44354         var sel = ans[n];
44355         
44356          // pick
44357         var range = this.editorcore.createRange();
44358         
44359         range.selectNodeContents(sel);
44360         //range.selectNode(sel);
44361         
44362         
44363         var selection = this.editorcore.getSelection();
44364         selection.removeAllRanges();
44365         selection.addRange(range);
44366         
44367         
44368         
44369         this.updateToolbar(null, null, sel);
44370         
44371         
44372     }
44373     
44374     
44375     
44376     
44377     
44378 });
44379
44380
44381
44382
44383
44384 /*
44385  * Based on:
44386  * Ext JS Library 1.1.1
44387  * Copyright(c) 2006-2007, Ext JS, LLC.
44388  *
44389  * Originally Released Under LGPL - original licence link has changed is not relivant.
44390  *
44391  * Fork - LGPL
44392  * <script type="text/javascript">
44393  */
44394  
44395 /**
44396  * @class Roo.form.BasicForm
44397  * @extends Roo.util.Observable
44398  * Supplies the functionality to do "actions" on forms and initialize Roo.form.Field types on existing markup.
44399  * @constructor
44400  * @param {String/HTMLElement/Roo.Element} el The form element or its id
44401  * @param {Object} config Configuration options
44402  */
44403 Roo.form.BasicForm = function(el, config){
44404     this.allItems = [];
44405     this.childForms = [];
44406     Roo.apply(this, config);
44407     /*
44408      * The Roo.form.Field items in this form.
44409      * @type MixedCollection
44410      */
44411      
44412      
44413     this.items = new Roo.util.MixedCollection(false, function(o){
44414         return o.id || (o.id = Roo.id());
44415     });
44416     this.addEvents({
44417         /**
44418          * @event beforeaction
44419          * Fires before any action is performed. Return false to cancel the action.
44420          * @param {Form} this
44421          * @param {Action} action The action to be performed
44422          */
44423         beforeaction: true,
44424         /**
44425          * @event actionfailed
44426          * Fires when an action fails.
44427          * @param {Form} this
44428          * @param {Action} action The action that failed
44429          */
44430         actionfailed : true,
44431         /**
44432          * @event actioncomplete
44433          * Fires when an action is completed.
44434          * @param {Form} this
44435          * @param {Action} action The action that completed
44436          */
44437         actioncomplete : true
44438     });
44439     if(el){
44440         this.initEl(el);
44441     }
44442     Roo.form.BasicForm.superclass.constructor.call(this);
44443 };
44444
44445 Roo.extend(Roo.form.BasicForm, Roo.util.Observable, {
44446     /**
44447      * @cfg {String} method
44448      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
44449      */
44450     /**
44451      * @cfg {DataReader} reader
44452      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when executing "load" actions.
44453      * This is optional as there is built-in support for processing JSON.
44454      */
44455     /**
44456      * @cfg {DataReader} errorReader
44457      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when reading validation errors on "submit" actions.
44458      * This is completely optional as there is built-in support for processing JSON.
44459      */
44460     /**
44461      * @cfg {String} url
44462      * The URL to use for form actions if one isn't supplied in the action options.
44463      */
44464     /**
44465      * @cfg {Boolean} fileUpload
44466      * Set to true if this form is a file upload.
44467      */
44468      
44469     /**
44470      * @cfg {Object} baseParams
44471      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
44472      */
44473      /**
44474      
44475     /**
44476      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
44477      */
44478     timeout: 30,
44479
44480     // private
44481     activeAction : null,
44482
44483     /**
44484      * @cfg {Boolean} trackResetOnLoad If set to true, form.reset() resets to the last loaded
44485      * or setValues() data instead of when the form was first created.
44486      */
44487     trackResetOnLoad : false,
44488     
44489     
44490     /**
44491      * childForms - used for multi-tab forms
44492      * @type {Array}
44493      */
44494     childForms : false,
44495     
44496     /**
44497      * allItems - full list of fields.
44498      * @type {Array}
44499      */
44500     allItems : false,
44501     
44502     /**
44503      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
44504      * element by passing it or its id or mask the form itself by passing in true.
44505      * @type Mixed
44506      */
44507     waitMsgTarget : false,
44508
44509     // private
44510     initEl : function(el){
44511         this.el = Roo.get(el);
44512         this.id = this.el.id || Roo.id();
44513         this.el.on('submit', this.onSubmit, this);
44514         this.el.addClass('x-form');
44515     },
44516
44517     // private
44518     onSubmit : function(e){
44519         e.stopEvent();
44520     },
44521
44522     /**
44523      * Returns true if client-side validation on the form is successful.
44524      * @return Boolean
44525      */
44526     isValid : function(){
44527         var valid = true;
44528         this.items.each(function(f){
44529            if(!f.validate()){
44530                valid = false;
44531            }
44532         });
44533         return valid;
44534     },
44535
44536     /**
44537      * Returns true if any fields in this form have changed since their original load.
44538      * @return Boolean
44539      */
44540     isDirty : function(){
44541         var dirty = false;
44542         this.items.each(function(f){
44543            if(f.isDirty()){
44544                dirty = true;
44545                return false;
44546            }
44547         });
44548         return dirty;
44549     },
44550
44551     /**
44552      * Performs a predefined action (submit or load) or custom actions you define on this form.
44553      * @param {String} actionName The name of the action type
44554      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
44555      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
44556      * accept other config options):
44557      * <pre>
44558 Property          Type             Description
44559 ----------------  ---------------  ----------------------------------------------------------------------------------
44560 url               String           The url for the action (defaults to the form's url)
44561 method            String           The form method to use (defaults to the form's method, or POST if not defined)
44562 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
44563 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
44564                                    validate the form on the client (defaults to false)
44565      * </pre>
44566      * @return {BasicForm} this
44567      */
44568     doAction : function(action, options){
44569         if(typeof action == 'string'){
44570             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
44571         }
44572         if(this.fireEvent('beforeaction', this, action) !== false){
44573             this.beforeAction(action);
44574             action.run.defer(100, action);
44575         }
44576         return this;
44577     },
44578
44579     /**
44580      * Shortcut to do a submit action.
44581      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
44582      * @return {BasicForm} this
44583      */
44584     submit : function(options){
44585         this.doAction('submit', options);
44586         return this;
44587     },
44588
44589     /**
44590      * Shortcut to do a load action.
44591      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
44592      * @return {BasicForm} this
44593      */
44594     load : function(options){
44595         this.doAction('load', options);
44596         return this;
44597     },
44598
44599     /**
44600      * Persists the values in this form into the passed Roo.data.Record object in a beginEdit/endEdit block.
44601      * @param {Record} record The record to edit
44602      * @return {BasicForm} this
44603      */
44604     updateRecord : function(record){
44605         record.beginEdit();
44606         var fs = record.fields;
44607         fs.each(function(f){
44608             var field = this.findField(f.name);
44609             if(field){
44610                 record.set(f.name, field.getValue());
44611             }
44612         }, this);
44613         record.endEdit();
44614         return this;
44615     },
44616
44617     /**
44618      * Loads an Roo.data.Record into this form.
44619      * @param {Record} record The record to load
44620      * @return {BasicForm} this
44621      */
44622     loadRecord : function(record){
44623         this.setValues(record.data);
44624         return this;
44625     },
44626
44627     // private
44628     beforeAction : function(action){
44629         var o = action.options;
44630         
44631        
44632         if(this.waitMsgTarget === true){
44633             this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
44634         }else if(this.waitMsgTarget){
44635             this.waitMsgTarget = Roo.get(this.waitMsgTarget);
44636             this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
44637         }else {
44638             Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
44639         }
44640          
44641     },
44642
44643     // private
44644     afterAction : function(action, success){
44645         this.activeAction = null;
44646         var o = action.options;
44647         
44648         if(this.waitMsgTarget === true){
44649             this.el.unmask();
44650         }else if(this.waitMsgTarget){
44651             this.waitMsgTarget.unmask();
44652         }else{
44653             Roo.MessageBox.updateProgress(1);
44654             Roo.MessageBox.hide();
44655         }
44656          
44657         if(success){
44658             if(o.reset){
44659                 this.reset();
44660             }
44661             Roo.callback(o.success, o.scope, [this, action]);
44662             this.fireEvent('actioncomplete', this, action);
44663             
44664         }else{
44665             
44666             // failure condition..
44667             // we have a scenario where updates need confirming.
44668             // eg. if a locking scenario exists..
44669             // we look for { errors : { needs_confirm : true }} in the response.
44670             if (
44671                 (typeof(action.result) != 'undefined')  &&
44672                 (typeof(action.result.errors) != 'undefined')  &&
44673                 (typeof(action.result.errors.needs_confirm) != 'undefined')
44674            ){
44675                 var _t = this;
44676                 Roo.MessageBox.confirm(
44677                     "Change requires confirmation",
44678                     action.result.errorMsg,
44679                     function(r) {
44680                         if (r != 'yes') {
44681                             return;
44682                         }
44683                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
44684                     }
44685                     
44686                 );
44687                 
44688                 
44689                 
44690                 return;
44691             }
44692             
44693             Roo.callback(o.failure, o.scope, [this, action]);
44694             // show an error message if no failed handler is set..
44695             if (!this.hasListener('actionfailed')) {
44696                 Roo.MessageBox.alert("Error",
44697                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
44698                         action.result.errorMsg :
44699                         "Saving Failed, please check your entries or try again"
44700                 );
44701             }
44702             
44703             this.fireEvent('actionfailed', this, action);
44704         }
44705         
44706     },
44707
44708     /**
44709      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
44710      * @param {String} id The value to search for
44711      * @return Field
44712      */
44713     findField : function(id){
44714         var field = this.items.get(id);
44715         if(!field){
44716             this.items.each(function(f){
44717                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
44718                     field = f;
44719                     return false;
44720                 }
44721             });
44722         }
44723         return field || null;
44724     },
44725
44726     /**
44727      * Add a secondary form to this one, 
44728      * Used to provide tabbed forms. One form is primary, with hidden values 
44729      * which mirror the elements from the other forms.
44730      * 
44731      * @param {Roo.form.Form} form to add.
44732      * 
44733      */
44734     addForm : function(form)
44735     {
44736        
44737         if (this.childForms.indexOf(form) > -1) {
44738             // already added..
44739             return;
44740         }
44741         this.childForms.push(form);
44742         var n = '';
44743         Roo.each(form.allItems, function (fe) {
44744             
44745             n = typeof(fe.getName) == 'undefined' ? fe.name : fe.getName();
44746             if (this.findField(n)) { // already added..
44747                 return;
44748             }
44749             var add = new Roo.form.Hidden({
44750                 name : n
44751             });
44752             add.render(this.el);
44753             
44754             this.add( add );
44755         }, this);
44756         
44757     },
44758     /**
44759      * Mark fields in this form invalid in bulk.
44760      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
44761      * @return {BasicForm} this
44762      */
44763     markInvalid : function(errors){
44764         if(errors instanceof Array){
44765             for(var i = 0, len = errors.length; i < len; i++){
44766                 var fieldError = errors[i];
44767                 var f = this.findField(fieldError.id);
44768                 if(f){
44769                     f.markInvalid(fieldError.msg);
44770                 }
44771             }
44772         }else{
44773             var field, id;
44774             for(id in errors){
44775                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
44776                     field.markInvalid(errors[id]);
44777                 }
44778             }
44779         }
44780         Roo.each(this.childForms || [], function (f) {
44781             f.markInvalid(errors);
44782         });
44783         
44784         return this;
44785     },
44786
44787     /**
44788      * Set values for fields in this form in bulk.
44789      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
44790      * @return {BasicForm} this
44791      */
44792     setValues : function(values){
44793         if(values instanceof Array){ // array of objects
44794             for(var i = 0, len = values.length; i < len; i++){
44795                 var v = values[i];
44796                 var f = this.findField(v.id);
44797                 if(f){
44798                     f.setValue(v.value);
44799                     if(this.trackResetOnLoad){
44800                         f.originalValue = f.getValue();
44801                     }
44802                 }
44803             }
44804         }else{ // object hash
44805             var field, id;
44806             for(id in values){
44807                 if(typeof values[id] != 'function' && (field = this.findField(id))){
44808                     
44809                     if (field.setFromData && 
44810                         field.valueField && 
44811                         field.displayField &&
44812                         // combos' with local stores can 
44813                         // be queried via setValue()
44814                         // to set their value..
44815                         (field.store && !field.store.isLocal)
44816                         ) {
44817                         // it's a combo
44818                         var sd = { };
44819                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
44820                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
44821                         field.setFromData(sd);
44822                         
44823                     } else {
44824                         field.setValue(values[id]);
44825                     }
44826                     
44827                     
44828                     if(this.trackResetOnLoad){
44829                         field.originalValue = field.getValue();
44830                     }
44831                 }
44832             }
44833         }
44834          
44835         Roo.each(this.childForms || [], function (f) {
44836             f.setValues(values);
44837         });
44838                 
44839         return this;
44840     },
44841
44842     /**
44843      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
44844      * they are returned as an array.
44845      * @param {Boolean} asString
44846      * @return {Object}
44847      */
44848     getValues : function(asString){
44849         if (this.childForms) {
44850             // copy values from the child forms
44851             Roo.each(this.childForms, function (f) {
44852                 this.setValues(f.getValues());
44853             }, this);
44854         }
44855         
44856         
44857         
44858         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
44859         if(asString === true){
44860             return fs;
44861         }
44862         return Roo.urlDecode(fs);
44863     },
44864     
44865     /**
44866      * Returns the fields in this form as an object with key/value pairs. 
44867      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
44868      * @return {Object}
44869      */
44870     getFieldValues : function(with_hidden)
44871     {
44872         if (this.childForms) {
44873             // copy values from the child forms
44874             // should this call getFieldValues - probably not as we do not currently copy
44875             // hidden fields when we generate..
44876             Roo.each(this.childForms, function (f) {
44877                 this.setValues(f.getValues());
44878             }, this);
44879         }
44880         
44881         var ret = {};
44882         this.items.each(function(f){
44883             if (!f.getName()) {
44884                 return;
44885             }
44886             var v = f.getValue();
44887             if (f.inputType =='radio') {
44888                 if (typeof(ret[f.getName()]) == 'undefined') {
44889                     ret[f.getName()] = ''; // empty..
44890                 }
44891                 
44892                 if (!f.el.dom.checked) {
44893                     return;
44894                     
44895                 }
44896                 v = f.el.dom.value;
44897                 
44898             }
44899             
44900             // not sure if this supported any more..
44901             if ((typeof(v) == 'object') && f.getRawValue) {
44902                 v = f.getRawValue() ; // dates..
44903             }
44904             // combo boxes where name != hiddenName...
44905             if (f.name != f.getName()) {
44906                 ret[f.name] = f.getRawValue();
44907             }
44908             ret[f.getName()] = v;
44909         });
44910         
44911         return ret;
44912     },
44913
44914     /**
44915      * Clears all invalid messages in this form.
44916      * @return {BasicForm} this
44917      */
44918     clearInvalid : function(){
44919         this.items.each(function(f){
44920            f.clearInvalid();
44921         });
44922         
44923         Roo.each(this.childForms || [], function (f) {
44924             f.clearInvalid();
44925         });
44926         
44927         
44928         return this;
44929     },
44930
44931     /**
44932      * Resets this form.
44933      * @return {BasicForm} this
44934      */
44935     reset : function(){
44936         this.items.each(function(f){
44937             f.reset();
44938         });
44939         
44940         Roo.each(this.childForms || [], function (f) {
44941             f.reset();
44942         });
44943        
44944         
44945         return this;
44946     },
44947
44948     /**
44949      * Add Roo.form components to this form.
44950      * @param {Field} field1
44951      * @param {Field} field2 (optional)
44952      * @param {Field} etc (optional)
44953      * @return {BasicForm} this
44954      */
44955     add : function(){
44956         this.items.addAll(Array.prototype.slice.call(arguments, 0));
44957         return this;
44958     },
44959
44960
44961     /**
44962      * Removes a field from the items collection (does NOT remove its markup).
44963      * @param {Field} field
44964      * @return {BasicForm} this
44965      */
44966     remove : function(field){
44967         this.items.remove(field);
44968         return this;
44969     },
44970
44971     /**
44972      * Looks at the fields in this form, checks them for an id attribute,
44973      * and calls applyTo on the existing dom element with that id.
44974      * @return {BasicForm} this
44975      */
44976     render : function(){
44977         this.items.each(function(f){
44978             if(f.isFormField && !f.rendered && document.getElementById(f.id)){ // if the element exists
44979                 f.applyTo(f.id);
44980             }
44981         });
44982         return this;
44983     },
44984
44985     /**
44986      * Calls {@link Ext#apply} for all fields in this form with the passed object.
44987      * @param {Object} values
44988      * @return {BasicForm} this
44989      */
44990     applyToFields : function(o){
44991         this.items.each(function(f){
44992            Roo.apply(f, o);
44993         });
44994         return this;
44995     },
44996
44997     /**
44998      * Calls {@link Ext#applyIf} for all field in this form with the passed object.
44999      * @param {Object} values
45000      * @return {BasicForm} this
45001      */
45002     applyIfToFields : function(o){
45003         this.items.each(function(f){
45004            Roo.applyIf(f, o);
45005         });
45006         return this;
45007     }
45008 });
45009
45010 // back compat
45011 Roo.BasicForm = Roo.form.BasicForm;/*
45012  * Based on:
45013  * Ext JS Library 1.1.1
45014  * Copyright(c) 2006-2007, Ext JS, LLC.
45015  *
45016  * Originally Released Under LGPL - original licence link has changed is not relivant.
45017  *
45018  * Fork - LGPL
45019  * <script type="text/javascript">
45020  */
45021
45022 /**
45023  * @class Roo.form.Form
45024  * @extends Roo.form.BasicForm
45025  * Adds the ability to dynamically render forms with JavaScript to {@link Roo.form.BasicForm}.
45026  * @constructor
45027  * @param {Object} config Configuration options
45028  */
45029 Roo.form.Form = function(config){
45030     var xitems =  [];
45031     if (config.items) {
45032         xitems = config.items;
45033         delete config.items;
45034     }
45035    
45036     
45037     Roo.form.Form.superclass.constructor.call(this, null, config);
45038     this.url = this.url || this.action;
45039     if(!this.root){
45040         this.root = new Roo.form.Layout(Roo.applyIf({
45041             id: Roo.id()
45042         }, config));
45043     }
45044     this.active = this.root;
45045     /**
45046      * Array of all the buttons that have been added to this form via {@link addButton}
45047      * @type Array
45048      */
45049     this.buttons = [];
45050     this.allItems = [];
45051     this.addEvents({
45052         /**
45053          * @event clientvalidation
45054          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
45055          * @param {Form} this
45056          * @param {Boolean} valid true if the form has passed client-side validation
45057          */
45058         clientvalidation: true,
45059         /**
45060          * @event rendered
45061          * Fires when the form is rendered
45062          * @param {Roo.form.Form} form
45063          */
45064         rendered : true
45065     });
45066     
45067     if (this.progressUrl) {
45068             // push a hidden field onto the list of fields..
45069             this.addxtype( {
45070                     xns: Roo.form, 
45071                     xtype : 'Hidden', 
45072                     name : 'UPLOAD_IDENTIFIER' 
45073             });
45074         }
45075         
45076     
45077     Roo.each(xitems, this.addxtype, this);
45078     
45079     
45080     
45081 };
45082
45083 Roo.extend(Roo.form.Form, Roo.form.BasicForm, {
45084     /**
45085      * @cfg {Number} labelWidth The width of labels. This property cascades to child containers.
45086      */
45087     /**
45088      * @cfg {String} itemCls A css class to apply to the x-form-item of fields. This property cascades to child containers.
45089      */
45090     /**
45091      * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "center")
45092      */
45093     buttonAlign:'center',
45094
45095     /**
45096      * @cfg {Number} minButtonWidth Minimum width of all buttons in pixels (defaults to 75)
45097      */
45098     minButtonWidth:75,
45099
45100     /**
45101      * @cfg {String} labelAlign Valid values are "left," "top" and "right" (defaults to "left").
45102      * This property cascades to child containers if not set.
45103      */
45104     labelAlign:'left',
45105
45106     /**
45107      * @cfg {Boolean} monitorValid If true the form monitors its valid state <b>client-side</b> and
45108      * fires a looping event with that state. This is required to bind buttons to the valid
45109      * state using the config value formBind:true on the button.
45110      */
45111     monitorValid : false,
45112
45113     /**
45114      * @cfg {Number} monitorPoll The milliseconds to poll valid state, ignored if monitorValid is not true (defaults to 200)
45115      */
45116     monitorPoll : 200,
45117     
45118     /**
45119      * @cfg {String} progressUrl - Url to return progress data 
45120      */
45121     
45122     progressUrl : false,
45123   
45124     /**
45125      * Opens a new {@link Roo.form.Column} container in the layout stack. If fields are passed after the config, the
45126      * fields are added and the column is closed. If no fields are passed the column remains open
45127      * until end() is called.
45128      * @param {Object} config The config to pass to the column
45129      * @param {Field} field1 (optional)
45130      * @param {Field} field2 (optional)
45131      * @param {Field} etc (optional)
45132      * @return Column The column container object
45133      */
45134     column : function(c){
45135         var col = new Roo.form.Column(c);
45136         this.start(col);
45137         if(arguments.length > 1){ // duplicate code required because of Opera
45138             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
45139             this.end();
45140         }
45141         return col;
45142     },
45143
45144     /**
45145      * Opens a new {@link Roo.form.FieldSet} container in the layout stack. If fields are passed after the config, the
45146      * fields are added and the fieldset is closed. If no fields are passed the fieldset remains open
45147      * until end() is called.
45148      * @param {Object} config The config to pass to the fieldset
45149      * @param {Field} field1 (optional)
45150      * @param {Field} field2 (optional)
45151      * @param {Field} etc (optional)
45152      * @return FieldSet The fieldset container object
45153      */
45154     fieldset : function(c){
45155         var fs = new Roo.form.FieldSet(c);
45156         this.start(fs);
45157         if(arguments.length > 1){ // duplicate code required because of Opera
45158             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
45159             this.end();
45160         }
45161         return fs;
45162     },
45163
45164     /**
45165      * Opens a new {@link Roo.form.Layout} container in the layout stack. If fields are passed after the config, the
45166      * fields are added and the container is closed. If no fields are passed the container remains open
45167      * until end() is called.
45168      * @param {Object} config The config to pass to the Layout
45169      * @param {Field} field1 (optional)
45170      * @param {Field} field2 (optional)
45171      * @param {Field} etc (optional)
45172      * @return Layout The container object
45173      */
45174     container : function(c){
45175         var l = new Roo.form.Layout(c);
45176         this.start(l);
45177         if(arguments.length > 1){ // duplicate code required because of Opera
45178             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
45179             this.end();
45180         }
45181         return l;
45182     },
45183
45184     /**
45185      * Opens the passed container in the layout stack. The container can be any {@link Roo.form.Layout} or subclass.
45186      * @param {Object} container A Roo.form.Layout or subclass of Layout
45187      * @return {Form} this
45188      */
45189     start : function(c){
45190         // cascade label info
45191         Roo.applyIf(c, {'labelAlign': this.active.labelAlign, 'labelWidth': this.active.labelWidth, 'itemCls': this.active.itemCls});
45192         this.active.stack.push(c);
45193         c.ownerCt = this.active;
45194         this.active = c;
45195         return this;
45196     },
45197
45198     /**
45199      * Closes the current open container
45200      * @return {Form} this
45201      */
45202     end : function(){
45203         if(this.active == this.root){
45204             return this;
45205         }
45206         this.active = this.active.ownerCt;
45207         return this;
45208     },
45209
45210     /**
45211      * Add Roo.form components to the current open container (e.g. column, fieldset, etc.).  Fields added via this method
45212      * can also be passed with an additional property of fieldLabel, which if supplied, will provide the text to display
45213      * as the label of the field.
45214      * @param {Field} field1
45215      * @param {Field} field2 (optional)
45216      * @param {Field} etc. (optional)
45217      * @return {Form} this
45218      */
45219     add : function(){
45220         this.active.stack.push.apply(this.active.stack, arguments);
45221         this.allItems.push.apply(this.allItems,arguments);
45222         var r = [];
45223         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
45224             if(a[i].isFormField){
45225                 r.push(a[i]);
45226             }
45227         }
45228         if(r.length > 0){
45229             Roo.form.Form.superclass.add.apply(this, r);
45230         }
45231         return this;
45232     },
45233     
45234
45235     
45236     
45237     
45238      /**
45239      * Find any element that has been added to a form, using it's ID or name
45240      * This can include framesets, columns etc. along with regular fields..
45241      * @param {String} id - id or name to find.
45242      
45243      * @return {Element} e - or false if nothing found.
45244      */
45245     findbyId : function(id)
45246     {
45247         var ret = false;
45248         if (!id) {
45249             return ret;
45250         }
45251         Roo.each(this.allItems, function(f){
45252             if (f.id == id || f.name == id ){
45253                 ret = f;
45254                 return false;
45255             }
45256         });
45257         return ret;
45258     },
45259
45260     
45261     
45262     /**
45263      * Render this form into the passed container. This should only be called once!
45264      * @param {String/HTMLElement/Element} container The element this component should be rendered into
45265      * @return {Form} this
45266      */
45267     render : function(ct)
45268     {
45269         
45270         
45271         
45272         ct = Roo.get(ct);
45273         var o = this.autoCreate || {
45274             tag: 'form',
45275             method : this.method || 'POST',
45276             id : this.id || Roo.id()
45277         };
45278         this.initEl(ct.createChild(o));
45279
45280         this.root.render(this.el);
45281         
45282        
45283              
45284         this.items.each(function(f){
45285             f.render('x-form-el-'+f.id);
45286         });
45287
45288         if(this.buttons.length > 0){
45289             // tables are required to maintain order and for correct IE layout
45290             var tb = this.el.createChild({cls:'x-form-btns-ct', cn: {
45291                 cls:"x-form-btns x-form-btns-"+this.buttonAlign,
45292                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
45293             }}, null, true);
45294             var tr = tb.getElementsByTagName('tr')[0];
45295             for(var i = 0, len = this.buttons.length; i < len; i++) {
45296                 var b = this.buttons[i];
45297                 var td = document.createElement('td');
45298                 td.className = 'x-form-btn-td';
45299                 b.render(tr.appendChild(td));
45300             }
45301         }
45302         if(this.monitorValid){ // initialize after render
45303             this.startMonitoring();
45304         }
45305         this.fireEvent('rendered', this);
45306         return this;
45307     },
45308
45309     /**
45310      * Adds a button to the footer of the form - this <b>must</b> be called before the form is rendered.
45311      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
45312      * object or a valid Roo.DomHelper element config
45313      * @param {Function} handler The function called when the button is clicked
45314      * @param {Object} scope (optional) The scope of the handler function
45315      * @return {Roo.Button}
45316      */
45317     addButton : function(config, handler, scope){
45318         var bc = {
45319             handler: handler,
45320             scope: scope,
45321             minWidth: this.minButtonWidth,
45322             hideParent:true
45323         };
45324         if(typeof config == "string"){
45325             bc.text = config;
45326         }else{
45327             Roo.apply(bc, config);
45328         }
45329         var btn = new Roo.Button(null, bc);
45330         this.buttons.push(btn);
45331         return btn;
45332     },
45333
45334      /**
45335      * Adds a series of form elements (using the xtype property as the factory method.
45336      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column, (and 'end' to close a block)
45337      * @param {Object} config 
45338      */
45339     
45340     addxtype : function()
45341     {
45342         var ar = Array.prototype.slice.call(arguments, 0);
45343         var ret = false;
45344         for(var i = 0; i < ar.length; i++) {
45345             if (!ar[i]) {
45346                 continue; // skip -- if this happends something invalid got sent, we 
45347                 // should ignore it, as basically that interface element will not show up
45348                 // and that should be pretty obvious!!
45349             }
45350             
45351             if (Roo.form[ar[i].xtype]) {
45352                 ar[i].form = this;
45353                 var fe = Roo.factory(ar[i], Roo.form);
45354                 if (!ret) {
45355                     ret = fe;
45356                 }
45357                 fe.form = this;
45358                 if (fe.store) {
45359                     fe.store.form = this;
45360                 }
45361                 if (fe.isLayout) {  
45362                          
45363                     this.start(fe);
45364                     this.allItems.push(fe);
45365                     if (fe.items && fe.addxtype) {
45366                         fe.addxtype.apply(fe, fe.items);
45367                         delete fe.items;
45368                     }
45369                      this.end();
45370                     continue;
45371                 }
45372                 
45373                 
45374                  
45375                 this.add(fe);
45376               //  console.log('adding ' + ar[i].xtype);
45377             }
45378             if (ar[i].xtype == 'Button') {  
45379                 //console.log('adding button');
45380                 //console.log(ar[i]);
45381                 this.addButton(ar[i]);
45382                 this.allItems.push(fe);
45383                 continue;
45384             }
45385             
45386             if (ar[i].xtype == 'end') { // so we can add fieldsets... / layout etc.
45387                 alert('end is not supported on xtype any more, use items');
45388             //    this.end();
45389             //    //console.log('adding end');
45390             }
45391             
45392         }
45393         return ret;
45394     },
45395     
45396     /**
45397      * Starts monitoring of the valid state of this form. Usually this is done by passing the config
45398      * option "monitorValid"
45399      */
45400     startMonitoring : function(){
45401         if(!this.bound){
45402             this.bound = true;
45403             Roo.TaskMgr.start({
45404                 run : this.bindHandler,
45405                 interval : this.monitorPoll || 200,
45406                 scope: this
45407             });
45408         }
45409     },
45410
45411     /**
45412      * Stops monitoring of the valid state of this form
45413      */
45414     stopMonitoring : function(){
45415         this.bound = false;
45416     },
45417
45418     // private
45419     bindHandler : function(){
45420         if(!this.bound){
45421             return false; // stops binding
45422         }
45423         var valid = true;
45424         this.items.each(function(f){
45425             if(!f.isValid(true)){
45426                 valid = false;
45427                 return false;
45428             }
45429         });
45430         for(var i = 0, len = this.buttons.length; i < len; i++){
45431             var btn = this.buttons[i];
45432             if(btn.formBind === true && btn.disabled === valid){
45433                 btn.setDisabled(!valid);
45434             }
45435         }
45436         this.fireEvent('clientvalidation', this, valid);
45437     }
45438     
45439     
45440     
45441     
45442     
45443     
45444     
45445     
45446 });
45447
45448
45449 // back compat
45450 Roo.Form = Roo.form.Form;
45451 /*
45452  * Based on:
45453  * Ext JS Library 1.1.1
45454  * Copyright(c) 2006-2007, Ext JS, LLC.
45455  *
45456  * Originally Released Under LGPL - original licence link has changed is not relivant.
45457  *
45458  * Fork - LGPL
45459  * <script type="text/javascript">
45460  */
45461
45462 // as we use this in bootstrap.
45463 Roo.namespace('Roo.form');
45464  /**
45465  * @class Roo.form.Action
45466  * Internal Class used to handle form actions
45467  * @constructor
45468  * @param {Roo.form.BasicForm} el The form element or its id
45469  * @param {Object} config Configuration options
45470  */
45471
45472  
45473  
45474 // define the action interface
45475 Roo.form.Action = function(form, options){
45476     this.form = form;
45477     this.options = options || {};
45478 };
45479 /**
45480  * Client Validation Failed
45481  * @const 
45482  */
45483 Roo.form.Action.CLIENT_INVALID = 'client';
45484 /**
45485  * Server Validation Failed
45486  * @const 
45487  */
45488 Roo.form.Action.SERVER_INVALID = 'server';
45489  /**
45490  * Connect to Server Failed
45491  * @const 
45492  */
45493 Roo.form.Action.CONNECT_FAILURE = 'connect';
45494 /**
45495  * Reading Data from Server Failed
45496  * @const 
45497  */
45498 Roo.form.Action.LOAD_FAILURE = 'load';
45499
45500 Roo.form.Action.prototype = {
45501     type : 'default',
45502     failureType : undefined,
45503     response : undefined,
45504     result : undefined,
45505
45506     // interface method
45507     run : function(options){
45508
45509     },
45510
45511     // interface method
45512     success : function(response){
45513
45514     },
45515
45516     // interface method
45517     handleResponse : function(response){
45518
45519     },
45520
45521     // default connection failure
45522     failure : function(response){
45523         
45524         this.response = response;
45525         this.failureType = Roo.form.Action.CONNECT_FAILURE;
45526         this.form.afterAction(this, false);
45527     },
45528
45529     processResponse : function(response){
45530         this.response = response;
45531         if(!response.responseText){
45532             return true;
45533         }
45534         this.result = this.handleResponse(response);
45535         return this.result;
45536     },
45537
45538     // utility functions used internally
45539     getUrl : function(appendParams){
45540         var url = this.options.url || this.form.url || this.form.el.dom.action;
45541         if(appendParams){
45542             var p = this.getParams();
45543             if(p){
45544                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
45545             }
45546         }
45547         return url;
45548     },
45549
45550     getMethod : function(){
45551         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
45552     },
45553
45554     getParams : function(){
45555         var bp = this.form.baseParams;
45556         var p = this.options.params;
45557         if(p){
45558             if(typeof p == "object"){
45559                 p = Roo.urlEncode(Roo.applyIf(p, bp));
45560             }else if(typeof p == 'string' && bp){
45561                 p += '&' + Roo.urlEncode(bp);
45562             }
45563         }else if(bp){
45564             p = Roo.urlEncode(bp);
45565         }
45566         return p;
45567     },
45568
45569     createCallback : function(){
45570         return {
45571             success: this.success,
45572             failure: this.failure,
45573             scope: this,
45574             timeout: (this.form.timeout*1000),
45575             upload: this.form.fileUpload ? this.success : undefined
45576         };
45577     }
45578 };
45579
45580 Roo.form.Action.Submit = function(form, options){
45581     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
45582 };
45583
45584 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
45585     type : 'submit',
45586
45587     haveProgress : false,
45588     uploadComplete : false,
45589     
45590     // uploadProgress indicator.
45591     uploadProgress : function()
45592     {
45593         if (!this.form.progressUrl) {
45594             return;
45595         }
45596         
45597         if (!this.haveProgress) {
45598             Roo.MessageBox.progress("Uploading", "Uploading");
45599         }
45600         if (this.uploadComplete) {
45601            Roo.MessageBox.hide();
45602            return;
45603         }
45604         
45605         this.haveProgress = true;
45606    
45607         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
45608         
45609         var c = new Roo.data.Connection();
45610         c.request({
45611             url : this.form.progressUrl,
45612             params: {
45613                 id : uid
45614             },
45615             method: 'GET',
45616             success : function(req){
45617                //console.log(data);
45618                 var rdata = false;
45619                 var edata;
45620                 try  {
45621                    rdata = Roo.decode(req.responseText)
45622                 } catch (e) {
45623                     Roo.log("Invalid data from server..");
45624                     Roo.log(edata);
45625                     return;
45626                 }
45627                 if (!rdata || !rdata.success) {
45628                     Roo.log(rdata);
45629                     Roo.MessageBox.alert(Roo.encode(rdata));
45630                     return;
45631                 }
45632                 var data = rdata.data;
45633                 
45634                 if (this.uploadComplete) {
45635                    Roo.MessageBox.hide();
45636                    return;
45637                 }
45638                    
45639                 if (data){
45640                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
45641                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
45642                     );
45643                 }
45644                 this.uploadProgress.defer(2000,this);
45645             },
45646        
45647             failure: function(data) {
45648                 Roo.log('progress url failed ');
45649                 Roo.log(data);
45650             },
45651             scope : this
45652         });
45653            
45654     },
45655     
45656     
45657     run : function()
45658     {
45659         // run get Values on the form, so it syncs any secondary forms.
45660         this.form.getValues();
45661         
45662         var o = this.options;
45663         var method = this.getMethod();
45664         var isPost = method == 'POST';
45665         if(o.clientValidation === false || this.form.isValid()){
45666             
45667             if (this.form.progressUrl) {
45668                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
45669                     (new Date() * 1) + '' + Math.random());
45670                     
45671             } 
45672             
45673             
45674             Roo.Ajax.request(Roo.apply(this.createCallback(), {
45675                 form:this.form.el.dom,
45676                 url:this.getUrl(!isPost),
45677                 method: method,
45678                 params:isPost ? this.getParams() : null,
45679                 isUpload: this.form.fileUpload
45680             }));
45681             
45682             this.uploadProgress();
45683
45684         }else if (o.clientValidation !== false){ // client validation failed
45685             this.failureType = Roo.form.Action.CLIENT_INVALID;
45686             this.form.afterAction(this, false);
45687         }
45688     },
45689
45690     success : function(response)
45691     {
45692         this.uploadComplete= true;
45693         if (this.haveProgress) {
45694             Roo.MessageBox.hide();
45695         }
45696         
45697         
45698         var result = this.processResponse(response);
45699         if(result === true || result.success){
45700             this.form.afterAction(this, true);
45701             return;
45702         }
45703         if(result.errors){
45704             this.form.markInvalid(result.errors);
45705             this.failureType = Roo.form.Action.SERVER_INVALID;
45706         }
45707         this.form.afterAction(this, false);
45708     },
45709     failure : function(response)
45710     {
45711         this.uploadComplete= true;
45712         if (this.haveProgress) {
45713             Roo.MessageBox.hide();
45714         }
45715         
45716         this.response = response;
45717         this.failureType = Roo.form.Action.CONNECT_FAILURE;
45718         this.form.afterAction(this, false);
45719     },
45720     
45721     handleResponse : function(response){
45722         if(this.form.errorReader){
45723             var rs = this.form.errorReader.read(response);
45724             var errors = [];
45725             if(rs.records){
45726                 for(var i = 0, len = rs.records.length; i < len; i++) {
45727                     var r = rs.records[i];
45728                     errors[i] = r.data;
45729                 }
45730             }
45731             if(errors.length < 1){
45732                 errors = null;
45733             }
45734             return {
45735                 success : rs.success,
45736                 errors : errors
45737             };
45738         }
45739         var ret = false;
45740         try {
45741             ret = Roo.decode(response.responseText);
45742         } catch (e) {
45743             ret = {
45744                 success: false,
45745                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
45746                 errors : []
45747             };
45748         }
45749         return ret;
45750         
45751     }
45752 });
45753
45754
45755 Roo.form.Action.Load = function(form, options){
45756     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
45757     this.reader = this.form.reader;
45758 };
45759
45760 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
45761     type : 'load',
45762
45763     run : function(){
45764         
45765         Roo.Ajax.request(Roo.apply(
45766                 this.createCallback(), {
45767                     method:this.getMethod(),
45768                     url:this.getUrl(false),
45769                     params:this.getParams()
45770         }));
45771     },
45772
45773     success : function(response){
45774         
45775         var result = this.processResponse(response);
45776         if(result === true || !result.success || !result.data){
45777             this.failureType = Roo.form.Action.LOAD_FAILURE;
45778             this.form.afterAction(this, false);
45779             return;
45780         }
45781         this.form.clearInvalid();
45782         this.form.setValues(result.data);
45783         this.form.afterAction(this, true);
45784     },
45785
45786     handleResponse : function(response){
45787         if(this.form.reader){
45788             var rs = this.form.reader.read(response);
45789             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
45790             return {
45791                 success : rs.success,
45792                 data : data
45793             };
45794         }
45795         return Roo.decode(response.responseText);
45796     }
45797 });
45798
45799 Roo.form.Action.ACTION_TYPES = {
45800     'load' : Roo.form.Action.Load,
45801     'submit' : Roo.form.Action.Submit
45802 };/*
45803  * Based on:
45804  * Ext JS Library 1.1.1
45805  * Copyright(c) 2006-2007, Ext JS, LLC.
45806  *
45807  * Originally Released Under LGPL - original licence link has changed is not relivant.
45808  *
45809  * Fork - LGPL
45810  * <script type="text/javascript">
45811  */
45812  
45813 /**
45814  * @class Roo.form.Layout
45815  * @extends Roo.Component
45816  * Creates a container for layout and rendering of fields in an {@link Roo.form.Form}.
45817  * @constructor
45818  * @param {Object} config Configuration options
45819  */
45820 Roo.form.Layout = function(config){
45821     var xitems = [];
45822     if (config.items) {
45823         xitems = config.items;
45824         delete config.items;
45825     }
45826     Roo.form.Layout.superclass.constructor.call(this, config);
45827     this.stack = [];
45828     Roo.each(xitems, this.addxtype, this);
45829      
45830 };
45831
45832 Roo.extend(Roo.form.Layout, Roo.Component, {
45833     /**
45834      * @cfg {String/Object} autoCreate
45835      * A DomHelper element spec used to autocreate the layout (defaults to {tag: 'div', cls: 'x-form-ct'})
45836      */
45837     /**
45838      * @cfg {String/Object/Function} style
45839      * A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
45840      * a function which returns such a specification.
45841      */
45842     /**
45843      * @cfg {String} labelAlign
45844      * Valid values are "left," "top" and "right" (defaults to "left")
45845      */
45846     /**
45847      * @cfg {Number} labelWidth
45848      * Fixed width in pixels of all field labels (defaults to undefined)
45849      */
45850     /**
45851      * @cfg {Boolean} clear
45852      * True to add a clearing element at the end of this layout, equivalent to CSS clear: both (defaults to true)
45853      */
45854     clear : true,
45855     /**
45856      * @cfg {String} labelSeparator
45857      * The separator to use after field labels (defaults to ':')
45858      */
45859     labelSeparator : ':',
45860     /**
45861      * @cfg {Boolean} hideLabels
45862      * True to suppress the display of field labels in this layout (defaults to false)
45863      */
45864     hideLabels : false,
45865
45866     // private
45867     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct'},
45868     
45869     isLayout : true,
45870     
45871     // private
45872     onRender : function(ct, position){
45873         if(this.el){ // from markup
45874             this.el = Roo.get(this.el);
45875         }else {  // generate
45876             var cfg = this.getAutoCreate();
45877             this.el = ct.createChild(cfg, position);
45878         }
45879         if(this.style){
45880             this.el.applyStyles(this.style);
45881         }
45882         if(this.labelAlign){
45883             this.el.addClass('x-form-label-'+this.labelAlign);
45884         }
45885         if(this.hideLabels){
45886             this.labelStyle = "display:none";
45887             this.elementStyle = "padding-left:0;";
45888         }else{
45889             if(typeof this.labelWidth == 'number'){
45890                 this.labelStyle = "width:"+this.labelWidth+"px;";
45891                 this.elementStyle = "padding-left:"+((this.labelWidth+(typeof this.labelPad == 'number' ? this.labelPad : 5))+'px')+";";
45892             }
45893             if(this.labelAlign == 'top'){
45894                 this.labelStyle = "width:auto;";
45895                 this.elementStyle = "padding-left:0;";
45896             }
45897         }
45898         var stack = this.stack;
45899         var slen = stack.length;
45900         if(slen > 0){
45901             if(!this.fieldTpl){
45902                 var t = new Roo.Template(
45903                     '<div class="x-form-item {5}">',
45904                         '<label for="{0}" style="{2}">{1}{4}</label>',
45905                         '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
45906                         '</div>',
45907                     '</div><div class="x-form-clear-left"></div>'
45908                 );
45909                 t.disableFormats = true;
45910                 t.compile();
45911                 Roo.form.Layout.prototype.fieldTpl = t;
45912             }
45913             for(var i = 0; i < slen; i++) {
45914                 if(stack[i].isFormField){
45915                     this.renderField(stack[i]);
45916                 }else{
45917                     this.renderComponent(stack[i]);
45918                 }
45919             }
45920         }
45921         if(this.clear){
45922             this.el.createChild({cls:'x-form-clear'});
45923         }
45924     },
45925
45926     // private
45927     renderField : function(f){
45928         f.fieldEl = Roo.get(this.fieldTpl.append(this.el, [
45929                f.id, //0
45930                f.fieldLabel, //1
45931                f.labelStyle||this.labelStyle||'', //2
45932                this.elementStyle||'', //3
45933                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator, //4
45934                f.itemCls||this.itemCls||''  //5
45935        ], true).getPrevSibling());
45936     },
45937
45938     // private
45939     renderComponent : function(c){
45940         c.render(c.isLayout ? this.el : this.el.createChild());    
45941     },
45942     /**
45943      * Adds a object form elements (using the xtype property as the factory method.)
45944      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column
45945      * @param {Object} config 
45946      */
45947     addxtype : function(o)
45948     {
45949         // create the lement.
45950         o.form = this.form;
45951         var fe = Roo.factory(o, Roo.form);
45952         this.form.allItems.push(fe);
45953         this.stack.push(fe);
45954         
45955         if (fe.isFormField) {
45956             this.form.items.add(fe);
45957         }
45958          
45959         return fe;
45960     }
45961 });
45962
45963 /**
45964  * @class Roo.form.Column
45965  * @extends Roo.form.Layout
45966  * Creates a column container for layout and rendering of fields in an {@link Roo.form.Form}.
45967  * @constructor
45968  * @param {Object} config Configuration options
45969  */
45970 Roo.form.Column = function(config){
45971     Roo.form.Column.superclass.constructor.call(this, config);
45972 };
45973
45974 Roo.extend(Roo.form.Column, Roo.form.Layout, {
45975     /**
45976      * @cfg {Number/String} width
45977      * The fixed width of the column in pixels or CSS value (defaults to "auto")
45978      */
45979     /**
45980      * @cfg {String/Object} autoCreate
45981      * A DomHelper element spec used to autocreate the column (defaults to {tag: 'div', cls: 'x-form-ct x-form-column'})
45982      */
45983
45984     // private
45985     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-column'},
45986
45987     // private
45988     onRender : function(ct, position){
45989         Roo.form.Column.superclass.onRender.call(this, ct, position);
45990         if(this.width){
45991             this.el.setWidth(this.width);
45992         }
45993     }
45994 });
45995
45996
45997 /**
45998  * @class Roo.form.Row
45999  * @extends Roo.form.Layout
46000  * Creates a row container for layout and rendering of fields in an {@link Roo.form.Form}.
46001  * @constructor
46002  * @param {Object} config Configuration options
46003  */
46004
46005  
46006 Roo.form.Row = function(config){
46007     Roo.form.Row.superclass.constructor.call(this, config);
46008 };
46009  
46010 Roo.extend(Roo.form.Row, Roo.form.Layout, {
46011       /**
46012      * @cfg {Number/String} width
46013      * The fixed width of the column in pixels or CSS value (defaults to "auto")
46014      */
46015     /**
46016      * @cfg {Number/String} height
46017      * The fixed height of the column in pixels or CSS value (defaults to "auto")
46018      */
46019     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-row'},
46020     
46021     padWidth : 20,
46022     // private
46023     onRender : function(ct, position){
46024         //console.log('row render');
46025         if(!this.rowTpl){
46026             var t = new Roo.Template(
46027                 '<div class="x-form-item {5}" style="float:left;width:{6}px">',
46028                     '<label for="{0}" style="{2}">{1}{4}</label>',
46029                     '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
46030                     '</div>',
46031                 '</div>'
46032             );
46033             t.disableFormats = true;
46034             t.compile();
46035             Roo.form.Layout.prototype.rowTpl = t;
46036         }
46037         this.fieldTpl = this.rowTpl;
46038         
46039         //console.log('lw' + this.labelWidth +', la:' + this.labelAlign);
46040         var labelWidth = 100;
46041         
46042         if ((this.labelAlign != 'top')) {
46043             if (typeof this.labelWidth == 'number') {
46044                 labelWidth = this.labelWidth
46045             }
46046             this.padWidth =  20 + labelWidth;
46047             
46048         }
46049         
46050         Roo.form.Column.superclass.onRender.call(this, ct, position);
46051         if(this.width){
46052             this.el.setWidth(this.width);
46053         }
46054         if(this.height){
46055             this.el.setHeight(this.height);
46056         }
46057     },
46058     
46059     // private
46060     renderField : function(f){
46061         f.fieldEl = this.fieldTpl.append(this.el, [
46062                f.id, f.fieldLabel,
46063                f.labelStyle||this.labelStyle||'',
46064                this.elementStyle||'',
46065                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator,
46066                f.itemCls||this.itemCls||'',
46067                f.width ? f.width + this.padWidth : 160 + this.padWidth
46068        ],true);
46069     }
46070 });
46071  
46072
46073 /**
46074  * @class Roo.form.FieldSet
46075  * @extends Roo.form.Layout
46076  * Creates a fieldset container for layout and rendering of fields in an {@link Roo.form.Form}.
46077  * @constructor
46078  * @param {Object} config Configuration options
46079  */
46080 Roo.form.FieldSet = function(config){
46081     Roo.form.FieldSet.superclass.constructor.call(this, config);
46082 };
46083
46084 Roo.extend(Roo.form.FieldSet, Roo.form.Layout, {
46085     /**
46086      * @cfg {String} legend
46087      * The text to display as the legend for the FieldSet (defaults to '')
46088      */
46089     /**
46090      * @cfg {String/Object} autoCreate
46091      * A DomHelper element spec used to autocreate the fieldset (defaults to {tag: 'fieldset', cn: {tag:'legend'}})
46092      */
46093
46094     // private
46095     defaultAutoCreate : {tag: 'fieldset', cn: {tag:'legend'}},
46096
46097     // private
46098     onRender : function(ct, position){
46099         Roo.form.FieldSet.superclass.onRender.call(this, ct, position);
46100         if(this.legend){
46101             this.setLegend(this.legend);
46102         }
46103     },
46104
46105     // private
46106     setLegend : function(text){
46107         if(this.rendered){
46108             this.el.child('legend').update(text);
46109         }
46110     }
46111 });/*
46112  * Based on:
46113  * Ext JS Library 1.1.1
46114  * Copyright(c) 2006-2007, Ext JS, LLC.
46115  *
46116  * Originally Released Under LGPL - original licence link has changed is not relivant.
46117  *
46118  * Fork - LGPL
46119  * <script type="text/javascript">
46120  */
46121 /**
46122  * @class Roo.form.VTypes
46123  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
46124  * @singleton
46125  */
46126 Roo.form.VTypes = function(){
46127     // closure these in so they are only created once.
46128     var alpha = /^[a-zA-Z_]+$/;
46129     var alphanum = /^[a-zA-Z0-9_]+$/;
46130     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,4}$/;
46131     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
46132
46133     // All these messages and functions are configurable
46134     return {
46135         /**
46136          * The function used to validate email addresses
46137          * @param {String} value The email address
46138          */
46139         'email' : function(v){
46140             return email.test(v);
46141         },
46142         /**
46143          * The error text to display when the email validation function returns false
46144          * @type String
46145          */
46146         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
46147         /**
46148          * The keystroke filter mask to be applied on email input
46149          * @type RegExp
46150          */
46151         'emailMask' : /[a-z0-9_\.\-@]/i,
46152
46153         /**
46154          * The function used to validate URLs
46155          * @param {String} value The URL
46156          */
46157         'url' : function(v){
46158             return url.test(v);
46159         },
46160         /**
46161          * The error text to display when the url validation function returns false
46162          * @type String
46163          */
46164         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
46165         
46166         /**
46167          * The function used to validate alpha values
46168          * @param {String} value The value
46169          */
46170         'alpha' : function(v){
46171             return alpha.test(v);
46172         },
46173         /**
46174          * The error text to display when the alpha validation function returns false
46175          * @type String
46176          */
46177         'alphaText' : 'This field should only contain letters and _',
46178         /**
46179          * The keystroke filter mask to be applied on alpha input
46180          * @type RegExp
46181          */
46182         'alphaMask' : /[a-z_]/i,
46183
46184         /**
46185          * The function used to validate alphanumeric values
46186          * @param {String} value The value
46187          */
46188         'alphanum' : function(v){
46189             return alphanum.test(v);
46190         },
46191         /**
46192          * The error text to display when the alphanumeric validation function returns false
46193          * @type String
46194          */
46195         'alphanumText' : 'This field should only contain letters, numbers and _',
46196         /**
46197          * The keystroke filter mask to be applied on alphanumeric input
46198          * @type RegExp
46199          */
46200         'alphanumMask' : /[a-z0-9_]/i
46201     };
46202 }();//<script type="text/javascript">
46203
46204 /**
46205  * @class Roo.form.FCKeditor
46206  * @extends Roo.form.TextArea
46207  * Wrapper around the FCKEditor http://www.fckeditor.net
46208  * @constructor
46209  * Creates a new FCKeditor
46210  * @param {Object} config Configuration options
46211  */
46212 Roo.form.FCKeditor = function(config){
46213     Roo.form.FCKeditor.superclass.constructor.call(this, config);
46214     this.addEvents({
46215          /**
46216          * @event editorinit
46217          * Fired when the editor is initialized - you can add extra handlers here..
46218          * @param {FCKeditor} this
46219          * @param {Object} the FCK object.
46220          */
46221         editorinit : true
46222     });
46223     
46224     
46225 };
46226 Roo.form.FCKeditor.editors = { };
46227 Roo.extend(Roo.form.FCKeditor, Roo.form.TextArea,
46228 {
46229     //defaultAutoCreate : {
46230     //    tag : "textarea",style   : "width:100px;height:60px;" ,autocomplete    : "off"
46231     //},
46232     // private
46233     /**
46234      * @cfg {Object} fck options - see fck manual for details.
46235      */
46236     fckconfig : false,
46237     
46238     /**
46239      * @cfg {Object} fck toolbar set (Basic or Default)
46240      */
46241     toolbarSet : 'Basic',
46242     /**
46243      * @cfg {Object} fck BasePath
46244      */ 
46245     basePath : '/fckeditor/',
46246     
46247     
46248     frame : false,
46249     
46250     value : '',
46251     
46252    
46253     onRender : function(ct, position)
46254     {
46255         if(!this.el){
46256             this.defaultAutoCreate = {
46257                 tag: "textarea",
46258                 style:"width:300px;height:60px;",
46259                 autocomplete: "off"
46260             };
46261         }
46262         Roo.form.FCKeditor.superclass.onRender.call(this, ct, position);
46263         /*
46264         if(this.grow){
46265             this.textSizeEl = Roo.DomHelper.append(document.body, {tag: "pre", cls: "x-form-grow-sizer"});
46266             if(this.preventScrollbars){
46267                 this.el.setStyle("overflow", "hidden");
46268             }
46269             this.el.setHeight(this.growMin);
46270         }
46271         */
46272         //console.log('onrender' + this.getId() );
46273         Roo.form.FCKeditor.editors[this.getId()] = this;
46274          
46275
46276         this.replaceTextarea() ;
46277         
46278     },
46279     
46280     getEditor : function() {
46281         return this.fckEditor;
46282     },
46283     /**
46284      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
46285      * @param {Mixed} value The value to set
46286      */
46287     
46288     
46289     setValue : function(value)
46290     {
46291         //console.log('setValue: ' + value);
46292         
46293         if(typeof(value) == 'undefined') { // not sure why this is happending...
46294             return;
46295         }
46296         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
46297         
46298         //if(!this.el || !this.getEditor()) {
46299         //    this.value = value;
46300             //this.setValue.defer(100,this,[value]);    
46301         //    return;
46302         //} 
46303         
46304         if(!this.getEditor()) {
46305             return;
46306         }
46307         
46308         this.getEditor().SetData(value);
46309         
46310         //
46311
46312     },
46313
46314     /**
46315      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
46316      * @return {Mixed} value The field value
46317      */
46318     getValue : function()
46319     {
46320         
46321         if (this.frame && this.frame.dom.style.display == 'none') {
46322             return Roo.form.FCKeditor.superclass.getValue.call(this);
46323         }
46324         
46325         if(!this.el || !this.getEditor()) {
46326            
46327            // this.getValue.defer(100,this); 
46328             return this.value;
46329         }
46330        
46331         
46332         var value=this.getEditor().GetData();
46333         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
46334         return Roo.form.FCKeditor.superclass.getValue.call(this);
46335         
46336
46337     },
46338
46339     /**
46340      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
46341      * @return {Mixed} value The field value
46342      */
46343     getRawValue : function()
46344     {
46345         if (this.frame && this.frame.dom.style.display == 'none') {
46346             return Roo.form.FCKeditor.superclass.getRawValue.call(this);
46347         }
46348         
46349         if(!this.el || !this.getEditor()) {
46350             //this.getRawValue.defer(100,this); 
46351             return this.value;
46352             return;
46353         }
46354         
46355         
46356         
46357         var value=this.getEditor().GetData();
46358         Roo.form.FCKeditor.superclass.setRawValue.apply(this,[value]);
46359         return Roo.form.FCKeditor.superclass.getRawValue.call(this);
46360          
46361     },
46362     
46363     setSize : function(w,h) {
46364         
46365         
46366         
46367         //if (this.frame && this.frame.dom.style.display == 'none') {
46368         //    Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
46369         //    return;
46370         //}
46371         //if(!this.el || !this.getEditor()) {
46372         //    this.setSize.defer(100,this, [w,h]); 
46373         //    return;
46374         //}
46375         
46376         
46377         
46378         Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
46379         
46380         this.frame.dom.setAttribute('width', w);
46381         this.frame.dom.setAttribute('height', h);
46382         this.frame.setSize(w,h);
46383         
46384     },
46385     
46386     toggleSourceEdit : function(value) {
46387         
46388       
46389          
46390         this.el.dom.style.display = value ? '' : 'none';
46391         this.frame.dom.style.display = value ?  'none' : '';
46392         
46393     },
46394     
46395     
46396     focus: function(tag)
46397     {
46398         if (this.frame.dom.style.display == 'none') {
46399             return Roo.form.FCKeditor.superclass.focus.call(this);
46400         }
46401         if(!this.el || !this.getEditor()) {
46402             this.focus.defer(100,this, [tag]); 
46403             return;
46404         }
46405         
46406         
46407         
46408         
46409         var tgs = this.getEditor().EditorDocument.getElementsByTagName(tag);
46410         this.getEditor().Focus();
46411         if (tgs.length) {
46412             if (!this.getEditor().Selection.GetSelection()) {
46413                 this.focus.defer(100,this, [tag]); 
46414                 return;
46415             }
46416             
46417             
46418             var r = this.getEditor().EditorDocument.createRange();
46419             r.setStart(tgs[0],0);
46420             r.setEnd(tgs[0],0);
46421             this.getEditor().Selection.GetSelection().removeAllRanges();
46422             this.getEditor().Selection.GetSelection().addRange(r);
46423             this.getEditor().Focus();
46424         }
46425         
46426     },
46427     
46428     
46429     
46430     replaceTextarea : function()
46431     {
46432         if ( document.getElementById( this.getId() + '___Frame' ) )
46433             return ;
46434         //if ( !this.checkBrowser || this._isCompatibleBrowser() )
46435         //{
46436             // We must check the elements firstly using the Id and then the name.
46437         var oTextarea = document.getElementById( this.getId() );
46438         
46439         var colElementsByName = document.getElementsByName( this.getId() ) ;
46440          
46441         oTextarea.style.display = 'none' ;
46442
46443         if ( oTextarea.tabIndex ) {            
46444             this.TabIndex = oTextarea.tabIndex ;
46445         }
46446         
46447         this._insertHtmlBefore( this._getConfigHtml(), oTextarea ) ;
46448         this._insertHtmlBefore( this._getIFrameHtml(), oTextarea ) ;
46449         this.frame = Roo.get(this.getId() + '___Frame')
46450     },
46451     
46452     _getConfigHtml : function()
46453     {
46454         var sConfig = '' ;
46455
46456         for ( var o in this.fckconfig ) {
46457             sConfig += sConfig.length > 0  ? '&amp;' : '';
46458             sConfig += encodeURIComponent( o ) + '=' + encodeURIComponent( this.fckconfig[o] ) ;
46459         }
46460
46461         return '<input type="hidden" id="' + this.getId() + '___Config" value="' + sConfig + '" style="display:none" />' ;
46462     },
46463     
46464     
46465     _getIFrameHtml : function()
46466     {
46467         var sFile = 'fckeditor.html' ;
46468         /* no idea what this is about..
46469         try
46470         {
46471             if ( (/fcksource=true/i).test( window.top.location.search ) )
46472                 sFile = 'fckeditor.original.html' ;
46473         }
46474         catch (e) { 
46475         */
46476
46477         var sLink = this.basePath + 'editor/' + sFile + '?InstanceName=' + encodeURIComponent( this.getId() ) ;
46478         sLink += this.toolbarSet ? ( '&amp;Toolbar=' + this.toolbarSet)  : '';
46479         
46480         
46481         var html = '<iframe id="' + this.getId() +
46482             '___Frame" src="' + sLink +
46483             '" width="' + this.width +
46484             '" height="' + this.height + '"' +
46485             (this.tabIndex ?  ' tabindex="' + this.tabIndex + '"' :'' ) +
46486             ' frameborder="0" scrolling="no"></iframe>' ;
46487
46488         return html ;
46489     },
46490     
46491     _insertHtmlBefore : function( html, element )
46492     {
46493         if ( element.insertAdjacentHTML )       {
46494             // IE
46495             element.insertAdjacentHTML( 'beforeBegin', html ) ;
46496         } else { // Gecko
46497             var oRange = document.createRange() ;
46498             oRange.setStartBefore( element ) ;
46499             var oFragment = oRange.createContextualFragment( html );
46500             element.parentNode.insertBefore( oFragment, element ) ;
46501         }
46502     }
46503     
46504     
46505   
46506     
46507     
46508     
46509     
46510
46511 });
46512
46513 //Roo.reg('fckeditor', Roo.form.FCKeditor);
46514
46515 function FCKeditor_OnComplete(editorInstance){
46516     var f = Roo.form.FCKeditor.editors[editorInstance.Name];
46517     f.fckEditor = editorInstance;
46518     //console.log("loaded");
46519     f.fireEvent('editorinit', f, editorInstance);
46520
46521   
46522
46523  
46524
46525
46526
46527
46528
46529
46530
46531
46532
46533
46534
46535
46536
46537
46538
46539 //<script type="text/javascript">
46540 /**
46541  * @class Roo.form.GridField
46542  * @extends Roo.form.Field
46543  * Embed a grid (or editable grid into a form)
46544  * STATUS ALPHA
46545  * 
46546  * This embeds a grid in a form, the value of the field should be the json encoded array of rows
46547  * it needs 
46548  * xgrid.store = Roo.data.Store
46549  * xgrid.store.proxy = Roo.data.MemoryProxy (data = [] )
46550  * xgrid.store.reader = Roo.data.JsonReader 
46551  * 
46552  * 
46553  * @constructor
46554  * Creates a new GridField
46555  * @param {Object} config Configuration options
46556  */
46557 Roo.form.GridField = function(config){
46558     Roo.form.GridField.superclass.constructor.call(this, config);
46559      
46560 };
46561
46562 Roo.extend(Roo.form.GridField, Roo.form.Field,  {
46563     /**
46564      * @cfg {Number} width  - used to restrict width of grid..
46565      */
46566     width : 100,
46567     /**
46568      * @cfg {Number} height - used to restrict height of grid..
46569      */
46570     height : 50,
46571      /**
46572      * @cfg {Object} xgrid (xtype'd description of grid) { xtype : 'Grid', dataSource: .... }
46573          * 
46574          *}
46575      */
46576     xgrid : false, 
46577     /**
46578      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
46579      * {tag: "input", type: "checkbox", autocomplete: "off"})
46580      */
46581    // defaultAutoCreate : { tag: 'div' },
46582     defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
46583     /**
46584      * @cfg {String} addTitle Text to include for adding a title.
46585      */
46586     addTitle : false,
46587     //
46588     onResize : function(){
46589         Roo.form.Field.superclass.onResize.apply(this, arguments);
46590     },
46591
46592     initEvents : function(){
46593         // Roo.form.Checkbox.superclass.initEvents.call(this);
46594         // has no events...
46595        
46596     },
46597
46598
46599     getResizeEl : function(){
46600         return this.wrap;
46601     },
46602
46603     getPositionEl : function(){
46604         return this.wrap;
46605     },
46606
46607     // private
46608     onRender : function(ct, position){
46609         
46610         this.style = this.style || 'overflow: hidden; border:1px solid #c3daf9;';
46611         var style = this.style;
46612         delete this.style;
46613         
46614         Roo.form.GridField.superclass.onRender.call(this, ct, position);
46615         this.wrap = this.el.wrap({cls: ''}); // not sure why ive done thsi...
46616         this.viewEl = this.wrap.createChild({ tag: 'div' });
46617         if (style) {
46618             this.viewEl.applyStyles(style);
46619         }
46620         if (this.width) {
46621             this.viewEl.setWidth(this.width);
46622         }
46623         if (this.height) {
46624             this.viewEl.setHeight(this.height);
46625         }
46626         //if(this.inputValue !== undefined){
46627         //this.setValue(this.value);
46628         
46629         
46630         this.grid = new Roo.grid[this.xgrid.xtype](this.viewEl, this.xgrid);
46631         
46632         
46633         this.grid.render();
46634         this.grid.getDataSource().on('remove', this.refreshValue, this);
46635         this.grid.getDataSource().on('update', this.refreshValue, this);
46636         this.grid.on('afteredit', this.refreshValue, this);
46637  
46638     },
46639      
46640     
46641     /**
46642      * Sets the value of the item. 
46643      * @param {String} either an object  or a string..
46644      */
46645     setValue : function(v){
46646         //this.value = v;
46647         v = v || []; // empty set..
46648         // this does not seem smart - it really only affects memoryproxy grids..
46649         if (this.grid && this.grid.getDataSource() && typeof(v) != 'undefined') {
46650             var ds = this.grid.getDataSource();
46651             // assumes a json reader..
46652             var data = {}
46653             data[ds.reader.meta.root ] =  typeof(v) == 'string' ? Roo.decode(v) : v;
46654             ds.loadData( data);
46655         }
46656         // clear selection so it does not get stale.
46657         if (this.grid.sm) { 
46658             this.grid.sm.clearSelections();
46659         }
46660         
46661         Roo.form.GridField.superclass.setValue.call(this, v);
46662         this.refreshValue();
46663         // should load data in the grid really....
46664     },
46665     
46666     // private
46667     refreshValue: function() {
46668          var val = [];
46669         this.grid.getDataSource().each(function(r) {
46670             val.push(r.data);
46671         });
46672         this.el.dom.value = Roo.encode(val);
46673     }
46674     
46675      
46676     
46677     
46678 });/*
46679  * Based on:
46680  * Ext JS Library 1.1.1
46681  * Copyright(c) 2006-2007, Ext JS, LLC.
46682  *
46683  * Originally Released Under LGPL - original licence link has changed is not relivant.
46684  *
46685  * Fork - LGPL
46686  * <script type="text/javascript">
46687  */
46688 /**
46689  * @class Roo.form.DisplayField
46690  * @extends Roo.form.Field
46691  * A generic Field to display non-editable data.
46692  * @constructor
46693  * Creates a new Display Field item.
46694  * @param {Object} config Configuration options
46695  */
46696 Roo.form.DisplayField = function(config){
46697     Roo.form.DisplayField.superclass.constructor.call(this, config);
46698     
46699 };
46700
46701 Roo.extend(Roo.form.DisplayField, Roo.form.TextField,  {
46702     inputType:      'hidden',
46703     allowBlank:     true,
46704     readOnly:         true,
46705     
46706  
46707     /**
46708      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
46709      */
46710     focusClass : undefined,
46711     /**
46712      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
46713      */
46714     fieldClass: 'x-form-field',
46715     
46716      /**
46717      * @cfg {Function} valueRenderer The renderer for the field (so you can reformat output). should return raw HTML
46718      */
46719     valueRenderer: undefined,
46720     
46721     width: 100,
46722     /**
46723      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
46724      * {tag: "input", type: "checkbox", autocomplete: "off"})
46725      */
46726      
46727  //   defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
46728
46729     onResize : function(){
46730         Roo.form.DisplayField.superclass.onResize.apply(this, arguments);
46731         
46732     },
46733
46734     initEvents : function(){
46735         // Roo.form.Checkbox.superclass.initEvents.call(this);
46736         // has no events...
46737        
46738     },
46739
46740
46741     getResizeEl : function(){
46742         return this.wrap;
46743     },
46744
46745     getPositionEl : function(){
46746         return this.wrap;
46747     },
46748
46749     // private
46750     onRender : function(ct, position){
46751         
46752         Roo.form.DisplayField.superclass.onRender.call(this, ct, position);
46753         //if(this.inputValue !== undefined){
46754         this.wrap = this.el.wrap();
46755         
46756         this.viewEl = this.wrap.createChild({ tag: 'div', cls: 'x-form-displayfield'});
46757         
46758         if (this.bodyStyle) {
46759             this.viewEl.applyStyles(this.bodyStyle);
46760         }
46761         //this.viewEl.setStyle('padding', '2px');
46762         
46763         this.setValue(this.value);
46764         
46765     },
46766 /*
46767     // private
46768     initValue : Roo.emptyFn,
46769
46770   */
46771
46772         // private
46773     onClick : function(){
46774         
46775     },
46776
46777     /**
46778      * Sets the checked state of the checkbox.
46779      * @param {Boolean/String} checked True, 'true', '1', or 'on' to check the checkbox, any other value will uncheck it.
46780      */
46781     setValue : function(v){
46782         this.value = v;
46783         var html = this.valueRenderer ?  this.valueRenderer(v) : String.format('{0}', v);
46784         // this might be called before we have a dom element..
46785         if (!this.viewEl) {
46786             return;
46787         }
46788         this.viewEl.dom.innerHTML = html;
46789         Roo.form.DisplayField.superclass.setValue.call(this, v);
46790
46791     }
46792 });/*
46793  * 
46794  * Licence- LGPL
46795  * 
46796  */
46797
46798 /**
46799  * @class Roo.form.DayPicker
46800  * @extends Roo.form.Field
46801  * A Day picker show [M] [T] [W] ....
46802  * @constructor
46803  * Creates a new Day Picker
46804  * @param {Object} config Configuration options
46805  */
46806 Roo.form.DayPicker= function(config){
46807     Roo.form.DayPicker.superclass.constructor.call(this, config);
46808      
46809 };
46810
46811 Roo.extend(Roo.form.DayPicker, Roo.form.Field,  {
46812     /**
46813      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
46814      */
46815     focusClass : undefined,
46816     /**
46817      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
46818      */
46819     fieldClass: "x-form-field",
46820    
46821     /**
46822      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
46823      * {tag: "input", type: "checkbox", autocomplete: "off"})
46824      */
46825     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "off"},
46826     
46827    
46828     actionMode : 'viewEl', 
46829     //
46830     // private
46831  
46832     inputType : 'hidden',
46833     
46834      
46835     inputElement: false, // real input element?
46836     basedOn: false, // ????
46837     
46838     isFormField: true, // not sure where this is needed!!!!
46839
46840     onResize : function(){
46841         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
46842         if(!this.boxLabel){
46843             this.el.alignTo(this.wrap, 'c-c');
46844         }
46845     },
46846
46847     initEvents : function(){
46848         Roo.form.Checkbox.superclass.initEvents.call(this);
46849         this.el.on("click", this.onClick,  this);
46850         this.el.on("change", this.onClick,  this);
46851     },
46852
46853
46854     getResizeEl : function(){
46855         return this.wrap;
46856     },
46857
46858     getPositionEl : function(){
46859         return this.wrap;
46860     },
46861
46862     
46863     // private
46864     onRender : function(ct, position){
46865         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
46866        
46867         this.wrap = this.el.wrap({cls: 'x-form-daypick-item '});
46868         
46869         var r1 = '<table><tr>';
46870         var r2 = '<tr class="x-form-daypick-icons">';
46871         for (var i=0; i < 7; i++) {
46872             r1+= '<td><div>' + Date.dayNames[i].substring(0,3) + '</div></td>';
46873             r2+= '<td><img class="x-menu-item-icon" src="' + Roo.BLANK_IMAGE_URL  +'"></td>';
46874         }
46875         
46876         var viewEl = this.wrap.createChild( r1 + '</tr>' + r2 + '</tr></table>');
46877         viewEl.select('img').on('click', this.onClick, this);
46878         this.viewEl = viewEl;   
46879         
46880         
46881         // this will not work on Chrome!!!
46882         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
46883         this.el.on('propertychange', this.setFromHidden,  this);  //ie
46884         
46885         
46886           
46887
46888     },
46889
46890     // private
46891     initValue : Roo.emptyFn,
46892
46893     /**
46894      * Returns the checked state of the checkbox.
46895      * @return {Boolean} True if checked, else false
46896      */
46897     getValue : function(){
46898         return this.el.dom.value;
46899         
46900     },
46901
46902         // private
46903     onClick : function(e){ 
46904         //this.setChecked(!this.checked);
46905         Roo.get(e.target).toggleClass('x-menu-item-checked');
46906         this.refreshValue();
46907         //if(this.el.dom.checked != this.checked){
46908         //    this.setValue(this.el.dom.checked);
46909        // }
46910     },
46911     
46912     // private
46913     refreshValue : function()
46914     {
46915         var val = '';
46916         this.viewEl.select('img',true).each(function(e,i,n)  {
46917             val += e.is(".x-menu-item-checked") ? String(n) : '';
46918         });
46919         this.setValue(val, true);
46920     },
46921
46922     /**
46923      * Sets the checked state of the checkbox.
46924      * On is always based on a string comparison between inputValue and the param.
46925      * @param {Boolean/String} value - the value to set 
46926      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
46927      */
46928     setValue : function(v,suppressEvent){
46929         if (!this.el.dom) {
46930             return;
46931         }
46932         var old = this.el.dom.value ;
46933         this.el.dom.value = v;
46934         if (suppressEvent) {
46935             return ;
46936         }
46937          
46938         // update display..
46939         this.viewEl.select('img',true).each(function(e,i,n)  {
46940             
46941             var on = e.is(".x-menu-item-checked");
46942             var newv = v.indexOf(String(n)) > -1;
46943             if (on != newv) {
46944                 e.toggleClass('x-menu-item-checked');
46945             }
46946             
46947         });
46948         
46949         
46950         this.fireEvent('change', this, v, old);
46951         
46952         
46953     },
46954    
46955     // handle setting of hidden value by some other method!!?!?
46956     setFromHidden: function()
46957     {
46958         if(!this.el){
46959             return;
46960         }
46961         //console.log("SET FROM HIDDEN");
46962         //alert('setFrom hidden');
46963         this.setValue(this.el.dom.value);
46964     },
46965     
46966     onDestroy : function()
46967     {
46968         if(this.viewEl){
46969             Roo.get(this.viewEl).remove();
46970         }
46971          
46972         Roo.form.DayPicker.superclass.onDestroy.call(this);
46973     }
46974
46975 });/*
46976  * RooJS Library 1.1.1
46977  * Copyright(c) 2008-2011  Alan Knowles
46978  *
46979  * License - LGPL
46980  */
46981  
46982
46983 /**
46984  * @class Roo.form.ComboCheck
46985  * @extends Roo.form.ComboBox
46986  * A combobox for multiple select items.
46987  *
46988  * FIXME - could do with a reset button..
46989  * 
46990  * @constructor
46991  * Create a new ComboCheck
46992  * @param {Object} config Configuration options
46993  */
46994 Roo.form.ComboCheck = function(config){
46995     Roo.form.ComboCheck.superclass.constructor.call(this, config);
46996     // should verify some data...
46997     // like
46998     // hiddenName = required..
46999     // displayField = required
47000     // valudField == required
47001     var req= [ 'hiddenName', 'displayField', 'valueField' ];
47002     var _t = this;
47003     Roo.each(req, function(e) {
47004         if ((typeof(_t[e]) == 'undefined' ) || !_t[e].length) {
47005             throw "Roo.form.ComboCheck : missing value for: " + e;
47006         }
47007     });
47008     
47009     
47010 };
47011
47012 Roo.extend(Roo.form.ComboCheck, Roo.form.ComboBox, {
47013      
47014      
47015     editable : false,
47016      
47017     selectedClass: 'x-menu-item-checked', 
47018     
47019     // private
47020     onRender : function(ct, position){
47021         var _t = this;
47022         
47023         
47024         
47025         if(!this.tpl){
47026             var cls = 'x-combo-list';
47027
47028             
47029             this.tpl =  new Roo.Template({
47030                 html :  '<div class="'+cls+'-item x-menu-check-item">' +
47031                    '<img class="x-menu-item-icon" style="margin: 0px;" src="' + Roo.BLANK_IMAGE_URL + '">' + 
47032                    '<span>{' + this.displayField + '}</span>' +
47033                     '</div>' 
47034                 
47035             });
47036         }
47037  
47038         
47039         Roo.form.ComboCheck.superclass.onRender.call(this, ct, position);
47040         this.view.singleSelect = false;
47041         this.view.multiSelect = true;
47042         this.view.toggleSelect = true;
47043         this.pageTb.add(new Roo.Toolbar.Fill(), {
47044             
47045             text: 'Done',
47046             handler: function()
47047             {
47048                 _t.collapse();
47049             }
47050         });
47051     },
47052     
47053     onViewOver : function(e, t){
47054         // do nothing...
47055         return;
47056         
47057     },
47058     
47059     onViewClick : function(doFocus,index){
47060         return;
47061         
47062     },
47063     select: function () {
47064         //Roo.log("SELECT CALLED");
47065     },
47066      
47067     selectByValue : function(xv, scrollIntoView){
47068         var ar = this.getValueArray();
47069         var sels = [];
47070         
47071         Roo.each(ar, function(v) {
47072             if(v === undefined || v === null){
47073                 return;
47074             }
47075             var r = this.findRecord(this.valueField, v);
47076             if(r){
47077                 sels.push(this.store.indexOf(r))
47078                 
47079             }
47080         },this);
47081         this.view.select(sels);
47082         return false;
47083     },
47084     
47085     
47086     
47087     onSelect : function(record, index){
47088        // Roo.log("onselect Called");
47089        // this is only called by the clear button now..
47090         this.view.clearSelections();
47091         this.setValue('[]');
47092         if (this.value != this.valueBefore) {
47093             this.fireEvent('change', this, this.value, this.valueBefore);
47094             this.valueBefore = this.value;
47095         }
47096     },
47097     getValueArray : function()
47098     {
47099         var ar = [] ;
47100         
47101         try {
47102             //Roo.log(this.value);
47103             if (typeof(this.value) == 'undefined') {
47104                 return [];
47105             }
47106             var ar = Roo.decode(this.value);
47107             return  ar instanceof Array ? ar : []; //?? valid?
47108             
47109         } catch(e) {
47110             Roo.log(e + "\nRoo.form.ComboCheck:getValueArray  invalid data:" + this.getValue());
47111             return [];
47112         }
47113          
47114     },
47115     expand : function ()
47116     {
47117         
47118         Roo.form.ComboCheck.superclass.expand.call(this);
47119         this.valueBefore = typeof(this.value) == 'undefined' ? '' : this.value;
47120         //this.valueBefore = typeof(this.valueBefore) == 'undefined' ? '' : this.valueBefore;
47121         
47122
47123     },
47124     
47125     collapse : function(){
47126         Roo.form.ComboCheck.superclass.collapse.call(this);
47127         var sl = this.view.getSelectedIndexes();
47128         var st = this.store;
47129         var nv = [];
47130         var tv = [];
47131         var r;
47132         Roo.each(sl, function(i) {
47133             r = st.getAt(i);
47134             nv.push(r.get(this.valueField));
47135         },this);
47136         this.setValue(Roo.encode(nv));
47137         if (this.value != this.valueBefore) {
47138
47139             this.fireEvent('change', this, this.value, this.valueBefore);
47140             this.valueBefore = this.value;
47141         }
47142         
47143     },
47144     
47145     setValue : function(v){
47146         // Roo.log(v);
47147         this.value = v;
47148         
47149         var vals = this.getValueArray();
47150         var tv = [];
47151         Roo.each(vals, function(k) {
47152             var r = this.findRecord(this.valueField, k);
47153             if(r){
47154                 tv.push(r.data[this.displayField]);
47155             }else if(this.valueNotFoundText !== undefined){
47156                 tv.push( this.valueNotFoundText );
47157             }
47158         },this);
47159        // Roo.log(tv);
47160         
47161         Roo.form.ComboBox.superclass.setValue.call(this, tv.join(', '));
47162         this.hiddenField.value = v;
47163         this.value = v;
47164     }
47165     
47166 });/*
47167  * Based on:
47168  * Ext JS Library 1.1.1
47169  * Copyright(c) 2006-2007, Ext JS, LLC.
47170  *
47171  * Originally Released Under LGPL - original licence link has changed is not relivant.
47172  *
47173  * Fork - LGPL
47174  * <script type="text/javascript">
47175  */
47176  
47177 /**
47178  * @class Roo.form.Signature
47179  * @extends Roo.form.Field
47180  * Signature field.  
47181  * @constructor
47182  * 
47183  * @param {Object} config Configuration options
47184  */
47185
47186 Roo.form.Signature = function(config){
47187     Roo.form.Signature.superclass.constructor.call(this, config);
47188     
47189     this.addEvents({// not in used??
47190          /**
47191          * @event confirm
47192          * Fires when the 'confirm' icon is pressed (add a listener to enable add button)
47193              * @param {Roo.form.Signature} combo This combo box
47194              */
47195         'confirm' : true,
47196         /**
47197          * @event reset
47198          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
47199              * @param {Roo.form.ComboBox} combo This combo box
47200              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
47201              */
47202         'reset' : true
47203     });
47204 };
47205
47206 Roo.extend(Roo.form.Signature, Roo.form.Field,  {
47207     /**
47208      * @cfg {Object} labels Label to use when rendering a form.
47209      * defaults to 
47210      * labels : { 
47211      *      clear : "Clear",
47212      *      confirm : "Confirm"
47213      *  }
47214      */
47215     labels : { 
47216         clear : "Clear",
47217         confirm : "Confirm"
47218     },
47219     /**
47220      * @cfg {Number} width The signature panel width (defaults to 300)
47221      */
47222     width: 300,
47223     /**
47224      * @cfg {Number} height The signature panel height (defaults to 100)
47225      */
47226     height : 100,
47227     /**
47228      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to false)
47229      */
47230     allowBlank : false,
47231     
47232     //private
47233     // {Object} signPanel The signature SVG panel element (defaults to {})
47234     signPanel : {},
47235     // {Boolean} isMouseDown False to validate that the mouse down event (defaults to false)
47236     isMouseDown : false,
47237     // {Boolean} isConfirmed validate the signature is confirmed or not for submitting form (defaults to false)
47238     isConfirmed : false,
47239     // {String} signatureTmp SVG mapping string (defaults to empty string)
47240     signatureTmp : '',
47241     
47242     
47243     defaultAutoCreate : { // modified by initCompnoent..
47244         tag: "input",
47245         type:"hidden"
47246     },
47247
47248     // private
47249     onRender : function(ct, position){
47250         
47251         Roo.form.Signature.superclass.onRender.call(this, ct, position);
47252         
47253         this.wrap = this.el.wrap({
47254             cls:'x-form-signature-wrap', style : 'width: ' + this.width + 'px', cn:{cls:'x-form-signature'}
47255         });
47256         
47257         this.createToolbar(this);
47258         this.signPanel = this.wrap.createChild({
47259                 tag: 'div',
47260                 style: 'width: ' + this.width + 'px; height: ' + this.height + 'px; border: 0;'
47261             }, this.el
47262         );
47263             
47264         this.svgID = Roo.id();
47265         this.svgEl = this.signPanel.createChild({
47266               xmlns : 'http://www.w3.org/2000/svg',
47267               tag : 'svg',
47268               id : this.svgID + "-svg",
47269               width: this.width,
47270               height: this.height,
47271               viewBox: '0 0 '+this.width+' '+this.height,
47272               cn : [
47273                 {
47274                     tag: "rect",
47275                     id: this.svgID + "-svg-r",
47276                     width: this.width,
47277                     height: this.height,
47278                     fill: "#ffa"
47279                 },
47280                 {
47281                     tag: "line",
47282                     id: this.svgID + "-svg-l",
47283                     x1: "0", // start
47284                     y1: (this.height*0.8), // start set the line in 80% of height
47285                     x2: this.width, // end
47286                     y2: (this.height*0.8), // end set the line in 80% of height
47287                     'stroke': "#666",
47288                     'stroke-width': "1",
47289                     'stroke-dasharray': "3",
47290                     'shape-rendering': "crispEdges",
47291                     'pointer-events': "none"
47292                 },
47293                 {
47294                     tag: "path",
47295                     id: this.svgID + "-svg-p",
47296                     'stroke': "navy",
47297                     'stroke-width': "3",
47298                     'fill': "none",
47299                     'pointer-events': 'none'
47300                 }
47301               ]
47302         });
47303         this.createSVG();
47304         this.svgBox = this.svgEl.dom.getScreenCTM();
47305     },
47306     createSVG : function(){ 
47307         var svg = this.signPanel;
47308         var r = svg.select('#'+ this.svgID + '-svg-r', true).first().dom;
47309         var t = this;
47310
47311         r.addEventListener('mousedown', function(e) { return t.down(e); }, false);
47312         r.addEventListener('mousemove', function(e) { return t.move(e); }, false);
47313         r.addEventListener('mouseup', function(e) { return t.up(e); }, false);
47314         r.addEventListener('mouseout', function(e) { return t.up(e); }, false);
47315         r.addEventListener('touchstart', function(e) { return t.down(e); }, false);
47316         r.addEventListener('touchmove', function(e) { return t.move(e); }, false);
47317         r.addEventListener('touchend', function(e) { return t.up(e); }, false);
47318         
47319     },
47320     isTouchEvent : function(e){
47321         return e.type.match(/^touch/);
47322     },
47323     getCoords : function (e) {
47324         var pt    = this.svgEl.dom.createSVGPoint();
47325         pt.x = e.clientX; 
47326         pt.y = e.clientY;
47327         if (this.isTouchEvent(e)) {
47328             pt.x =  e.targetTouches[0].clientX 
47329             pt.y = e.targetTouches[0].clientY;
47330         }
47331         var a = this.svgEl.dom.getScreenCTM();
47332         var b = a.inverse();
47333         var mx = pt.matrixTransform(b);
47334         return mx.x + ',' + mx.y;
47335     },
47336     //mouse event headler 
47337     down : function (e) {
47338         this.signatureTmp += 'M' + this.getCoords(e) + ' ';
47339         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr('d', this.signatureTmp);
47340         
47341         this.isMouseDown = true;
47342         
47343         e.preventDefault();
47344     },
47345     move : function (e) {
47346         if (this.isMouseDown) {
47347             this.signatureTmp += 'L' + this.getCoords(e) + ' ';
47348             this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', this.signatureTmp);
47349         }
47350         
47351         e.preventDefault();
47352     },
47353     up : function (e) {
47354         this.isMouseDown = false;
47355         var sp = this.signatureTmp.split(' ');
47356         
47357         if(sp.length > 1){
47358             if(!sp[sp.length-2].match(/^L/)){
47359                 sp.pop();
47360                 sp.pop();
47361                 sp.push("");
47362                 this.signatureTmp = sp.join(" ");
47363             }
47364         }
47365         if(this.getValue() != this.signatureTmp){
47366             this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
47367             this.isConfirmed = false;
47368         }
47369         e.preventDefault();
47370     },
47371     
47372     /**
47373      * Protected method that will not generally be called directly. It
47374      * is called when the editor creates its toolbar. Override this method if you need to
47375      * add custom toolbar buttons.
47376      * @param {HtmlEditor} editor
47377      */
47378     createToolbar : function(editor){
47379          function btn(id, toggle, handler){
47380             var xid = fid + '-'+ id ;
47381             return {
47382                 id : xid,
47383                 cmd : id,
47384                 cls : 'x-btn-icon x-edit-'+id,
47385                 enableToggle:toggle !== false,
47386                 scope: editor, // was editor...
47387                 handler:handler||editor.relayBtnCmd,
47388                 clickEvent:'mousedown',
47389                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
47390                 tabIndex:-1
47391             };
47392         }
47393         
47394         
47395         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
47396         this.tb = tb;
47397         this.tb.add(
47398            {
47399                 cls : ' x-signature-btn x-signature-'+id,
47400                 scope: editor, // was editor...
47401                 handler: this.reset,
47402                 clickEvent:'mousedown',
47403                 text: this.labels.clear
47404             },
47405             {
47406                  xtype : 'Fill',
47407                  xns: Roo.Toolbar
47408             }, 
47409             {
47410                 cls : '  x-signature-btn x-signature-'+id,
47411                 scope: editor, // was editor...
47412                 handler: this.confirmHandler,
47413                 clickEvent:'mousedown',
47414                 text: this.labels.confirm
47415             }
47416         );
47417     
47418     },
47419     //public
47420     /**
47421      * when user is clicked confirm then show this image.....
47422      * 
47423      * @return {String} Image Data URI
47424      */
47425     getImageDataURI : function(){
47426         var svg = this.svgEl.dom.parentNode.innerHTML;
47427         var src = 'data:image/svg+xml;base64,'+window.btoa(svg);
47428         return src; 
47429     },
47430     /**
47431      * 
47432      * @return {Boolean} this.isConfirmed
47433      */
47434     getConfirmed : function(){
47435         return this.isConfirmed;
47436     },
47437     /**
47438      * 
47439      * @return {Number} this.width
47440      */
47441     getWidth : function(){
47442         return this.width;
47443     },
47444     /**
47445      * 
47446      * @return {Number} this.height
47447      */
47448     getHeight : function(){
47449         return this.height;
47450     },
47451     // private
47452     getSignature : function(){
47453         return this.signatureTmp;
47454     },
47455     // private
47456     reset : function(){
47457         this.signatureTmp = '';
47458         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
47459         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', '');
47460         this.isConfirmed = false;
47461         Roo.form.Signature.superclass.reset.call(this);
47462     },
47463     setSignature : function(s){
47464         this.signatureTmp = s;
47465         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
47466         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', s);
47467         this.setValue(s);
47468         this.isConfirmed = false;
47469         Roo.form.Signature.superclass.reset.call(this);
47470     }, 
47471     test : function(){
47472 //        Roo.log(this.signPanel.dom.contentWindow.up())
47473     },
47474     //private
47475     setConfirmed : function(){
47476         
47477         
47478         
47479 //        Roo.log(Roo.get(this.signPanel.dom.contentWindow.r).attr('fill', '#cfc'));
47480     },
47481     // private
47482     confirmHandler : function(){
47483         if(!this.getSignature()){
47484             return;
47485         }
47486         
47487         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#cfc');
47488         this.setValue(this.getSignature());
47489         this.isConfirmed = true;
47490         
47491         this.fireEvent('confirm', this);
47492     },
47493     // private
47494     // Subclasses should provide the validation implementation by overriding this
47495     validateValue : function(value){
47496         if(this.allowBlank){
47497             return true;
47498         }
47499         
47500         if(this.isConfirmed){
47501             return true;
47502         }
47503         return false;
47504     }
47505 });/*
47506  * Based on:
47507  * Ext JS Library 1.1.1
47508  * Copyright(c) 2006-2007, Ext JS, LLC.
47509  *
47510  * Originally Released Under LGPL - original licence link has changed is not relivant.
47511  *
47512  * Fork - LGPL
47513  * <script type="text/javascript">
47514  */
47515  
47516
47517 /**
47518  * @class Roo.form.ComboBox
47519  * @extends Roo.form.TriggerField
47520  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
47521  * @constructor
47522  * Create a new ComboBox.
47523  * @param {Object} config Configuration options
47524  */
47525 Roo.form.Select = function(config){
47526     Roo.form.Select.superclass.constructor.call(this, config);
47527      
47528 };
47529
47530 Roo.extend(Roo.form.Select , Roo.form.ComboBox, {
47531     /**
47532      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
47533      */
47534     /**
47535      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
47536      * rendering into an Roo.Editor, defaults to false)
47537      */
47538     /**
47539      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
47540      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
47541      */
47542     /**
47543      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
47544      */
47545     /**
47546      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
47547      * the dropdown list (defaults to undefined, with no header element)
47548      */
47549
47550      /**
47551      * @cfg {String/Roo.Template} tpl The template to use to render the output
47552      */
47553      
47554     // private
47555     defaultAutoCreate : {tag: "select"  },
47556     /**
47557      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
47558      */
47559     listWidth: undefined,
47560     /**
47561      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
47562      * mode = 'remote' or 'text' if mode = 'local')
47563      */
47564     displayField: undefined,
47565     /**
47566      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
47567      * mode = 'remote' or 'value' if mode = 'local'). 
47568      * Note: use of a valueField requires the user make a selection
47569      * in order for a value to be mapped.
47570      */
47571     valueField: undefined,
47572     
47573     
47574     /**
47575      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
47576      * field's data value (defaults to the underlying DOM element's name)
47577      */
47578     hiddenName: undefined,
47579     /**
47580      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
47581      */
47582     listClass: '',
47583     /**
47584      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
47585      */
47586     selectedClass: 'x-combo-selected',
47587     /**
47588      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
47589      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
47590      * which displays a downward arrow icon).
47591      */
47592     triggerClass : 'x-form-arrow-trigger',
47593     /**
47594      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
47595      */
47596     shadow:'sides',
47597     /**
47598      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
47599      * anchor positions (defaults to 'tl-bl')
47600      */
47601     listAlign: 'tl-bl?',
47602     /**
47603      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
47604      */
47605     maxHeight: 300,
47606     /**
47607      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
47608      * query specified by the allQuery config option (defaults to 'query')
47609      */
47610     triggerAction: 'query',
47611     /**
47612      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
47613      * (defaults to 4, does not apply if editable = false)
47614      */
47615     minChars : 4,
47616     /**
47617      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
47618      * delay (typeAheadDelay) if it matches a known value (defaults to false)
47619      */
47620     typeAhead: false,
47621     /**
47622      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
47623      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
47624      */
47625     queryDelay: 500,
47626     /**
47627      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
47628      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
47629      */
47630     pageSize: 0,
47631     /**
47632      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
47633      * when editable = true (defaults to false)
47634      */
47635     selectOnFocus:false,
47636     /**
47637      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
47638      */
47639     queryParam: 'query',
47640     /**
47641      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
47642      * when mode = 'remote' (defaults to 'Loading...')
47643      */
47644     loadingText: 'Loading...',
47645     /**
47646      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
47647      */
47648     resizable: false,
47649     /**
47650      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
47651      */
47652     handleHeight : 8,
47653     /**
47654      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
47655      * traditional select (defaults to true)
47656      */
47657     editable: true,
47658     /**
47659      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
47660      */
47661     allQuery: '',
47662     /**
47663      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
47664      */
47665     mode: 'remote',
47666     /**
47667      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
47668      * listWidth has a higher value)
47669      */
47670     minListWidth : 70,
47671     /**
47672      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
47673      * allow the user to set arbitrary text into the field (defaults to false)
47674      */
47675     forceSelection:false,
47676     /**
47677      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
47678      * if typeAhead = true (defaults to 250)
47679      */
47680     typeAheadDelay : 250,
47681     /**
47682      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
47683      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
47684      */
47685     valueNotFoundText : undefined,
47686     
47687     /**
47688      * @cfg {String} defaultValue The value displayed after loading the store.
47689      */
47690     defaultValue: '',
47691     
47692     /**
47693      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
47694      */
47695     blockFocus : false,
47696     
47697     /**
47698      * @cfg {Boolean} disableClear Disable showing of clear button.
47699      */
47700     disableClear : false,
47701     /**
47702      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
47703      */
47704     alwaysQuery : false,
47705     
47706     //private
47707     addicon : false,
47708     editicon: false,
47709     
47710     // element that contains real text value.. (when hidden is used..)
47711      
47712     // private
47713     onRender : function(ct, position){
47714         Roo.form.Field.prototype.onRender.call(this, ct, position);
47715         
47716         if(this.store){
47717             this.store.on('beforeload', this.onBeforeLoad, this);
47718             this.store.on('load', this.onLoad, this);
47719             this.store.on('loadexception', this.onLoadException, this);
47720             this.store.load({});
47721         }
47722         
47723         
47724         
47725     },
47726
47727     // private
47728     initEvents : function(){
47729         //Roo.form.ComboBox.superclass.initEvents.call(this);
47730  
47731     },
47732
47733     onDestroy : function(){
47734        
47735         if(this.store){
47736             this.store.un('beforeload', this.onBeforeLoad, this);
47737             this.store.un('load', this.onLoad, this);
47738             this.store.un('loadexception', this.onLoadException, this);
47739         }
47740         //Roo.form.ComboBox.superclass.onDestroy.call(this);
47741     },
47742
47743     // private
47744     fireKey : function(e){
47745         if(e.isNavKeyPress() && !this.list.isVisible()){
47746             this.fireEvent("specialkey", this, e);
47747         }
47748     },
47749
47750     // private
47751     onResize: function(w, h){
47752         
47753         return; 
47754     
47755         
47756     },
47757
47758     /**
47759      * Allow or prevent the user from directly editing the field text.  If false is passed,
47760      * the user will only be able to select from the items defined in the dropdown list.  This method
47761      * is the runtime equivalent of setting the 'editable' config option at config time.
47762      * @param {Boolean} value True to allow the user to directly edit the field text
47763      */
47764     setEditable : function(value){
47765          
47766     },
47767
47768     // private
47769     onBeforeLoad : function(){
47770         
47771         Roo.log("Select before load");
47772         return;
47773     
47774         this.innerList.update(this.loadingText ?
47775                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
47776         //this.restrictHeight();
47777         this.selectedIndex = -1;
47778     },
47779
47780     // private
47781     onLoad : function(){
47782
47783     
47784         var dom = this.el.dom;
47785         dom.innerHTML = '';
47786          var od = dom.ownerDocument;
47787          
47788         if (this.emptyText) {
47789             var op = od.createElement('option');
47790             op.setAttribute('value', '');
47791             op.innerHTML = String.format('{0}', this.emptyText);
47792             dom.appendChild(op);
47793         }
47794         if(this.store.getCount() > 0){
47795            
47796             var vf = this.valueField;
47797             var df = this.displayField;
47798             this.store.data.each(function(r) {
47799                 // which colmsn to use... testing - cdoe / title..
47800                 var op = od.createElement('option');
47801                 op.setAttribute('value', r.data[vf]);
47802                 op.innerHTML = String.format('{0}', r.data[df]);
47803                 dom.appendChild(op);
47804             });
47805             if (typeof(this.defaultValue != 'undefined')) {
47806                 this.setValue(this.defaultValue);
47807             }
47808             
47809              
47810         }else{
47811             //this.onEmptyResults();
47812         }
47813         //this.el.focus();
47814     },
47815     // private
47816     onLoadException : function()
47817     {
47818         dom.innerHTML = '';
47819             
47820         Roo.log("Select on load exception");
47821         return;
47822     
47823         this.collapse();
47824         Roo.log(this.store.reader.jsonData);
47825         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
47826             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
47827         }
47828         
47829         
47830     },
47831     // private
47832     onTypeAhead : function(){
47833          
47834     },
47835
47836     // private
47837     onSelect : function(record, index){
47838         Roo.log('on select?');
47839         return;
47840         if(this.fireEvent('beforeselect', this, record, index) !== false){
47841             this.setFromData(index > -1 ? record.data : false);
47842             this.collapse();
47843             this.fireEvent('select', this, record, index);
47844         }
47845     },
47846
47847     /**
47848      * Returns the currently selected field value or empty string if no value is set.
47849      * @return {String} value The selected value
47850      */
47851     getValue : function(){
47852         var dom = this.el.dom;
47853         this.value = dom.options[dom.selectedIndex].value;
47854         return this.value;
47855         
47856     },
47857
47858     /**
47859      * Clears any text/value currently set in the field
47860      */
47861     clearValue : function(){
47862         this.value = '';
47863         this.el.dom.selectedIndex = this.emptyText ? 0 : -1;
47864         
47865     },
47866
47867     /**
47868      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
47869      * will be displayed in the field.  If the value does not match the data value of an existing item,
47870      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
47871      * Otherwise the field will be blank (although the value will still be set).
47872      * @param {String} value The value to match
47873      */
47874     setValue : function(v){
47875         var d = this.el.dom;
47876         for (var i =0; i < d.options.length;i++) {
47877             if (v == d.options[i].value) {
47878                 d.selectedIndex = i;
47879                 this.value = v;
47880                 return;
47881             }
47882         }
47883         this.clearValue();
47884     },
47885     /**
47886      * @property {Object} the last set data for the element
47887      */
47888     
47889     lastData : false,
47890     /**
47891      * Sets the value of the field based on a object which is related to the record format for the store.
47892      * @param {Object} value the value to set as. or false on reset?
47893      */
47894     setFromData : function(o){
47895         Roo.log('setfrom data?');
47896          
47897         
47898         
47899     },
47900     // private
47901     reset : function(){
47902         this.clearValue();
47903     },
47904     // private
47905     findRecord : function(prop, value){
47906         
47907         return false;
47908     
47909         var record;
47910         if(this.store.getCount() > 0){
47911             this.store.each(function(r){
47912                 if(r.data[prop] == value){
47913                     record = r;
47914                     return false;
47915                 }
47916                 return true;
47917             });
47918         }
47919         return record;
47920     },
47921     
47922     getName: function()
47923     {
47924         // returns hidden if it's set..
47925         if (!this.rendered) {return ''};
47926         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
47927         
47928     },
47929      
47930
47931     
47932
47933     // private
47934     onEmptyResults : function(){
47935         Roo.log('empty results');
47936         //this.collapse();
47937     },
47938
47939     /**
47940      * Returns true if the dropdown list is expanded, else false.
47941      */
47942     isExpanded : function(){
47943         return false;
47944     },
47945
47946     /**
47947      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
47948      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
47949      * @param {String} value The data value of the item to select
47950      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
47951      * selected item if it is not currently in view (defaults to true)
47952      * @return {Boolean} True if the value matched an item in the list, else false
47953      */
47954     selectByValue : function(v, scrollIntoView){
47955         Roo.log('select By Value');
47956         return false;
47957     
47958         if(v !== undefined && v !== null){
47959             var r = this.findRecord(this.valueField || this.displayField, v);
47960             if(r){
47961                 this.select(this.store.indexOf(r), scrollIntoView);
47962                 return true;
47963             }
47964         }
47965         return false;
47966     },
47967
47968     /**
47969      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
47970      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
47971      * @param {Number} index The zero-based index of the list item to select
47972      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
47973      * selected item if it is not currently in view (defaults to true)
47974      */
47975     select : function(index, scrollIntoView){
47976         Roo.log('select ');
47977         return  ;
47978         
47979         this.selectedIndex = index;
47980         this.view.select(index);
47981         if(scrollIntoView !== false){
47982             var el = this.view.getNode(index);
47983             if(el){
47984                 this.innerList.scrollChildIntoView(el, false);
47985             }
47986         }
47987     },
47988
47989       
47990
47991     // private
47992     validateBlur : function(){
47993         
47994         return;
47995         
47996     },
47997
47998     // private
47999     initQuery : function(){
48000         this.doQuery(this.getRawValue());
48001     },
48002
48003     // private
48004     doForce : function(){
48005         if(this.el.dom.value.length > 0){
48006             this.el.dom.value =
48007                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
48008              
48009         }
48010     },
48011
48012     /**
48013      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
48014      * query allowing the query action to be canceled if needed.
48015      * @param {String} query The SQL query to execute
48016      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
48017      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
48018      * saved in the current store (defaults to false)
48019      */
48020     doQuery : function(q, forceAll){
48021         
48022         Roo.log('doQuery?');
48023         if(q === undefined || q === null){
48024             q = '';
48025         }
48026         var qe = {
48027             query: q,
48028             forceAll: forceAll,
48029             combo: this,
48030             cancel:false
48031         };
48032         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
48033             return false;
48034         }
48035         q = qe.query;
48036         forceAll = qe.forceAll;
48037         if(forceAll === true || (q.length >= this.minChars)){
48038             if(this.lastQuery != q || this.alwaysQuery){
48039                 this.lastQuery = q;
48040                 if(this.mode == 'local'){
48041                     this.selectedIndex = -1;
48042                     if(forceAll){
48043                         this.store.clearFilter();
48044                     }else{
48045                         this.store.filter(this.displayField, q);
48046                     }
48047                     this.onLoad();
48048                 }else{
48049                     this.store.baseParams[this.queryParam] = q;
48050                     this.store.load({
48051                         params: this.getParams(q)
48052                     });
48053                     this.expand();
48054                 }
48055             }else{
48056                 this.selectedIndex = -1;
48057                 this.onLoad();   
48058             }
48059         }
48060     },
48061
48062     // private
48063     getParams : function(q){
48064         var p = {};
48065         //p[this.queryParam] = q;
48066         if(this.pageSize){
48067             p.start = 0;
48068             p.limit = this.pageSize;
48069         }
48070         return p;
48071     },
48072
48073     /**
48074      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
48075      */
48076     collapse : function(){
48077         
48078     },
48079
48080     // private
48081     collapseIf : function(e){
48082         
48083     },
48084
48085     /**
48086      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
48087      */
48088     expand : function(){
48089         
48090     } ,
48091
48092     // private
48093      
48094
48095     /** 
48096     * @cfg {Boolean} grow 
48097     * @hide 
48098     */
48099     /** 
48100     * @cfg {Number} growMin 
48101     * @hide 
48102     */
48103     /** 
48104     * @cfg {Number} growMax 
48105     * @hide 
48106     */
48107     /**
48108      * @hide
48109      * @method autoSize
48110      */
48111     
48112     setWidth : function()
48113     {
48114         
48115     },
48116     getResizeEl : function(){
48117         return this.el;
48118     }
48119 });//<script type="text/javasscript">
48120  
48121
48122 /**
48123  * @class Roo.DDView
48124  * A DnD enabled version of Roo.View.
48125  * @param {Element/String} container The Element in which to create the View.
48126  * @param {String} tpl The template string used to create the markup for each element of the View
48127  * @param {Object} config The configuration properties. These include all the config options of
48128  * {@link Roo.View} plus some specific to this class.<br>
48129  * <p>
48130  * Drag/drop is implemented by adding {@link Roo.data.Record}s to the target DDView. If copying is
48131  * not being performed, the original {@link Roo.data.Record} is removed from the source DDView.<br>
48132  * <p>
48133  * The following extra CSS rules are needed to provide insertion point highlighting:<pre><code>
48134 .x-view-drag-insert-above {
48135         border-top:1px dotted #3366cc;
48136 }
48137 .x-view-drag-insert-below {
48138         border-bottom:1px dotted #3366cc;
48139 }
48140 </code></pre>
48141  * 
48142  */
48143  
48144 Roo.DDView = function(container, tpl, config) {
48145     Roo.DDView.superclass.constructor.apply(this, arguments);
48146     this.getEl().setStyle("outline", "0px none");
48147     this.getEl().unselectable();
48148     if (this.dragGroup) {
48149                 this.setDraggable(this.dragGroup.split(","));
48150     }
48151     if (this.dropGroup) {
48152                 this.setDroppable(this.dropGroup.split(","));
48153     }
48154     if (this.deletable) {
48155         this.setDeletable();
48156     }
48157     this.isDirtyFlag = false;
48158         this.addEvents({
48159                 "drop" : true
48160         });
48161 };
48162
48163 Roo.extend(Roo.DDView, Roo.View, {
48164 /**     @cfg {String/Array} dragGroup The ddgroup name(s) for the View's DragZone. */
48165 /**     @cfg {String/Array} dropGroup The ddgroup name(s) for the View's DropZone. */
48166 /**     @cfg {Boolean} copy Causes drag operations to copy nodes rather than move. */
48167 /**     @cfg {Boolean} allowCopy Causes ctrl/drag operations to copy nodes rather than move. */
48168
48169         isFormField: true,
48170
48171         reset: Roo.emptyFn,
48172         
48173         clearInvalid: Roo.form.Field.prototype.clearInvalid,
48174
48175         validate: function() {
48176                 return true;
48177         },
48178         
48179         destroy: function() {
48180                 this.purgeListeners();
48181                 this.getEl.removeAllListeners();
48182                 this.getEl().remove();
48183                 if (this.dragZone) {
48184                         if (this.dragZone.destroy) {
48185                                 this.dragZone.destroy();
48186                         }
48187                 }
48188                 if (this.dropZone) {
48189                         if (this.dropZone.destroy) {
48190                                 this.dropZone.destroy();
48191                         }
48192                 }
48193         },
48194
48195 /**     Allows this class to be an Roo.form.Field so it can be found using {@link Roo.form.BasicForm#findField}. */
48196         getName: function() {
48197                 return this.name;
48198         },
48199
48200 /**     Loads the View from a JSON string representing the Records to put into the Store. */
48201         setValue: function(v) {
48202                 if (!this.store) {
48203                         throw "DDView.setValue(). DDView must be constructed with a valid Store";
48204                 }
48205                 var data = {};
48206                 data[this.store.reader.meta.root] = v ? [].concat(v) : [];
48207                 this.store.proxy = new Roo.data.MemoryProxy(data);
48208                 this.store.load();
48209         },
48210
48211 /**     @return {String} a parenthesised list of the ids of the Records in the View. */
48212         getValue: function() {
48213                 var result = '(';
48214                 this.store.each(function(rec) {
48215                         result += rec.id + ',';
48216                 });
48217                 return result.substr(0, result.length - 1) + ')';
48218         },
48219         
48220         getIds: function() {
48221                 var i = 0, result = new Array(this.store.getCount());
48222                 this.store.each(function(rec) {
48223                         result[i++] = rec.id;
48224                 });
48225                 return result;
48226         },
48227         
48228         isDirty: function() {
48229                 return this.isDirtyFlag;
48230         },
48231
48232 /**
48233  *      Part of the Roo.dd.DropZone interface. If no target node is found, the
48234  *      whole Element becomes the target, and this causes the drop gesture to append.
48235  */
48236     getTargetFromEvent : function(e) {
48237                 var target = e.getTarget();
48238                 while ((target !== null) && (target.parentNode != this.el.dom)) {
48239                 target = target.parentNode;
48240                 }
48241                 if (!target) {
48242                         target = this.el.dom.lastChild || this.el.dom;
48243                 }
48244                 return target;
48245     },
48246
48247 /**
48248  *      Create the drag data which consists of an object which has the property "ddel" as
48249  *      the drag proxy element. 
48250  */
48251     getDragData : function(e) {
48252         var target = this.findItemFromChild(e.getTarget());
48253                 if(target) {
48254                         this.handleSelection(e);
48255                         var selNodes = this.getSelectedNodes();
48256             var dragData = {
48257                 source: this,
48258                 copy: this.copy || (this.allowCopy && e.ctrlKey),
48259                 nodes: selNodes,
48260                 records: []
48261                         };
48262                         var selectedIndices = this.getSelectedIndexes();
48263                         for (var i = 0; i < selectedIndices.length; i++) {
48264                                 dragData.records.push(this.store.getAt(selectedIndices[i]));
48265                         }
48266                         if (selNodes.length == 1) {
48267                                 dragData.ddel = target.cloneNode(true); // the div element
48268                         } else {
48269                                 var div = document.createElement('div'); // create the multi element drag "ghost"
48270                                 div.className = 'multi-proxy';
48271                                 for (var i = 0, len = selNodes.length; i < len; i++) {
48272                                         div.appendChild(selNodes[i].cloneNode(true));
48273                                 }
48274                                 dragData.ddel = div;
48275                         }
48276             //console.log(dragData)
48277             //console.log(dragData.ddel.innerHTML)
48278                         return dragData;
48279                 }
48280         //console.log('nodragData')
48281                 return false;
48282     },
48283     
48284 /**     Specify to which ddGroup items in this DDView may be dragged. */
48285     setDraggable: function(ddGroup) {
48286         if (ddGroup instanceof Array) {
48287                 Roo.each(ddGroup, this.setDraggable, this);
48288                 return;
48289         }
48290         if (this.dragZone) {
48291                 this.dragZone.addToGroup(ddGroup);
48292         } else {
48293                         this.dragZone = new Roo.dd.DragZone(this.getEl(), {
48294                                 containerScroll: true,
48295                                 ddGroup: ddGroup 
48296
48297                         });
48298 //                      Draggability implies selection. DragZone's mousedown selects the element.
48299                         if (!this.multiSelect) { this.singleSelect = true; }
48300
48301 //                      Wire the DragZone's handlers up to methods in *this*
48302                         this.dragZone.getDragData = this.getDragData.createDelegate(this);
48303                 }
48304     },
48305
48306 /**     Specify from which ddGroup this DDView accepts drops. */
48307     setDroppable: function(ddGroup) {
48308         if (ddGroup instanceof Array) {
48309                 Roo.each(ddGroup, this.setDroppable, this);
48310                 return;
48311         }
48312         if (this.dropZone) {
48313                 this.dropZone.addToGroup(ddGroup);
48314         } else {
48315                         this.dropZone = new Roo.dd.DropZone(this.getEl(), {
48316                                 containerScroll: true,
48317                                 ddGroup: ddGroup
48318                         });
48319
48320 //                      Wire the DropZone's handlers up to methods in *this*
48321                         this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
48322                         this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
48323                         this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
48324                         this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
48325                         this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
48326                 }
48327     },
48328
48329 /**     Decide whether to drop above or below a View node. */
48330     getDropPoint : function(e, n, dd){
48331         if (n == this.el.dom) { return "above"; }
48332                 var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
48333                 var c = t + (b - t) / 2;
48334                 var y = Roo.lib.Event.getPageY(e);
48335                 if(y <= c) {
48336                         return "above";
48337                 }else{
48338                         return "below";
48339                 }
48340     },
48341
48342     onNodeEnter : function(n, dd, e, data){
48343                 return false;
48344     },
48345     
48346     onNodeOver : function(n, dd, e, data){
48347                 var pt = this.getDropPoint(e, n, dd);
48348                 // set the insert point style on the target node
48349                 var dragElClass = this.dropNotAllowed;
48350                 if (pt) {
48351                         var targetElClass;
48352                         if (pt == "above"){
48353                                 dragElClass = n.previousSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-above";
48354                                 targetElClass = "x-view-drag-insert-above";
48355                         } else {
48356                                 dragElClass = n.nextSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-below";
48357                                 targetElClass = "x-view-drag-insert-below";
48358                         }
48359                         if (this.lastInsertClass != targetElClass){
48360                                 Roo.fly(n).replaceClass(this.lastInsertClass, targetElClass);
48361                                 this.lastInsertClass = targetElClass;
48362                         }
48363                 }
48364                 return dragElClass;
48365         },
48366
48367     onNodeOut : function(n, dd, e, data){
48368                 this.removeDropIndicators(n);
48369     },
48370
48371     onNodeDrop : function(n, dd, e, data){
48372         if (this.fireEvent("drop", this, n, dd, e, data) === false) {
48373                 return false;
48374         }
48375         var pt = this.getDropPoint(e, n, dd);
48376                 var insertAt = (n == this.el.dom) ? this.nodes.length : n.nodeIndex;
48377                 if (pt == "below") { insertAt++; }
48378                 for (var i = 0; i < data.records.length; i++) {
48379                         var r = data.records[i];
48380                         var dup = this.store.getById(r.id);
48381                         if (dup && (dd != this.dragZone)) {
48382                                 Roo.fly(this.getNode(this.store.indexOf(dup))).frame("red", 1);
48383                         } else {
48384                                 if (data.copy) {
48385                                         this.store.insert(insertAt++, r.copy());
48386                                 } else {
48387                                         data.source.isDirtyFlag = true;
48388                                         r.store.remove(r);
48389                                         this.store.insert(insertAt++, r);
48390                                 }
48391                                 this.isDirtyFlag = true;
48392                         }
48393                 }
48394                 this.dragZone.cachedTarget = null;
48395                 return true;
48396     },
48397
48398     removeDropIndicators : function(n){
48399                 if(n){
48400                         Roo.fly(n).removeClass([
48401                                 "x-view-drag-insert-above",
48402                                 "x-view-drag-insert-below"]);
48403                         this.lastInsertClass = "_noclass";
48404                 }
48405     },
48406
48407 /**
48408  *      Utility method. Add a delete option to the DDView's context menu.
48409  *      @param {String} imageUrl The URL of the "delete" icon image.
48410  */
48411         setDeletable: function(imageUrl) {
48412                 if (!this.singleSelect && !this.multiSelect) {
48413                         this.singleSelect = true;
48414                 }
48415                 var c = this.getContextMenu();
48416                 this.contextMenu.on("itemclick", function(item) {
48417                         switch (item.id) {
48418                                 case "delete":
48419                                         this.remove(this.getSelectedIndexes());
48420                                         break;
48421                         }
48422                 }, this);
48423                 this.contextMenu.add({
48424                         icon: imageUrl,
48425                         id: "delete",
48426                         text: 'Delete'
48427                 });
48428         },
48429         
48430 /**     Return the context menu for this DDView. */
48431         getContextMenu: function() {
48432                 if (!this.contextMenu) {
48433 //                      Create the View's context menu
48434                         this.contextMenu = new Roo.menu.Menu({
48435                                 id: this.id + "-contextmenu"
48436                         });
48437                         this.el.on("contextmenu", this.showContextMenu, this);
48438                 }
48439                 return this.contextMenu;
48440         },
48441         
48442         disableContextMenu: function() {
48443                 if (this.contextMenu) {
48444                         this.el.un("contextmenu", this.showContextMenu, this);
48445                 }
48446         },
48447
48448         showContextMenu: function(e, item) {
48449         item = this.findItemFromChild(e.getTarget());
48450                 if (item) {
48451                         e.stopEvent();
48452                         this.select(this.getNode(item), this.multiSelect && e.ctrlKey, true);
48453                         this.contextMenu.showAt(e.getXY());
48454             }
48455     },
48456
48457 /**
48458  *      Remove {@link Roo.data.Record}s at the specified indices.
48459  *      @param {Array/Number} selectedIndices The index (or Array of indices) of Records to remove.
48460  */
48461     remove: function(selectedIndices) {
48462                 selectedIndices = [].concat(selectedIndices);
48463                 for (var i = 0; i < selectedIndices.length; i++) {
48464                         var rec = this.store.getAt(selectedIndices[i]);
48465                         this.store.remove(rec);
48466                 }
48467     },
48468
48469 /**
48470  *      Double click fires the event, but also, if this is draggable, and there is only one other
48471  *      related DropZone, it transfers the selected node.
48472  */
48473     onDblClick : function(e){
48474         var item = this.findItemFromChild(e.getTarget());
48475         if(item){
48476             if (this.fireEvent("dblclick", this, this.indexOf(item), item, e) === false) {
48477                 return false;
48478             }
48479             if (this.dragGroup) {
48480                     var targets = Roo.dd.DragDropMgr.getRelated(this.dragZone, true);
48481                     while (targets.indexOf(this.dropZone) > -1) {
48482                             targets.remove(this.dropZone);
48483                                 }
48484                     if (targets.length == 1) {
48485                                         this.dragZone.cachedTarget = null;
48486                         var el = Roo.get(targets[0].getEl());
48487                         var box = el.getBox(true);
48488                         targets[0].onNodeDrop(el.dom, {
48489                                 target: el.dom,
48490                                 xy: [box.x, box.y + box.height - 1]
48491                         }, null, this.getDragData(e));
48492                     }
48493                 }
48494         }
48495     },
48496     
48497     handleSelection: function(e) {
48498                 this.dragZone.cachedTarget = null;
48499         var item = this.findItemFromChild(e.getTarget());
48500         if (!item) {
48501                 this.clearSelections(true);
48502                 return;
48503         }
48504                 if (item && (this.multiSelect || this.singleSelect)){
48505                         if(this.multiSelect && e.shiftKey && (!e.ctrlKey) && this.lastSelection){
48506                                 this.select(this.getNodes(this.indexOf(this.lastSelection), item.nodeIndex), false);
48507                         }else if (this.isSelected(this.getNode(item)) && e.ctrlKey){
48508                                 this.unselect(item);
48509                         } else {
48510                                 this.select(item, this.multiSelect && e.ctrlKey);
48511                                 this.lastSelection = item;
48512                         }
48513                 }
48514     },
48515
48516     onItemClick : function(item, index, e){
48517                 if(this.fireEvent("beforeclick", this, index, item, e) === false){
48518                         return false;
48519                 }
48520                 return true;
48521     },
48522
48523     unselect : function(nodeInfo, suppressEvent){
48524                 var node = this.getNode(nodeInfo);
48525                 if(node && this.isSelected(node)){
48526                         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
48527                                 Roo.fly(node).removeClass(this.selectedClass);
48528                                 this.selections.remove(node);
48529                                 if(!suppressEvent){
48530                                         this.fireEvent("selectionchange", this, this.selections);
48531                                 }
48532                         }
48533                 }
48534     }
48535 });
48536 /*
48537  * Based on:
48538  * Ext JS Library 1.1.1
48539  * Copyright(c) 2006-2007, Ext JS, LLC.
48540  *
48541  * Originally Released Under LGPL - original licence link has changed is not relivant.
48542  *
48543  * Fork - LGPL
48544  * <script type="text/javascript">
48545  */
48546  
48547 /**
48548  * @class Roo.LayoutManager
48549  * @extends Roo.util.Observable
48550  * Base class for layout managers.
48551  */
48552 Roo.LayoutManager = function(container, config){
48553     Roo.LayoutManager.superclass.constructor.call(this);
48554     this.el = Roo.get(container);
48555     // ie scrollbar fix
48556     if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
48557         document.body.scroll = "no";
48558     }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
48559         this.el.position('relative');
48560     }
48561     this.id = this.el.id;
48562     this.el.addClass("x-layout-container");
48563     /** false to disable window resize monitoring @type Boolean */
48564     this.monitorWindowResize = true;
48565     this.regions = {};
48566     this.addEvents({
48567         /**
48568          * @event layout
48569          * Fires when a layout is performed. 
48570          * @param {Roo.LayoutManager} this
48571          */
48572         "layout" : true,
48573         /**
48574          * @event regionresized
48575          * Fires when the user resizes a region. 
48576          * @param {Roo.LayoutRegion} region The resized region
48577          * @param {Number} newSize The new size (width for east/west, height for north/south)
48578          */
48579         "regionresized" : true,
48580         /**
48581          * @event regioncollapsed
48582          * Fires when a region is collapsed. 
48583          * @param {Roo.LayoutRegion} region The collapsed region
48584          */
48585         "regioncollapsed" : true,
48586         /**
48587          * @event regionexpanded
48588          * Fires when a region is expanded.  
48589          * @param {Roo.LayoutRegion} region The expanded region
48590          */
48591         "regionexpanded" : true
48592     });
48593     this.updating = false;
48594     Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
48595 };
48596
48597 Roo.extend(Roo.LayoutManager, Roo.util.Observable, {
48598     /**
48599      * Returns true if this layout is currently being updated
48600      * @return {Boolean}
48601      */
48602     isUpdating : function(){
48603         return this.updating; 
48604     },
48605     
48606     /**
48607      * Suspend the LayoutManager from doing auto-layouts while
48608      * making multiple add or remove calls
48609      */
48610     beginUpdate : function(){
48611         this.updating = true;    
48612     },
48613     
48614     /**
48615      * Restore auto-layouts and optionally disable the manager from performing a layout
48616      * @param {Boolean} noLayout true to disable a layout update 
48617      */
48618     endUpdate : function(noLayout){
48619         this.updating = false;
48620         if(!noLayout){
48621             this.layout();
48622         }    
48623     },
48624     
48625     layout: function(){
48626         
48627     },
48628     
48629     onRegionResized : function(region, newSize){
48630         this.fireEvent("regionresized", region, newSize);
48631         this.layout();
48632     },
48633     
48634     onRegionCollapsed : function(region){
48635         this.fireEvent("regioncollapsed", region);
48636     },
48637     
48638     onRegionExpanded : function(region){
48639         this.fireEvent("regionexpanded", region);
48640     },
48641         
48642     /**
48643      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
48644      * performs box-model adjustments.
48645      * @return {Object} The size as an object {width: (the width), height: (the height)}
48646      */
48647     getViewSize : function(){
48648         var size;
48649         if(this.el.dom != document.body){
48650             size = this.el.getSize();
48651         }else{
48652             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
48653         }
48654         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
48655         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
48656         return size;
48657     },
48658     
48659     /**
48660      * Returns the Element this layout is bound to.
48661      * @return {Roo.Element}
48662      */
48663     getEl : function(){
48664         return this.el;
48665     },
48666     
48667     /**
48668      * Returns the specified region.
48669      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
48670      * @return {Roo.LayoutRegion}
48671      */
48672     getRegion : function(target){
48673         return this.regions[target.toLowerCase()];
48674     },
48675     
48676     onWindowResize : function(){
48677         if(this.monitorWindowResize){
48678             this.layout();
48679         }
48680     }
48681 });/*
48682  * Based on:
48683  * Ext JS Library 1.1.1
48684  * Copyright(c) 2006-2007, Ext JS, LLC.
48685  *
48686  * Originally Released Under LGPL - original licence link has changed is not relivant.
48687  *
48688  * Fork - LGPL
48689  * <script type="text/javascript">
48690  */
48691 /**
48692  * @class Roo.BorderLayout
48693  * @extends Roo.LayoutManager
48694  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
48695  * please see: <br><br>
48696  * <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>
48697  * <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>
48698  * Example:
48699  <pre><code>
48700  var layout = new Roo.BorderLayout(document.body, {
48701     north: {
48702         initialSize: 25,
48703         titlebar: false
48704     },
48705     west: {
48706         split:true,
48707         initialSize: 200,
48708         minSize: 175,
48709         maxSize: 400,
48710         titlebar: true,
48711         collapsible: true
48712     },
48713     east: {
48714         split:true,
48715         initialSize: 202,
48716         minSize: 175,
48717         maxSize: 400,
48718         titlebar: true,
48719         collapsible: true
48720     },
48721     south: {
48722         split:true,
48723         initialSize: 100,
48724         minSize: 100,
48725         maxSize: 200,
48726         titlebar: true,
48727         collapsible: true
48728     },
48729     center: {
48730         titlebar: true,
48731         autoScroll:true,
48732         resizeTabs: true,
48733         minTabWidth: 50,
48734         preferredTabWidth: 150
48735     }
48736 });
48737
48738 // shorthand
48739 var CP = Roo.ContentPanel;
48740
48741 layout.beginUpdate();
48742 layout.add("north", new CP("north", "North"));
48743 layout.add("south", new CP("south", {title: "South", closable: true}));
48744 layout.add("west", new CP("west", {title: "West"}));
48745 layout.add("east", new CP("autoTabs", {title: "Auto Tabs", closable: true}));
48746 layout.add("center", new CP("center1", {title: "Close Me", closable: true}));
48747 layout.add("center", new CP("center2", {title: "Center Panel", closable: false}));
48748 layout.getRegion("center").showPanel("center1");
48749 layout.endUpdate();
48750 </code></pre>
48751
48752 <b>The container the layout is rendered into can be either the body element or any other element.
48753 If it is not the body element, the container needs to either be an absolute positioned element,
48754 or you will need to add "position:relative" to the css of the container.  You will also need to specify
48755 the container size if it is not the body element.</b>
48756
48757 * @constructor
48758 * Create a new BorderLayout
48759 * @param {String/HTMLElement/Element} container The container this layout is bound to
48760 * @param {Object} config Configuration options
48761  */
48762 Roo.BorderLayout = function(container, config){
48763     config = config || {};
48764     Roo.BorderLayout.superclass.constructor.call(this, container, config);
48765     this.factory = config.factory || Roo.BorderLayout.RegionFactory;
48766     for(var i = 0, len = this.factory.validRegions.length; i < len; i++) {
48767         var target = this.factory.validRegions[i];
48768         if(config[target]){
48769             this.addRegion(target, config[target]);
48770         }
48771     }
48772 };
48773
48774 Roo.extend(Roo.BorderLayout, Roo.LayoutManager, {
48775     /**
48776      * Creates and adds a new region if it doesn't already exist.
48777      * @param {String} target The target region key (north, south, east, west or center).
48778      * @param {Object} config The regions config object
48779      * @return {BorderLayoutRegion} The new region
48780      */
48781     addRegion : function(target, config){
48782         if(!this.regions[target]){
48783             var r = this.factory.create(target, this, config);
48784             this.bindRegion(target, r);
48785         }
48786         return this.regions[target];
48787     },
48788
48789     // private (kinda)
48790     bindRegion : function(name, r){
48791         this.regions[name] = r;
48792         r.on("visibilitychange", this.layout, this);
48793         r.on("paneladded", this.layout, this);
48794         r.on("panelremoved", this.layout, this);
48795         r.on("invalidated", this.layout, this);
48796         r.on("resized", this.onRegionResized, this);
48797         r.on("collapsed", this.onRegionCollapsed, this);
48798         r.on("expanded", this.onRegionExpanded, this);
48799     },
48800
48801     /**
48802      * Performs a layout update.
48803      */
48804     layout : function(){
48805         if(this.updating) return;
48806         var size = this.getViewSize();
48807         var w = size.width;
48808         var h = size.height;
48809         var centerW = w;
48810         var centerH = h;
48811         var centerY = 0;
48812         var centerX = 0;
48813         //var x = 0, y = 0;
48814
48815         var rs = this.regions;
48816         var north = rs["north"];
48817         var south = rs["south"]; 
48818         var west = rs["west"];
48819         var east = rs["east"];
48820         var center = rs["center"];
48821         //if(this.hideOnLayout){ // not supported anymore
48822             //c.el.setStyle("display", "none");
48823         //}
48824         if(north && north.isVisible()){
48825             var b = north.getBox();
48826             var m = north.getMargins();
48827             b.width = w - (m.left+m.right);
48828             b.x = m.left;
48829             b.y = m.top;
48830             centerY = b.height + b.y + m.bottom;
48831             centerH -= centerY;
48832             north.updateBox(this.safeBox(b));
48833         }
48834         if(south && south.isVisible()){
48835             var b = south.getBox();
48836             var m = south.getMargins();
48837             b.width = w - (m.left+m.right);
48838             b.x = m.left;
48839             var totalHeight = (b.height + m.top + m.bottom);
48840             b.y = h - totalHeight + m.top;
48841             centerH -= totalHeight;
48842             south.updateBox(this.safeBox(b));
48843         }
48844         if(west && west.isVisible()){
48845             var b = west.getBox();
48846             var m = west.getMargins();
48847             b.height = centerH - (m.top+m.bottom);
48848             b.x = m.left;
48849             b.y = centerY + m.top;
48850             var totalWidth = (b.width + m.left + m.right);
48851             centerX += totalWidth;
48852             centerW -= totalWidth;
48853             west.updateBox(this.safeBox(b));
48854         }
48855         if(east && east.isVisible()){
48856             var b = east.getBox();
48857             var m = east.getMargins();
48858             b.height = centerH - (m.top+m.bottom);
48859             var totalWidth = (b.width + m.left + m.right);
48860             b.x = w - totalWidth + m.left;
48861             b.y = centerY + m.top;
48862             centerW -= totalWidth;
48863             east.updateBox(this.safeBox(b));
48864         }
48865         if(center){
48866             var m = center.getMargins();
48867             var centerBox = {
48868                 x: centerX + m.left,
48869                 y: centerY + m.top,
48870                 width: centerW - (m.left+m.right),
48871                 height: centerH - (m.top+m.bottom)
48872             };
48873             //if(this.hideOnLayout){
48874                 //center.el.setStyle("display", "block");
48875             //}
48876             center.updateBox(this.safeBox(centerBox));
48877         }
48878         this.el.repaint();
48879         this.fireEvent("layout", this);
48880     },
48881
48882     // private
48883     safeBox : function(box){
48884         box.width = Math.max(0, box.width);
48885         box.height = Math.max(0, box.height);
48886         return box;
48887     },
48888
48889     /**
48890      * Adds a ContentPanel (or subclass) to this layout.
48891      * @param {String} target The target region key (north, south, east, west or center).
48892      * @param {Roo.ContentPanel} panel The panel to add
48893      * @return {Roo.ContentPanel} The added panel
48894      */
48895     add : function(target, panel){
48896          
48897         target = target.toLowerCase();
48898         return this.regions[target].add(panel);
48899     },
48900
48901     /**
48902      * Remove a ContentPanel (or subclass) to this layout.
48903      * @param {String} target The target region key (north, south, east, west or center).
48904      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
48905      * @return {Roo.ContentPanel} The removed panel
48906      */
48907     remove : function(target, panel){
48908         target = target.toLowerCase();
48909         return this.regions[target].remove(panel);
48910     },
48911
48912     /**
48913      * Searches all regions for a panel with the specified id
48914      * @param {String} panelId
48915      * @return {Roo.ContentPanel} The panel or null if it wasn't found
48916      */
48917     findPanel : function(panelId){
48918         var rs = this.regions;
48919         for(var target in rs){
48920             if(typeof rs[target] != "function"){
48921                 var p = rs[target].getPanel(panelId);
48922                 if(p){
48923                     return p;
48924                 }
48925             }
48926         }
48927         return null;
48928     },
48929
48930     /**
48931      * Searches all regions for a panel with the specified id and activates (shows) it.
48932      * @param {String/ContentPanel} panelId The panels id or the panel itself
48933      * @return {Roo.ContentPanel} The shown panel or null
48934      */
48935     showPanel : function(panelId) {
48936       var rs = this.regions;
48937       for(var target in rs){
48938          var r = rs[target];
48939          if(typeof r != "function"){
48940             if(r.hasPanel(panelId)){
48941                return r.showPanel(panelId);
48942             }
48943          }
48944       }
48945       return null;
48946    },
48947
48948    /**
48949      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
48950      * @param {Roo.state.Provider} provider (optional) An alternate state provider
48951      */
48952     restoreState : function(provider){
48953         if(!provider){
48954             provider = Roo.state.Manager;
48955         }
48956         var sm = new Roo.LayoutStateManager();
48957         sm.init(this, provider);
48958     },
48959
48960     /**
48961      * Adds a batch of multiple ContentPanels dynamically by passing a special regions config object.  This config
48962      * object should contain properties for each region to add ContentPanels to, and each property's value should be
48963      * a valid ContentPanel config object.  Example:
48964      * <pre><code>
48965 // Create the main layout
48966 var layout = new Roo.BorderLayout('main-ct', {
48967     west: {
48968         split:true,
48969         minSize: 175,
48970         titlebar: true
48971     },
48972     center: {
48973         title:'Components'
48974     }
48975 }, 'main-ct');
48976
48977 // Create and add multiple ContentPanels at once via configs
48978 layout.batchAdd({
48979    west: {
48980        id: 'source-files',
48981        autoCreate:true,
48982        title:'Ext Source Files',
48983        autoScroll:true,
48984        fitToFrame:true
48985    },
48986    center : {
48987        el: cview,
48988        autoScroll:true,
48989        fitToFrame:true,
48990        toolbar: tb,
48991        resizeEl:'cbody'
48992    }
48993 });
48994 </code></pre>
48995      * @param {Object} regions An object containing ContentPanel configs by region name
48996      */
48997     batchAdd : function(regions){
48998         this.beginUpdate();
48999         for(var rname in regions){
49000             var lr = this.regions[rname];
49001             if(lr){
49002                 this.addTypedPanels(lr, regions[rname]);
49003             }
49004         }
49005         this.endUpdate();
49006     },
49007
49008     // private
49009     addTypedPanels : function(lr, ps){
49010         if(typeof ps == 'string'){
49011             lr.add(new Roo.ContentPanel(ps));
49012         }
49013         else if(ps instanceof Array){
49014             for(var i =0, len = ps.length; i < len; i++){
49015                 this.addTypedPanels(lr, ps[i]);
49016             }
49017         }
49018         else if(!ps.events){ // raw config?
49019             var el = ps.el;
49020             delete ps.el; // prevent conflict
49021             lr.add(new Roo.ContentPanel(el || Roo.id(), ps));
49022         }
49023         else {  // panel object assumed!
49024             lr.add(ps);
49025         }
49026     },
49027     /**
49028      * Adds a xtype elements to the layout.
49029      * <pre><code>
49030
49031 layout.addxtype({
49032        xtype : 'ContentPanel',
49033        region: 'west',
49034        items: [ .... ]
49035    }
49036 );
49037
49038 layout.addxtype({
49039         xtype : 'NestedLayoutPanel',
49040         region: 'west',
49041         layout: {
49042            center: { },
49043            west: { }   
49044         },
49045         items : [ ... list of content panels or nested layout panels.. ]
49046    }
49047 );
49048 </code></pre>
49049      * @param {Object} cfg Xtype definition of item to add.
49050      */
49051     addxtype : function(cfg)
49052     {
49053         // basically accepts a pannel...
49054         // can accept a layout region..!?!?
49055         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
49056         
49057         if (!cfg.xtype.match(/Panel$/)) {
49058             return false;
49059         }
49060         var ret = false;
49061         
49062         if (typeof(cfg.region) == 'undefined') {
49063             Roo.log("Failed to add Panel, region was not set");
49064             Roo.log(cfg);
49065             return false;
49066         }
49067         var region = cfg.region;
49068         delete cfg.region;
49069         
49070           
49071         var xitems = [];
49072         if (cfg.items) {
49073             xitems = cfg.items;
49074             delete cfg.items;
49075         }
49076         var nb = false;
49077         
49078         switch(cfg.xtype) 
49079         {
49080             case 'ContentPanel':  // ContentPanel (el, cfg)
49081             case 'ScrollPanel':  // ContentPanel (el, cfg)
49082             case 'ViewPanel': 
49083                 if(cfg.autoCreate) {
49084                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
49085                 } else {
49086                     var el = this.el.createChild();
49087                     ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
49088                 }
49089                 
49090                 this.add(region, ret);
49091                 break;
49092             
49093             
49094             case 'TreePanel': // our new panel!
49095                 cfg.el = this.el.createChild();
49096                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
49097                 this.add(region, ret);
49098                 break;
49099             
49100             case 'NestedLayoutPanel': 
49101                 // create a new Layout (which is  a Border Layout...
49102                 var el = this.el.createChild();
49103                 var clayout = cfg.layout;
49104                 delete cfg.layout;
49105                 clayout.items   = clayout.items  || [];
49106                 // replace this exitems with the clayout ones..
49107                 xitems = clayout.items;
49108                  
49109                 
49110                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
49111                     cfg.background = false;
49112                 }
49113                 var layout = new Roo.BorderLayout(el, clayout);
49114                 
49115                 ret = new Roo[cfg.xtype](layout, cfg); // new panel!!!!!
49116                 //console.log('adding nested layout panel '  + cfg.toSource());
49117                 this.add(region, ret);
49118                 nb = {}; /// find first...
49119                 break;
49120                 
49121             case 'GridPanel': 
49122             
49123                 // needs grid and region
49124                 
49125                 //var el = this.getRegion(region).el.createChild();
49126                 var el = this.el.createChild();
49127                 // create the grid first...
49128                 
49129                 var grid = new Roo.grid[cfg.grid.xtype](el, cfg.grid);
49130                 delete cfg.grid;
49131                 if (region == 'center' && this.active ) {
49132                     cfg.background = false;
49133                 }
49134                 ret = new Roo[cfg.xtype](grid, cfg); // new panel!!!!!
49135                 
49136                 this.add(region, ret);
49137                 if (cfg.background) {
49138                     ret.on('activate', function(gp) {
49139                         if (!gp.grid.rendered) {
49140                             gp.grid.render();
49141                         }
49142                     });
49143                 } else {
49144                     grid.render();
49145                 }
49146                 break;
49147            
49148            
49149            
49150                 
49151                 
49152                 
49153             default:
49154                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
49155                     
49156                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
49157                     this.add(region, ret);
49158                 } else {
49159                 
49160                     alert("Can not add '" + cfg.xtype + "' to BorderLayout");
49161                     return null;
49162                 }
49163                 
49164              // GridPanel (grid, cfg)
49165             
49166         }
49167         this.beginUpdate();
49168         // add children..
49169         var region = '';
49170         var abn = {};
49171         Roo.each(xitems, function(i)  {
49172             region = nb && i.region ? i.region : false;
49173             
49174             var add = ret.addxtype(i);
49175            
49176             if (region) {
49177                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
49178                 if (!i.background) {
49179                     abn[region] = nb[region] ;
49180                 }
49181             }
49182             
49183         });
49184         this.endUpdate();
49185
49186         // make the last non-background panel active..
49187         //if (nb) { Roo.log(abn); }
49188         if (nb) {
49189             
49190             for(var r in abn) {
49191                 region = this.getRegion(r);
49192                 if (region) {
49193                     // tried using nb[r], but it does not work..
49194                      
49195                     region.showPanel(abn[r]);
49196                    
49197                 }
49198             }
49199         }
49200         return ret;
49201         
49202     }
49203 });
49204
49205 /**
49206  * Shortcut for creating a new BorderLayout object and adding one or more ContentPanels to it in a single step, handling
49207  * the beginUpdate and endUpdate calls internally.  The key to this method is the <b>panels</b> property that can be
49208  * provided with each region config, which allows you to add ContentPanel configs in addition to the region configs
49209  * during creation.  The following code is equivalent to the constructor-based example at the beginning of this class:
49210  * <pre><code>
49211 // shorthand
49212 var CP = Roo.ContentPanel;
49213
49214 var layout = Roo.BorderLayout.create({
49215     north: {
49216         initialSize: 25,
49217         titlebar: false,
49218         panels: [new CP("north", "North")]
49219     },
49220     west: {
49221         split:true,
49222         initialSize: 200,
49223         minSize: 175,
49224         maxSize: 400,
49225         titlebar: true,
49226         collapsible: true,
49227         panels: [new CP("west", {title: "West"})]
49228     },
49229     east: {
49230         split:true,
49231         initialSize: 202,
49232         minSize: 175,
49233         maxSize: 400,
49234         titlebar: true,
49235         collapsible: true,
49236         panels: [new CP("autoTabs", {title: "Auto Tabs", closable: true})]
49237     },
49238     south: {
49239         split:true,
49240         initialSize: 100,
49241         minSize: 100,
49242         maxSize: 200,
49243         titlebar: true,
49244         collapsible: true,
49245         panels: [new CP("south", {title: "South", closable: true})]
49246     },
49247     center: {
49248         titlebar: true,
49249         autoScroll:true,
49250         resizeTabs: true,
49251         minTabWidth: 50,
49252         preferredTabWidth: 150,
49253         panels: [
49254             new CP("center1", {title: "Close Me", closable: true}),
49255             new CP("center2", {title: "Center Panel", closable: false})
49256         ]
49257     }
49258 }, document.body);
49259
49260 layout.getRegion("center").showPanel("center1");
49261 </code></pre>
49262  * @param config
49263  * @param targetEl
49264  */
49265 Roo.BorderLayout.create = function(config, targetEl){
49266     var layout = new Roo.BorderLayout(targetEl || document.body, config);
49267     layout.beginUpdate();
49268     var regions = Roo.BorderLayout.RegionFactory.validRegions;
49269     for(var j = 0, jlen = regions.length; j < jlen; j++){
49270         var lr = regions[j];
49271         if(layout.regions[lr] && config[lr].panels){
49272             var r = layout.regions[lr];
49273             var ps = config[lr].panels;
49274             layout.addTypedPanels(r, ps);
49275         }
49276     }
49277     layout.endUpdate();
49278     return layout;
49279 };
49280
49281 // private
49282 Roo.BorderLayout.RegionFactory = {
49283     // private
49284     validRegions : ["north","south","east","west","center"],
49285
49286     // private
49287     create : function(target, mgr, config){
49288         target = target.toLowerCase();
49289         if(config.lightweight || config.basic){
49290             return new Roo.BasicLayoutRegion(mgr, config, target);
49291         }
49292         switch(target){
49293             case "north":
49294                 return new Roo.NorthLayoutRegion(mgr, config);
49295             case "south":
49296                 return new Roo.SouthLayoutRegion(mgr, config);
49297             case "east":
49298                 return new Roo.EastLayoutRegion(mgr, config);
49299             case "west":
49300                 return new Roo.WestLayoutRegion(mgr, config);
49301             case "center":
49302                 return new Roo.CenterLayoutRegion(mgr, config);
49303         }
49304         throw 'Layout region "'+target+'" not supported.';
49305     }
49306 };/*
49307  * Based on:
49308  * Ext JS Library 1.1.1
49309  * Copyright(c) 2006-2007, Ext JS, LLC.
49310  *
49311  * Originally Released Under LGPL - original licence link has changed is not relivant.
49312  *
49313  * Fork - LGPL
49314  * <script type="text/javascript">
49315  */
49316  
49317 /**
49318  * @class Roo.BasicLayoutRegion
49319  * @extends Roo.util.Observable
49320  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
49321  * and does not have a titlebar, tabs or any other features. All it does is size and position 
49322  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
49323  */
49324 Roo.BasicLayoutRegion = function(mgr, config, pos, skipConfig){
49325     this.mgr = mgr;
49326     this.position  = pos;
49327     this.events = {
49328         /**
49329          * @scope Roo.BasicLayoutRegion
49330          */
49331         
49332         /**
49333          * @event beforeremove
49334          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
49335          * @param {Roo.LayoutRegion} this
49336          * @param {Roo.ContentPanel} panel The panel
49337          * @param {Object} e The cancel event object
49338          */
49339         "beforeremove" : true,
49340         /**
49341          * @event invalidated
49342          * Fires when the layout for this region is changed.
49343          * @param {Roo.LayoutRegion} this
49344          */
49345         "invalidated" : true,
49346         /**
49347          * @event visibilitychange
49348          * Fires when this region is shown or hidden 
49349          * @param {Roo.LayoutRegion} this
49350          * @param {Boolean} visibility true or false
49351          */
49352         "visibilitychange" : true,
49353         /**
49354          * @event paneladded
49355          * Fires when a panel is added. 
49356          * @param {Roo.LayoutRegion} this
49357          * @param {Roo.ContentPanel} panel The panel
49358          */
49359         "paneladded" : true,
49360         /**
49361          * @event panelremoved
49362          * Fires when a panel is removed. 
49363          * @param {Roo.LayoutRegion} this
49364          * @param {Roo.ContentPanel} panel The panel
49365          */
49366         "panelremoved" : true,
49367         /**
49368          * @event collapsed
49369          * Fires when this region is collapsed.
49370          * @param {Roo.LayoutRegion} this
49371          */
49372         "collapsed" : true,
49373         /**
49374          * @event expanded
49375          * Fires when this region is expanded.
49376          * @param {Roo.LayoutRegion} this
49377          */
49378         "expanded" : true,
49379         /**
49380          * @event slideshow
49381          * Fires when this region is slid into view.
49382          * @param {Roo.LayoutRegion} this
49383          */
49384         "slideshow" : true,
49385         /**
49386          * @event slidehide
49387          * Fires when this region slides out of view. 
49388          * @param {Roo.LayoutRegion} this
49389          */
49390         "slidehide" : true,
49391         /**
49392          * @event panelactivated
49393          * Fires when a panel is activated. 
49394          * @param {Roo.LayoutRegion} this
49395          * @param {Roo.ContentPanel} panel The activated panel
49396          */
49397         "panelactivated" : true,
49398         /**
49399          * @event resized
49400          * Fires when the user resizes this region. 
49401          * @param {Roo.LayoutRegion} this
49402          * @param {Number} newSize The new size (width for east/west, height for north/south)
49403          */
49404         "resized" : true
49405     };
49406     /** A collection of panels in this region. @type Roo.util.MixedCollection */
49407     this.panels = new Roo.util.MixedCollection();
49408     this.panels.getKey = this.getPanelId.createDelegate(this);
49409     this.box = null;
49410     this.activePanel = null;
49411     // ensure listeners are added...
49412     
49413     if (config.listeners || config.events) {
49414         Roo.BasicLayoutRegion.superclass.constructor.call(this, {
49415             listeners : config.listeners || {},
49416             events : config.events || {}
49417         });
49418     }
49419     
49420     if(skipConfig !== true){
49421         this.applyConfig(config);
49422     }
49423 };
49424
49425 Roo.extend(Roo.BasicLayoutRegion, Roo.util.Observable, {
49426     getPanelId : function(p){
49427         return p.getId();
49428     },
49429     
49430     applyConfig : function(config){
49431         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
49432         this.config = config;
49433         
49434     },
49435     
49436     /**
49437      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
49438      * the width, for horizontal (north, south) the height.
49439      * @param {Number} newSize The new width or height
49440      */
49441     resizeTo : function(newSize){
49442         var el = this.el ? this.el :
49443                  (this.activePanel ? this.activePanel.getEl() : null);
49444         if(el){
49445             switch(this.position){
49446                 case "east":
49447                 case "west":
49448                     el.setWidth(newSize);
49449                     this.fireEvent("resized", this, newSize);
49450                 break;
49451                 case "north":
49452                 case "south":
49453                     el.setHeight(newSize);
49454                     this.fireEvent("resized", this, newSize);
49455                 break;                
49456             }
49457         }
49458     },
49459     
49460     getBox : function(){
49461         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
49462     },
49463     
49464     getMargins : function(){
49465         return this.margins;
49466     },
49467     
49468     updateBox : function(box){
49469         this.box = box;
49470         var el = this.activePanel.getEl();
49471         el.dom.style.left = box.x + "px";
49472         el.dom.style.top = box.y + "px";
49473         this.activePanel.setSize(box.width, box.height);
49474     },
49475     
49476     /**
49477      * Returns the container element for this region.
49478      * @return {Roo.Element}
49479      */
49480     getEl : function(){
49481         return this.activePanel;
49482     },
49483     
49484     /**
49485      * Returns true if this region is currently visible.
49486      * @return {Boolean}
49487      */
49488     isVisible : function(){
49489         return this.activePanel ? true : false;
49490     },
49491     
49492     setActivePanel : function(panel){
49493         panel = this.getPanel(panel);
49494         if(this.activePanel && this.activePanel != panel){
49495             this.activePanel.setActiveState(false);
49496             this.activePanel.getEl().setLeftTop(-10000,-10000);
49497         }
49498         this.activePanel = panel;
49499         panel.setActiveState(true);
49500         if(this.box){
49501             panel.setSize(this.box.width, this.box.height);
49502         }
49503         this.fireEvent("panelactivated", this, panel);
49504         this.fireEvent("invalidated");
49505     },
49506     
49507     /**
49508      * Show the specified panel.
49509      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
49510      * @return {Roo.ContentPanel} The shown panel or null
49511      */
49512     showPanel : function(panel){
49513         if(panel = this.getPanel(panel)){
49514             this.setActivePanel(panel);
49515         }
49516         return panel;
49517     },
49518     
49519     /**
49520      * Get the active panel for this region.
49521      * @return {Roo.ContentPanel} The active panel or null
49522      */
49523     getActivePanel : function(){
49524         return this.activePanel;
49525     },
49526     
49527     /**
49528      * Add the passed ContentPanel(s)
49529      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
49530      * @return {Roo.ContentPanel} The panel added (if only one was added)
49531      */
49532     add : function(panel){
49533         if(arguments.length > 1){
49534             for(var i = 0, len = arguments.length; i < len; i++) {
49535                 this.add(arguments[i]);
49536             }
49537             return null;
49538         }
49539         if(this.hasPanel(panel)){
49540             this.showPanel(panel);
49541             return panel;
49542         }
49543         var el = panel.getEl();
49544         if(el.dom.parentNode != this.mgr.el.dom){
49545             this.mgr.el.dom.appendChild(el.dom);
49546         }
49547         if(panel.setRegion){
49548             panel.setRegion(this);
49549         }
49550         this.panels.add(panel);
49551         el.setStyle("position", "absolute");
49552         if(!panel.background){
49553             this.setActivePanel(panel);
49554             if(this.config.initialSize && this.panels.getCount()==1){
49555                 this.resizeTo(this.config.initialSize);
49556             }
49557         }
49558         this.fireEvent("paneladded", this, panel);
49559         return panel;
49560     },
49561     
49562     /**
49563      * Returns true if the panel is in this region.
49564      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
49565      * @return {Boolean}
49566      */
49567     hasPanel : function(panel){
49568         if(typeof panel == "object"){ // must be panel obj
49569             panel = panel.getId();
49570         }
49571         return this.getPanel(panel) ? true : false;
49572     },
49573     
49574     /**
49575      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
49576      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
49577      * @param {Boolean} preservePanel Overrides the config preservePanel option
49578      * @return {Roo.ContentPanel} The panel that was removed
49579      */
49580     remove : function(panel, preservePanel){
49581         panel = this.getPanel(panel);
49582         if(!panel){
49583             return null;
49584         }
49585         var e = {};
49586         this.fireEvent("beforeremove", this, panel, e);
49587         if(e.cancel === true){
49588             return null;
49589         }
49590         var panelId = panel.getId();
49591         this.panels.removeKey(panelId);
49592         return panel;
49593     },
49594     
49595     /**
49596      * Returns the panel specified or null if it's not in this region.
49597      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
49598      * @return {Roo.ContentPanel}
49599      */
49600     getPanel : function(id){
49601         if(typeof id == "object"){ // must be panel obj
49602             return id;
49603         }
49604         return this.panels.get(id);
49605     },
49606     
49607     /**
49608      * Returns this regions position (north/south/east/west/center).
49609      * @return {String} 
49610      */
49611     getPosition: function(){
49612         return this.position;    
49613     }
49614 });/*
49615  * Based on:
49616  * Ext JS Library 1.1.1
49617  * Copyright(c) 2006-2007, Ext JS, LLC.
49618  *
49619  * Originally Released Under LGPL - original licence link has changed is not relivant.
49620  *
49621  * Fork - LGPL
49622  * <script type="text/javascript">
49623  */
49624  
49625 /**
49626  * @class Roo.LayoutRegion
49627  * @extends Roo.BasicLayoutRegion
49628  * This class represents a region in a layout manager.
49629  * @cfg {Boolean}   collapsible     False to disable collapsing (defaults to true)
49630  * @cfg {Boolean}   collapsed       True to set the initial display to collapsed (defaults to false)
49631  * @cfg {Boolean}   floatable       False to disable floating (defaults to true)
49632  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
49633  * @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})
49634  * @cfg {String}    tabPosition     "top" or "bottom" (defaults to "bottom")
49635  * @cfg {String}    collapsedTitle  Optional string message to display in the collapsed block of a north or south region
49636  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
49637  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
49638  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
49639  * @cfg {String}    title           The title for the region (overrides panel titles)
49640  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
49641  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
49642  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
49643  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
49644  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
49645  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
49646  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
49647  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
49648  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
49649  * @cfg {Boolean}   showPin         True to show a pin button
49650  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
49651  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
49652  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
49653  * @cfg {Number}    width           For East/West panels
49654  * @cfg {Number}    height          For North/South panels
49655  * @cfg {Boolean}   split           To show the splitter
49656  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
49657  */
49658 Roo.LayoutRegion = function(mgr, config, pos){
49659     Roo.LayoutRegion.superclass.constructor.call(this, mgr, config, pos, true);
49660     var dh = Roo.DomHelper;
49661     /** This region's container element 
49662     * @type Roo.Element */
49663     this.el = dh.append(mgr.el.dom, {tag: "div", cls: "x-layout-panel x-layout-panel-" + this.position}, true);
49664     /** This region's title element 
49665     * @type Roo.Element */
49666
49667     this.titleEl = dh.append(this.el.dom, {tag: "div", unselectable: "on", cls: "x-unselectable x-layout-panel-hd x-layout-title-"+this.position, children:[
49668         {tag: "span", cls: "x-unselectable x-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
49669         {tag: "div", cls: "x-unselectable x-layout-panel-hd-tools", unselectable: "on"}
49670     ]}, true);
49671     this.titleEl.enableDisplayMode();
49672     /** This region's title text element 
49673     * @type HTMLElement */
49674     this.titleTextEl = this.titleEl.dom.firstChild;
49675     this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
49676     this.closeBtn = this.createTool(this.tools.dom, "x-layout-close");
49677     this.closeBtn.enableDisplayMode();
49678     this.closeBtn.on("click", this.closeClicked, this);
49679     this.closeBtn.hide();
49680
49681     this.createBody(config);
49682     this.visible = true;
49683     this.collapsed = false;
49684
49685     if(config.hideWhenEmpty){
49686         this.hide();
49687         this.on("paneladded", this.validateVisibility, this);
49688         this.on("panelremoved", this.validateVisibility, this);
49689     }
49690     this.applyConfig(config);
49691 };
49692
49693 Roo.extend(Roo.LayoutRegion, Roo.BasicLayoutRegion, {
49694
49695     createBody : function(){
49696         /** This region's body element 
49697         * @type Roo.Element */
49698         this.bodyEl = this.el.createChild({tag: "div", cls: "x-layout-panel-body"});
49699     },
49700
49701     applyConfig : function(c){
49702         if(c.collapsible && this.position != "center" && !this.collapsedEl){
49703             var dh = Roo.DomHelper;
49704             if(c.titlebar !== false){
49705                 this.collapseBtn = this.createTool(this.tools.dom, "x-layout-collapse-"+this.position);
49706                 this.collapseBtn.on("click", this.collapse, this);
49707                 this.collapseBtn.enableDisplayMode();
49708
49709                 if(c.showPin === true || this.showPin){
49710                     this.stickBtn = this.createTool(this.tools.dom, "x-layout-stick");
49711                     this.stickBtn.enableDisplayMode();
49712                     this.stickBtn.on("click", this.expand, this);
49713                     this.stickBtn.hide();
49714                 }
49715             }
49716             /** This region's collapsed element
49717             * @type Roo.Element */
49718             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
49719                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
49720             ]}, true);
49721             if(c.floatable !== false){
49722                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
49723                this.collapsedEl.on("click", this.collapseClick, this);
49724             }
49725
49726             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
49727                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
49728                    id: "message", unselectable: "on", style:{"float":"left"}});
49729                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
49730              }
49731             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
49732             this.expandBtn.on("click", this.expand, this);
49733         }
49734         if(this.collapseBtn){
49735             this.collapseBtn.setVisible(c.collapsible == true);
49736         }
49737         this.cmargins = c.cmargins || this.cmargins ||
49738                          (this.position == "west" || this.position == "east" ?
49739                              {top: 0, left: 2, right:2, bottom: 0} :
49740                              {top: 2, left: 0, right:0, bottom: 2});
49741         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
49742         this.bottomTabs = c.tabPosition != "top";
49743         this.autoScroll = c.autoScroll || false;
49744         if(this.autoScroll){
49745             this.bodyEl.setStyle("overflow", "auto");
49746         }else{
49747             this.bodyEl.setStyle("overflow", "hidden");
49748         }
49749         //if(c.titlebar !== false){
49750             if((!c.titlebar && !c.title) || c.titlebar === false){
49751                 this.titleEl.hide();
49752             }else{
49753                 this.titleEl.show();
49754                 if(c.title){
49755                     this.titleTextEl.innerHTML = c.title;
49756                 }
49757             }
49758         //}
49759         this.duration = c.duration || .30;
49760         this.slideDuration = c.slideDuration || .45;
49761         this.config = c;
49762         if(c.collapsed){
49763             this.collapse(true);
49764         }
49765         if(c.hidden){
49766             this.hide();
49767         }
49768     },
49769     /**
49770      * Returns true if this region is currently visible.
49771      * @return {Boolean}
49772      */
49773     isVisible : function(){
49774         return this.visible;
49775     },
49776
49777     /**
49778      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
49779      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
49780      */
49781     setCollapsedTitle : function(title){
49782         title = title || "&#160;";
49783         if(this.collapsedTitleTextEl){
49784             this.collapsedTitleTextEl.innerHTML = title;
49785         }
49786     },
49787
49788     getBox : function(){
49789         var b;
49790         if(!this.collapsed){
49791             b = this.el.getBox(false, true);
49792         }else{
49793             b = this.collapsedEl.getBox(false, true);
49794         }
49795         return b;
49796     },
49797
49798     getMargins : function(){
49799         return this.collapsed ? this.cmargins : this.margins;
49800     },
49801
49802     highlight : function(){
49803         this.el.addClass("x-layout-panel-dragover");
49804     },
49805
49806     unhighlight : function(){
49807         this.el.removeClass("x-layout-panel-dragover");
49808     },
49809
49810     updateBox : function(box){
49811         this.box = box;
49812         if(!this.collapsed){
49813             this.el.dom.style.left = box.x + "px";
49814             this.el.dom.style.top = box.y + "px";
49815             this.updateBody(box.width, box.height);
49816         }else{
49817             this.collapsedEl.dom.style.left = box.x + "px";
49818             this.collapsedEl.dom.style.top = box.y + "px";
49819             this.collapsedEl.setSize(box.width, box.height);
49820         }
49821         if(this.tabs){
49822             this.tabs.autoSizeTabs();
49823         }
49824     },
49825
49826     updateBody : function(w, h){
49827         if(w !== null){
49828             this.el.setWidth(w);
49829             w -= this.el.getBorderWidth("rl");
49830             if(this.config.adjustments){
49831                 w += this.config.adjustments[0];
49832             }
49833         }
49834         if(h !== null){
49835             this.el.setHeight(h);
49836             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
49837             h -= this.el.getBorderWidth("tb");
49838             if(this.config.adjustments){
49839                 h += this.config.adjustments[1];
49840             }
49841             this.bodyEl.setHeight(h);
49842             if(this.tabs){
49843                 h = this.tabs.syncHeight(h);
49844             }
49845         }
49846         if(this.panelSize){
49847             w = w !== null ? w : this.panelSize.width;
49848             h = h !== null ? h : this.panelSize.height;
49849         }
49850         if(this.activePanel){
49851             var el = this.activePanel.getEl();
49852             w = w !== null ? w : el.getWidth();
49853             h = h !== null ? h : el.getHeight();
49854             this.panelSize = {width: w, height: h};
49855             this.activePanel.setSize(w, h);
49856         }
49857         if(Roo.isIE && this.tabs){
49858             this.tabs.el.repaint();
49859         }
49860     },
49861
49862     /**
49863      * Returns the container element for this region.
49864      * @return {Roo.Element}
49865      */
49866     getEl : function(){
49867         return this.el;
49868     },
49869
49870     /**
49871      * Hides this region.
49872      */
49873     hide : function(){
49874         if(!this.collapsed){
49875             this.el.dom.style.left = "-2000px";
49876             this.el.hide();
49877         }else{
49878             this.collapsedEl.dom.style.left = "-2000px";
49879             this.collapsedEl.hide();
49880         }
49881         this.visible = false;
49882         this.fireEvent("visibilitychange", this, false);
49883     },
49884
49885     /**
49886      * Shows this region if it was previously hidden.
49887      */
49888     show : function(){
49889         if(!this.collapsed){
49890             this.el.show();
49891         }else{
49892             this.collapsedEl.show();
49893         }
49894         this.visible = true;
49895         this.fireEvent("visibilitychange", this, true);
49896     },
49897
49898     closeClicked : function(){
49899         if(this.activePanel){
49900             this.remove(this.activePanel);
49901         }
49902     },
49903
49904     collapseClick : function(e){
49905         if(this.isSlid){
49906            e.stopPropagation();
49907            this.slideIn();
49908         }else{
49909            e.stopPropagation();
49910            this.slideOut();
49911         }
49912     },
49913
49914     /**
49915      * Collapses this region.
49916      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
49917      */
49918     collapse : function(skipAnim){
49919         if(this.collapsed) return;
49920         this.collapsed = true;
49921         if(this.split){
49922             this.split.el.hide();
49923         }
49924         if(this.config.animate && skipAnim !== true){
49925             this.fireEvent("invalidated", this);
49926             this.animateCollapse();
49927         }else{
49928             this.el.setLocation(-20000,-20000);
49929             this.el.hide();
49930             this.collapsedEl.show();
49931             this.fireEvent("collapsed", this);
49932             this.fireEvent("invalidated", this);
49933         }
49934     },
49935
49936     animateCollapse : function(){
49937         // overridden
49938     },
49939
49940     /**
49941      * Expands this region if it was previously collapsed.
49942      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
49943      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
49944      */
49945     expand : function(e, skipAnim){
49946         if(e) e.stopPropagation();
49947         if(!this.collapsed || this.el.hasActiveFx()) return;
49948         if(this.isSlid){
49949             this.afterSlideIn();
49950             skipAnim = true;
49951         }
49952         this.collapsed = false;
49953         if(this.config.animate && skipAnim !== true){
49954             this.animateExpand();
49955         }else{
49956             this.el.show();
49957             if(this.split){
49958                 this.split.el.show();
49959             }
49960             this.collapsedEl.setLocation(-2000,-2000);
49961             this.collapsedEl.hide();
49962             this.fireEvent("invalidated", this);
49963             this.fireEvent("expanded", this);
49964         }
49965     },
49966
49967     animateExpand : function(){
49968         // overridden
49969     },
49970
49971     initTabs : function()
49972     {
49973         this.bodyEl.setStyle("overflow", "hidden");
49974         var ts = new Roo.TabPanel(
49975                 this.bodyEl.dom,
49976                 {
49977                     tabPosition: this.bottomTabs ? 'bottom' : 'top',
49978                     disableTooltips: this.config.disableTabTips,
49979                     toolbar : this.config.toolbar
49980                 }
49981         );
49982         if(this.config.hideTabs){
49983             ts.stripWrap.setDisplayed(false);
49984         }
49985         this.tabs = ts;
49986         ts.resizeTabs = this.config.resizeTabs === true;
49987         ts.minTabWidth = this.config.minTabWidth || 40;
49988         ts.maxTabWidth = this.config.maxTabWidth || 250;
49989         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
49990         ts.monitorResize = false;
49991         ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
49992         ts.bodyEl.addClass('x-layout-tabs-body');
49993         this.panels.each(this.initPanelAsTab, this);
49994     },
49995
49996     initPanelAsTab : function(panel){
49997         var ti = this.tabs.addTab(panel.getEl().id, panel.getTitle(), null,
49998                     this.config.closeOnTab && panel.isClosable());
49999         if(panel.tabTip !== undefined){
50000             ti.setTooltip(panel.tabTip);
50001         }
50002         ti.on("activate", function(){
50003               this.setActivePanel(panel);
50004         }, this);
50005         if(this.config.closeOnTab){
50006             ti.on("beforeclose", function(t, e){
50007                 e.cancel = true;
50008                 this.remove(panel);
50009             }, this);
50010         }
50011         return ti;
50012     },
50013
50014     updatePanelTitle : function(panel, title){
50015         if(this.activePanel == panel){
50016             this.updateTitle(title);
50017         }
50018         if(this.tabs){
50019             var ti = this.tabs.getTab(panel.getEl().id);
50020             ti.setText(title);
50021             if(panel.tabTip !== undefined){
50022                 ti.setTooltip(panel.tabTip);
50023             }
50024         }
50025     },
50026
50027     updateTitle : function(title){
50028         if(this.titleTextEl && !this.config.title){
50029             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
50030         }
50031     },
50032
50033     setActivePanel : function(panel){
50034         panel = this.getPanel(panel);
50035         if(this.activePanel && this.activePanel != panel){
50036             this.activePanel.setActiveState(false);
50037         }
50038         this.activePanel = panel;
50039         panel.setActiveState(true);
50040         if(this.panelSize){
50041             panel.setSize(this.panelSize.width, this.panelSize.height);
50042         }
50043         if(this.closeBtn){
50044             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
50045         }
50046         this.updateTitle(panel.getTitle());
50047         if(this.tabs){
50048             this.fireEvent("invalidated", this);
50049         }
50050         this.fireEvent("panelactivated", this, panel);
50051     },
50052
50053     /**
50054      * Shows the specified panel.
50055      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
50056      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
50057      */
50058     showPanel : function(panel){
50059         if(panel = this.getPanel(panel)){
50060             if(this.tabs){
50061                 var tab = this.tabs.getTab(panel.getEl().id);
50062                 if(tab.isHidden()){
50063                     this.tabs.unhideTab(tab.id);
50064                 }
50065                 tab.activate();
50066             }else{
50067                 this.setActivePanel(panel);
50068             }
50069         }
50070         return panel;
50071     },
50072
50073     /**
50074      * Get the active panel for this region.
50075      * @return {Roo.ContentPanel} The active panel or null
50076      */
50077     getActivePanel : function(){
50078         return this.activePanel;
50079     },
50080
50081     validateVisibility : function(){
50082         if(this.panels.getCount() < 1){
50083             this.updateTitle("&#160;");
50084             this.closeBtn.hide();
50085             this.hide();
50086         }else{
50087             if(!this.isVisible()){
50088                 this.show();
50089             }
50090         }
50091     },
50092
50093     /**
50094      * Adds the passed ContentPanel(s) to this region.
50095      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
50096      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
50097      */
50098     add : function(panel){
50099         if(arguments.length > 1){
50100             for(var i = 0, len = arguments.length; i < len; i++) {
50101                 this.add(arguments[i]);
50102             }
50103             return null;
50104         }
50105         if(this.hasPanel(panel)){
50106             this.showPanel(panel);
50107             return panel;
50108         }
50109         panel.setRegion(this);
50110         this.panels.add(panel);
50111         if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
50112             this.bodyEl.dom.appendChild(panel.getEl().dom);
50113             if(panel.background !== true){
50114                 this.setActivePanel(panel);
50115             }
50116             this.fireEvent("paneladded", this, panel);
50117             return panel;
50118         }
50119         if(!this.tabs){
50120             this.initTabs();
50121         }else{
50122             this.initPanelAsTab(panel);
50123         }
50124         if(panel.background !== true){
50125             this.tabs.activate(panel.getEl().id);
50126         }
50127         this.fireEvent("paneladded", this, panel);
50128         return panel;
50129     },
50130
50131     /**
50132      * Hides the tab for the specified panel.
50133      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
50134      */
50135     hidePanel : function(panel){
50136         if(this.tabs && (panel = this.getPanel(panel))){
50137             this.tabs.hideTab(panel.getEl().id);
50138         }
50139     },
50140
50141     /**
50142      * Unhides the tab for a previously hidden panel.
50143      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
50144      */
50145     unhidePanel : function(panel){
50146         if(this.tabs && (panel = this.getPanel(panel))){
50147             this.tabs.unhideTab(panel.getEl().id);
50148         }
50149     },
50150
50151     clearPanels : function(){
50152         while(this.panels.getCount() > 0){
50153              this.remove(this.panels.first());
50154         }
50155     },
50156
50157     /**
50158      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
50159      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
50160      * @param {Boolean} preservePanel Overrides the config preservePanel option
50161      * @return {Roo.ContentPanel} The panel that was removed
50162      */
50163     remove : function(panel, preservePanel){
50164         panel = this.getPanel(panel);
50165         if(!panel){
50166             return null;
50167         }
50168         var e = {};
50169         this.fireEvent("beforeremove", this, panel, e);
50170         if(e.cancel === true){
50171             return null;
50172         }
50173         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
50174         var panelId = panel.getId();
50175         this.panels.removeKey(panelId);
50176         if(preservePanel){
50177             document.body.appendChild(panel.getEl().dom);
50178         }
50179         if(this.tabs){
50180             this.tabs.removeTab(panel.getEl().id);
50181         }else if (!preservePanel){
50182             this.bodyEl.dom.removeChild(panel.getEl().dom);
50183         }
50184         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
50185             var p = this.panels.first();
50186             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
50187             tempEl.appendChild(p.getEl().dom);
50188             this.bodyEl.update("");
50189             this.bodyEl.dom.appendChild(p.getEl().dom);
50190             tempEl = null;
50191             this.updateTitle(p.getTitle());
50192             this.tabs = null;
50193             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
50194             this.setActivePanel(p);
50195         }
50196         panel.setRegion(null);
50197         if(this.activePanel == panel){
50198             this.activePanel = null;
50199         }
50200         if(this.config.autoDestroy !== false && preservePanel !== true){
50201             try{panel.destroy();}catch(e){}
50202         }
50203         this.fireEvent("panelremoved", this, panel);
50204         return panel;
50205     },
50206
50207     /**
50208      * Returns the TabPanel component used by this region
50209      * @return {Roo.TabPanel}
50210      */
50211     getTabs : function(){
50212         return this.tabs;
50213     },
50214
50215     createTool : function(parentEl, className){
50216         var btn = Roo.DomHelper.append(parentEl, {tag: "div", cls: "x-layout-tools-button",
50217             children: [{tag: "div", cls: "x-layout-tools-button-inner " + className, html: "&#160;"}]}, true);
50218         btn.addClassOnOver("x-layout-tools-button-over");
50219         return btn;
50220     }
50221 });/*
50222  * Based on:
50223  * Ext JS Library 1.1.1
50224  * Copyright(c) 2006-2007, Ext JS, LLC.
50225  *
50226  * Originally Released Under LGPL - original licence link has changed is not relivant.
50227  *
50228  * Fork - LGPL
50229  * <script type="text/javascript">
50230  */
50231  
50232
50233
50234 /**
50235  * @class Roo.SplitLayoutRegion
50236  * @extends Roo.LayoutRegion
50237  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
50238  */
50239 Roo.SplitLayoutRegion = function(mgr, config, pos, cursor){
50240     this.cursor = cursor;
50241     Roo.SplitLayoutRegion.superclass.constructor.call(this, mgr, config, pos);
50242 };
50243
50244 Roo.extend(Roo.SplitLayoutRegion, Roo.LayoutRegion, {
50245     splitTip : "Drag to resize.",
50246     collapsibleSplitTip : "Drag to resize. Double click to hide.",
50247     useSplitTips : false,
50248
50249     applyConfig : function(config){
50250         Roo.SplitLayoutRegion.superclass.applyConfig.call(this, config);
50251         if(config.split){
50252             if(!this.split){
50253                 var splitEl = Roo.DomHelper.append(this.mgr.el.dom, 
50254                         {tag: "div", id: this.el.id + "-split", cls: "x-layout-split x-layout-split-"+this.position, html: "&#160;"});
50255                 /** The SplitBar for this region 
50256                 * @type Roo.SplitBar */
50257                 this.split = new Roo.SplitBar(splitEl, this.el, this.orientation);
50258                 this.split.on("moved", this.onSplitMove, this);
50259                 this.split.useShim = config.useShim === true;
50260                 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
50261                 if(this.useSplitTips){
50262                     this.split.el.dom.title = config.collapsible ? this.collapsibleSplitTip : this.splitTip;
50263                 }
50264                 if(config.collapsible){
50265                     this.split.el.on("dblclick", this.collapse,  this);
50266                 }
50267             }
50268             if(typeof config.minSize != "undefined"){
50269                 this.split.minSize = config.minSize;
50270             }
50271             if(typeof config.maxSize != "undefined"){
50272                 this.split.maxSize = config.maxSize;
50273             }
50274             if(config.hideWhenEmpty || config.hidden || config.collapsed){
50275                 this.hideSplitter();
50276             }
50277         }
50278     },
50279
50280     getHMaxSize : function(){
50281          var cmax = this.config.maxSize || 10000;
50282          var center = this.mgr.getRegion("center");
50283          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
50284     },
50285
50286     getVMaxSize : function(){
50287          var cmax = this.config.maxSize || 10000;
50288          var center = this.mgr.getRegion("center");
50289          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
50290     },
50291
50292     onSplitMove : function(split, newSize){
50293         this.fireEvent("resized", this, newSize);
50294     },
50295     
50296     /** 
50297      * Returns the {@link Roo.SplitBar} for this region.
50298      * @return {Roo.SplitBar}
50299      */
50300     getSplitBar : function(){
50301         return this.split;
50302     },
50303     
50304     hide : function(){
50305         this.hideSplitter();
50306         Roo.SplitLayoutRegion.superclass.hide.call(this);
50307     },
50308
50309     hideSplitter : function(){
50310         if(this.split){
50311             this.split.el.setLocation(-2000,-2000);
50312             this.split.el.hide();
50313         }
50314     },
50315
50316     show : function(){
50317         if(this.split){
50318             this.split.el.show();
50319         }
50320         Roo.SplitLayoutRegion.superclass.show.call(this);
50321     },
50322     
50323     beforeSlide: function(){
50324         if(Roo.isGecko){// firefox overflow auto bug workaround
50325             this.bodyEl.clip();
50326             if(this.tabs) this.tabs.bodyEl.clip();
50327             if(this.activePanel){
50328                 this.activePanel.getEl().clip();
50329                 
50330                 if(this.activePanel.beforeSlide){
50331                     this.activePanel.beforeSlide();
50332                 }
50333             }
50334         }
50335     },
50336     
50337     afterSlide : function(){
50338         if(Roo.isGecko){// firefox overflow auto bug workaround
50339             this.bodyEl.unclip();
50340             if(this.tabs) this.tabs.bodyEl.unclip();
50341             if(this.activePanel){
50342                 this.activePanel.getEl().unclip();
50343                 if(this.activePanel.afterSlide){
50344                     this.activePanel.afterSlide();
50345                 }
50346             }
50347         }
50348     },
50349
50350     initAutoHide : function(){
50351         if(this.autoHide !== false){
50352             if(!this.autoHideHd){
50353                 var st = new Roo.util.DelayedTask(this.slideIn, this);
50354                 this.autoHideHd = {
50355                     "mouseout": function(e){
50356                         if(!e.within(this.el, true)){
50357                             st.delay(500);
50358                         }
50359                     },
50360                     "mouseover" : function(e){
50361                         st.cancel();
50362                     },
50363                     scope : this
50364                 };
50365             }
50366             this.el.on(this.autoHideHd);
50367         }
50368     },
50369
50370     clearAutoHide : function(){
50371         if(this.autoHide !== false){
50372             this.el.un("mouseout", this.autoHideHd.mouseout);
50373             this.el.un("mouseover", this.autoHideHd.mouseover);
50374         }
50375     },
50376
50377     clearMonitor : function(){
50378         Roo.get(document).un("click", this.slideInIf, this);
50379     },
50380
50381     // these names are backwards but not changed for compat
50382     slideOut : function(){
50383         if(this.isSlid || this.el.hasActiveFx()){
50384             return;
50385         }
50386         this.isSlid = true;
50387         if(this.collapseBtn){
50388             this.collapseBtn.hide();
50389         }
50390         this.closeBtnState = this.closeBtn.getStyle('display');
50391         this.closeBtn.hide();
50392         if(this.stickBtn){
50393             this.stickBtn.show();
50394         }
50395         this.el.show();
50396         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
50397         this.beforeSlide();
50398         this.el.setStyle("z-index", 10001);
50399         this.el.slideIn(this.getSlideAnchor(), {
50400             callback: function(){
50401                 this.afterSlide();
50402                 this.initAutoHide();
50403                 Roo.get(document).on("click", this.slideInIf, this);
50404                 this.fireEvent("slideshow", this);
50405             },
50406             scope: this,
50407             block: true
50408         });
50409     },
50410
50411     afterSlideIn : function(){
50412         this.clearAutoHide();
50413         this.isSlid = false;
50414         this.clearMonitor();
50415         this.el.setStyle("z-index", "");
50416         if(this.collapseBtn){
50417             this.collapseBtn.show();
50418         }
50419         this.closeBtn.setStyle('display', this.closeBtnState);
50420         if(this.stickBtn){
50421             this.stickBtn.hide();
50422         }
50423         this.fireEvent("slidehide", this);
50424     },
50425
50426     slideIn : function(cb){
50427         if(!this.isSlid || this.el.hasActiveFx()){
50428             Roo.callback(cb);
50429             return;
50430         }
50431         this.isSlid = false;
50432         this.beforeSlide();
50433         this.el.slideOut(this.getSlideAnchor(), {
50434             callback: function(){
50435                 this.el.setLeftTop(-10000, -10000);
50436                 this.afterSlide();
50437                 this.afterSlideIn();
50438                 Roo.callback(cb);
50439             },
50440             scope: this,
50441             block: true
50442         });
50443     },
50444     
50445     slideInIf : function(e){
50446         if(!e.within(this.el)){
50447             this.slideIn();
50448         }
50449     },
50450
50451     animateCollapse : function(){
50452         this.beforeSlide();
50453         this.el.setStyle("z-index", 20000);
50454         var anchor = this.getSlideAnchor();
50455         this.el.slideOut(anchor, {
50456             callback : function(){
50457                 this.el.setStyle("z-index", "");
50458                 this.collapsedEl.slideIn(anchor, {duration:.3});
50459                 this.afterSlide();
50460                 this.el.setLocation(-10000,-10000);
50461                 this.el.hide();
50462                 this.fireEvent("collapsed", this);
50463             },
50464             scope: this,
50465             block: true
50466         });
50467     },
50468
50469     animateExpand : function(){
50470         this.beforeSlide();
50471         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
50472         this.el.setStyle("z-index", 20000);
50473         this.collapsedEl.hide({
50474             duration:.1
50475         });
50476         this.el.slideIn(this.getSlideAnchor(), {
50477             callback : function(){
50478                 this.el.setStyle("z-index", "");
50479                 this.afterSlide();
50480                 if(this.split){
50481                     this.split.el.show();
50482                 }
50483                 this.fireEvent("invalidated", this);
50484                 this.fireEvent("expanded", this);
50485             },
50486             scope: this,
50487             block: true
50488         });
50489     },
50490
50491     anchors : {
50492         "west" : "left",
50493         "east" : "right",
50494         "north" : "top",
50495         "south" : "bottom"
50496     },
50497
50498     sanchors : {
50499         "west" : "l",
50500         "east" : "r",
50501         "north" : "t",
50502         "south" : "b"
50503     },
50504
50505     canchors : {
50506         "west" : "tl-tr",
50507         "east" : "tr-tl",
50508         "north" : "tl-bl",
50509         "south" : "bl-tl"
50510     },
50511
50512     getAnchor : function(){
50513         return this.anchors[this.position];
50514     },
50515
50516     getCollapseAnchor : function(){
50517         return this.canchors[this.position];
50518     },
50519
50520     getSlideAnchor : function(){
50521         return this.sanchors[this.position];
50522     },
50523
50524     getAlignAdj : function(){
50525         var cm = this.cmargins;
50526         switch(this.position){
50527             case "west":
50528                 return [0, 0];
50529             break;
50530             case "east":
50531                 return [0, 0];
50532             break;
50533             case "north":
50534                 return [0, 0];
50535             break;
50536             case "south":
50537                 return [0, 0];
50538             break;
50539         }
50540     },
50541
50542     getExpandAdj : function(){
50543         var c = this.collapsedEl, cm = this.cmargins;
50544         switch(this.position){
50545             case "west":
50546                 return [-(cm.right+c.getWidth()+cm.left), 0];
50547             break;
50548             case "east":
50549                 return [cm.right+c.getWidth()+cm.left, 0];
50550             break;
50551             case "north":
50552                 return [0, -(cm.top+cm.bottom+c.getHeight())];
50553             break;
50554             case "south":
50555                 return [0, cm.top+cm.bottom+c.getHeight()];
50556             break;
50557         }
50558     }
50559 });/*
50560  * Based on:
50561  * Ext JS Library 1.1.1
50562  * Copyright(c) 2006-2007, Ext JS, LLC.
50563  *
50564  * Originally Released Under LGPL - original licence link has changed is not relivant.
50565  *
50566  * Fork - LGPL
50567  * <script type="text/javascript">
50568  */
50569 /*
50570  * These classes are private internal classes
50571  */
50572 Roo.CenterLayoutRegion = function(mgr, config){
50573     Roo.LayoutRegion.call(this, mgr, config, "center");
50574     this.visible = true;
50575     this.minWidth = config.minWidth || 20;
50576     this.minHeight = config.minHeight || 20;
50577 };
50578
50579 Roo.extend(Roo.CenterLayoutRegion, Roo.LayoutRegion, {
50580     hide : function(){
50581         // center panel can't be hidden
50582     },
50583     
50584     show : function(){
50585         // center panel can't be hidden
50586     },
50587     
50588     getMinWidth: function(){
50589         return this.minWidth;
50590     },
50591     
50592     getMinHeight: function(){
50593         return this.minHeight;
50594     }
50595 });
50596
50597
50598 Roo.NorthLayoutRegion = function(mgr, config){
50599     Roo.LayoutRegion.call(this, mgr, config, "north", "n-resize");
50600     if(this.split){
50601         this.split.placement = Roo.SplitBar.TOP;
50602         this.split.orientation = Roo.SplitBar.VERTICAL;
50603         this.split.el.addClass("x-layout-split-v");
50604     }
50605     var size = config.initialSize || config.height;
50606     if(typeof size != "undefined"){
50607         this.el.setHeight(size);
50608     }
50609 };
50610 Roo.extend(Roo.NorthLayoutRegion, Roo.SplitLayoutRegion, {
50611     orientation: Roo.SplitBar.VERTICAL,
50612     getBox : function(){
50613         if(this.collapsed){
50614             return this.collapsedEl.getBox();
50615         }
50616         var box = this.el.getBox();
50617         if(this.split){
50618             box.height += this.split.el.getHeight();
50619         }
50620         return box;
50621     },
50622     
50623     updateBox : function(box){
50624         if(this.split && !this.collapsed){
50625             box.height -= this.split.el.getHeight();
50626             this.split.el.setLeft(box.x);
50627             this.split.el.setTop(box.y+box.height);
50628             this.split.el.setWidth(box.width);
50629         }
50630         if(this.collapsed){
50631             this.updateBody(box.width, null);
50632         }
50633         Roo.LayoutRegion.prototype.updateBox.call(this, box);
50634     }
50635 });
50636
50637 Roo.SouthLayoutRegion = function(mgr, config){
50638     Roo.SplitLayoutRegion.call(this, mgr, config, "south", "s-resize");
50639     if(this.split){
50640         this.split.placement = Roo.SplitBar.BOTTOM;
50641         this.split.orientation = Roo.SplitBar.VERTICAL;
50642         this.split.el.addClass("x-layout-split-v");
50643     }
50644     var size = config.initialSize || config.height;
50645     if(typeof size != "undefined"){
50646         this.el.setHeight(size);
50647     }
50648 };
50649 Roo.extend(Roo.SouthLayoutRegion, Roo.SplitLayoutRegion, {
50650     orientation: Roo.SplitBar.VERTICAL,
50651     getBox : function(){
50652         if(this.collapsed){
50653             return this.collapsedEl.getBox();
50654         }
50655         var box = this.el.getBox();
50656         if(this.split){
50657             var sh = this.split.el.getHeight();
50658             box.height += sh;
50659             box.y -= sh;
50660         }
50661         return box;
50662     },
50663     
50664     updateBox : function(box){
50665         if(this.split && !this.collapsed){
50666             var sh = this.split.el.getHeight();
50667             box.height -= sh;
50668             box.y += sh;
50669             this.split.el.setLeft(box.x);
50670             this.split.el.setTop(box.y-sh);
50671             this.split.el.setWidth(box.width);
50672         }
50673         if(this.collapsed){
50674             this.updateBody(box.width, null);
50675         }
50676         Roo.LayoutRegion.prototype.updateBox.call(this, box);
50677     }
50678 });
50679
50680 Roo.EastLayoutRegion = function(mgr, config){
50681     Roo.SplitLayoutRegion.call(this, mgr, config, "east", "e-resize");
50682     if(this.split){
50683         this.split.placement = Roo.SplitBar.RIGHT;
50684         this.split.orientation = Roo.SplitBar.HORIZONTAL;
50685         this.split.el.addClass("x-layout-split-h");
50686     }
50687     var size = config.initialSize || config.width;
50688     if(typeof size != "undefined"){
50689         this.el.setWidth(size);
50690     }
50691 };
50692 Roo.extend(Roo.EastLayoutRegion, Roo.SplitLayoutRegion, {
50693     orientation: Roo.SplitBar.HORIZONTAL,
50694     getBox : function(){
50695         if(this.collapsed){
50696             return this.collapsedEl.getBox();
50697         }
50698         var box = this.el.getBox();
50699         if(this.split){
50700             var sw = this.split.el.getWidth();
50701             box.width += sw;
50702             box.x -= sw;
50703         }
50704         return box;
50705     },
50706
50707     updateBox : function(box){
50708         if(this.split && !this.collapsed){
50709             var sw = this.split.el.getWidth();
50710             box.width -= sw;
50711             this.split.el.setLeft(box.x);
50712             this.split.el.setTop(box.y);
50713             this.split.el.setHeight(box.height);
50714             box.x += sw;
50715         }
50716         if(this.collapsed){
50717             this.updateBody(null, box.height);
50718         }
50719         Roo.LayoutRegion.prototype.updateBox.call(this, box);
50720     }
50721 });
50722
50723 Roo.WestLayoutRegion = function(mgr, config){
50724     Roo.SplitLayoutRegion.call(this, mgr, config, "west", "w-resize");
50725     if(this.split){
50726         this.split.placement = Roo.SplitBar.LEFT;
50727         this.split.orientation = Roo.SplitBar.HORIZONTAL;
50728         this.split.el.addClass("x-layout-split-h");
50729     }
50730     var size = config.initialSize || config.width;
50731     if(typeof size != "undefined"){
50732         this.el.setWidth(size);
50733     }
50734 };
50735 Roo.extend(Roo.WestLayoutRegion, Roo.SplitLayoutRegion, {
50736     orientation: Roo.SplitBar.HORIZONTAL,
50737     getBox : function(){
50738         if(this.collapsed){
50739             return this.collapsedEl.getBox();
50740         }
50741         var box = this.el.getBox();
50742         if(this.split){
50743             box.width += this.split.el.getWidth();
50744         }
50745         return box;
50746     },
50747     
50748     updateBox : function(box){
50749         if(this.split && !this.collapsed){
50750             var sw = this.split.el.getWidth();
50751             box.width -= sw;
50752             this.split.el.setLeft(box.x+box.width);
50753             this.split.el.setTop(box.y);
50754             this.split.el.setHeight(box.height);
50755         }
50756         if(this.collapsed){
50757             this.updateBody(null, box.height);
50758         }
50759         Roo.LayoutRegion.prototype.updateBox.call(this, box);
50760     }
50761 });
50762 /*
50763  * Based on:
50764  * Ext JS Library 1.1.1
50765  * Copyright(c) 2006-2007, Ext JS, LLC.
50766  *
50767  * Originally Released Under LGPL - original licence link has changed is not relivant.
50768  *
50769  * Fork - LGPL
50770  * <script type="text/javascript">
50771  */
50772  
50773  
50774 /*
50775  * Private internal class for reading and applying state
50776  */
50777 Roo.LayoutStateManager = function(layout){
50778      // default empty state
50779      this.state = {
50780         north: {},
50781         south: {},
50782         east: {},
50783         west: {}       
50784     };
50785 };
50786
50787 Roo.LayoutStateManager.prototype = {
50788     init : function(layout, provider){
50789         this.provider = provider;
50790         var state = provider.get(layout.id+"-layout-state");
50791         if(state){
50792             var wasUpdating = layout.isUpdating();
50793             if(!wasUpdating){
50794                 layout.beginUpdate();
50795             }
50796             for(var key in state){
50797                 if(typeof state[key] != "function"){
50798                     var rstate = state[key];
50799                     var r = layout.getRegion(key);
50800                     if(r && rstate){
50801                         if(rstate.size){
50802                             r.resizeTo(rstate.size);
50803                         }
50804                         if(rstate.collapsed == true){
50805                             r.collapse(true);
50806                         }else{
50807                             r.expand(null, true);
50808                         }
50809                     }
50810                 }
50811             }
50812             if(!wasUpdating){
50813                 layout.endUpdate();
50814             }
50815             this.state = state; 
50816         }
50817         this.layout = layout;
50818         layout.on("regionresized", this.onRegionResized, this);
50819         layout.on("regioncollapsed", this.onRegionCollapsed, this);
50820         layout.on("regionexpanded", this.onRegionExpanded, this);
50821     },
50822     
50823     storeState : function(){
50824         this.provider.set(this.layout.id+"-layout-state", this.state);
50825     },
50826     
50827     onRegionResized : function(region, newSize){
50828         this.state[region.getPosition()].size = newSize;
50829         this.storeState();
50830     },
50831     
50832     onRegionCollapsed : function(region){
50833         this.state[region.getPosition()].collapsed = true;
50834         this.storeState();
50835     },
50836     
50837     onRegionExpanded : function(region){
50838         this.state[region.getPosition()].collapsed = false;
50839         this.storeState();
50840     }
50841 };/*
50842  * Based on:
50843  * Ext JS Library 1.1.1
50844  * Copyright(c) 2006-2007, Ext JS, LLC.
50845  *
50846  * Originally Released Under LGPL - original licence link has changed is not relivant.
50847  *
50848  * Fork - LGPL
50849  * <script type="text/javascript">
50850  */
50851 /**
50852  * @class Roo.ContentPanel
50853  * @extends Roo.util.Observable
50854  * A basic ContentPanel element.
50855  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
50856  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
50857  * @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
50858  * @cfg {Boolean}   closable      True if the panel can be closed/removed
50859  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
50860  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
50861  * @cfg {Toolbar}   toolbar       A toolbar for this panel
50862  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
50863  * @cfg {String} title          The title for this panel
50864  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
50865  * @cfg {String} url            Calls {@link #setUrl} with this value
50866  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
50867  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
50868  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
50869  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
50870
50871  * @constructor
50872  * Create a new ContentPanel.
50873  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
50874  * @param {String/Object} config A string to set only the title or a config object
50875  * @param {String} content (optional) Set the HTML content for this panel
50876  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
50877  */
50878 Roo.ContentPanel = function(el, config, content){
50879     
50880      
50881     /*
50882     if(el.autoCreate || el.xtype){ // xtype is available if this is called from factory
50883         config = el;
50884         el = Roo.id();
50885     }
50886     if (config && config.parentLayout) { 
50887         el = config.parentLayout.el.createChild(); 
50888     }
50889     */
50890     if(el.autoCreate){ // xtype is available if this is called from factory
50891         config = el;
50892         el = Roo.id();
50893     }
50894     this.el = Roo.get(el);
50895     if(!this.el && config && config.autoCreate){
50896         if(typeof config.autoCreate == "object"){
50897             if(!config.autoCreate.id){
50898                 config.autoCreate.id = config.id||el;
50899             }
50900             this.el = Roo.DomHelper.append(document.body,
50901                         config.autoCreate, true);
50902         }else{
50903             this.el = Roo.DomHelper.append(document.body,
50904                         {tag: "div", cls: "x-layout-inactive-content", id: config.id||el}, true);
50905         }
50906     }
50907     this.closable = false;
50908     this.loaded = false;
50909     this.active = false;
50910     if(typeof config == "string"){
50911         this.title = config;
50912     }else{
50913         Roo.apply(this, config);
50914     }
50915     
50916     if (this.toolbar && !this.toolbar.el && this.toolbar.xtype) {
50917         this.wrapEl = this.el.wrap();
50918         this.toolbar.container = this.el.insertSibling(false, 'before');
50919         this.toolbar = new Roo.Toolbar(this.toolbar);
50920     }
50921     
50922     // xtype created footer. - not sure if will work as we normally have to render first..
50923     if (this.footer && !this.footer.el && this.footer.xtype) {
50924         if (!this.wrapEl) {
50925             this.wrapEl = this.el.wrap();
50926         }
50927     
50928         this.footer.container = this.wrapEl.createChild();
50929          
50930         this.footer = Roo.factory(this.footer, Roo);
50931         
50932     }
50933     
50934     if(this.resizeEl){
50935         this.resizeEl = Roo.get(this.resizeEl, true);
50936     }else{
50937         this.resizeEl = this.el;
50938     }
50939     // handle view.xtype
50940     
50941  
50942     
50943     
50944     this.addEvents({
50945         /**
50946          * @event activate
50947          * Fires when this panel is activated. 
50948          * @param {Roo.ContentPanel} this
50949          */
50950         "activate" : true,
50951         /**
50952          * @event deactivate
50953          * Fires when this panel is activated. 
50954          * @param {Roo.ContentPanel} this
50955          */
50956         "deactivate" : true,
50957
50958         /**
50959          * @event resize
50960          * Fires when this panel is resized if fitToFrame is true.
50961          * @param {Roo.ContentPanel} this
50962          * @param {Number} width The width after any component adjustments
50963          * @param {Number} height The height after any component adjustments
50964          */
50965         "resize" : true,
50966         
50967          /**
50968          * @event render
50969          * Fires when this tab is created
50970          * @param {Roo.ContentPanel} this
50971          */
50972         "render" : true
50973         
50974         
50975         
50976     });
50977     
50978
50979     
50980     
50981     if(this.autoScroll){
50982         this.resizeEl.setStyle("overflow", "auto");
50983     } else {
50984         // fix randome scrolling
50985         this.el.on('scroll', function() {
50986             Roo.log('fix random scolling');
50987             this.scrollTo('top',0); 
50988         });
50989     }
50990     content = content || this.content;
50991     if(content){
50992         this.setContent(content);
50993     }
50994     if(config && config.url){
50995         this.setUrl(this.url, this.params, this.loadOnce);
50996     }
50997     
50998     
50999     
51000     Roo.ContentPanel.superclass.constructor.call(this);
51001     
51002     if (this.view && typeof(this.view.xtype) != 'undefined') {
51003         this.view.el = this.el.appendChild(document.createElement("div"));
51004         this.view = Roo.factory(this.view); 
51005         this.view.render  &&  this.view.render(false, '');  
51006     }
51007     
51008     
51009     this.fireEvent('render', this);
51010 };
51011
51012 Roo.extend(Roo.ContentPanel, Roo.util.Observable, {
51013     tabTip:'',
51014     setRegion : function(region){
51015         this.region = region;
51016         if(region){
51017            this.el.replaceClass("x-layout-inactive-content", "x-layout-active-content");
51018         }else{
51019            this.el.replaceClass("x-layout-active-content", "x-layout-inactive-content");
51020         } 
51021     },
51022     
51023     /**
51024      * Returns the toolbar for this Panel if one was configured. 
51025      * @return {Roo.Toolbar} 
51026      */
51027     getToolbar : function(){
51028         return this.toolbar;
51029     },
51030     
51031     setActiveState : function(active){
51032         this.active = active;
51033         if(!active){
51034             this.fireEvent("deactivate", this);
51035         }else{
51036             this.fireEvent("activate", this);
51037         }
51038     },
51039     /**
51040      * Updates this panel's element
51041      * @param {String} content The new content
51042      * @param {Boolean} loadScripts (optional) true to look for and process scripts
51043     */
51044     setContent : function(content, loadScripts){
51045         this.el.update(content, loadScripts);
51046     },
51047
51048     ignoreResize : function(w, h){
51049         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
51050             return true;
51051         }else{
51052             this.lastSize = {width: w, height: h};
51053             return false;
51054         }
51055     },
51056     /**
51057      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
51058      * @return {Roo.UpdateManager} The UpdateManager
51059      */
51060     getUpdateManager : function(){
51061         return this.el.getUpdateManager();
51062     },
51063      /**
51064      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
51065      * @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:
51066 <pre><code>
51067 panel.load({
51068     url: "your-url.php",
51069     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
51070     callback: yourFunction,
51071     scope: yourObject, //(optional scope)
51072     discardUrl: false,
51073     nocache: false,
51074     text: "Loading...",
51075     timeout: 30,
51076     scripts: false
51077 });
51078 </code></pre>
51079      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
51080      * 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.
51081      * @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}
51082      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
51083      * @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.
51084      * @return {Roo.ContentPanel} this
51085      */
51086     load : function(){
51087         var um = this.el.getUpdateManager();
51088         um.update.apply(um, arguments);
51089         return this;
51090     },
51091
51092
51093     /**
51094      * 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.
51095      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
51096      * @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)
51097      * @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)
51098      * @return {Roo.UpdateManager} The UpdateManager
51099      */
51100     setUrl : function(url, params, loadOnce){
51101         if(this.refreshDelegate){
51102             this.removeListener("activate", this.refreshDelegate);
51103         }
51104         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
51105         this.on("activate", this.refreshDelegate);
51106         return this.el.getUpdateManager();
51107     },
51108     
51109     _handleRefresh : function(url, params, loadOnce){
51110         if(!loadOnce || !this.loaded){
51111             var updater = this.el.getUpdateManager();
51112             updater.update(url, params, this._setLoaded.createDelegate(this));
51113         }
51114     },
51115     
51116     _setLoaded : function(){
51117         this.loaded = true;
51118     }, 
51119     
51120     /**
51121      * Returns this panel's id
51122      * @return {String} 
51123      */
51124     getId : function(){
51125         return this.el.id;
51126     },
51127     
51128     /** 
51129      * Returns this panel's element - used by regiosn to add.
51130      * @return {Roo.Element} 
51131      */
51132     getEl : function(){
51133         return this.wrapEl || this.el;
51134     },
51135     
51136     adjustForComponents : function(width, height)
51137     {
51138         //Roo.log('adjustForComponents ');
51139         if(this.resizeEl != this.el){
51140             width -= this.el.getFrameWidth('lr');
51141             height -= this.el.getFrameWidth('tb');
51142         }
51143         if(this.toolbar){
51144             var te = this.toolbar.getEl();
51145             height -= te.getHeight();
51146             te.setWidth(width);
51147         }
51148         if(this.footer){
51149             var te = this.footer.getEl();
51150             Roo.log("footer:" + te.getHeight());
51151             
51152             height -= te.getHeight();
51153             te.setWidth(width);
51154         }
51155         
51156         
51157         if(this.adjustments){
51158             width += this.adjustments[0];
51159             height += this.adjustments[1];
51160         }
51161         return {"width": width, "height": height};
51162     },
51163     
51164     setSize : function(width, height){
51165         if(this.fitToFrame && !this.ignoreResize(width, height)){
51166             if(this.fitContainer && this.resizeEl != this.el){
51167                 this.el.setSize(width, height);
51168             }
51169             var size = this.adjustForComponents(width, height);
51170             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
51171             this.fireEvent('resize', this, size.width, size.height);
51172         }
51173     },
51174     
51175     /**
51176      * Returns this panel's title
51177      * @return {String} 
51178      */
51179     getTitle : function(){
51180         return this.title;
51181     },
51182     
51183     /**
51184      * Set this panel's title
51185      * @param {String} title
51186      */
51187     setTitle : function(title){
51188         this.title = title;
51189         if(this.region){
51190             this.region.updatePanelTitle(this, title);
51191         }
51192     },
51193     
51194     /**
51195      * Returns true is this panel was configured to be closable
51196      * @return {Boolean} 
51197      */
51198     isClosable : function(){
51199         return this.closable;
51200     },
51201     
51202     beforeSlide : function(){
51203         this.el.clip();
51204         this.resizeEl.clip();
51205     },
51206     
51207     afterSlide : function(){
51208         this.el.unclip();
51209         this.resizeEl.unclip();
51210     },
51211     
51212     /**
51213      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
51214      *   Will fail silently if the {@link #setUrl} method has not been called.
51215      *   This does not activate the panel, just updates its content.
51216      */
51217     refresh : function(){
51218         if(this.refreshDelegate){
51219            this.loaded = false;
51220            this.refreshDelegate();
51221         }
51222     },
51223     
51224     /**
51225      * Destroys this panel
51226      */
51227     destroy : function(){
51228         this.el.removeAllListeners();
51229         var tempEl = document.createElement("span");
51230         tempEl.appendChild(this.el.dom);
51231         tempEl.innerHTML = "";
51232         this.el.remove();
51233         this.el = null;
51234     },
51235     
51236     /**
51237      * form - if the content panel contains a form - this is a reference to it.
51238      * @type {Roo.form.Form}
51239      */
51240     form : false,
51241     /**
51242      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
51243      *    This contains a reference to it.
51244      * @type {Roo.View}
51245      */
51246     view : false,
51247     
51248       /**
51249      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
51250      * <pre><code>
51251
51252 layout.addxtype({
51253        xtype : 'Form',
51254        items: [ .... ]
51255    }
51256 );
51257
51258 </code></pre>
51259      * @param {Object} cfg Xtype definition of item to add.
51260      */
51261     
51262     addxtype : function(cfg) {
51263         // add form..
51264         if (cfg.xtype.match(/^Form$/)) {
51265             
51266             var el;
51267             //if (this.footer) {
51268             //    el = this.footer.container.insertSibling(false, 'before');
51269             //} else {
51270                 el = this.el.createChild();
51271             //}
51272
51273             this.form = new  Roo.form.Form(cfg);
51274             
51275             
51276             if ( this.form.allItems.length) this.form.render(el.dom);
51277             return this.form;
51278         }
51279         // should only have one of theses..
51280         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
51281             // views.. should not be just added - used named prop 'view''
51282             
51283             cfg.el = this.el.appendChild(document.createElement("div"));
51284             // factory?
51285             
51286             var ret = new Roo.factory(cfg);
51287              
51288              ret.render && ret.render(false, ''); // render blank..
51289             this.view = ret;
51290             return ret;
51291         }
51292         return false;
51293     }
51294 });
51295
51296 /**
51297  * @class Roo.GridPanel
51298  * @extends Roo.ContentPanel
51299  * @constructor
51300  * Create a new GridPanel.
51301  * @param {Roo.grid.Grid} grid The grid for this panel
51302  * @param {String/Object} config A string to set only the panel's title, or a config object
51303  */
51304 Roo.GridPanel = function(grid, config){
51305     
51306   
51307     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
51308         {tag: "div", cls: "x-layout-grid-wrapper x-layout-inactive-content"}, true);
51309         
51310     this.wrapper.dom.appendChild(grid.getGridEl().dom);
51311     
51312     Roo.GridPanel.superclass.constructor.call(this, this.wrapper, config);
51313     
51314     if(this.toolbar){
51315         this.toolbar.el.insertBefore(this.wrapper.dom.firstChild);
51316     }
51317     // xtype created footer. - not sure if will work as we normally have to render first..
51318     if (this.footer && !this.footer.el && this.footer.xtype) {
51319         
51320         this.footer.container = this.grid.getView().getFooterPanel(true);
51321         this.footer.dataSource = this.grid.dataSource;
51322         this.footer = Roo.factory(this.footer, Roo);
51323         
51324     }
51325     
51326     grid.monitorWindowResize = false; // turn off autosizing
51327     grid.autoHeight = false;
51328     grid.autoWidth = false;
51329     this.grid = grid;
51330     this.grid.getGridEl().replaceClass("x-layout-inactive-content", "x-layout-component-panel");
51331 };
51332
51333 Roo.extend(Roo.GridPanel, Roo.ContentPanel, {
51334     getId : function(){
51335         return this.grid.id;
51336     },
51337     
51338     /**
51339      * Returns the grid for this panel
51340      * @return {Roo.grid.Grid} 
51341      */
51342     getGrid : function(){
51343         return this.grid;    
51344     },
51345     
51346     setSize : function(width, height){
51347         if(!this.ignoreResize(width, height)){
51348             var grid = this.grid;
51349             var size = this.adjustForComponents(width, height);
51350             grid.getGridEl().setSize(size.width, size.height);
51351             grid.autoSize();
51352         }
51353     },
51354     
51355     beforeSlide : function(){
51356         this.grid.getView().scroller.clip();
51357     },
51358     
51359     afterSlide : function(){
51360         this.grid.getView().scroller.unclip();
51361     },
51362     
51363     destroy : function(){
51364         this.grid.destroy();
51365         delete this.grid;
51366         Roo.GridPanel.superclass.destroy.call(this); 
51367     }
51368 });
51369
51370
51371 /**
51372  * @class Roo.NestedLayoutPanel
51373  * @extends Roo.ContentPanel
51374  * @constructor
51375  * Create a new NestedLayoutPanel.
51376  * 
51377  * 
51378  * @param {Roo.BorderLayout} layout The layout for this panel
51379  * @param {String/Object} config A string to set only the title or a config object
51380  */
51381 Roo.NestedLayoutPanel = function(layout, config)
51382 {
51383     // construct with only one argument..
51384     /* FIXME - implement nicer consturctors
51385     if (layout.layout) {
51386         config = layout;
51387         layout = config.layout;
51388         delete config.layout;
51389     }
51390     if (layout.xtype && !layout.getEl) {
51391         // then layout needs constructing..
51392         layout = Roo.factory(layout, Roo);
51393     }
51394     */
51395     
51396     
51397     Roo.NestedLayoutPanel.superclass.constructor.call(this, layout.getEl(), config);
51398     
51399     layout.monitorWindowResize = false; // turn off autosizing
51400     this.layout = layout;
51401     this.layout.getEl().addClass("x-layout-nested-layout");
51402     
51403     
51404     
51405     
51406 };
51407
51408 Roo.extend(Roo.NestedLayoutPanel, Roo.ContentPanel, {
51409
51410     setSize : function(width, height){
51411         if(!this.ignoreResize(width, height)){
51412             var size = this.adjustForComponents(width, height);
51413             var el = this.layout.getEl();
51414             el.setSize(size.width, size.height);
51415             var touch = el.dom.offsetWidth;
51416             this.layout.layout();
51417             // ie requires a double layout on the first pass
51418             if(Roo.isIE && !this.initialized){
51419                 this.initialized = true;
51420                 this.layout.layout();
51421             }
51422         }
51423     },
51424     
51425     // activate all subpanels if not currently active..
51426     
51427     setActiveState : function(active){
51428         this.active = active;
51429         if(!active){
51430             this.fireEvent("deactivate", this);
51431             return;
51432         }
51433         
51434         this.fireEvent("activate", this);
51435         // not sure if this should happen before or after..
51436         if (!this.layout) {
51437             return; // should not happen..
51438         }
51439         var reg = false;
51440         for (var r in this.layout.regions) {
51441             reg = this.layout.getRegion(r);
51442             if (reg.getActivePanel()) {
51443                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
51444                 reg.setActivePanel(reg.getActivePanel());
51445                 continue;
51446             }
51447             if (!reg.panels.length) {
51448                 continue;
51449             }
51450             reg.showPanel(reg.getPanel(0));
51451         }
51452         
51453         
51454         
51455         
51456     },
51457     
51458     /**
51459      * Returns the nested BorderLayout for this panel
51460      * @return {Roo.BorderLayout} 
51461      */
51462     getLayout : function(){
51463         return this.layout;
51464     },
51465     
51466      /**
51467      * Adds a xtype elements to the layout of the nested panel
51468      * <pre><code>
51469
51470 panel.addxtype({
51471        xtype : 'ContentPanel',
51472        region: 'west',
51473        items: [ .... ]
51474    }
51475 );
51476
51477 panel.addxtype({
51478         xtype : 'NestedLayoutPanel',
51479         region: 'west',
51480         layout: {
51481            center: { },
51482            west: { }   
51483         },
51484         items : [ ... list of content panels or nested layout panels.. ]
51485    }
51486 );
51487 </code></pre>
51488      * @param {Object} cfg Xtype definition of item to add.
51489      */
51490     addxtype : function(cfg) {
51491         return this.layout.addxtype(cfg);
51492     
51493     }
51494 });
51495
51496 Roo.ScrollPanel = function(el, config, content){
51497     config = config || {};
51498     config.fitToFrame = true;
51499     Roo.ScrollPanel.superclass.constructor.call(this, el, config, content);
51500     
51501     this.el.dom.style.overflow = "hidden";
51502     var wrap = this.el.wrap({cls: "x-scroller x-layout-inactive-content"});
51503     this.el.removeClass("x-layout-inactive-content");
51504     this.el.on("mousewheel", this.onWheel, this);
51505
51506     var up = wrap.createChild({cls: "x-scroller-up", html: "&#160;"}, this.el.dom);
51507     var down = wrap.createChild({cls: "x-scroller-down", html: "&#160;"});
51508     up.unselectable(); down.unselectable();
51509     up.on("click", this.scrollUp, this);
51510     down.on("click", this.scrollDown, this);
51511     up.addClassOnOver("x-scroller-btn-over");
51512     down.addClassOnOver("x-scroller-btn-over");
51513     up.addClassOnClick("x-scroller-btn-click");
51514     down.addClassOnClick("x-scroller-btn-click");
51515     this.adjustments = [0, -(up.getHeight() + down.getHeight())];
51516
51517     this.resizeEl = this.el;
51518     this.el = wrap; this.up = up; this.down = down;
51519 };
51520
51521 Roo.extend(Roo.ScrollPanel, Roo.ContentPanel, {
51522     increment : 100,
51523     wheelIncrement : 5,
51524     scrollUp : function(){
51525         this.resizeEl.scroll("up", this.increment, {callback: this.afterScroll, scope: this});
51526     },
51527
51528     scrollDown : function(){
51529         this.resizeEl.scroll("down", this.increment, {callback: this.afterScroll, scope: this});
51530     },
51531
51532     afterScroll : function(){
51533         var el = this.resizeEl;
51534         var t = el.dom.scrollTop, h = el.dom.scrollHeight, ch = el.dom.clientHeight;
51535         this.up[t == 0 ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
51536         this.down[h - t <= ch ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
51537     },
51538
51539     setSize : function(){
51540         Roo.ScrollPanel.superclass.setSize.apply(this, arguments);
51541         this.afterScroll();
51542     },
51543
51544     onWheel : function(e){
51545         var d = e.getWheelDelta();
51546         this.resizeEl.dom.scrollTop -= (d*this.wheelIncrement);
51547         this.afterScroll();
51548         e.stopEvent();
51549     },
51550
51551     setContent : function(content, loadScripts){
51552         this.resizeEl.update(content, loadScripts);
51553     }
51554
51555 });
51556
51557
51558
51559
51560
51561
51562
51563
51564
51565 /**
51566  * @class Roo.TreePanel
51567  * @extends Roo.ContentPanel
51568  * @constructor
51569  * Create a new TreePanel. - defaults to fit/scoll contents.
51570  * @param {String/Object} config A string to set only the panel's title, or a config object
51571  * @cfg {Roo.tree.TreePanel} tree The tree TreePanel, with config etc.
51572  */
51573 Roo.TreePanel = function(config){
51574     var el = config.el;
51575     var tree = config.tree;
51576     delete config.tree; 
51577     delete config.el; // hopefull!
51578     
51579     // wrapper for IE7 strict & safari scroll issue
51580     
51581     var treeEl = el.createChild();
51582     config.resizeEl = treeEl;
51583     
51584     
51585     
51586     Roo.TreePanel.superclass.constructor.call(this, el, config);
51587  
51588  
51589     this.tree = new Roo.tree.TreePanel(treeEl , tree);
51590     //console.log(tree);
51591     this.on('activate', function()
51592     {
51593         if (this.tree.rendered) {
51594             return;
51595         }
51596         //console.log('render tree');
51597         this.tree.render();
51598     });
51599     // this should not be needed.. - it's actually the 'el' that resizes?
51600     // actuall it breaks the containerScroll - dragging nodes auto scroll at top
51601     
51602     //this.on('resize',  function (cp, w, h) {
51603     //        this.tree.innerCt.setWidth(w);
51604     //        this.tree.innerCt.setHeight(h);
51605     //        //this.tree.innerCt.setStyle('overflow-y', 'auto');
51606     //});
51607
51608         
51609     
51610 };
51611
51612 Roo.extend(Roo.TreePanel, Roo.ContentPanel, {   
51613     fitToFrame : true,
51614     autoScroll : true
51615 });
51616
51617
51618
51619
51620
51621
51622
51623
51624
51625
51626
51627 /*
51628  * Based on:
51629  * Ext JS Library 1.1.1
51630  * Copyright(c) 2006-2007, Ext JS, LLC.
51631  *
51632  * Originally Released Under LGPL - original licence link has changed is not relivant.
51633  *
51634  * Fork - LGPL
51635  * <script type="text/javascript">
51636  */
51637  
51638
51639 /**
51640  * @class Roo.ReaderLayout
51641  * @extends Roo.BorderLayout
51642  * This is a pre-built layout that represents a classic, 5-pane application.  It consists of a header, a primary
51643  * center region containing two nested regions (a top one for a list view and one for item preview below),
51644  * and regions on either side that can be used for navigation, application commands, informational displays, etc.
51645  * The setup and configuration work exactly the same as it does for a {@link Roo.BorderLayout} - this class simply
51646  * expedites the setup of the overall layout and regions for this common application style.
51647  * Example:
51648  <pre><code>
51649 var reader = new Roo.ReaderLayout();
51650 var CP = Roo.ContentPanel;  // shortcut for adding
51651
51652 reader.beginUpdate();
51653 reader.add("north", new CP("north", "North"));
51654 reader.add("west", new CP("west", {title: "West"}));
51655 reader.add("east", new CP("east", {title: "East"}));
51656
51657 reader.regions.listView.add(new CP("listView", "List"));
51658 reader.regions.preview.add(new CP("preview", "Preview"));
51659 reader.endUpdate();
51660 </code></pre>
51661 * @constructor
51662 * Create a new ReaderLayout
51663 * @param {Object} config Configuration options
51664 * @param {String/HTMLElement/Element} container (optional) The container this layout is bound to (defaults to
51665 * document.body if omitted)
51666 */
51667 Roo.ReaderLayout = function(config, renderTo){
51668     var c = config || {size:{}};
51669     Roo.ReaderLayout.superclass.constructor.call(this, renderTo || document.body, {
51670         north: c.north !== false ? Roo.apply({
51671             split:false,
51672             initialSize: 32,
51673             titlebar: false
51674         }, c.north) : false,
51675         west: c.west !== false ? Roo.apply({
51676             split:true,
51677             initialSize: 200,
51678             minSize: 175,
51679             maxSize: 400,
51680             titlebar: true,
51681             collapsible: true,
51682             animate: true,
51683             margins:{left:5,right:0,bottom:5,top:5},
51684             cmargins:{left:5,right:5,bottom:5,top:5}
51685         }, c.west) : false,
51686         east: c.east !== false ? Roo.apply({
51687             split:true,
51688             initialSize: 200,
51689             minSize: 175,
51690             maxSize: 400,
51691             titlebar: true,
51692             collapsible: true,
51693             animate: true,
51694             margins:{left:0,right:5,bottom:5,top:5},
51695             cmargins:{left:5,right:5,bottom:5,top:5}
51696         }, c.east) : false,
51697         center: Roo.apply({
51698             tabPosition: 'top',
51699             autoScroll:false,
51700             closeOnTab: true,
51701             titlebar:false,
51702             margins:{left:c.west!==false ? 0 : 5,right:c.east!==false ? 0 : 5,bottom:5,top:2}
51703         }, c.center)
51704     });
51705
51706     this.el.addClass('x-reader');
51707
51708     this.beginUpdate();
51709
51710     var inner = new Roo.BorderLayout(Roo.get(document.body).createChild(), {
51711         south: c.preview !== false ? Roo.apply({
51712             split:true,
51713             initialSize: 200,
51714             minSize: 100,
51715             autoScroll:true,
51716             collapsible:true,
51717             titlebar: true,
51718             cmargins:{top:5,left:0, right:0, bottom:0}
51719         }, c.preview) : false,
51720         center: Roo.apply({
51721             autoScroll:false,
51722             titlebar:false,
51723             minHeight:200
51724         }, c.listView)
51725     });
51726     this.add('center', new Roo.NestedLayoutPanel(inner,
51727             Roo.apply({title: c.mainTitle || '',tabTip:''},c.innerPanelCfg)));
51728
51729     this.endUpdate();
51730
51731     this.regions.preview = inner.getRegion('south');
51732     this.regions.listView = inner.getRegion('center');
51733 };
51734
51735 Roo.extend(Roo.ReaderLayout, Roo.BorderLayout);/*
51736  * Based on:
51737  * Ext JS Library 1.1.1
51738  * Copyright(c) 2006-2007, Ext JS, LLC.
51739  *
51740  * Originally Released Under LGPL - original licence link has changed is not relivant.
51741  *
51742  * Fork - LGPL
51743  * <script type="text/javascript">
51744  */
51745  
51746 /**
51747  * @class Roo.grid.Grid
51748  * @extends Roo.util.Observable
51749  * This class represents the primary interface of a component based grid control.
51750  * <br><br>Usage:<pre><code>
51751  var grid = new Roo.grid.Grid("my-container-id", {
51752      ds: myDataStore,
51753      cm: myColModel,
51754      selModel: mySelectionModel,
51755      autoSizeColumns: true,
51756      monitorWindowResize: false,
51757      trackMouseOver: true
51758  });
51759  // set any options
51760  grid.render();
51761  * </code></pre>
51762  * <b>Common Problems:</b><br/>
51763  * - Grid does not resize properly when going smaller: Setting overflow hidden on the container
51764  * element will correct this<br/>
51765  * - If you get el.style[camel]= NaNpx or -2px or something related, be certain you have given your container element
51766  * dimensions. The grid adapts to your container's size, if your container has no size defined then the results
51767  * are unpredictable.<br/>
51768  * - Do not render the grid into an element with display:none. Try using visibility:hidden. Otherwise there is no way for the
51769  * grid to calculate dimensions/offsets.<br/>
51770   * @constructor
51771  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
51772  * The container MUST have some type of size defined for the grid to fill. The container will be
51773  * automatically set to position relative if it isn't already.
51774  * @param {Object} config A config object that sets properties on this grid.
51775  */
51776 Roo.grid.Grid = function(container, config){
51777         // initialize the container
51778         this.container = Roo.get(container);
51779         this.container.update("");
51780         this.container.setStyle("overflow", "hidden");
51781     this.container.addClass('x-grid-container');
51782
51783     this.id = this.container.id;
51784
51785     Roo.apply(this, config);
51786     // check and correct shorthanded configs
51787     if(this.ds){
51788         this.dataSource = this.ds;
51789         delete this.ds;
51790     }
51791     if(this.cm){
51792         this.colModel = this.cm;
51793         delete this.cm;
51794     }
51795     if(this.sm){
51796         this.selModel = this.sm;
51797         delete this.sm;
51798     }
51799
51800     if (this.selModel) {
51801         this.selModel = Roo.factory(this.selModel, Roo.grid);
51802         this.sm = this.selModel;
51803         this.sm.xmodule = this.xmodule || false;
51804     }
51805     if (typeof(this.colModel.config) == 'undefined') {
51806         this.colModel = new Roo.grid.ColumnModel(this.colModel);
51807         this.cm = this.colModel;
51808         this.cm.xmodule = this.xmodule || false;
51809     }
51810     if (this.dataSource) {
51811         this.dataSource= Roo.factory(this.dataSource, Roo.data);
51812         this.ds = this.dataSource;
51813         this.ds.xmodule = this.xmodule || false;
51814          
51815     }
51816     
51817     
51818     
51819     if(this.width){
51820         this.container.setWidth(this.width);
51821     }
51822
51823     if(this.height){
51824         this.container.setHeight(this.height);
51825     }
51826     /** @private */
51827         this.addEvents({
51828         // raw events
51829         /**
51830          * @event click
51831          * The raw click event for the entire grid.
51832          * @param {Roo.EventObject} e
51833          */
51834         "click" : true,
51835         /**
51836          * @event dblclick
51837          * The raw dblclick event for the entire grid.
51838          * @param {Roo.EventObject} e
51839          */
51840         "dblclick" : true,
51841         /**
51842          * @event contextmenu
51843          * The raw contextmenu event for the entire grid.
51844          * @param {Roo.EventObject} e
51845          */
51846         "contextmenu" : true,
51847         /**
51848          * @event mousedown
51849          * The raw mousedown event for the entire grid.
51850          * @param {Roo.EventObject} e
51851          */
51852         "mousedown" : true,
51853         /**
51854          * @event mouseup
51855          * The raw mouseup event for the entire grid.
51856          * @param {Roo.EventObject} e
51857          */
51858         "mouseup" : true,
51859         /**
51860          * @event mouseover
51861          * The raw mouseover event for the entire grid.
51862          * @param {Roo.EventObject} e
51863          */
51864         "mouseover" : true,
51865         /**
51866          * @event mouseout
51867          * The raw mouseout event for the entire grid.
51868          * @param {Roo.EventObject} e
51869          */
51870         "mouseout" : true,
51871         /**
51872          * @event keypress
51873          * The raw keypress event for the entire grid.
51874          * @param {Roo.EventObject} e
51875          */
51876         "keypress" : true,
51877         /**
51878          * @event keydown
51879          * The raw keydown event for the entire grid.
51880          * @param {Roo.EventObject} e
51881          */
51882         "keydown" : true,
51883
51884         // custom events
51885
51886         /**
51887          * @event cellclick
51888          * Fires when a cell is clicked
51889          * @param {Grid} this
51890          * @param {Number} rowIndex
51891          * @param {Number} columnIndex
51892          * @param {Roo.EventObject} e
51893          */
51894         "cellclick" : true,
51895         /**
51896          * @event celldblclick
51897          * Fires when a cell is double clicked
51898          * @param {Grid} this
51899          * @param {Number} rowIndex
51900          * @param {Number} columnIndex
51901          * @param {Roo.EventObject} e
51902          */
51903         "celldblclick" : true,
51904         /**
51905          * @event rowclick
51906          * Fires when a row is clicked
51907          * @param {Grid} this
51908          * @param {Number} rowIndex
51909          * @param {Roo.EventObject} e
51910          */
51911         "rowclick" : true,
51912         /**
51913          * @event rowdblclick
51914          * Fires when a row is double clicked
51915          * @param {Grid} this
51916          * @param {Number} rowIndex
51917          * @param {Roo.EventObject} e
51918          */
51919         "rowdblclick" : true,
51920         /**
51921          * @event headerclick
51922          * Fires when a header is clicked
51923          * @param {Grid} this
51924          * @param {Number} columnIndex
51925          * @param {Roo.EventObject} e
51926          */
51927         "headerclick" : true,
51928         /**
51929          * @event headerdblclick
51930          * Fires when a header cell is double clicked
51931          * @param {Grid} this
51932          * @param {Number} columnIndex
51933          * @param {Roo.EventObject} e
51934          */
51935         "headerdblclick" : true,
51936         /**
51937          * @event rowcontextmenu
51938          * Fires when a row is right clicked
51939          * @param {Grid} this
51940          * @param {Number} rowIndex
51941          * @param {Roo.EventObject} e
51942          */
51943         "rowcontextmenu" : true,
51944         /**
51945          * @event cellcontextmenu
51946          * Fires when a cell is right clicked
51947          * @param {Grid} this
51948          * @param {Number} rowIndex
51949          * @param {Number} cellIndex
51950          * @param {Roo.EventObject} e
51951          */
51952          "cellcontextmenu" : true,
51953         /**
51954          * @event headercontextmenu
51955          * Fires when a header is right clicked
51956          * @param {Grid} this
51957          * @param {Number} columnIndex
51958          * @param {Roo.EventObject} e
51959          */
51960         "headercontextmenu" : true,
51961         /**
51962          * @event bodyscroll
51963          * Fires when the body element is scrolled
51964          * @param {Number} scrollLeft
51965          * @param {Number} scrollTop
51966          */
51967         "bodyscroll" : true,
51968         /**
51969          * @event columnresize
51970          * Fires when the user resizes a column
51971          * @param {Number} columnIndex
51972          * @param {Number} newSize
51973          */
51974         "columnresize" : true,
51975         /**
51976          * @event columnmove
51977          * Fires when the user moves a column
51978          * @param {Number} oldIndex
51979          * @param {Number} newIndex
51980          */
51981         "columnmove" : true,
51982         /**
51983          * @event startdrag
51984          * Fires when row(s) start being dragged
51985          * @param {Grid} this
51986          * @param {Roo.GridDD} dd The drag drop object
51987          * @param {event} e The raw browser event
51988          */
51989         "startdrag" : true,
51990         /**
51991          * @event enddrag
51992          * Fires when a drag operation is complete
51993          * @param {Grid} this
51994          * @param {Roo.GridDD} dd The drag drop object
51995          * @param {event} e The raw browser event
51996          */
51997         "enddrag" : true,
51998         /**
51999          * @event dragdrop
52000          * Fires when dragged row(s) are dropped on a valid DD target
52001          * @param {Grid} this
52002          * @param {Roo.GridDD} dd The drag drop object
52003          * @param {String} targetId The target drag drop object
52004          * @param {event} e The raw browser event
52005          */
52006         "dragdrop" : true,
52007         /**
52008          * @event dragover
52009          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
52010          * @param {Grid} this
52011          * @param {Roo.GridDD} dd The drag drop object
52012          * @param {String} targetId The target drag drop object
52013          * @param {event} e The raw browser event
52014          */
52015         "dragover" : true,
52016         /**
52017          * @event dragenter
52018          *  Fires when the dragged row(s) first cross another DD target while being dragged
52019          * @param {Grid} this
52020          * @param {Roo.GridDD} dd The drag drop object
52021          * @param {String} targetId The target drag drop object
52022          * @param {event} e The raw browser event
52023          */
52024         "dragenter" : true,
52025         /**
52026          * @event dragout
52027          * Fires when the dragged row(s) leave another DD target while being dragged
52028          * @param {Grid} this
52029          * @param {Roo.GridDD} dd The drag drop object
52030          * @param {String} targetId The target drag drop object
52031          * @param {event} e The raw browser event
52032          */
52033         "dragout" : true,
52034         /**
52035          * @event rowclass
52036          * Fires when a row is rendered, so you can change add a style to it.
52037          * @param {GridView} gridview   The grid view
52038          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
52039          */
52040         'rowclass' : true,
52041
52042         /**
52043          * @event render
52044          * Fires when the grid is rendered
52045          * @param {Grid} grid
52046          */
52047         'render' : true
52048     });
52049
52050     Roo.grid.Grid.superclass.constructor.call(this);
52051 };
52052 Roo.extend(Roo.grid.Grid, Roo.util.Observable, {
52053     
52054     /**
52055      * @cfg {String} ddGroup - drag drop group.
52056      */
52057
52058     /**
52059      * @cfg {Number} minColumnWidth The minimum width a column can be resized to. Default is 25.
52060      */
52061     minColumnWidth : 25,
52062
52063     /**
52064      * @cfg {Boolean} autoSizeColumns True to automatically resize the columns to fit their content
52065      * <b>on initial render.</b> It is more efficient to explicitly size the columns
52066      * through the ColumnModel's {@link Roo.grid.ColumnModel#width} config option.  Default is false.
52067      */
52068     autoSizeColumns : false,
52069
52070     /**
52071      * @cfg {Boolean} autoSizeHeaders True to measure headers with column data when auto sizing columns. Default is true.
52072      */
52073     autoSizeHeaders : true,
52074
52075     /**
52076      * @cfg {Boolean} monitorWindowResize True to autoSize the grid when the window resizes. Default is true.
52077      */
52078     monitorWindowResize : true,
52079
52080     /**
52081      * @cfg {Boolean} maxRowsToMeasure If autoSizeColumns is on, maxRowsToMeasure can be used to limit the number of
52082      * rows measured to get a columns size. Default is 0 (all rows).
52083      */
52084     maxRowsToMeasure : 0,
52085
52086     /**
52087      * @cfg {Boolean} trackMouseOver True to highlight rows when the mouse is over. Default is true.
52088      */
52089     trackMouseOver : true,
52090
52091     /**
52092     * @cfg {Boolean} enableDrag  True to enable drag of rows. Default is false. (double check if this is needed?)
52093     */
52094     
52095     /**
52096     * @cfg {Boolean} enableDragDrop True to enable drag and drop of rows. Default is false.
52097     */
52098     enableDragDrop : false,
52099     
52100     /**
52101     * @cfg {Boolean} enableColumnMove True to enable drag and drop reorder of columns. Default is true.
52102     */
52103     enableColumnMove : true,
52104     
52105     /**
52106     * @cfg {Boolean} enableColumnHide True to enable hiding of columns with the header context menu. Default is true.
52107     */
52108     enableColumnHide : true,
52109     
52110     /**
52111     * @cfg {Boolean} enableRowHeightSync True to manually sync row heights across locked and not locked rows. Default is false.
52112     */
52113     enableRowHeightSync : false,
52114     
52115     /**
52116     * @cfg {Boolean} stripeRows True to stripe the rows.  Default is true.
52117     */
52118     stripeRows : true,
52119     
52120     /**
52121     * @cfg {Boolean} autoHeight True to fit the height of the grid container to the height of the data. Default is false.
52122     */
52123     autoHeight : false,
52124
52125     /**
52126      * @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.
52127      */
52128     autoExpandColumn : false,
52129
52130     /**
52131     * @cfg {Number} autoExpandMin The minimum width the autoExpandColumn can have (if enabled).
52132     * Default is 50.
52133     */
52134     autoExpandMin : 50,
52135
52136     /**
52137     * @cfg {Number} autoExpandMax The maximum width the autoExpandColumn can have (if enabled). Default is 1000.
52138     */
52139     autoExpandMax : 1000,
52140
52141     /**
52142     * @cfg {Object} view The {@link Roo.grid.GridView} used by the grid. This can be set before a call to render().
52143     */
52144     view : null,
52145
52146     /**
52147     * @cfg {Object} loadMask An {@link Roo.LoadMask} config or true to mask the grid while loading. Default is false.
52148     */
52149     loadMask : false,
52150     /**
52151     * @cfg {Roo.dd.DropTarget} dropTarget An {@link Roo.dd.DropTarget} config
52152     */
52153     dropTarget: false,
52154     
52155    
52156     
52157     // private
52158     rendered : false,
52159
52160     /**
52161     * @cfg {Boolean} autoWidth True to set the grid's width to the default total width of the grid's columns instead
52162     * of a fixed width. Default is false.
52163     */
52164     /**
52165     * @cfg {Number} maxHeight Sets the maximum height of the grid - ignored if autoHeight is not on.
52166     */
52167     /**
52168      * Called once after all setup has been completed and the grid is ready to be rendered.
52169      * @return {Roo.grid.Grid} this
52170      */
52171     render : function()
52172     {
52173         var c = this.container;
52174         // try to detect autoHeight/width mode
52175         if((!c.dom.offsetHeight || c.dom.offsetHeight < 20) || c.getStyle("height") == "auto"){
52176             this.autoHeight = true;
52177         }
52178         var view = this.getView();
52179         view.init(this);
52180
52181         c.on("click", this.onClick, this);
52182         c.on("dblclick", this.onDblClick, this);
52183         c.on("contextmenu", this.onContextMenu, this);
52184         c.on("keydown", this.onKeyDown, this);
52185         if (Roo.isTouch) {
52186             c.on("touchstart", this.onTouchStart, this);
52187         }
52188
52189         this.relayEvents(c, ["mousedown","mouseup","mouseover","mouseout","keypress"]);
52190
52191         this.getSelectionModel().init(this);
52192
52193         view.render();
52194
52195         if(this.loadMask){
52196             this.loadMask = new Roo.LoadMask(this.container,
52197                     Roo.apply({store:this.dataSource}, this.loadMask));
52198         }
52199         
52200         
52201         if (this.toolbar && this.toolbar.xtype) {
52202             this.toolbar.container = this.getView().getHeaderPanel(true);
52203             this.toolbar = new Roo.Toolbar(this.toolbar);
52204         }
52205         if (this.footer && this.footer.xtype) {
52206             this.footer.dataSource = this.getDataSource();
52207             this.footer.container = this.getView().getFooterPanel(true);
52208             this.footer = Roo.factory(this.footer, Roo);
52209         }
52210         if (this.dropTarget && this.dropTarget.xtype) {
52211             delete this.dropTarget.xtype;
52212             this.dropTarget =  new Roo.dd.DropTarget(this.getView().mainBody, this.dropTarget);
52213         }
52214         
52215         
52216         this.rendered = true;
52217         this.fireEvent('render', this);
52218         return this;
52219     },
52220
52221         /**
52222          * Reconfigures the grid to use a different Store and Column Model.
52223          * The View will be bound to the new objects and refreshed.
52224          * @param {Roo.data.Store} dataSource The new {@link Roo.data.Store} object
52225          * @param {Roo.grid.ColumnModel} The new {@link Roo.grid.ColumnModel} object
52226          */
52227     reconfigure : function(dataSource, colModel){
52228         if(this.loadMask){
52229             this.loadMask.destroy();
52230             this.loadMask = new Roo.LoadMask(this.container,
52231                     Roo.apply({store:dataSource}, this.loadMask));
52232         }
52233         this.view.bind(dataSource, colModel);
52234         this.dataSource = dataSource;
52235         this.colModel = colModel;
52236         this.view.refresh(true);
52237     },
52238
52239     // private
52240     onKeyDown : function(e){
52241         this.fireEvent("keydown", e);
52242     },
52243
52244     /**
52245      * Destroy this grid.
52246      * @param {Boolean} removeEl True to remove the element
52247      */
52248     destroy : function(removeEl, keepListeners){
52249         if(this.loadMask){
52250             this.loadMask.destroy();
52251         }
52252         var c = this.container;
52253         c.removeAllListeners();
52254         this.view.destroy();
52255         this.colModel.purgeListeners();
52256         if(!keepListeners){
52257             this.purgeListeners();
52258         }
52259         c.update("");
52260         if(removeEl === true){
52261             c.remove();
52262         }
52263     },
52264
52265     // private
52266     processEvent : function(name, e){
52267         // does this fire select???
52268         Roo.log('grid:processEvent '  + name);
52269         
52270         if (name != 'touchstart' ) {
52271             this.fireEvent(name, e);    
52272         }
52273         
52274         var t = e.getTarget();
52275         var v = this.view;
52276         var header = v.findHeaderIndex(t);
52277         if(header !== false){
52278             var ename = name == 'touchstart' ? 'click' : name;
52279              
52280             this.fireEvent("header" + ename, this, header, e);
52281         }else{
52282             var row = v.findRowIndex(t);
52283             var cell = v.findCellIndex(t);
52284             if (name == 'touchstart') {
52285                 // first touch is always a click.
52286                 // hopefull this happens after selection is updated.?
52287                 name = false;
52288                 
52289                 if (typeof(this.selModel.getSelectedCell) != 'undefined') {
52290                     var cs = this.selModel.getSelectedCell();
52291                     if (row == cs[0] && cell == cs[1]){
52292                         name = 'dblclick';
52293                     }
52294                 }
52295                 if (typeof(this.selModel.getSelections) != 'undefined') {
52296                     var cs = this.selModel.getSelections();
52297                     var ds = this.dataSource;
52298                     if (cs.length == 1 && ds.getAt(row) == cs[0]){
52299                         name = 'dblclick';
52300                     }
52301                 }
52302                 if (!name) {
52303                     return;
52304                 }
52305             }
52306             
52307             
52308             if(row !== false){
52309                 this.fireEvent("row" + name, this, row, e);
52310                 if(cell !== false){
52311                     this.fireEvent("cell" + name, this, row, cell, e);
52312                 }
52313             }
52314         }
52315     },
52316
52317     // private
52318     onClick : function(e){
52319         this.processEvent("click", e);
52320     },
52321    // private
52322     onTouchStart : function(e){
52323         this.processEvent("touchstart", e);
52324     },
52325
52326     // private
52327     onContextMenu : function(e, t){
52328         this.processEvent("contextmenu", e);
52329     },
52330
52331     // private
52332     onDblClick : function(e){
52333         this.processEvent("dblclick", e);
52334     },
52335
52336     // private
52337     walkCells : function(row, col, step, fn, scope){
52338         var cm = this.colModel, clen = cm.getColumnCount();
52339         var ds = this.dataSource, rlen = ds.getCount(), first = true;
52340         if(step < 0){
52341             if(col < 0){
52342                 row--;
52343                 first = false;
52344             }
52345             while(row >= 0){
52346                 if(!first){
52347                     col = clen-1;
52348                 }
52349                 first = false;
52350                 while(col >= 0){
52351                     if(fn.call(scope || this, row, col, cm) === true){
52352                         return [row, col];
52353                     }
52354                     col--;
52355                 }
52356                 row--;
52357             }
52358         } else {
52359             if(col >= clen){
52360                 row++;
52361                 first = false;
52362             }
52363             while(row < rlen){
52364                 if(!first){
52365                     col = 0;
52366                 }
52367                 first = false;
52368                 while(col < clen){
52369                     if(fn.call(scope || this, row, col, cm) === true){
52370                         return [row, col];
52371                     }
52372                     col++;
52373                 }
52374                 row++;
52375             }
52376         }
52377         return null;
52378     },
52379
52380     // private
52381     getSelections : function(){
52382         return this.selModel.getSelections();
52383     },
52384
52385     /**
52386      * Causes the grid to manually recalculate its dimensions. Generally this is done automatically,
52387      * but if manual update is required this method will initiate it.
52388      */
52389     autoSize : function(){
52390         if(this.rendered){
52391             this.view.layout();
52392             if(this.view.adjustForScroll){
52393                 this.view.adjustForScroll();
52394             }
52395         }
52396     },
52397
52398     /**
52399      * Returns the grid's underlying element.
52400      * @return {Element} The element
52401      */
52402     getGridEl : function(){
52403         return this.container;
52404     },
52405
52406     // private for compatibility, overridden by editor grid
52407     stopEditing : function(){},
52408
52409     /**
52410      * Returns the grid's SelectionModel.
52411      * @return {SelectionModel}
52412      */
52413     getSelectionModel : function(){
52414         if(!this.selModel){
52415             this.selModel = new Roo.grid.RowSelectionModel();
52416         }
52417         return this.selModel;
52418     },
52419
52420     /**
52421      * Returns the grid's DataSource.
52422      * @return {DataSource}
52423      */
52424     getDataSource : function(){
52425         return this.dataSource;
52426     },
52427
52428     /**
52429      * Returns the grid's ColumnModel.
52430      * @return {ColumnModel}
52431      */
52432     getColumnModel : function(){
52433         return this.colModel;
52434     },
52435
52436     /**
52437      * Returns the grid's GridView object.
52438      * @return {GridView}
52439      */
52440     getView : function(){
52441         if(!this.view){
52442             this.view = new Roo.grid.GridView(this.viewConfig);
52443         }
52444         return this.view;
52445     },
52446     /**
52447      * Called to get grid's drag proxy text, by default returns this.ddText.
52448      * @return {String}
52449      */
52450     getDragDropText : function(){
52451         var count = this.selModel.getCount();
52452         return String.format(this.ddText, count, count == 1 ? '' : 's');
52453     }
52454 });
52455 /**
52456  * Configures the text is the drag proxy (defaults to "%0 selected row(s)").
52457  * %0 is replaced with the number of selected rows.
52458  * @type String
52459  */
52460 Roo.grid.Grid.prototype.ddText = "{0} selected row{1}";/*
52461  * Based on:
52462  * Ext JS Library 1.1.1
52463  * Copyright(c) 2006-2007, Ext JS, LLC.
52464  *
52465  * Originally Released Under LGPL - original licence link has changed is not relivant.
52466  *
52467  * Fork - LGPL
52468  * <script type="text/javascript">
52469  */
52470  
52471 Roo.grid.AbstractGridView = function(){
52472         this.grid = null;
52473         
52474         this.events = {
52475             "beforerowremoved" : true,
52476             "beforerowsinserted" : true,
52477             "beforerefresh" : true,
52478             "rowremoved" : true,
52479             "rowsinserted" : true,
52480             "rowupdated" : true,
52481             "refresh" : true
52482         };
52483     Roo.grid.AbstractGridView.superclass.constructor.call(this);
52484 };
52485
52486 Roo.extend(Roo.grid.AbstractGridView, Roo.util.Observable, {
52487     rowClass : "x-grid-row",
52488     cellClass : "x-grid-cell",
52489     tdClass : "x-grid-td",
52490     hdClass : "x-grid-hd",
52491     splitClass : "x-grid-hd-split",
52492     
52493     init: function(grid){
52494         this.grid = grid;
52495                 var cid = this.grid.getGridEl().id;
52496         this.colSelector = "#" + cid + " ." + this.cellClass + "-";
52497         this.tdSelector = "#" + cid + " ." + this.tdClass + "-";
52498         this.hdSelector = "#" + cid + " ." + this.hdClass + "-";
52499         this.splitSelector = "#" + cid + " ." + this.splitClass + "-";
52500         },
52501         
52502     getColumnRenderers : function(){
52503         var renderers = [];
52504         var cm = this.grid.colModel;
52505         var colCount = cm.getColumnCount();
52506         for(var i = 0; i < colCount; i++){
52507             renderers[i] = cm.getRenderer(i);
52508         }
52509         return renderers;
52510     },
52511     
52512     getColumnIds : function(){
52513         var ids = [];
52514         var cm = this.grid.colModel;
52515         var colCount = cm.getColumnCount();
52516         for(var i = 0; i < colCount; i++){
52517             ids[i] = cm.getColumnId(i);
52518         }
52519         return ids;
52520     },
52521     
52522     getDataIndexes : function(){
52523         if(!this.indexMap){
52524             this.indexMap = this.buildIndexMap();
52525         }
52526         return this.indexMap.colToData;
52527     },
52528     
52529     getColumnIndexByDataIndex : function(dataIndex){
52530         if(!this.indexMap){
52531             this.indexMap = this.buildIndexMap();
52532         }
52533         return this.indexMap.dataToCol[dataIndex];
52534     },
52535     
52536     /**
52537      * Set a css style for a column dynamically. 
52538      * @param {Number} colIndex The index of the column
52539      * @param {String} name The css property name
52540      * @param {String} value The css value
52541      */
52542     setCSSStyle : function(colIndex, name, value){
52543         var selector = "#" + this.grid.id + " .x-grid-col-" + colIndex;
52544         Roo.util.CSS.updateRule(selector, name, value);
52545     },
52546     
52547     generateRules : function(cm){
52548         var ruleBuf = [], rulesId = this.grid.id + '-cssrules';
52549         Roo.util.CSS.removeStyleSheet(rulesId);
52550         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
52551             var cid = cm.getColumnId(i);
52552             ruleBuf.push(this.colSelector, cid, " {\n", cm.config[i].css, "}\n",
52553                          this.tdSelector, cid, " {\n}\n",
52554                          this.hdSelector, cid, " {\n}\n",
52555                          this.splitSelector, cid, " {\n}\n");
52556         }
52557         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
52558     }
52559 });/*
52560  * Based on:
52561  * Ext JS Library 1.1.1
52562  * Copyright(c) 2006-2007, Ext JS, LLC.
52563  *
52564  * Originally Released Under LGPL - original licence link has changed is not relivant.
52565  *
52566  * Fork - LGPL
52567  * <script type="text/javascript">
52568  */
52569
52570 // private
52571 // This is a support class used internally by the Grid components
52572 Roo.grid.HeaderDragZone = function(grid, hd, hd2){
52573     this.grid = grid;
52574     this.view = grid.getView();
52575     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
52576     Roo.grid.HeaderDragZone.superclass.constructor.call(this, hd);
52577     if(hd2){
52578         this.setHandleElId(Roo.id(hd));
52579         this.setOuterHandleElId(Roo.id(hd2));
52580     }
52581     this.scroll = false;
52582 };
52583 Roo.extend(Roo.grid.HeaderDragZone, Roo.dd.DragZone, {
52584     maxDragWidth: 120,
52585     getDragData : function(e){
52586         var t = Roo.lib.Event.getTarget(e);
52587         var h = this.view.findHeaderCell(t);
52588         if(h){
52589             return {ddel: h.firstChild, header:h};
52590         }
52591         return false;
52592     },
52593
52594     onInitDrag : function(e){
52595         this.view.headersDisabled = true;
52596         var clone = this.dragData.ddel.cloneNode(true);
52597         clone.id = Roo.id();
52598         clone.style.width = Math.min(this.dragData.header.offsetWidth,this.maxDragWidth) + "px";
52599         this.proxy.update(clone);
52600         return true;
52601     },
52602
52603     afterValidDrop : function(){
52604         var v = this.view;
52605         setTimeout(function(){
52606             v.headersDisabled = false;
52607         }, 50);
52608     },
52609
52610     afterInvalidDrop : function(){
52611         var v = this.view;
52612         setTimeout(function(){
52613             v.headersDisabled = false;
52614         }, 50);
52615     }
52616 });
52617 /*
52618  * Based on:
52619  * Ext JS Library 1.1.1
52620  * Copyright(c) 2006-2007, Ext JS, LLC.
52621  *
52622  * Originally Released Under LGPL - original licence link has changed is not relivant.
52623  *
52624  * Fork - LGPL
52625  * <script type="text/javascript">
52626  */
52627 // private
52628 // This is a support class used internally by the Grid components
52629 Roo.grid.HeaderDropZone = function(grid, hd, hd2){
52630     this.grid = grid;
52631     this.view = grid.getView();
52632     // split the proxies so they don't interfere with mouse events
52633     this.proxyTop = Roo.DomHelper.append(document.body, {
52634         cls:"col-move-top", html:"&#160;"
52635     }, true);
52636     this.proxyBottom = Roo.DomHelper.append(document.body, {
52637         cls:"col-move-bottom", html:"&#160;"
52638     }, true);
52639     this.proxyTop.hide = this.proxyBottom.hide = function(){
52640         this.setLeftTop(-100,-100);
52641         this.setStyle("visibility", "hidden");
52642     };
52643     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
52644     // temporarily disabled
52645     //Roo.dd.ScrollManager.register(this.view.scroller.dom);
52646     Roo.grid.HeaderDropZone.superclass.constructor.call(this, grid.getGridEl().dom);
52647 };
52648 Roo.extend(Roo.grid.HeaderDropZone, Roo.dd.DropZone, {
52649     proxyOffsets : [-4, -9],
52650     fly: Roo.Element.fly,
52651
52652     getTargetFromEvent : function(e){
52653         var t = Roo.lib.Event.getTarget(e);
52654         var cindex = this.view.findCellIndex(t);
52655         if(cindex !== false){
52656             return this.view.getHeaderCell(cindex);
52657         }
52658         return null;
52659     },
52660
52661     nextVisible : function(h){
52662         var v = this.view, cm = this.grid.colModel;
52663         h = h.nextSibling;
52664         while(h){
52665             if(!cm.isHidden(v.getCellIndex(h))){
52666                 return h;
52667             }
52668             h = h.nextSibling;
52669         }
52670         return null;
52671     },
52672
52673     prevVisible : function(h){
52674         var v = this.view, cm = this.grid.colModel;
52675         h = h.prevSibling;
52676         while(h){
52677             if(!cm.isHidden(v.getCellIndex(h))){
52678                 return h;
52679             }
52680             h = h.prevSibling;
52681         }
52682         return null;
52683     },
52684
52685     positionIndicator : function(h, n, e){
52686         var x = Roo.lib.Event.getPageX(e);
52687         var r = Roo.lib.Dom.getRegion(n.firstChild);
52688         var px, pt, py = r.top + this.proxyOffsets[1];
52689         if((r.right - x) <= (r.right-r.left)/2){
52690             px = r.right+this.view.borderWidth;
52691             pt = "after";
52692         }else{
52693             px = r.left;
52694             pt = "before";
52695         }
52696         var oldIndex = this.view.getCellIndex(h);
52697         var newIndex = this.view.getCellIndex(n);
52698
52699         if(this.grid.colModel.isFixed(newIndex)){
52700             return false;
52701         }
52702
52703         var locked = this.grid.colModel.isLocked(newIndex);
52704
52705         if(pt == "after"){
52706             newIndex++;
52707         }
52708         if(oldIndex < newIndex){
52709             newIndex--;
52710         }
52711         if(oldIndex == newIndex && (locked == this.grid.colModel.isLocked(oldIndex))){
52712             return false;
52713         }
52714         px +=  this.proxyOffsets[0];
52715         this.proxyTop.setLeftTop(px, py);
52716         this.proxyTop.show();
52717         if(!this.bottomOffset){
52718             this.bottomOffset = this.view.mainHd.getHeight();
52719         }
52720         this.proxyBottom.setLeftTop(px, py+this.proxyTop.dom.offsetHeight+this.bottomOffset);
52721         this.proxyBottom.show();
52722         return pt;
52723     },
52724
52725     onNodeEnter : function(n, dd, e, data){
52726         if(data.header != n){
52727             this.positionIndicator(data.header, n, e);
52728         }
52729     },
52730
52731     onNodeOver : function(n, dd, e, data){
52732         var result = false;
52733         if(data.header != n){
52734             result = this.positionIndicator(data.header, n, e);
52735         }
52736         if(!result){
52737             this.proxyTop.hide();
52738             this.proxyBottom.hide();
52739         }
52740         return result ? this.dropAllowed : this.dropNotAllowed;
52741     },
52742
52743     onNodeOut : function(n, dd, e, data){
52744         this.proxyTop.hide();
52745         this.proxyBottom.hide();
52746     },
52747
52748     onNodeDrop : function(n, dd, e, data){
52749         var h = data.header;
52750         if(h != n){
52751             var cm = this.grid.colModel;
52752             var x = Roo.lib.Event.getPageX(e);
52753             var r = Roo.lib.Dom.getRegion(n.firstChild);
52754             var pt = (r.right - x) <= ((r.right-r.left)/2) ? "after" : "before";
52755             var oldIndex = this.view.getCellIndex(h);
52756             var newIndex = this.view.getCellIndex(n);
52757             var locked = cm.isLocked(newIndex);
52758             if(pt == "after"){
52759                 newIndex++;
52760             }
52761             if(oldIndex < newIndex){
52762                 newIndex--;
52763             }
52764             if(oldIndex == newIndex && (locked == cm.isLocked(oldIndex))){
52765                 return false;
52766             }
52767             cm.setLocked(oldIndex, locked, true);
52768             cm.moveColumn(oldIndex, newIndex);
52769             this.grid.fireEvent("columnmove", oldIndex, newIndex);
52770             return true;
52771         }
52772         return false;
52773     }
52774 });
52775 /*
52776  * Based on:
52777  * Ext JS Library 1.1.1
52778  * Copyright(c) 2006-2007, Ext JS, LLC.
52779  *
52780  * Originally Released Under LGPL - original licence link has changed is not relivant.
52781  *
52782  * Fork - LGPL
52783  * <script type="text/javascript">
52784  */
52785   
52786 /**
52787  * @class Roo.grid.GridView
52788  * @extends Roo.util.Observable
52789  *
52790  * @constructor
52791  * @param {Object} config
52792  */
52793 Roo.grid.GridView = function(config){
52794     Roo.grid.GridView.superclass.constructor.call(this);
52795     this.el = null;
52796
52797     Roo.apply(this, config);
52798 };
52799
52800 Roo.extend(Roo.grid.GridView, Roo.grid.AbstractGridView, {
52801
52802     unselectable :  'unselectable="on"',
52803     unselectableCls :  'x-unselectable',
52804     
52805     
52806     rowClass : "x-grid-row",
52807
52808     cellClass : "x-grid-col",
52809
52810     tdClass : "x-grid-td",
52811
52812     hdClass : "x-grid-hd",
52813
52814     splitClass : "x-grid-split",
52815
52816     sortClasses : ["sort-asc", "sort-desc"],
52817
52818     enableMoveAnim : false,
52819
52820     hlColor: "C3DAF9",
52821
52822     dh : Roo.DomHelper,
52823
52824     fly : Roo.Element.fly,
52825
52826     css : Roo.util.CSS,
52827
52828     borderWidth: 1,
52829
52830     splitOffset: 3,
52831
52832     scrollIncrement : 22,
52833
52834     cellRE: /(?:.*?)x-grid-(?:hd|cell|csplit)-(?:[\d]+)-([\d]+)(?:.*?)/,
52835
52836     findRE: /\s?(?:x-grid-hd|x-grid-col|x-grid-csplit)\s/,
52837
52838     bind : function(ds, cm){
52839         if(this.ds){
52840             this.ds.un("load", this.onLoad, this);
52841             this.ds.un("datachanged", this.onDataChange, this);
52842             this.ds.un("add", this.onAdd, this);
52843             this.ds.un("remove", this.onRemove, this);
52844             this.ds.un("update", this.onUpdate, this);
52845             this.ds.un("clear", this.onClear, this);
52846         }
52847         if(ds){
52848             ds.on("load", this.onLoad, this);
52849             ds.on("datachanged", this.onDataChange, this);
52850             ds.on("add", this.onAdd, this);
52851             ds.on("remove", this.onRemove, this);
52852             ds.on("update", this.onUpdate, this);
52853             ds.on("clear", this.onClear, this);
52854         }
52855         this.ds = ds;
52856
52857         if(this.cm){
52858             this.cm.un("widthchange", this.onColWidthChange, this);
52859             this.cm.un("headerchange", this.onHeaderChange, this);
52860             this.cm.un("hiddenchange", this.onHiddenChange, this);
52861             this.cm.un("columnmoved", this.onColumnMove, this);
52862             this.cm.un("columnlockchange", this.onColumnLock, this);
52863         }
52864         if(cm){
52865             this.generateRules(cm);
52866             cm.on("widthchange", this.onColWidthChange, this);
52867             cm.on("headerchange", this.onHeaderChange, this);
52868             cm.on("hiddenchange", this.onHiddenChange, this);
52869             cm.on("columnmoved", this.onColumnMove, this);
52870             cm.on("columnlockchange", this.onColumnLock, this);
52871         }
52872         this.cm = cm;
52873     },
52874
52875     init: function(grid){
52876         Roo.grid.GridView.superclass.init.call(this, grid);
52877
52878         this.bind(grid.dataSource, grid.colModel);
52879
52880         grid.on("headerclick", this.handleHeaderClick, this);
52881
52882         if(grid.trackMouseOver){
52883             grid.on("mouseover", this.onRowOver, this);
52884             grid.on("mouseout", this.onRowOut, this);
52885         }
52886         grid.cancelTextSelection = function(){};
52887         this.gridId = grid.id;
52888
52889         var tpls = this.templates || {};
52890
52891         if(!tpls.master){
52892             tpls.master = new Roo.Template(
52893                '<div class="x-grid" hidefocus="true">',
52894                 '<a href="#" class="x-grid-focus" tabIndex="-1"></a>',
52895                   '<div class="x-grid-topbar"></div>',
52896                   '<div class="x-grid-scroller"><div></div></div>',
52897                   '<div class="x-grid-locked">',
52898                       '<div class="x-grid-header">{lockedHeader}</div>',
52899                       '<div class="x-grid-body">{lockedBody}</div>',
52900                   "</div>",
52901                   '<div class="x-grid-viewport">',
52902                       '<div class="x-grid-header">{header}</div>',
52903                       '<div class="x-grid-body">{body}</div>',
52904                   "</div>",
52905                   '<div class="x-grid-bottombar"></div>',
52906                  
52907                   '<div class="x-grid-resize-proxy">&#160;</div>',
52908                "</div>"
52909             );
52910             tpls.master.disableformats = true;
52911         }
52912
52913         if(!tpls.header){
52914             tpls.header = new Roo.Template(
52915                '<table border="0" cellspacing="0" cellpadding="0">',
52916                '<tbody><tr class="x-grid-hd-row">{cells}</tr></tbody>',
52917                "</table>{splits}"
52918             );
52919             tpls.header.disableformats = true;
52920         }
52921         tpls.header.compile();
52922
52923         if(!tpls.hcell){
52924             tpls.hcell = new Roo.Template(
52925                 '<td class="x-grid-hd x-grid-td-{id} {cellId}"><div title="{title}" class="x-grid-hd-inner x-grid-hd-{id}">',
52926                 '<div class="x-grid-hd-text ' + this.unselectableCls +  '" ' + this.unselectable +'>{value}<img class="x-grid-sort-icon" src="', Roo.BLANK_IMAGE_URL, '" /></div>',
52927                 "</div></td>"
52928              );
52929              tpls.hcell.disableFormats = true;
52930         }
52931         tpls.hcell.compile();
52932
52933         if(!tpls.hsplit){
52934             tpls.hsplit = new Roo.Template('<div class="x-grid-split {splitId} x-grid-split-{id}" style="{style} ' +
52935                                             this.unselectableCls +  '" ' + this.unselectable +'>&#160;</div>');
52936             tpls.hsplit.disableFormats = true;
52937         }
52938         tpls.hsplit.compile();
52939
52940         if(!tpls.body){
52941             tpls.body = new Roo.Template(
52942                '<table border="0" cellspacing="0" cellpadding="0">',
52943                "<tbody>{rows}</tbody>",
52944                "</table>"
52945             );
52946             tpls.body.disableFormats = true;
52947         }
52948         tpls.body.compile();
52949
52950         if(!tpls.row){
52951             tpls.row = new Roo.Template('<tr class="x-grid-row {alt}">{cells}</tr>');
52952             tpls.row.disableFormats = true;
52953         }
52954         tpls.row.compile();
52955
52956         if(!tpls.cell){
52957             tpls.cell = new Roo.Template(
52958                 '<td class="x-grid-col x-grid-td-{id} {cellId} {css}" tabIndex="0">',
52959                 '<div class="x-grid-col-{id} x-grid-cell-inner"><div class="x-grid-cell-text ' +
52960                     this.unselectableCls +  '" ' + this.unselectable +'" {attr}>{value}</div></div>',
52961                 "</td>"
52962             );
52963             tpls.cell.disableFormats = true;
52964         }
52965         tpls.cell.compile();
52966
52967         this.templates = tpls;
52968     },
52969
52970     // remap these for backwards compat
52971     onColWidthChange : function(){
52972         this.updateColumns.apply(this, arguments);
52973     },
52974     onHeaderChange : function(){
52975         this.updateHeaders.apply(this, arguments);
52976     }, 
52977     onHiddenChange : function(){
52978         this.handleHiddenChange.apply(this, arguments);
52979     },
52980     onColumnMove : function(){
52981         this.handleColumnMove.apply(this, arguments);
52982     },
52983     onColumnLock : function(){
52984         this.handleLockChange.apply(this, arguments);
52985     },
52986
52987     onDataChange : function(){
52988         this.refresh();
52989         this.updateHeaderSortState();
52990     },
52991
52992     onClear : function(){
52993         this.refresh();
52994     },
52995
52996     onUpdate : function(ds, record){
52997         this.refreshRow(record);
52998     },
52999
53000     refreshRow : function(record){
53001         var ds = this.ds, index;
53002         if(typeof record == 'number'){
53003             index = record;
53004             record = ds.getAt(index);
53005         }else{
53006             index = ds.indexOf(record);
53007         }
53008         this.insertRows(ds, index, index, true);
53009         this.onRemove(ds, record, index+1, true);
53010         this.syncRowHeights(index, index);
53011         this.layout();
53012         this.fireEvent("rowupdated", this, index, record);
53013     },
53014
53015     onAdd : function(ds, records, index){
53016         this.insertRows(ds, index, index + (records.length-1));
53017     },
53018
53019     onRemove : function(ds, record, index, isUpdate){
53020         if(isUpdate !== true){
53021             this.fireEvent("beforerowremoved", this, index, record);
53022         }
53023         var bt = this.getBodyTable(), lt = this.getLockedTable();
53024         if(bt.rows[index]){
53025             bt.firstChild.removeChild(bt.rows[index]);
53026         }
53027         if(lt.rows[index]){
53028             lt.firstChild.removeChild(lt.rows[index]);
53029         }
53030         if(isUpdate !== true){
53031             this.stripeRows(index);
53032             this.syncRowHeights(index, index);
53033             this.layout();
53034             this.fireEvent("rowremoved", this, index, record);
53035         }
53036     },
53037
53038     onLoad : function(){
53039         this.scrollToTop();
53040     },
53041
53042     /**
53043      * Scrolls the grid to the top
53044      */
53045     scrollToTop : function(){
53046         if(this.scroller){
53047             this.scroller.dom.scrollTop = 0;
53048             this.syncScroll();
53049         }
53050     },
53051
53052     /**
53053      * Gets a panel in the header of the grid that can be used for toolbars etc.
53054      * After modifying the contents of this panel a call to grid.autoSize() may be
53055      * required to register any changes in size.
53056      * @param {Boolean} doShow By default the header is hidden. Pass true to show the panel
53057      * @return Roo.Element
53058      */
53059     getHeaderPanel : function(doShow){
53060         if(doShow){
53061             this.headerPanel.show();
53062         }
53063         return this.headerPanel;
53064     },
53065
53066     /**
53067      * Gets a panel in the footer of the grid that can be used for toolbars etc.
53068      * After modifying the contents of this panel a call to grid.autoSize() may be
53069      * required to register any changes in size.
53070      * @param {Boolean} doShow By default the footer is hidden. Pass true to show the panel
53071      * @return Roo.Element
53072      */
53073     getFooterPanel : function(doShow){
53074         if(doShow){
53075             this.footerPanel.show();
53076         }
53077         return this.footerPanel;
53078     },
53079
53080     initElements : function(){
53081         var E = Roo.Element;
53082         var el = this.grid.getGridEl().dom.firstChild;
53083         var cs = el.childNodes;
53084
53085         this.el = new E(el);
53086         
53087          this.focusEl = new E(el.firstChild);
53088         this.focusEl.swallowEvent("click", true);
53089         
53090         this.headerPanel = new E(cs[1]);
53091         this.headerPanel.enableDisplayMode("block");
53092
53093         this.scroller = new E(cs[2]);
53094         this.scrollSizer = new E(this.scroller.dom.firstChild);
53095
53096         this.lockedWrap = new E(cs[3]);
53097         this.lockedHd = new E(this.lockedWrap.dom.firstChild);
53098         this.lockedBody = new E(this.lockedWrap.dom.childNodes[1]);
53099
53100         this.mainWrap = new E(cs[4]);
53101         this.mainHd = new E(this.mainWrap.dom.firstChild);
53102         this.mainBody = new E(this.mainWrap.dom.childNodes[1]);
53103
53104         this.footerPanel = new E(cs[5]);
53105         this.footerPanel.enableDisplayMode("block");
53106
53107         this.resizeProxy = new E(cs[6]);
53108
53109         this.headerSelector = String.format(
53110            '#{0} td.x-grid-hd, #{1} td.x-grid-hd',
53111            this.lockedHd.id, this.mainHd.id
53112         );
53113
53114         this.splitterSelector = String.format(
53115            '#{0} div.x-grid-split, #{1} div.x-grid-split',
53116            this.idToCssName(this.lockedHd.id), this.idToCssName(this.mainHd.id)
53117         );
53118     },
53119     idToCssName : function(s)
53120     {
53121         return s.replace(/[^a-z0-9]+/ig, '-');
53122     },
53123
53124     getHeaderCell : function(index){
53125         return Roo.DomQuery.select(this.headerSelector)[index];
53126     },
53127
53128     getHeaderCellMeasure : function(index){
53129         return this.getHeaderCell(index).firstChild;
53130     },
53131
53132     getHeaderCellText : function(index){
53133         return this.getHeaderCell(index).firstChild.firstChild;
53134     },
53135
53136     getLockedTable : function(){
53137         return this.lockedBody.dom.firstChild;
53138     },
53139
53140     getBodyTable : function(){
53141         return this.mainBody.dom.firstChild;
53142     },
53143
53144     getLockedRow : function(index){
53145         return this.getLockedTable().rows[index];
53146     },
53147
53148     getRow : function(index){
53149         return this.getBodyTable().rows[index];
53150     },
53151
53152     getRowComposite : function(index){
53153         if(!this.rowEl){
53154             this.rowEl = new Roo.CompositeElementLite();
53155         }
53156         var els = [], lrow, mrow;
53157         if(lrow = this.getLockedRow(index)){
53158             els.push(lrow);
53159         }
53160         if(mrow = this.getRow(index)){
53161             els.push(mrow);
53162         }
53163         this.rowEl.elements = els;
53164         return this.rowEl;
53165     },
53166     /**
53167      * Gets the 'td' of the cell
53168      * 
53169      * @param {Integer} rowIndex row to select
53170      * @param {Integer} colIndex column to select
53171      * 
53172      * @return {Object} 
53173      */
53174     getCell : function(rowIndex, colIndex){
53175         var locked = this.cm.getLockedCount();
53176         var source;
53177         if(colIndex < locked){
53178             source = this.lockedBody.dom.firstChild;
53179         }else{
53180             source = this.mainBody.dom.firstChild;
53181             colIndex -= locked;
53182         }
53183         return source.rows[rowIndex].childNodes[colIndex];
53184     },
53185
53186     getCellText : function(rowIndex, colIndex){
53187         return this.getCell(rowIndex, colIndex).firstChild.firstChild;
53188     },
53189
53190     getCellBox : function(cell){
53191         var b = this.fly(cell).getBox();
53192         if(Roo.isOpera){ // opera fails to report the Y
53193             b.y = cell.offsetTop + this.mainBody.getY();
53194         }
53195         return b;
53196     },
53197
53198     getCellIndex : function(cell){
53199         var id = String(cell.className).match(this.cellRE);
53200         if(id){
53201             return parseInt(id[1], 10);
53202         }
53203         return 0;
53204     },
53205
53206     findHeaderIndex : function(n){
53207         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
53208         return r ? this.getCellIndex(r) : false;
53209     },
53210
53211     findHeaderCell : function(n){
53212         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
53213         return r ? r : false;
53214     },
53215
53216     findRowIndex : function(n){
53217         if(!n){
53218             return false;
53219         }
53220         var r = Roo.fly(n).findParent("tr." + this.rowClass, 6);
53221         return r ? r.rowIndex : false;
53222     },
53223
53224     findCellIndex : function(node){
53225         var stop = this.el.dom;
53226         while(node && node != stop){
53227             if(this.findRE.test(node.className)){
53228                 return this.getCellIndex(node);
53229             }
53230             node = node.parentNode;
53231         }
53232         return false;
53233     },
53234
53235     getColumnId : function(index){
53236         return this.cm.getColumnId(index);
53237     },
53238
53239     getSplitters : function()
53240     {
53241         if(this.splitterSelector){
53242            return Roo.DomQuery.select(this.splitterSelector);
53243         }else{
53244             return null;
53245       }
53246     },
53247
53248     getSplitter : function(index){
53249         return this.getSplitters()[index];
53250     },
53251
53252     onRowOver : function(e, t){
53253         var row;
53254         if((row = this.findRowIndex(t)) !== false){
53255             this.getRowComposite(row).addClass("x-grid-row-over");
53256         }
53257     },
53258
53259     onRowOut : function(e, t){
53260         var row;
53261         if((row = this.findRowIndex(t)) !== false && row !== this.findRowIndex(e.getRelatedTarget())){
53262             this.getRowComposite(row).removeClass("x-grid-row-over");
53263         }
53264     },
53265
53266     renderHeaders : function(){
53267         var cm = this.cm;
53268         var ct = this.templates.hcell, ht = this.templates.header, st = this.templates.hsplit;
53269         var cb = [], lb = [], sb = [], lsb = [], p = {};
53270         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
53271             p.cellId = "x-grid-hd-0-" + i;
53272             p.splitId = "x-grid-csplit-0-" + i;
53273             p.id = cm.getColumnId(i);
53274             p.title = cm.getColumnTooltip(i) || "";
53275             p.value = cm.getColumnHeader(i) || "";
53276             p.style = (this.grid.enableColumnResize === false || !cm.isResizable(i) || cm.isFixed(i)) ? 'cursor:default' : '';
53277             if(!cm.isLocked(i)){
53278                 cb[cb.length] = ct.apply(p);
53279                 sb[sb.length] = st.apply(p);
53280             }else{
53281                 lb[lb.length] = ct.apply(p);
53282                 lsb[lsb.length] = st.apply(p);
53283             }
53284         }
53285         return [ht.apply({cells: lb.join(""), splits:lsb.join("")}),
53286                 ht.apply({cells: cb.join(""), splits:sb.join("")})];
53287     },
53288
53289     updateHeaders : function(){
53290         var html = this.renderHeaders();
53291         this.lockedHd.update(html[0]);
53292         this.mainHd.update(html[1]);
53293     },
53294
53295     /**
53296      * Focuses the specified row.
53297      * @param {Number} row The row index
53298      */
53299     focusRow : function(row)
53300     {
53301         //Roo.log('GridView.focusRow');
53302         var x = this.scroller.dom.scrollLeft;
53303         this.focusCell(row, 0, false);
53304         this.scroller.dom.scrollLeft = x;
53305     },
53306
53307     /**
53308      * Focuses the specified cell.
53309      * @param {Number} row The row index
53310      * @param {Number} col The column index
53311      * @param {Boolean} hscroll false to disable horizontal scrolling
53312      */
53313     focusCell : function(row, col, hscroll)
53314     {
53315         //Roo.log('GridView.focusCell');
53316         var el = this.ensureVisible(row, col, hscroll);
53317         this.focusEl.alignTo(el, "tl-tl");
53318         if(Roo.isGecko){
53319             this.focusEl.focus();
53320         }else{
53321             this.focusEl.focus.defer(1, this.focusEl);
53322         }
53323     },
53324
53325     /**
53326      * Scrolls the specified cell into view
53327      * @param {Number} row The row index
53328      * @param {Number} col The column index
53329      * @param {Boolean} hscroll false to disable horizontal scrolling
53330      */
53331     ensureVisible : function(row, col, hscroll)
53332     {
53333         //Roo.log('GridView.ensureVisible,' + row + ',' + col);
53334         //return null; //disable for testing.
53335         if(typeof row != "number"){
53336             row = row.rowIndex;
53337         }
53338         if(row < 0 && row >= this.ds.getCount()){
53339             return  null;
53340         }
53341         col = (col !== undefined ? col : 0);
53342         var cm = this.grid.colModel;
53343         while(cm.isHidden(col)){
53344             col++;
53345         }
53346
53347         var el = this.getCell(row, col);
53348         if(!el){
53349             return null;
53350         }
53351         var c = this.scroller.dom;
53352
53353         var ctop = parseInt(el.offsetTop, 10);
53354         var cleft = parseInt(el.offsetLeft, 10);
53355         var cbot = ctop + el.offsetHeight;
53356         var cright = cleft + el.offsetWidth;
53357         
53358         var ch = c.clientHeight - this.mainHd.dom.offsetHeight;
53359         var stop = parseInt(c.scrollTop, 10);
53360         var sleft = parseInt(c.scrollLeft, 10);
53361         var sbot = stop + ch;
53362         var sright = sleft + c.clientWidth;
53363         /*
53364         Roo.log('GridView.ensureVisible:' +
53365                 ' ctop:' + ctop +
53366                 ' c.clientHeight:' + c.clientHeight +
53367                 ' this.mainHd.dom.offsetHeight:' + this.mainHd.dom.offsetHeight +
53368                 ' stop:' + stop +
53369                 ' cbot:' + cbot +
53370                 ' sbot:' + sbot +
53371                 ' ch:' + ch  
53372                 );
53373         */
53374         if(ctop < stop){
53375              c.scrollTop = ctop;
53376             //Roo.log("set scrolltop to ctop DISABLE?");
53377         }else if(cbot > sbot){
53378             //Roo.log("set scrolltop to cbot-ch");
53379             c.scrollTop = cbot-ch;
53380         }
53381         
53382         if(hscroll !== false){
53383             if(cleft < sleft){
53384                 c.scrollLeft = cleft;
53385             }else if(cright > sright){
53386                 c.scrollLeft = cright-c.clientWidth;
53387             }
53388         }
53389          
53390         return el;
53391     },
53392
53393     updateColumns : function(){
53394         this.grid.stopEditing();
53395         var cm = this.grid.colModel, colIds = this.getColumnIds();
53396         //var totalWidth = cm.getTotalWidth();
53397         var pos = 0;
53398         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
53399             //if(cm.isHidden(i)) continue;
53400             var w = cm.getColumnWidth(i);
53401             this.css.updateRule(this.colSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
53402             this.css.updateRule(this.hdSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
53403         }
53404         this.updateSplitters();
53405     },
53406
53407     generateRules : function(cm){
53408         var ruleBuf = [], rulesId = this.idToCssName(this.grid.id)+ '-cssrules';
53409         Roo.util.CSS.removeStyleSheet(rulesId);
53410         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
53411             var cid = cm.getColumnId(i);
53412             var align = '';
53413             if(cm.config[i].align){
53414                 align = 'text-align:'+cm.config[i].align+';';
53415             }
53416             var hidden = '';
53417             if(cm.isHidden(i)){
53418                 hidden = 'display:none;';
53419             }
53420             var width = "width:" + (cm.getColumnWidth(i) - this.borderWidth) + "px;";
53421             ruleBuf.push(
53422                     this.colSelector, cid, " {\n", cm.config[i].css, align, width, "\n}\n",
53423                     this.hdSelector, cid, " {\n", align, width, "}\n",
53424                     this.tdSelector, cid, " {\n",hidden,"\n}\n",
53425                     this.splitSelector, cid, " {\n", hidden , "\n}\n");
53426         }
53427         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
53428     },
53429
53430     updateSplitters : function(){
53431         var cm = this.cm, s = this.getSplitters();
53432         if(s){ // splitters not created yet
53433             var pos = 0, locked = true;
53434             for(var i = 0, len = cm.getColumnCount(); i < len; i++){
53435                 if(cm.isHidden(i)) continue;
53436                 var w = cm.getColumnWidth(i); // make sure it's a number
53437                 if(!cm.isLocked(i) && locked){
53438                     pos = 0;
53439                     locked = false;
53440                 }
53441                 pos += w;
53442                 s[i].style.left = (pos-this.splitOffset) + "px";
53443             }
53444         }
53445     },
53446
53447     handleHiddenChange : function(colModel, colIndex, hidden){
53448         if(hidden){
53449             this.hideColumn(colIndex);
53450         }else{
53451             this.unhideColumn(colIndex);
53452         }
53453     },
53454
53455     hideColumn : function(colIndex){
53456         var cid = this.getColumnId(colIndex);
53457         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "none");
53458         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "none");
53459         if(Roo.isSafari){
53460             this.updateHeaders();
53461         }
53462         this.updateSplitters();
53463         this.layout();
53464     },
53465
53466     unhideColumn : function(colIndex){
53467         var cid = this.getColumnId(colIndex);
53468         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "");
53469         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "");
53470
53471         if(Roo.isSafari){
53472             this.updateHeaders();
53473         }
53474         this.updateSplitters();
53475         this.layout();
53476     },
53477
53478     insertRows : function(dm, firstRow, lastRow, isUpdate){
53479         if(firstRow == 0 && lastRow == dm.getCount()-1){
53480             this.refresh();
53481         }else{
53482             if(!isUpdate){
53483                 this.fireEvent("beforerowsinserted", this, firstRow, lastRow);
53484             }
53485             var s = this.getScrollState();
53486             var markup = this.renderRows(firstRow, lastRow);
53487             this.bufferRows(markup[0], this.getLockedTable(), firstRow);
53488             this.bufferRows(markup[1], this.getBodyTable(), firstRow);
53489             this.restoreScroll(s);
53490             if(!isUpdate){
53491                 this.fireEvent("rowsinserted", this, firstRow, lastRow);
53492                 this.syncRowHeights(firstRow, lastRow);
53493                 this.stripeRows(firstRow);
53494                 this.layout();
53495             }
53496         }
53497     },
53498
53499     bufferRows : function(markup, target, index){
53500         var before = null, trows = target.rows, tbody = target.tBodies[0];
53501         if(index < trows.length){
53502             before = trows[index];
53503         }
53504         var b = document.createElement("div");
53505         b.innerHTML = "<table><tbody>"+markup+"</tbody></table>";
53506         var rows = b.firstChild.rows;
53507         for(var i = 0, len = rows.length; i < len; i++){
53508             if(before){
53509                 tbody.insertBefore(rows[0], before);
53510             }else{
53511                 tbody.appendChild(rows[0]);
53512             }
53513         }
53514         b.innerHTML = "";
53515         b = null;
53516     },
53517
53518     deleteRows : function(dm, firstRow, lastRow){
53519         if(dm.getRowCount()<1){
53520             this.fireEvent("beforerefresh", this);
53521             this.mainBody.update("");
53522             this.lockedBody.update("");
53523             this.fireEvent("refresh", this);
53524         }else{
53525             this.fireEvent("beforerowsdeleted", this, firstRow, lastRow);
53526             var bt = this.getBodyTable();
53527             var tbody = bt.firstChild;
53528             var rows = bt.rows;
53529             for(var rowIndex = firstRow; rowIndex <= lastRow; rowIndex++){
53530                 tbody.removeChild(rows[firstRow]);
53531             }
53532             this.stripeRows(firstRow);
53533             this.fireEvent("rowsdeleted", this, firstRow, lastRow);
53534         }
53535     },
53536
53537     updateRows : function(dataSource, firstRow, lastRow){
53538         var s = this.getScrollState();
53539         this.refresh();
53540         this.restoreScroll(s);
53541     },
53542
53543     handleSort : function(dataSource, sortColumnIndex, sortDir, noRefresh){
53544         if(!noRefresh){
53545            this.refresh();
53546         }
53547         this.updateHeaderSortState();
53548     },
53549
53550     getScrollState : function(){
53551         
53552         var sb = this.scroller.dom;
53553         return {left: sb.scrollLeft, top: sb.scrollTop};
53554     },
53555
53556     stripeRows : function(startRow){
53557         if(!this.grid.stripeRows || this.ds.getCount() < 1){
53558             return;
53559         }
53560         startRow = startRow || 0;
53561         var rows = this.getBodyTable().rows;
53562         var lrows = this.getLockedTable().rows;
53563         var cls = ' x-grid-row-alt ';
53564         for(var i = startRow, len = rows.length; i < len; i++){
53565             var row = rows[i], lrow = lrows[i];
53566             var isAlt = ((i+1) % 2 == 0);
53567             var hasAlt = (' '+row.className + ' ').indexOf(cls) != -1;
53568             if(isAlt == hasAlt){
53569                 continue;
53570             }
53571             if(isAlt){
53572                 row.className += " x-grid-row-alt";
53573             }else{
53574                 row.className = row.className.replace("x-grid-row-alt", "");
53575             }
53576             if(lrow){
53577                 lrow.className = row.className;
53578             }
53579         }
53580     },
53581
53582     restoreScroll : function(state){
53583         //Roo.log('GridView.restoreScroll');
53584         var sb = this.scroller.dom;
53585         sb.scrollLeft = state.left;
53586         sb.scrollTop = state.top;
53587         this.syncScroll();
53588     },
53589
53590     syncScroll : function(){
53591         //Roo.log('GridView.syncScroll');
53592         var sb = this.scroller.dom;
53593         var sh = this.mainHd.dom;
53594         var bs = this.mainBody.dom;
53595         var lv = this.lockedBody.dom;
53596         sh.scrollLeft = bs.scrollLeft = sb.scrollLeft;
53597         lv.scrollTop = bs.scrollTop = sb.scrollTop;
53598     },
53599
53600     handleScroll : function(e){
53601         this.syncScroll();
53602         var sb = this.scroller.dom;
53603         this.grid.fireEvent("bodyscroll", sb.scrollLeft, sb.scrollTop);
53604         e.stopEvent();
53605     },
53606
53607     handleWheel : function(e){
53608         var d = e.getWheelDelta();
53609         this.scroller.dom.scrollTop -= d*22;
53610         // set this here to prevent jumpy scrolling on large tables
53611         this.lockedBody.dom.scrollTop = this.mainBody.dom.scrollTop = this.scroller.dom.scrollTop;
53612         e.stopEvent();
53613     },
53614
53615     renderRows : function(startRow, endRow){
53616         // pull in all the crap needed to render rows
53617         var g = this.grid, cm = g.colModel, ds = g.dataSource, stripe = g.stripeRows;
53618         var colCount = cm.getColumnCount();
53619
53620         if(ds.getCount() < 1){
53621             return ["", ""];
53622         }
53623
53624         // build a map for all the columns
53625         var cs = [];
53626         for(var i = 0; i < colCount; i++){
53627             var name = cm.getDataIndex(i);
53628             cs[i] = {
53629                 name : typeof name == 'undefined' ? ds.fields.get(i).name : name,
53630                 renderer : cm.getRenderer(i),
53631                 id : cm.getColumnId(i),
53632                 locked : cm.isLocked(i)
53633             };
53634         }
53635
53636         startRow = startRow || 0;
53637         endRow = typeof endRow == "undefined"? ds.getCount()-1 : endRow;
53638
53639         // records to render
53640         var rs = ds.getRange(startRow, endRow);
53641
53642         return this.doRender(cs, rs, ds, startRow, colCount, stripe);
53643     },
53644
53645     // As much as I hate to duplicate code, this was branched because FireFox really hates
53646     // [].join("") on strings. The performance difference was substantial enough to
53647     // branch this function
53648     doRender : Roo.isGecko ?
53649             function(cs, rs, ds, startRow, colCount, stripe){
53650                 var ts = this.templates, ct = ts.cell, rt = ts.row;
53651                 // buffers
53652                 var buf = "", lbuf = "", cb, lcb, c, p = {}, rp = {}, r, rowIndex;
53653                 
53654                 var hasListener = this.grid.hasListener('rowclass');
53655                 var rowcfg = {};
53656                 for(var j = 0, len = rs.length; j < len; j++){
53657                     r = rs[j]; cb = ""; lcb = ""; rowIndex = (j+startRow);
53658                     for(var i = 0; i < colCount; i++){
53659                         c = cs[i];
53660                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
53661                         p.id = c.id;
53662                         p.css = p.attr = "";
53663                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
53664                         if(p.value == undefined || p.value === "") p.value = "&#160;";
53665                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
53666                             p.css += p.css ? ' x-grid-dirty-cell' : 'x-grid-dirty-cell';
53667                         }
53668                         var markup = ct.apply(p);
53669                         if(!c.locked){
53670                             cb+= markup;
53671                         }else{
53672                             lcb+= markup;
53673                         }
53674                     }
53675                     var alt = [];
53676                     if(stripe && ((rowIndex+1) % 2 == 0)){
53677                         alt.push("x-grid-row-alt")
53678                     }
53679                     if(r.dirty){
53680                         alt.push(  " x-grid-dirty-row");
53681                     }
53682                     rp.cells = lcb;
53683                     if(this.getRowClass){
53684                         alt.push(this.getRowClass(r, rowIndex));
53685                     }
53686                     if (hasListener) {
53687                         rowcfg = {
53688                              
53689                             record: r,
53690                             rowIndex : rowIndex,
53691                             rowClass : ''
53692                         }
53693                         this.grid.fireEvent('rowclass', this, rowcfg);
53694                         alt.push(rowcfg.rowClass);
53695                     }
53696                     rp.alt = alt.join(" ");
53697                     lbuf+= rt.apply(rp);
53698                     rp.cells = cb;
53699                     buf+=  rt.apply(rp);
53700                 }
53701                 return [lbuf, buf];
53702             } :
53703             function(cs, rs, ds, startRow, colCount, stripe){
53704                 var ts = this.templates, ct = ts.cell, rt = ts.row;
53705                 // buffers
53706                 var buf = [], lbuf = [], cb, lcb, c, p = {}, rp = {}, r, rowIndex;
53707                 var hasListener = this.grid.hasListener('rowclass');
53708  
53709                 var rowcfg = {};
53710                 for(var j = 0, len = rs.length; j < len; j++){
53711                     r = rs[j]; cb = []; lcb = []; rowIndex = (j+startRow);
53712                     for(var i = 0; i < colCount; i++){
53713                         c = cs[i];
53714                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
53715                         p.id = c.id;
53716                         p.css = p.attr = "";
53717                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
53718                         if(p.value == undefined || p.value === "") p.value = "&#160;";
53719                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
53720                             p.css += p.css ? ' x-grid-dirty-cell' : 'x-grid-dirty-cell';
53721                         }
53722                         
53723                         var markup = ct.apply(p);
53724                         if(!c.locked){
53725                             cb[cb.length] = markup;
53726                         }else{
53727                             lcb[lcb.length] = markup;
53728                         }
53729                     }
53730                     var alt = [];
53731                     if(stripe && ((rowIndex+1) % 2 == 0)){
53732                         alt.push( "x-grid-row-alt");
53733                     }
53734                     if(r.dirty){
53735                         alt.push(" x-grid-dirty-row");
53736                     }
53737                     rp.cells = lcb;
53738                     if(this.getRowClass){
53739                         alt.push( this.getRowClass(r, rowIndex));
53740                     }
53741                     if (hasListener) {
53742                         rowcfg = {
53743                              
53744                             record: r,
53745                             rowIndex : rowIndex,
53746                             rowClass : ''
53747                         }
53748                         this.grid.fireEvent('rowclass', this, rowcfg);
53749                         alt.push(rowcfg.rowClass);
53750                     }
53751                     rp.alt = alt.join(" ");
53752                     rp.cells = lcb.join("");
53753                     lbuf[lbuf.length] = rt.apply(rp);
53754                     rp.cells = cb.join("");
53755                     buf[buf.length] =  rt.apply(rp);
53756                 }
53757                 return [lbuf.join(""), buf.join("")];
53758             },
53759
53760     renderBody : function(){
53761         var markup = this.renderRows();
53762         var bt = this.templates.body;
53763         return [bt.apply({rows: markup[0]}), bt.apply({rows: markup[1]})];
53764     },
53765
53766     /**
53767      * Refreshes the grid
53768      * @param {Boolean} headersToo
53769      */
53770     refresh : function(headersToo){
53771         this.fireEvent("beforerefresh", this);
53772         this.grid.stopEditing();
53773         var result = this.renderBody();
53774         this.lockedBody.update(result[0]);
53775         this.mainBody.update(result[1]);
53776         if(headersToo === true){
53777             this.updateHeaders();
53778             this.updateColumns();
53779             this.updateSplitters();
53780             this.updateHeaderSortState();
53781         }
53782         this.syncRowHeights();
53783         this.layout();
53784         this.fireEvent("refresh", this);
53785     },
53786
53787     handleColumnMove : function(cm, oldIndex, newIndex){
53788         this.indexMap = null;
53789         var s = this.getScrollState();
53790         this.refresh(true);
53791         this.restoreScroll(s);
53792         this.afterMove(newIndex);
53793     },
53794
53795     afterMove : function(colIndex){
53796         if(this.enableMoveAnim && Roo.enableFx){
53797             this.fly(this.getHeaderCell(colIndex).firstChild).highlight(this.hlColor);
53798         }
53799         // if multisort - fix sortOrder, and reload..
53800         if (this.grid.dataSource.multiSort) {
53801             // the we can call sort again..
53802             var dm = this.grid.dataSource;
53803             var cm = this.grid.colModel;
53804             var so = [];
53805             for(var i = 0; i < cm.config.length; i++ ) {
53806                 
53807                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined')) {
53808                     continue; // dont' bother, it's not in sort list or being set.
53809                 }
53810                 
53811                 so.push(cm.config[i].dataIndex);
53812             };
53813             dm.sortOrder = so;
53814             dm.load(dm.lastOptions);
53815             
53816             
53817         }
53818         
53819     },
53820
53821     updateCell : function(dm, rowIndex, dataIndex){
53822         var colIndex = this.getColumnIndexByDataIndex(dataIndex);
53823         if(typeof colIndex == "undefined"){ // not present in grid
53824             return;
53825         }
53826         var cm = this.grid.colModel;
53827         var cell = this.getCell(rowIndex, colIndex);
53828         var cellText = this.getCellText(rowIndex, colIndex);
53829
53830         var p = {
53831             cellId : "x-grid-cell-" + rowIndex + "-" + colIndex,
53832             id : cm.getColumnId(colIndex),
53833             css: colIndex == cm.getColumnCount()-1 ? "x-grid-col-last" : ""
53834         };
53835         var renderer = cm.getRenderer(colIndex);
53836         var val = renderer(dm.getValueAt(rowIndex, dataIndex), p, rowIndex, colIndex, dm);
53837         if(typeof val == "undefined" || val === "") val = "&#160;";
53838         cellText.innerHTML = val;
53839         cell.className = this.cellClass + " " + this.idToCssName(p.cellId) + " " + p.css;
53840         this.syncRowHeights(rowIndex, rowIndex);
53841     },
53842
53843     calcColumnWidth : function(colIndex, maxRowsToMeasure){
53844         var maxWidth = 0;
53845         if(this.grid.autoSizeHeaders){
53846             var h = this.getHeaderCellMeasure(colIndex);
53847             maxWidth = Math.max(maxWidth, h.scrollWidth);
53848         }
53849         var tb, index;
53850         if(this.cm.isLocked(colIndex)){
53851             tb = this.getLockedTable();
53852             index = colIndex;
53853         }else{
53854             tb = this.getBodyTable();
53855             index = colIndex - this.cm.getLockedCount();
53856         }
53857         if(tb && tb.rows){
53858             var rows = tb.rows;
53859             var stopIndex = Math.min(maxRowsToMeasure || rows.length, rows.length);
53860             for(var i = 0; i < stopIndex; i++){
53861                 var cell = rows[i].childNodes[index].firstChild;
53862                 maxWidth = Math.max(maxWidth, cell.scrollWidth);
53863             }
53864         }
53865         return maxWidth + /*margin for error in IE*/ 5;
53866     },
53867     /**
53868      * Autofit a column to its content.
53869      * @param {Number} colIndex
53870      * @param {Boolean} forceMinSize true to force the column to go smaller if possible
53871      */
53872      autoSizeColumn : function(colIndex, forceMinSize, suppressEvent){
53873          if(this.cm.isHidden(colIndex)){
53874              return; // can't calc a hidden column
53875          }
53876         if(forceMinSize){
53877             var cid = this.cm.getColumnId(colIndex);
53878             this.css.updateRule(this.colSelector +this.idToCssName( cid), "width", this.grid.minColumnWidth + "px");
53879            if(this.grid.autoSizeHeaders){
53880                this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", this.grid.minColumnWidth + "px");
53881            }
53882         }
53883         var newWidth = this.calcColumnWidth(colIndex);
53884         this.cm.setColumnWidth(colIndex,
53885             Math.max(this.grid.minColumnWidth, newWidth), suppressEvent);
53886         if(!suppressEvent){
53887             this.grid.fireEvent("columnresize", colIndex, newWidth);
53888         }
53889     },
53890
53891     /**
53892      * Autofits all columns to their content and then expands to fit any extra space in the grid
53893      */
53894      autoSizeColumns : function(){
53895         var cm = this.grid.colModel;
53896         var colCount = cm.getColumnCount();
53897         for(var i = 0; i < colCount; i++){
53898             this.autoSizeColumn(i, true, true);
53899         }
53900         if(cm.getTotalWidth() < this.scroller.dom.clientWidth){
53901             this.fitColumns();
53902         }else{
53903             this.updateColumns();
53904             this.layout();
53905         }
53906     },
53907
53908     /**
53909      * Autofits all columns to the grid's width proportionate with their current size
53910      * @param {Boolean} reserveScrollSpace Reserve space for a scrollbar
53911      */
53912     fitColumns : function(reserveScrollSpace){
53913         var cm = this.grid.colModel;
53914         var colCount = cm.getColumnCount();
53915         var cols = [];
53916         var width = 0;
53917         var i, w;
53918         for (i = 0; i < colCount; i++){
53919             if(!cm.isHidden(i) && !cm.isFixed(i)){
53920                 w = cm.getColumnWidth(i);
53921                 cols.push(i);
53922                 cols.push(w);
53923                 width += w;
53924             }
53925         }
53926         var avail = Math.min(this.scroller.dom.clientWidth, this.el.getWidth());
53927         if(reserveScrollSpace){
53928             avail -= 17;
53929         }
53930         var frac = (avail - cm.getTotalWidth())/width;
53931         while (cols.length){
53932             w = cols.pop();
53933             i = cols.pop();
53934             cm.setColumnWidth(i, Math.floor(w + w*frac), true);
53935         }
53936         this.updateColumns();
53937         this.layout();
53938     },
53939
53940     onRowSelect : function(rowIndex){
53941         var row = this.getRowComposite(rowIndex);
53942         row.addClass("x-grid-row-selected");
53943     },
53944
53945     onRowDeselect : function(rowIndex){
53946         var row = this.getRowComposite(rowIndex);
53947         row.removeClass("x-grid-row-selected");
53948     },
53949
53950     onCellSelect : function(row, col){
53951         var cell = this.getCell(row, col);
53952         if(cell){
53953             Roo.fly(cell).addClass("x-grid-cell-selected");
53954         }
53955     },
53956
53957     onCellDeselect : function(row, col){
53958         var cell = this.getCell(row, col);
53959         if(cell){
53960             Roo.fly(cell).removeClass("x-grid-cell-selected");
53961         }
53962     },
53963
53964     updateHeaderSortState : function(){
53965         
53966         // sort state can be single { field: xxx, direction : yyy}
53967         // or   { xxx=>ASC , yyy : DESC ..... }
53968         
53969         var mstate = {};
53970         if (!this.ds.multiSort) { 
53971             var state = this.ds.getSortState();
53972             if(!state){
53973                 return;
53974             }
53975             mstate[state.field] = state.direction;
53976             // FIXME... - this is not used here.. but might be elsewhere..
53977             this.sortState = state;
53978             
53979         } else {
53980             mstate = this.ds.sortToggle;
53981         }
53982         //remove existing sort classes..
53983         
53984         var sc = this.sortClasses;
53985         var hds = this.el.select(this.headerSelector).removeClass(sc);
53986         
53987         for(var f in mstate) {
53988         
53989             var sortColumn = this.cm.findColumnIndex(f);
53990             
53991             if(sortColumn != -1){
53992                 var sortDir = mstate[f];        
53993                 hds.item(sortColumn).addClass(sc[sortDir == "DESC" ? 1 : 0]);
53994             }
53995         }
53996         
53997          
53998         
53999     },
54000
54001
54002     handleHeaderClick : function(g, index,e){
54003         
54004         Roo.log("header click");
54005         
54006         if (Roo.isTouch) {
54007             // touch events on header are handled by context
54008             this.handleHdCtx(g,index,e);
54009             return;
54010         }
54011         
54012         
54013         if(this.headersDisabled){
54014             return;
54015         }
54016         var dm = g.dataSource, cm = g.colModel;
54017         if(!cm.isSortable(index)){
54018             return;
54019         }
54020         g.stopEditing();
54021         
54022         if (dm.multiSort) {
54023             // update the sortOrder
54024             var so = [];
54025             for(var i = 0; i < cm.config.length; i++ ) {
54026                 
54027                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined') && (index != i)) {
54028                     continue; // dont' bother, it's not in sort list or being set.
54029                 }
54030                 
54031                 so.push(cm.config[i].dataIndex);
54032             };
54033             dm.sortOrder = so;
54034         }
54035         
54036         
54037         dm.sort(cm.getDataIndex(index));
54038     },
54039
54040
54041     destroy : function(){
54042         if(this.colMenu){
54043             this.colMenu.removeAll();
54044             Roo.menu.MenuMgr.unregister(this.colMenu);
54045             this.colMenu.getEl().remove();
54046             delete this.colMenu;
54047         }
54048         if(this.hmenu){
54049             this.hmenu.removeAll();
54050             Roo.menu.MenuMgr.unregister(this.hmenu);
54051             this.hmenu.getEl().remove();
54052             delete this.hmenu;
54053         }
54054         if(this.grid.enableColumnMove){
54055             var dds = Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
54056             if(dds){
54057                 for(var dd in dds){
54058                     if(!dds[dd].config.isTarget && dds[dd].dragElId){
54059                         var elid = dds[dd].dragElId;
54060                         dds[dd].unreg();
54061                         Roo.get(elid).remove();
54062                     } else if(dds[dd].config.isTarget){
54063                         dds[dd].proxyTop.remove();
54064                         dds[dd].proxyBottom.remove();
54065                         dds[dd].unreg();
54066                     }
54067                     if(Roo.dd.DDM.locationCache[dd]){
54068                         delete Roo.dd.DDM.locationCache[dd];
54069                     }
54070                 }
54071                 delete Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
54072             }
54073         }
54074         Roo.util.CSS.removeStyleSheet(this.idToCssName(this.grid.id) + '-cssrules');
54075         this.bind(null, null);
54076         Roo.EventManager.removeResizeListener(this.onWindowResize, this);
54077     },
54078
54079     handleLockChange : function(){
54080         this.refresh(true);
54081     },
54082
54083     onDenyColumnLock : function(){
54084
54085     },
54086
54087     onDenyColumnHide : function(){
54088
54089     },
54090
54091     handleHdMenuClick : function(item){
54092         var index = this.hdCtxIndex;
54093         var cm = this.cm, ds = this.ds;
54094         switch(item.id){
54095             case "asc":
54096                 ds.sort(cm.getDataIndex(index), "ASC");
54097                 break;
54098             case "desc":
54099                 ds.sort(cm.getDataIndex(index), "DESC");
54100                 break;
54101             case "lock":
54102                 var lc = cm.getLockedCount();
54103                 if(cm.getColumnCount(true) <= lc+1){
54104                     this.onDenyColumnLock();
54105                     return;
54106                 }
54107                 if(lc != index){
54108                     cm.setLocked(index, true, true);
54109                     cm.moveColumn(index, lc);
54110                     this.grid.fireEvent("columnmove", index, lc);
54111                 }else{
54112                     cm.setLocked(index, true);
54113                 }
54114             break;
54115             case "unlock":
54116                 var lc = cm.getLockedCount();
54117                 if((lc-1) != index){
54118                     cm.setLocked(index, false, true);
54119                     cm.moveColumn(index, lc-1);
54120                     this.grid.fireEvent("columnmove", index, lc-1);
54121                 }else{
54122                     cm.setLocked(index, false);
54123                 }
54124             break;
54125             case 'wider': // used to expand cols on touch..
54126             case 'narrow':
54127                 var cw = cm.getColumnWidth(index);
54128                 cw += (item.id == 'wider' ? 1 : -1) * 50;
54129                 cw = Math.max(0, cw);
54130                 cw = Math.min(cw,4000);
54131                 cm.setColumnWidth(index, cw);
54132                 break;
54133                 
54134             default:
54135                 index = cm.getIndexById(item.id.substr(4));
54136                 if(index != -1){
54137                     if(item.checked && cm.getColumnCount(true) <= 1){
54138                         this.onDenyColumnHide();
54139                         return false;
54140                     }
54141                     cm.setHidden(index, item.checked);
54142                 }
54143         }
54144         return true;
54145     },
54146
54147     beforeColMenuShow : function(){
54148         var cm = this.cm,  colCount = cm.getColumnCount();
54149         this.colMenu.removeAll();
54150         for(var i = 0; i < colCount; i++){
54151             this.colMenu.add(new Roo.menu.CheckItem({
54152                 id: "col-"+cm.getColumnId(i),
54153                 text: cm.getColumnHeader(i),
54154                 checked: !cm.isHidden(i),
54155                 hideOnClick:false
54156             }));
54157         }
54158     },
54159
54160     handleHdCtx : function(g, index, e){
54161         e.stopEvent();
54162         var hd = this.getHeaderCell(index);
54163         this.hdCtxIndex = index;
54164         var ms = this.hmenu.items, cm = this.cm;
54165         ms.get("asc").setDisabled(!cm.isSortable(index));
54166         ms.get("desc").setDisabled(!cm.isSortable(index));
54167         if(this.grid.enableColLock !== false){
54168             ms.get("lock").setDisabled(cm.isLocked(index));
54169             ms.get("unlock").setDisabled(!cm.isLocked(index));
54170         }
54171         this.hmenu.show(hd, "tl-bl");
54172     },
54173
54174     handleHdOver : function(e){
54175         var hd = this.findHeaderCell(e.getTarget());
54176         if(hd && !this.headersDisabled){
54177             if(this.grid.colModel.isSortable(this.getCellIndex(hd))){
54178                this.fly(hd).addClass("x-grid-hd-over");
54179             }
54180         }
54181     },
54182
54183     handleHdOut : function(e){
54184         var hd = this.findHeaderCell(e.getTarget());
54185         if(hd){
54186             this.fly(hd).removeClass("x-grid-hd-over");
54187         }
54188     },
54189
54190     handleSplitDblClick : function(e, t){
54191         var i = this.getCellIndex(t);
54192         if(this.grid.enableColumnResize !== false && this.cm.isResizable(i) && !this.cm.isFixed(i)){
54193             this.autoSizeColumn(i, true);
54194             this.layout();
54195         }
54196     },
54197
54198     render : function(){
54199
54200         var cm = this.cm;
54201         var colCount = cm.getColumnCount();
54202
54203         if(this.grid.monitorWindowResize === true){
54204             Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
54205         }
54206         var header = this.renderHeaders();
54207         var body = this.templates.body.apply({rows:""});
54208         var html = this.templates.master.apply({
54209             lockedBody: body,
54210             body: body,
54211             lockedHeader: header[0],
54212             header: header[1]
54213         });
54214
54215         //this.updateColumns();
54216
54217         this.grid.getGridEl().dom.innerHTML = html;
54218
54219         this.initElements();
54220         
54221         // a kludge to fix the random scolling effect in webkit
54222         this.el.on("scroll", function() {
54223             this.el.dom.scrollTop=0; // hopefully not recursive..
54224         },this);
54225
54226         this.scroller.on("scroll", this.handleScroll, this);
54227         this.lockedBody.on("mousewheel", this.handleWheel, this);
54228         this.mainBody.on("mousewheel", this.handleWheel, this);
54229
54230         this.mainHd.on("mouseover", this.handleHdOver, this);
54231         this.mainHd.on("mouseout", this.handleHdOut, this);
54232         this.mainHd.on("dblclick", this.handleSplitDblClick, this,
54233                 {delegate: "."+this.splitClass});
54234
54235         this.lockedHd.on("mouseover", this.handleHdOver, this);
54236         this.lockedHd.on("mouseout", this.handleHdOut, this);
54237         this.lockedHd.on("dblclick", this.handleSplitDblClick, this,
54238                 {delegate: "."+this.splitClass});
54239
54240         if(this.grid.enableColumnResize !== false && Roo.grid.SplitDragZone){
54241             new Roo.grid.SplitDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
54242         }
54243
54244         this.updateSplitters();
54245
54246         if(this.grid.enableColumnMove && Roo.grid.HeaderDragZone){
54247             new Roo.grid.HeaderDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
54248             new Roo.grid.HeaderDropZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
54249         }
54250
54251         if(this.grid.enableCtxMenu !== false && Roo.menu.Menu){
54252             this.hmenu = new Roo.menu.Menu({id: this.grid.id + "-hctx"});
54253             this.hmenu.add(
54254                 {id:"asc", text: this.sortAscText, cls: "xg-hmenu-sort-asc"},
54255                 {id:"desc", text: this.sortDescText, cls: "xg-hmenu-sort-desc"}
54256             );
54257             if(this.grid.enableColLock !== false){
54258                 this.hmenu.add('-',
54259                     {id:"lock", text: this.lockText, cls: "xg-hmenu-lock"},
54260                     {id:"unlock", text: this.unlockText, cls: "xg-hmenu-unlock"}
54261                 );
54262             }
54263             if (Roo.isTouch) {
54264                  this.hmenu.add('-',
54265                     {id:"wider", text: this.columnsWiderText},
54266                     {id:"narrow", text: this.columnsNarrowText }
54267                 );
54268                 
54269                  
54270             }
54271             
54272             if(this.grid.enableColumnHide !== false){
54273
54274                 this.colMenu = new Roo.menu.Menu({id:this.grid.id + "-hcols-menu"});
54275                 this.colMenu.on("beforeshow", this.beforeColMenuShow, this);
54276                 this.colMenu.on("itemclick", this.handleHdMenuClick, this);
54277
54278                 this.hmenu.add('-',
54279                     {id:"columns", text: this.columnsText, menu: this.colMenu}
54280                 );
54281             }
54282             this.hmenu.on("itemclick", this.handleHdMenuClick, this);
54283
54284             this.grid.on("headercontextmenu", this.handleHdCtx, this);
54285         }
54286
54287         if((this.grid.enableDragDrop || this.grid.enableDrag) && Roo.grid.GridDragZone){
54288             this.dd = new Roo.grid.GridDragZone(this.grid, {
54289                 ddGroup : this.grid.ddGroup || 'GridDD'
54290             });
54291             
54292         }
54293
54294         /*
54295         for(var i = 0; i < colCount; i++){
54296             if(cm.isHidden(i)){
54297                 this.hideColumn(i);
54298             }
54299             if(cm.config[i].align){
54300                 this.css.updateRule(this.colSelector + i, "textAlign", cm.config[i].align);
54301                 this.css.updateRule(this.hdSelector + i, "textAlign", cm.config[i].align);
54302             }
54303         }*/
54304         
54305         this.updateHeaderSortState();
54306
54307         this.beforeInitialResize();
54308         this.layout(true);
54309
54310         // two part rendering gives faster view to the user
54311         this.renderPhase2.defer(1, this);
54312     },
54313
54314     renderPhase2 : function(){
54315         // render the rows now
54316         this.refresh();
54317         if(this.grid.autoSizeColumns){
54318             this.autoSizeColumns();
54319         }
54320     },
54321
54322     beforeInitialResize : function(){
54323
54324     },
54325
54326     onColumnSplitterMoved : function(i, w){
54327         this.userResized = true;
54328         var cm = this.grid.colModel;
54329         cm.setColumnWidth(i, w, true);
54330         var cid = cm.getColumnId(i);
54331         this.css.updateRule(this.colSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
54332         this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
54333         this.updateSplitters();
54334         this.layout();
54335         this.grid.fireEvent("columnresize", i, w);
54336     },
54337
54338     syncRowHeights : function(startIndex, endIndex){
54339         if(this.grid.enableRowHeightSync === true && this.cm.getLockedCount() > 0){
54340             startIndex = startIndex || 0;
54341             var mrows = this.getBodyTable().rows;
54342             var lrows = this.getLockedTable().rows;
54343             var len = mrows.length-1;
54344             endIndex = Math.min(endIndex || len, len);
54345             for(var i = startIndex; i <= endIndex; i++){
54346                 var m = mrows[i], l = lrows[i];
54347                 var h = Math.max(m.offsetHeight, l.offsetHeight);
54348                 m.style.height = l.style.height = h + "px";
54349             }
54350         }
54351     },
54352
54353     layout : function(initialRender, is2ndPass){
54354         var g = this.grid;
54355         var auto = g.autoHeight;
54356         var scrollOffset = 16;
54357         var c = g.getGridEl(), cm = this.cm,
54358                 expandCol = g.autoExpandColumn,
54359                 gv = this;
54360         //c.beginMeasure();
54361
54362         if(!c.dom.offsetWidth){ // display:none?
54363             if(initialRender){
54364                 this.lockedWrap.show();
54365                 this.mainWrap.show();
54366             }
54367             return;
54368         }
54369
54370         var hasLock = this.cm.isLocked(0);
54371
54372         var tbh = this.headerPanel.getHeight();
54373         var bbh = this.footerPanel.getHeight();
54374
54375         if(auto){
54376             var ch = this.getBodyTable().offsetHeight + tbh + bbh + this.mainHd.getHeight();
54377             var newHeight = ch + c.getBorderWidth("tb");
54378             if(g.maxHeight){
54379                 newHeight = Math.min(g.maxHeight, newHeight);
54380             }
54381             c.setHeight(newHeight);
54382         }
54383
54384         if(g.autoWidth){
54385             c.setWidth(cm.getTotalWidth()+c.getBorderWidth('lr'));
54386         }
54387
54388         var s = this.scroller;
54389
54390         var csize = c.getSize(true);
54391
54392         this.el.setSize(csize.width, csize.height);
54393
54394         this.headerPanel.setWidth(csize.width);
54395         this.footerPanel.setWidth(csize.width);
54396
54397         var hdHeight = this.mainHd.getHeight();
54398         var vw = csize.width;
54399         var vh = csize.height - (tbh + bbh);
54400
54401         s.setSize(vw, vh);
54402
54403         var bt = this.getBodyTable();
54404         var ltWidth = hasLock ?
54405                       Math.max(this.getLockedTable().offsetWidth, this.lockedHd.dom.firstChild.offsetWidth) : 0;
54406
54407         var scrollHeight = bt.offsetHeight;
54408         var scrollWidth = ltWidth + bt.offsetWidth;
54409         var vscroll = false, hscroll = false;
54410
54411         this.scrollSizer.setSize(scrollWidth, scrollHeight+hdHeight);
54412
54413         var lw = this.lockedWrap, mw = this.mainWrap;
54414         var lb = this.lockedBody, mb = this.mainBody;
54415
54416         setTimeout(function(){
54417             var t = s.dom.offsetTop;
54418             var w = s.dom.clientWidth,
54419                 h = s.dom.clientHeight;
54420
54421             lw.setTop(t);
54422             lw.setSize(ltWidth, h);
54423
54424             mw.setLeftTop(ltWidth, t);
54425             mw.setSize(w-ltWidth, h);
54426
54427             lb.setHeight(h-hdHeight);
54428             mb.setHeight(h-hdHeight);
54429
54430             if(is2ndPass !== true && !gv.userResized && expandCol){
54431                 // high speed resize without full column calculation
54432                 
54433                 var ci = cm.getIndexById(expandCol);
54434                 if (ci < 0) {
54435                     ci = cm.findColumnIndex(expandCol);
54436                 }
54437                 ci = Math.max(0, ci); // make sure it's got at least the first col.
54438                 var expandId = cm.getColumnId(ci);
54439                 var  tw = cm.getTotalWidth(false);
54440                 var currentWidth = cm.getColumnWidth(ci);
54441                 var cw = Math.min(Math.max(((w-tw)+currentWidth-2)-/*scrollbar*/(w <= s.dom.offsetWidth ? 0 : 18), g.autoExpandMin), g.autoExpandMax);
54442                 if(currentWidth != cw){
54443                     cm.setColumnWidth(ci, cw, true);
54444                     gv.css.updateRule(gv.colSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
54445                     gv.css.updateRule(gv.hdSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
54446                     gv.updateSplitters();
54447                     gv.layout(false, true);
54448                 }
54449             }
54450
54451             if(initialRender){
54452                 lw.show();
54453                 mw.show();
54454             }
54455             //c.endMeasure();
54456         }, 10);
54457     },
54458
54459     onWindowResize : function(){
54460         if(!this.grid.monitorWindowResize || this.grid.autoHeight){
54461             return;
54462         }
54463         this.layout();
54464     },
54465
54466     appendFooter : function(parentEl){
54467         return null;
54468     },
54469
54470     sortAscText : "Sort Ascending",
54471     sortDescText : "Sort Descending",
54472     lockText : "Lock Column",
54473     unlockText : "Unlock Column",
54474     columnsText : "Columns",
54475  
54476     columnsWiderText : "Wider",
54477     columnsNarrowText : "Thinner"
54478 });
54479
54480
54481 Roo.grid.GridView.ColumnDragZone = function(grid, hd){
54482     Roo.grid.GridView.ColumnDragZone.superclass.constructor.call(this, grid, hd, null);
54483     this.proxy.el.addClass('x-grid3-col-dd');
54484 };
54485
54486 Roo.extend(Roo.grid.GridView.ColumnDragZone, Roo.grid.HeaderDragZone, {
54487     handleMouseDown : function(e){
54488
54489     },
54490
54491     callHandleMouseDown : function(e){
54492         Roo.grid.GridView.ColumnDragZone.superclass.handleMouseDown.call(this, e);
54493     }
54494 });
54495 /*
54496  * Based on:
54497  * Ext JS Library 1.1.1
54498  * Copyright(c) 2006-2007, Ext JS, LLC.
54499  *
54500  * Originally Released Under LGPL - original licence link has changed is not relivant.
54501  *
54502  * Fork - LGPL
54503  * <script type="text/javascript">
54504  */
54505  
54506 // private
54507 // This is a support class used internally by the Grid components
54508 Roo.grid.SplitDragZone = function(grid, hd, hd2){
54509     this.grid = grid;
54510     this.view = grid.getView();
54511     this.proxy = this.view.resizeProxy;
54512     Roo.grid.SplitDragZone.superclass.constructor.call(this, hd,
54513         "gridSplitters" + this.grid.getGridEl().id, {
54514         dragElId : Roo.id(this.proxy.dom), resizeFrame:false
54515     });
54516     this.setHandleElId(Roo.id(hd));
54517     this.setOuterHandleElId(Roo.id(hd2));
54518     this.scroll = false;
54519 };
54520 Roo.extend(Roo.grid.SplitDragZone, Roo.dd.DDProxy, {
54521     fly: Roo.Element.fly,
54522
54523     b4StartDrag : function(x, y){
54524         this.view.headersDisabled = true;
54525         this.proxy.setHeight(this.view.mainWrap.getHeight());
54526         var w = this.cm.getColumnWidth(this.cellIndex);
54527         var minw = Math.max(w-this.grid.minColumnWidth, 0);
54528         this.resetConstraints();
54529         this.setXConstraint(minw, 1000);
54530         this.setYConstraint(0, 0);
54531         this.minX = x - minw;
54532         this.maxX = x + 1000;
54533         this.startPos = x;
54534         Roo.dd.DDProxy.prototype.b4StartDrag.call(this, x, y);
54535     },
54536
54537
54538     handleMouseDown : function(e){
54539         ev = Roo.EventObject.setEvent(e);
54540         var t = this.fly(ev.getTarget());
54541         if(t.hasClass("x-grid-split")){
54542             this.cellIndex = this.view.getCellIndex(t.dom);
54543             this.split = t.dom;
54544             this.cm = this.grid.colModel;
54545             if(this.cm.isResizable(this.cellIndex) && !this.cm.isFixed(this.cellIndex)){
54546                 Roo.grid.SplitDragZone.superclass.handleMouseDown.apply(this, arguments);
54547             }
54548         }
54549     },
54550
54551     endDrag : function(e){
54552         this.view.headersDisabled = false;
54553         var endX = Math.max(this.minX, Roo.lib.Event.getPageX(e));
54554         var diff = endX - this.startPos;
54555         this.view.onColumnSplitterMoved(this.cellIndex, this.cm.getColumnWidth(this.cellIndex)+diff);
54556     },
54557
54558     autoOffset : function(){
54559         this.setDelta(0,0);
54560     }
54561 });/*
54562  * Based on:
54563  * Ext JS Library 1.1.1
54564  * Copyright(c) 2006-2007, Ext JS, LLC.
54565  *
54566  * Originally Released Under LGPL - original licence link has changed is not relivant.
54567  *
54568  * Fork - LGPL
54569  * <script type="text/javascript">
54570  */
54571  
54572 // private
54573 // This is a support class used internally by the Grid components
54574 Roo.grid.GridDragZone = function(grid, config){
54575     this.view = grid.getView();
54576     Roo.grid.GridDragZone.superclass.constructor.call(this, this.view.mainBody.dom, config);
54577     if(this.view.lockedBody){
54578         this.setHandleElId(Roo.id(this.view.mainBody.dom));
54579         this.setOuterHandleElId(Roo.id(this.view.lockedBody.dom));
54580     }
54581     this.scroll = false;
54582     this.grid = grid;
54583     this.ddel = document.createElement('div');
54584     this.ddel.className = 'x-grid-dd-wrap';
54585 };
54586
54587 Roo.extend(Roo.grid.GridDragZone, Roo.dd.DragZone, {
54588     ddGroup : "GridDD",
54589
54590     getDragData : function(e){
54591         var t = Roo.lib.Event.getTarget(e);
54592         var rowIndex = this.view.findRowIndex(t);
54593         var sm = this.grid.selModel;
54594             
54595         //Roo.log(rowIndex);
54596         
54597         if (sm.getSelectedCell) {
54598             // cell selection..
54599             if (!sm.getSelectedCell()) {
54600                 return false;
54601             }
54602             if (rowIndex != sm.getSelectedCell()[0]) {
54603                 return false;
54604             }
54605         
54606         }
54607         
54608         if(rowIndex !== false){
54609             
54610             // if editorgrid.. 
54611             
54612             
54613             //Roo.log([ sm.getSelectedCell() ? sm.getSelectedCell()[0] : 'NO' , rowIndex ]);
54614                
54615             //if(!sm.isSelected(rowIndex) || e.hasModifier()){
54616               //  
54617             //}
54618             if (e.hasModifier()){
54619                 sm.handleMouseDown(e, t); // non modifier buttons are handled by row select.
54620             }
54621             
54622             Roo.log("getDragData");
54623             
54624             return {
54625                 grid: this.grid,
54626                 ddel: this.ddel,
54627                 rowIndex: rowIndex,
54628                 selections:sm.getSelections ? sm.getSelections() : (
54629                     sm.getSelectedCell() ? [ this.grid.ds.getAt(sm.getSelectedCell()[0]) ] : []
54630                 )
54631             };
54632         }
54633         return false;
54634     },
54635
54636     onInitDrag : function(e){
54637         var data = this.dragData;
54638         this.ddel.innerHTML = this.grid.getDragDropText();
54639         this.proxy.update(this.ddel);
54640         // fire start drag?
54641     },
54642
54643     afterRepair : function(){
54644         this.dragging = false;
54645     },
54646
54647     getRepairXY : function(e, data){
54648         return false;
54649     },
54650
54651     onEndDrag : function(data, e){
54652         // fire end drag?
54653     },
54654
54655     onValidDrop : function(dd, e, id){
54656         // fire drag drop?
54657         this.hideProxy();
54658     },
54659
54660     beforeInvalidDrop : function(e, id){
54661
54662     }
54663 });/*
54664  * Based on:
54665  * Ext JS Library 1.1.1
54666  * Copyright(c) 2006-2007, Ext JS, LLC.
54667  *
54668  * Originally Released Under LGPL - original licence link has changed is not relivant.
54669  *
54670  * Fork - LGPL
54671  * <script type="text/javascript">
54672  */
54673  
54674
54675 /**
54676  * @class Roo.grid.ColumnModel
54677  * @extends Roo.util.Observable
54678  * This is the default implementation of a ColumnModel used by the Grid. It defines
54679  * the columns in the grid.
54680  * <br>Usage:<br>
54681  <pre><code>
54682  var colModel = new Roo.grid.ColumnModel([
54683         {header: "Ticker", width: 60, sortable: true, locked: true},
54684         {header: "Company Name", width: 150, sortable: true},
54685         {header: "Market Cap.", width: 100, sortable: true},
54686         {header: "$ Sales", width: 100, sortable: true, renderer: money},
54687         {header: "Employees", width: 100, sortable: true, resizable: false}
54688  ]);
54689  </code></pre>
54690  * <p>
54691  
54692  * The config options listed for this class are options which may appear in each
54693  * individual column definition.
54694  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
54695  * @constructor
54696  * @param {Object} config An Array of column config objects. See this class's
54697  * config objects for details.
54698 */
54699 Roo.grid.ColumnModel = function(config){
54700         /**
54701      * The config passed into the constructor
54702      */
54703     this.config = config;
54704     this.lookup = {};
54705
54706     // if no id, create one
54707     // if the column does not have a dataIndex mapping,
54708     // map it to the order it is in the config
54709     for(var i = 0, len = config.length; i < len; i++){
54710         var c = config[i];
54711         if(typeof c.dataIndex == "undefined"){
54712             c.dataIndex = i;
54713         }
54714         if(typeof c.renderer == "string"){
54715             c.renderer = Roo.util.Format[c.renderer];
54716         }
54717         if(typeof c.id == "undefined"){
54718             c.id = Roo.id();
54719         }
54720         if(c.editor && c.editor.xtype){
54721             c.editor  = Roo.factory(c.editor, Roo.grid);
54722         }
54723         if(c.editor && c.editor.isFormField){
54724             c.editor = new Roo.grid.GridEditor(c.editor);
54725         }
54726         this.lookup[c.id] = c;
54727     }
54728
54729     /**
54730      * The width of columns which have no width specified (defaults to 100)
54731      * @type Number
54732      */
54733     this.defaultWidth = 100;
54734
54735     /**
54736      * Default sortable of columns which have no sortable specified (defaults to false)
54737      * @type Boolean
54738      */
54739     this.defaultSortable = false;
54740
54741     this.addEvents({
54742         /**
54743              * @event widthchange
54744              * Fires when the width of a column changes.
54745              * @param {ColumnModel} this
54746              * @param {Number} columnIndex The column index
54747              * @param {Number} newWidth The new width
54748              */
54749             "widthchange": true,
54750         /**
54751              * @event headerchange
54752              * Fires when the text of a header changes.
54753              * @param {ColumnModel} this
54754              * @param {Number} columnIndex The column index
54755              * @param {Number} newText The new header text
54756              */
54757             "headerchange": true,
54758         /**
54759              * @event hiddenchange
54760              * Fires when a column is hidden or "unhidden".
54761              * @param {ColumnModel} this
54762              * @param {Number} columnIndex The column index
54763              * @param {Boolean} hidden true if hidden, false otherwise
54764              */
54765             "hiddenchange": true,
54766             /**
54767          * @event columnmoved
54768          * Fires when a column is moved.
54769          * @param {ColumnModel} this
54770          * @param {Number} oldIndex
54771          * @param {Number} newIndex
54772          */
54773         "columnmoved" : true,
54774         /**
54775          * @event columlockchange
54776          * Fires when a column's locked state is changed
54777          * @param {ColumnModel} this
54778          * @param {Number} colIndex
54779          * @param {Boolean} locked true if locked
54780          */
54781         "columnlockchange" : true
54782     });
54783     Roo.grid.ColumnModel.superclass.constructor.call(this);
54784 };
54785 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
54786     /**
54787      * @cfg {String} header The header text to display in the Grid view.
54788      */
54789     /**
54790      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
54791      * {@link Roo.data.Record} definition from which to draw the column's value. If not
54792      * specified, the column's index is used as an index into the Record's data Array.
54793      */
54794     /**
54795      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
54796      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
54797      */
54798     /**
54799      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
54800      * Defaults to the value of the {@link #defaultSortable} property.
54801      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
54802      */
54803     /**
54804      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
54805      */
54806     /**
54807      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
54808      */
54809     /**
54810      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
54811      */
54812     /**
54813      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
54814      */
54815     /**
54816      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
54817      * given the cell's data value. See {@link #setRenderer}. If not specified, the
54818      * default renderer uses the raw data value.
54819      */
54820        /**
54821      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
54822      */
54823     /**
54824      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
54825      */
54826
54827     /**
54828      * Returns the id of the column at the specified index.
54829      * @param {Number} index The column index
54830      * @return {String} the id
54831      */
54832     getColumnId : function(index){
54833         return this.config[index].id;
54834     },
54835
54836     /**
54837      * Returns the column for a specified id.
54838      * @param {String} id The column id
54839      * @return {Object} the column
54840      */
54841     getColumnById : function(id){
54842         return this.lookup[id];
54843     },
54844
54845     
54846     /**
54847      * Returns the column for a specified dataIndex.
54848      * @param {String} dataIndex The column dataIndex
54849      * @return {Object|Boolean} the column or false if not found
54850      */
54851     getColumnByDataIndex: function(dataIndex){
54852         var index = this.findColumnIndex(dataIndex);
54853         return index > -1 ? this.config[index] : false;
54854     },
54855     
54856     /**
54857      * Returns the index for a specified column id.
54858      * @param {String} id The column id
54859      * @return {Number} the index, or -1 if not found
54860      */
54861     getIndexById : function(id){
54862         for(var i = 0, len = this.config.length; i < len; i++){
54863             if(this.config[i].id == id){
54864                 return i;
54865             }
54866         }
54867         return -1;
54868     },
54869     
54870     /**
54871      * Returns the index for a specified column dataIndex.
54872      * @param {String} dataIndex The column dataIndex
54873      * @return {Number} the index, or -1 if not found
54874      */
54875     
54876     findColumnIndex : function(dataIndex){
54877         for(var i = 0, len = this.config.length; i < len; i++){
54878             if(this.config[i].dataIndex == dataIndex){
54879                 return i;
54880             }
54881         }
54882         return -1;
54883     },
54884     
54885     
54886     moveColumn : function(oldIndex, newIndex){
54887         var c = this.config[oldIndex];
54888         this.config.splice(oldIndex, 1);
54889         this.config.splice(newIndex, 0, c);
54890         this.dataMap = null;
54891         this.fireEvent("columnmoved", this, oldIndex, newIndex);
54892     },
54893
54894     isLocked : function(colIndex){
54895         return this.config[colIndex].locked === true;
54896     },
54897
54898     setLocked : function(colIndex, value, suppressEvent){
54899         if(this.isLocked(colIndex) == value){
54900             return;
54901         }
54902         this.config[colIndex].locked = value;
54903         if(!suppressEvent){
54904             this.fireEvent("columnlockchange", this, colIndex, value);
54905         }
54906     },
54907
54908     getTotalLockedWidth : function(){
54909         var totalWidth = 0;
54910         for(var i = 0; i < this.config.length; i++){
54911             if(this.isLocked(i) && !this.isHidden(i)){
54912                 this.totalWidth += this.getColumnWidth(i);
54913             }
54914         }
54915         return totalWidth;
54916     },
54917
54918     getLockedCount : function(){
54919         for(var i = 0, len = this.config.length; i < len; i++){
54920             if(!this.isLocked(i)){
54921                 return i;
54922             }
54923         }
54924     },
54925
54926     /**
54927      * Returns the number of columns.
54928      * @return {Number}
54929      */
54930     getColumnCount : function(visibleOnly){
54931         if(visibleOnly === true){
54932             var c = 0;
54933             for(var i = 0, len = this.config.length; i < len; i++){
54934                 if(!this.isHidden(i)){
54935                     c++;
54936                 }
54937             }
54938             return c;
54939         }
54940         return this.config.length;
54941     },
54942
54943     /**
54944      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
54945      * @param {Function} fn
54946      * @param {Object} scope (optional)
54947      * @return {Array} result
54948      */
54949     getColumnsBy : function(fn, scope){
54950         var r = [];
54951         for(var i = 0, len = this.config.length; i < len; i++){
54952             var c = this.config[i];
54953             if(fn.call(scope||this, c, i) === true){
54954                 r[r.length] = c;
54955             }
54956         }
54957         return r;
54958     },
54959
54960     /**
54961      * Returns true if the specified column is sortable.
54962      * @param {Number} col The column index
54963      * @return {Boolean}
54964      */
54965     isSortable : function(col){
54966         if(typeof this.config[col].sortable == "undefined"){
54967             return this.defaultSortable;
54968         }
54969         return this.config[col].sortable;
54970     },
54971
54972     /**
54973      * Returns the rendering (formatting) function defined for the column.
54974      * @param {Number} col The column index.
54975      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
54976      */
54977     getRenderer : function(col){
54978         if(!this.config[col].renderer){
54979             return Roo.grid.ColumnModel.defaultRenderer;
54980         }
54981         return this.config[col].renderer;
54982     },
54983
54984     /**
54985      * Sets the rendering (formatting) function for a column.
54986      * @param {Number} col The column index
54987      * @param {Function} fn The function to use to process the cell's raw data
54988      * to return HTML markup for the grid view. The render function is called with
54989      * the following parameters:<ul>
54990      * <li>Data value.</li>
54991      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
54992      * <li>css A CSS style string to apply to the table cell.</li>
54993      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
54994      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
54995      * <li>Row index</li>
54996      * <li>Column index</li>
54997      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
54998      */
54999     setRenderer : function(col, fn){
55000         this.config[col].renderer = fn;
55001     },
55002
55003     /**
55004      * Returns the width for the specified column.
55005      * @param {Number} col The column index
55006      * @return {Number}
55007      */
55008     getColumnWidth : function(col){
55009         return this.config[col].width * 1 || this.defaultWidth;
55010     },
55011
55012     /**
55013      * Sets the width for a column.
55014      * @param {Number} col The column index
55015      * @param {Number} width The new width
55016      */
55017     setColumnWidth : function(col, width, suppressEvent){
55018         this.config[col].width = width;
55019         this.totalWidth = null;
55020         if(!suppressEvent){
55021              this.fireEvent("widthchange", this, col, width);
55022         }
55023     },
55024
55025     /**
55026      * Returns the total width of all columns.
55027      * @param {Boolean} includeHidden True to include hidden column widths
55028      * @return {Number}
55029      */
55030     getTotalWidth : function(includeHidden){
55031         if(!this.totalWidth){
55032             this.totalWidth = 0;
55033             for(var i = 0, len = this.config.length; i < len; i++){
55034                 if(includeHidden || !this.isHidden(i)){
55035                     this.totalWidth += this.getColumnWidth(i);
55036                 }
55037             }
55038         }
55039         return this.totalWidth;
55040     },
55041
55042     /**
55043      * Returns the header for the specified column.
55044      * @param {Number} col The column index
55045      * @return {String}
55046      */
55047     getColumnHeader : function(col){
55048         return this.config[col].header;
55049     },
55050
55051     /**
55052      * Sets the header for a column.
55053      * @param {Number} col The column index
55054      * @param {String} header The new header
55055      */
55056     setColumnHeader : function(col, header){
55057         this.config[col].header = header;
55058         this.fireEvent("headerchange", this, col, header);
55059     },
55060
55061     /**
55062      * Returns the tooltip for the specified column.
55063      * @param {Number} col The column index
55064      * @return {String}
55065      */
55066     getColumnTooltip : function(col){
55067             return this.config[col].tooltip;
55068     },
55069     /**
55070      * Sets the tooltip for a column.
55071      * @param {Number} col The column index
55072      * @param {String} tooltip The new tooltip
55073      */
55074     setColumnTooltip : function(col, tooltip){
55075             this.config[col].tooltip = tooltip;
55076     },
55077
55078     /**
55079      * Returns the dataIndex for the specified column.
55080      * @param {Number} col The column index
55081      * @return {Number}
55082      */
55083     getDataIndex : function(col){
55084         return this.config[col].dataIndex;
55085     },
55086
55087     /**
55088      * Sets the dataIndex for a column.
55089      * @param {Number} col The column index
55090      * @param {Number} dataIndex The new dataIndex
55091      */
55092     setDataIndex : function(col, dataIndex){
55093         this.config[col].dataIndex = dataIndex;
55094     },
55095
55096     
55097     
55098     /**
55099      * Returns true if the cell is editable.
55100      * @param {Number} colIndex The column index
55101      * @param {Number} rowIndex The row index
55102      * @return {Boolean}
55103      */
55104     isCellEditable : function(colIndex, rowIndex){
55105         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
55106     },
55107
55108     /**
55109      * Returns the editor defined for the cell/column.
55110      * return false or null to disable editing.
55111      * @param {Number} colIndex The column index
55112      * @param {Number} rowIndex The row index
55113      * @return {Object}
55114      */
55115     getCellEditor : function(colIndex, rowIndex){
55116         return this.config[colIndex].editor;
55117     },
55118
55119     /**
55120      * Sets if a column is editable.
55121      * @param {Number} col The column index
55122      * @param {Boolean} editable True if the column is editable
55123      */
55124     setEditable : function(col, editable){
55125         this.config[col].editable = editable;
55126     },
55127
55128
55129     /**
55130      * Returns true if the column is hidden.
55131      * @param {Number} colIndex The column index
55132      * @return {Boolean}
55133      */
55134     isHidden : function(colIndex){
55135         return this.config[colIndex].hidden;
55136     },
55137
55138
55139     /**
55140      * Returns true if the column width cannot be changed
55141      */
55142     isFixed : function(colIndex){
55143         return this.config[colIndex].fixed;
55144     },
55145
55146     /**
55147      * Returns true if the column can be resized
55148      * @return {Boolean}
55149      */
55150     isResizable : function(colIndex){
55151         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
55152     },
55153     /**
55154      * Sets if a column is hidden.
55155      * @param {Number} colIndex The column index
55156      * @param {Boolean} hidden True if the column is hidden
55157      */
55158     setHidden : function(colIndex, hidden){
55159         this.config[colIndex].hidden = hidden;
55160         this.totalWidth = null;
55161         this.fireEvent("hiddenchange", this, colIndex, hidden);
55162     },
55163
55164     /**
55165      * Sets the editor for a column.
55166      * @param {Number} col The column index
55167      * @param {Object} editor The editor object
55168      */
55169     setEditor : function(col, editor){
55170         this.config[col].editor = editor;
55171     }
55172 });
55173
55174 Roo.grid.ColumnModel.defaultRenderer = function(value){
55175         if(typeof value == "string" && value.length < 1){
55176             return "&#160;";
55177         }
55178         return value;
55179 };
55180
55181 // Alias for backwards compatibility
55182 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
55183 /*
55184  * Based on:
55185  * Ext JS Library 1.1.1
55186  * Copyright(c) 2006-2007, Ext JS, LLC.
55187  *
55188  * Originally Released Under LGPL - original licence link has changed is not relivant.
55189  *
55190  * Fork - LGPL
55191  * <script type="text/javascript">
55192  */
55193
55194 /**
55195  * @class Roo.grid.AbstractSelectionModel
55196  * @extends Roo.util.Observable
55197  * Abstract base class for grid SelectionModels.  It provides the interface that should be
55198  * implemented by descendant classes.  This class should not be directly instantiated.
55199  * @constructor
55200  */
55201 Roo.grid.AbstractSelectionModel = function(){
55202     this.locked = false;
55203     Roo.grid.AbstractSelectionModel.superclass.constructor.call(this);
55204 };
55205
55206 Roo.extend(Roo.grid.AbstractSelectionModel, Roo.util.Observable,  {
55207     /** @ignore Called by the grid automatically. Do not call directly. */
55208     init : function(grid){
55209         this.grid = grid;
55210         this.initEvents();
55211     },
55212
55213     /**
55214      * Locks the selections.
55215      */
55216     lock : function(){
55217         this.locked = true;
55218     },
55219
55220     /**
55221      * Unlocks the selections.
55222      */
55223     unlock : function(){
55224         this.locked = false;
55225     },
55226
55227     /**
55228      * Returns true if the selections are locked.
55229      * @return {Boolean}
55230      */
55231     isLocked : function(){
55232         return this.locked;
55233     }
55234 });/*
55235  * Based on:
55236  * Ext JS Library 1.1.1
55237  * Copyright(c) 2006-2007, Ext JS, LLC.
55238  *
55239  * Originally Released Under LGPL - original licence link has changed is not relivant.
55240  *
55241  * Fork - LGPL
55242  * <script type="text/javascript">
55243  */
55244 /**
55245  * @extends Roo.grid.AbstractSelectionModel
55246  * @class Roo.grid.RowSelectionModel
55247  * The default SelectionModel used by {@link Roo.grid.Grid}.
55248  * It supports multiple selections and keyboard selection/navigation. 
55249  * @constructor
55250  * @param {Object} config
55251  */
55252 Roo.grid.RowSelectionModel = function(config){
55253     Roo.apply(this, config);
55254     this.selections = new Roo.util.MixedCollection(false, function(o){
55255         return o.id;
55256     });
55257
55258     this.last = false;
55259     this.lastActive = false;
55260
55261     this.addEvents({
55262         /**
55263              * @event selectionchange
55264              * Fires when the selection changes
55265              * @param {SelectionModel} this
55266              */
55267             "selectionchange" : true,
55268         /**
55269              * @event afterselectionchange
55270              * Fires after the selection changes (eg. by key press or clicking)
55271              * @param {SelectionModel} this
55272              */
55273             "afterselectionchange" : true,
55274         /**
55275              * @event beforerowselect
55276              * Fires when a row is selected being selected, return false to cancel.
55277              * @param {SelectionModel} this
55278              * @param {Number} rowIndex The selected index
55279              * @param {Boolean} keepExisting False if other selections will be cleared
55280              */
55281             "beforerowselect" : true,
55282         /**
55283              * @event rowselect
55284              * Fires when a row is selected.
55285              * @param {SelectionModel} this
55286              * @param {Number} rowIndex The selected index
55287              * @param {Roo.data.Record} r The record
55288              */
55289             "rowselect" : true,
55290         /**
55291              * @event rowdeselect
55292              * Fires when a row is deselected.
55293              * @param {SelectionModel} this
55294              * @param {Number} rowIndex The selected index
55295              */
55296         "rowdeselect" : true
55297     });
55298     Roo.grid.RowSelectionModel.superclass.constructor.call(this);
55299     this.locked = false;
55300 };
55301
55302 Roo.extend(Roo.grid.RowSelectionModel, Roo.grid.AbstractSelectionModel,  {
55303     /**
55304      * @cfg {Boolean} singleSelect
55305      * True to allow selection of only one row at a time (defaults to false)
55306      */
55307     singleSelect : false,
55308
55309     // private
55310     initEvents : function(){
55311
55312         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
55313             this.grid.on("mousedown", this.handleMouseDown, this);
55314         }else{ // allow click to work like normal
55315             this.grid.on("rowclick", this.handleDragableRowClick, this);
55316         }
55317
55318         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
55319             "up" : function(e){
55320                 if(!e.shiftKey){
55321                     this.selectPrevious(e.shiftKey);
55322                 }else if(this.last !== false && this.lastActive !== false){
55323                     var last = this.last;
55324                     this.selectRange(this.last,  this.lastActive-1);
55325                     this.grid.getView().focusRow(this.lastActive);
55326                     if(last !== false){
55327                         this.last = last;
55328                     }
55329                 }else{
55330                     this.selectFirstRow();
55331                 }
55332                 this.fireEvent("afterselectionchange", this);
55333             },
55334             "down" : function(e){
55335                 if(!e.shiftKey){
55336                     this.selectNext(e.shiftKey);
55337                 }else if(this.last !== false && this.lastActive !== false){
55338                     var last = this.last;
55339                     this.selectRange(this.last,  this.lastActive+1);
55340                     this.grid.getView().focusRow(this.lastActive);
55341                     if(last !== false){
55342                         this.last = last;
55343                     }
55344                 }else{
55345                     this.selectFirstRow();
55346                 }
55347                 this.fireEvent("afterselectionchange", this);
55348             },
55349             scope: this
55350         });
55351
55352         var view = this.grid.view;
55353         view.on("refresh", this.onRefresh, this);
55354         view.on("rowupdated", this.onRowUpdated, this);
55355         view.on("rowremoved", this.onRemove, this);
55356     },
55357
55358     // private
55359     onRefresh : function(){
55360         var ds = this.grid.dataSource, i, v = this.grid.view;
55361         var s = this.selections;
55362         s.each(function(r){
55363             if((i = ds.indexOfId(r.id)) != -1){
55364                 v.onRowSelect(i);
55365             }else{
55366                 s.remove(r);
55367             }
55368         });
55369     },
55370
55371     // private
55372     onRemove : function(v, index, r){
55373         this.selections.remove(r);
55374     },
55375
55376     // private
55377     onRowUpdated : function(v, index, r){
55378         if(this.isSelected(r)){
55379             v.onRowSelect(index);
55380         }
55381     },
55382
55383     /**
55384      * Select records.
55385      * @param {Array} records The records to select
55386      * @param {Boolean} keepExisting (optional) True to keep existing selections
55387      */
55388     selectRecords : function(records, keepExisting){
55389         if(!keepExisting){
55390             this.clearSelections();
55391         }
55392         var ds = this.grid.dataSource;
55393         for(var i = 0, len = records.length; i < len; i++){
55394             this.selectRow(ds.indexOf(records[i]), true);
55395         }
55396     },
55397
55398     /**
55399      * Gets the number of selected rows.
55400      * @return {Number}
55401      */
55402     getCount : function(){
55403         return this.selections.length;
55404     },
55405
55406     /**
55407      * Selects the first row in the grid.
55408      */
55409     selectFirstRow : function(){
55410         this.selectRow(0);
55411     },
55412
55413     /**
55414      * Select the last row.
55415      * @param {Boolean} keepExisting (optional) True to keep existing selections
55416      */
55417     selectLastRow : function(keepExisting){
55418         this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
55419     },
55420
55421     /**
55422      * Selects the row immediately following the last selected row.
55423      * @param {Boolean} keepExisting (optional) True to keep existing selections
55424      */
55425     selectNext : function(keepExisting){
55426         if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
55427             this.selectRow(this.last+1, keepExisting);
55428             this.grid.getView().focusRow(this.last);
55429         }
55430     },
55431
55432     /**
55433      * Selects the row that precedes the last selected row.
55434      * @param {Boolean} keepExisting (optional) True to keep existing selections
55435      */
55436     selectPrevious : function(keepExisting){
55437         if(this.last){
55438             this.selectRow(this.last-1, keepExisting);
55439             this.grid.getView().focusRow(this.last);
55440         }
55441     },
55442
55443     /**
55444      * Returns the selected records
55445      * @return {Array} Array of selected records
55446      */
55447     getSelections : function(){
55448         return [].concat(this.selections.items);
55449     },
55450
55451     /**
55452      * Returns the first selected record.
55453      * @return {Record}
55454      */
55455     getSelected : function(){
55456         return this.selections.itemAt(0);
55457     },
55458
55459
55460     /**
55461      * Clears all selections.
55462      */
55463     clearSelections : function(fast){
55464         if(this.locked) return;
55465         if(fast !== true){
55466             var ds = this.grid.dataSource;
55467             var s = this.selections;
55468             s.each(function(r){
55469                 this.deselectRow(ds.indexOfId(r.id));
55470             }, this);
55471             s.clear();
55472         }else{
55473             this.selections.clear();
55474         }
55475         this.last = false;
55476     },
55477
55478
55479     /**
55480      * Selects all rows.
55481      */
55482     selectAll : function(){
55483         if(this.locked) return;
55484         this.selections.clear();
55485         for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
55486             this.selectRow(i, true);
55487         }
55488     },
55489
55490     /**
55491      * Returns True if there is a selection.
55492      * @return {Boolean}
55493      */
55494     hasSelection : function(){
55495         return this.selections.length > 0;
55496     },
55497
55498     /**
55499      * Returns True if the specified row is selected.
55500      * @param {Number/Record} record The record or index of the record to check
55501      * @return {Boolean}
55502      */
55503     isSelected : function(index){
55504         var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
55505         return (r && this.selections.key(r.id) ? true : false);
55506     },
55507
55508     /**
55509      * Returns True if the specified record id is selected.
55510      * @param {String} id The id of record to check
55511      * @return {Boolean}
55512      */
55513     isIdSelected : function(id){
55514         return (this.selections.key(id) ? true : false);
55515     },
55516
55517     // private
55518     handleMouseDown : function(e, t){
55519         var view = this.grid.getView(), rowIndex;
55520         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
55521             return;
55522         };
55523         if(e.shiftKey && this.last !== false){
55524             var last = this.last;
55525             this.selectRange(last, rowIndex, e.ctrlKey);
55526             this.last = last; // reset the last
55527             view.focusRow(rowIndex);
55528         }else{
55529             var isSelected = this.isSelected(rowIndex);
55530             if(e.button !== 0 && isSelected){
55531                 view.focusRow(rowIndex);
55532             }else if(e.ctrlKey && isSelected){
55533                 this.deselectRow(rowIndex);
55534             }else if(!isSelected){
55535                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
55536                 view.focusRow(rowIndex);
55537             }
55538         }
55539         this.fireEvent("afterselectionchange", this);
55540     },
55541     // private
55542     handleDragableRowClick :  function(grid, rowIndex, e) 
55543     {
55544         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
55545             this.selectRow(rowIndex, false);
55546             grid.view.focusRow(rowIndex);
55547              this.fireEvent("afterselectionchange", this);
55548         }
55549     },
55550     
55551     /**
55552      * Selects multiple rows.
55553      * @param {Array} rows Array of the indexes of the row to select
55554      * @param {Boolean} keepExisting (optional) True to keep existing selections
55555      */
55556     selectRows : function(rows, keepExisting){
55557         if(!keepExisting){
55558             this.clearSelections();
55559         }
55560         for(var i = 0, len = rows.length; i < len; i++){
55561             this.selectRow(rows[i], true);
55562         }
55563     },
55564
55565     /**
55566      * Selects a range of rows. All rows in between startRow and endRow are also selected.
55567      * @param {Number} startRow The index of the first row in the range
55568      * @param {Number} endRow The index of the last row in the range
55569      * @param {Boolean} keepExisting (optional) True to retain existing selections
55570      */
55571     selectRange : function(startRow, endRow, keepExisting){
55572         if(this.locked) return;
55573         if(!keepExisting){
55574             this.clearSelections();
55575         }
55576         if(startRow <= endRow){
55577             for(var i = startRow; i <= endRow; i++){
55578                 this.selectRow(i, true);
55579             }
55580         }else{
55581             for(var i = startRow; i >= endRow; i--){
55582                 this.selectRow(i, true);
55583             }
55584         }
55585     },
55586
55587     /**
55588      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
55589      * @param {Number} startRow The index of the first row in the range
55590      * @param {Number} endRow The index of the last row in the range
55591      */
55592     deselectRange : function(startRow, endRow, preventViewNotify){
55593         if(this.locked) return;
55594         for(var i = startRow; i <= endRow; i++){
55595             this.deselectRow(i, preventViewNotify);
55596         }
55597     },
55598
55599     /**
55600      * Selects a row.
55601      * @param {Number} row The index of the row to select
55602      * @param {Boolean} keepExisting (optional) True to keep existing selections
55603      */
55604     selectRow : function(index, keepExisting, preventViewNotify){
55605         if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) return;
55606         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
55607             if(!keepExisting || this.singleSelect){
55608                 this.clearSelections();
55609             }
55610             var r = this.grid.dataSource.getAt(index);
55611             this.selections.add(r);
55612             this.last = this.lastActive = index;
55613             if(!preventViewNotify){
55614                 this.grid.getView().onRowSelect(index);
55615             }
55616             this.fireEvent("rowselect", this, index, r);
55617             this.fireEvent("selectionchange", this);
55618         }
55619     },
55620
55621     /**
55622      * Deselects a row.
55623      * @param {Number} row The index of the row to deselect
55624      */
55625     deselectRow : function(index, preventViewNotify){
55626         if(this.locked) return;
55627         if(this.last == index){
55628             this.last = false;
55629         }
55630         if(this.lastActive == index){
55631             this.lastActive = false;
55632         }
55633         var r = this.grid.dataSource.getAt(index);
55634         this.selections.remove(r);
55635         if(!preventViewNotify){
55636             this.grid.getView().onRowDeselect(index);
55637         }
55638         this.fireEvent("rowdeselect", this, index);
55639         this.fireEvent("selectionchange", this);
55640     },
55641
55642     // private
55643     restoreLast : function(){
55644         if(this._last){
55645             this.last = this._last;
55646         }
55647     },
55648
55649     // private
55650     acceptsNav : function(row, col, cm){
55651         return !cm.isHidden(col) && cm.isCellEditable(col, row);
55652     },
55653
55654     // private
55655     onEditorKey : function(field, e){
55656         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
55657         if(k == e.TAB){
55658             e.stopEvent();
55659             ed.completeEdit();
55660             if(e.shiftKey){
55661                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
55662             }else{
55663                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
55664             }
55665         }else if(k == e.ENTER && !e.ctrlKey){
55666             e.stopEvent();
55667             ed.completeEdit();
55668             if(e.shiftKey){
55669                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
55670             }else{
55671                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
55672             }
55673         }else if(k == e.ESC){
55674             ed.cancelEdit();
55675         }
55676         if(newCell){
55677             g.startEditing(newCell[0], newCell[1]);
55678         }
55679     }
55680 });/*
55681  * Based on:
55682  * Ext JS Library 1.1.1
55683  * Copyright(c) 2006-2007, Ext JS, LLC.
55684  *
55685  * Originally Released Under LGPL - original licence link has changed is not relivant.
55686  *
55687  * Fork - LGPL
55688  * <script type="text/javascript">
55689  */
55690 /**
55691  * @class Roo.grid.CellSelectionModel
55692  * @extends Roo.grid.AbstractSelectionModel
55693  * This class provides the basic implementation for cell selection in a grid.
55694  * @constructor
55695  * @param {Object} config The object containing the configuration of this model.
55696  * @cfg {Boolean} enter_is_tab Enter behaves the same as tab. (eg. goes to next cell) default: false
55697  */
55698 Roo.grid.CellSelectionModel = function(config){
55699     Roo.apply(this, config);
55700
55701     this.selection = null;
55702
55703     this.addEvents({
55704         /**
55705              * @event beforerowselect
55706              * Fires before a cell is selected.
55707              * @param {SelectionModel} this
55708              * @param {Number} rowIndex The selected row index
55709              * @param {Number} colIndex The selected cell index
55710              */
55711             "beforecellselect" : true,
55712         /**
55713              * @event cellselect
55714              * Fires when a cell is selected.
55715              * @param {SelectionModel} this
55716              * @param {Number} rowIndex The selected row index
55717              * @param {Number} colIndex The selected cell index
55718              */
55719             "cellselect" : true,
55720         /**
55721              * @event selectionchange
55722              * Fires when the active selection changes.
55723              * @param {SelectionModel} this
55724              * @param {Object} selection null for no selection or an object (o) with two properties
55725                 <ul>
55726                 <li>o.record: the record object for the row the selection is in</li>
55727                 <li>o.cell: An array of [rowIndex, columnIndex]</li>
55728                 </ul>
55729              */
55730             "selectionchange" : true,
55731         /**
55732              * @event tabend
55733              * Fires when the tab (or enter) was pressed on the last editable cell
55734              * You can use this to trigger add new row.
55735              * @param {SelectionModel} this
55736              */
55737             "tabend" : true,
55738          /**
55739              * @event beforeeditnext
55740              * Fires before the next editable sell is made active
55741              * You can use this to skip to another cell or fire the tabend
55742              *    if you set cell to false
55743              * @param {Object} eventdata object : { cell : [ row, col ] } 
55744              */
55745             "beforeeditnext" : true
55746     });
55747     Roo.grid.CellSelectionModel.superclass.constructor.call(this);
55748 };
55749
55750 Roo.extend(Roo.grid.CellSelectionModel, Roo.grid.AbstractSelectionModel,  {
55751     
55752     enter_is_tab: false,
55753
55754     /** @ignore */
55755     initEvents : function(){
55756         this.grid.on("mousedown", this.handleMouseDown, this);
55757         this.grid.getGridEl().on(Roo.isIE ? "keydown" : "keypress", this.handleKeyDown, this);
55758         var view = this.grid.view;
55759         view.on("refresh", this.onViewChange, this);
55760         view.on("rowupdated", this.onRowUpdated, this);
55761         view.on("beforerowremoved", this.clearSelections, this);
55762         view.on("beforerowsinserted", this.clearSelections, this);
55763         if(this.grid.isEditor){
55764             this.grid.on("beforeedit", this.beforeEdit,  this);
55765         }
55766     },
55767
55768         //private
55769     beforeEdit : function(e){
55770         this.select(e.row, e.column, false, true, e.record);
55771     },
55772
55773         //private
55774     onRowUpdated : function(v, index, r){
55775         if(this.selection && this.selection.record == r){
55776             v.onCellSelect(index, this.selection.cell[1]);
55777         }
55778     },
55779
55780         //private
55781     onViewChange : function(){
55782         this.clearSelections(true);
55783     },
55784
55785         /**
55786          * Returns the currently selected cell,.
55787          * @return {Array} The selected cell (row, column) or null if none selected.
55788          */
55789     getSelectedCell : function(){
55790         return this.selection ? this.selection.cell : null;
55791     },
55792
55793     /**
55794      * Clears all selections.
55795      * @param {Boolean} true to prevent the gridview from being notified about the change.
55796      */
55797     clearSelections : function(preventNotify){
55798         var s = this.selection;
55799         if(s){
55800             if(preventNotify !== true){
55801                 this.grid.view.onCellDeselect(s.cell[0], s.cell[1]);
55802             }
55803             this.selection = null;
55804             this.fireEvent("selectionchange", this, null);
55805         }
55806     },
55807
55808     /**
55809      * Returns true if there is a selection.
55810      * @return {Boolean}
55811      */
55812     hasSelection : function(){
55813         return this.selection ? true : false;
55814     },
55815
55816     /** @ignore */
55817     handleMouseDown : function(e, t){
55818         var v = this.grid.getView();
55819         if(this.isLocked()){
55820             return;
55821         };
55822         var row = v.findRowIndex(t);
55823         var cell = v.findCellIndex(t);
55824         if(row !== false && cell !== false){
55825             this.select(row, cell);
55826         }
55827     },
55828
55829     /**
55830      * Selects a cell.
55831      * @param {Number} rowIndex
55832      * @param {Number} collIndex
55833      */
55834     select : function(rowIndex, colIndex, preventViewNotify, preventFocus, /*internal*/ r){
55835         if(this.fireEvent("beforecellselect", this, rowIndex, colIndex) !== false){
55836             this.clearSelections();
55837             r = r || this.grid.dataSource.getAt(rowIndex);
55838             this.selection = {
55839                 record : r,
55840                 cell : [rowIndex, colIndex]
55841             };
55842             if(!preventViewNotify){
55843                 var v = this.grid.getView();
55844                 v.onCellSelect(rowIndex, colIndex);
55845                 if(preventFocus !== true){
55846                     v.focusCell(rowIndex, colIndex);
55847                 }
55848             }
55849             this.fireEvent("cellselect", this, rowIndex, colIndex);
55850             this.fireEvent("selectionchange", this, this.selection);
55851         }
55852     },
55853
55854         //private
55855     isSelectable : function(rowIndex, colIndex, cm){
55856         return !cm.isHidden(colIndex);
55857     },
55858
55859     /** @ignore */
55860     handleKeyDown : function(e){
55861         //Roo.log('Cell Sel Model handleKeyDown');
55862         if(!e.isNavKeyPress()){
55863             return;
55864         }
55865         var g = this.grid, s = this.selection;
55866         if(!s){
55867             e.stopEvent();
55868             var cell = g.walkCells(0, 0, 1, this.isSelectable,  this);
55869             if(cell){
55870                 this.select(cell[0], cell[1]);
55871             }
55872             return;
55873         }
55874         var sm = this;
55875         var walk = function(row, col, step){
55876             return g.walkCells(row, col, step, sm.isSelectable,  sm);
55877         };
55878         var k = e.getKey(), r = s.cell[0], c = s.cell[1];
55879         var newCell;
55880
55881       
55882
55883         switch(k){
55884             case e.TAB:
55885                 // handled by onEditorKey
55886                 if (g.isEditor && g.editing) {
55887                     return;
55888                 }
55889                 if(e.shiftKey) {
55890                     newCell = walk(r, c-1, -1);
55891                 } else {
55892                     newCell = walk(r, c+1, 1);
55893                 }
55894                 break;
55895             
55896             case e.DOWN:
55897                newCell = walk(r+1, c, 1);
55898                 break;
55899             
55900             case e.UP:
55901                 newCell = walk(r-1, c, -1);
55902                 break;
55903             
55904             case e.RIGHT:
55905                 newCell = walk(r, c+1, 1);
55906                 break;
55907             
55908             case e.LEFT:
55909                 newCell = walk(r, c-1, -1);
55910                 break;
55911             
55912             case e.ENTER:
55913                 
55914                 if(g.isEditor && !g.editing){
55915                    g.startEditing(r, c);
55916                    e.stopEvent();
55917                    return;
55918                 }
55919                 
55920                 
55921              break;
55922         };
55923         if(newCell){
55924             this.select(newCell[0], newCell[1]);
55925             e.stopEvent();
55926             
55927         }
55928     },
55929
55930     acceptsNav : function(row, col, cm){
55931         return !cm.isHidden(col) && cm.isCellEditable(col, row);
55932     },
55933     /**
55934      * Selects a cell.
55935      * @param {Number} field (not used) - as it's normally used as a listener
55936      * @param {Number} e - event - fake it by using
55937      *
55938      * var e = Roo.EventObjectImpl.prototype;
55939      * e.keyCode = e.TAB
55940      *
55941      * 
55942      */
55943     onEditorKey : function(field, e){
55944         
55945         var k = e.getKey(),
55946             newCell,
55947             g = this.grid,
55948             ed = g.activeEditor,
55949             forward = false;
55950         ///Roo.log('onEditorKey' + k);
55951         
55952         
55953         if (this.enter_is_tab && k == e.ENTER) {
55954             k = e.TAB;
55955         }
55956         
55957         if(k == e.TAB){
55958             if(e.shiftKey){
55959                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
55960             }else{
55961                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
55962                 forward = true;
55963             }
55964             
55965             e.stopEvent();
55966             
55967         } else if(k == e.ENTER &&  !e.ctrlKey){
55968             ed.completeEdit();
55969             e.stopEvent();
55970             newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
55971         
55972                 } else if(k == e.ESC){
55973             ed.cancelEdit();
55974         }
55975                 
55976         if (newCell) {
55977             var ecall = { cell : newCell, forward : forward };
55978             this.fireEvent('beforeeditnext', ecall );
55979             newCell = ecall.cell;
55980                         forward = ecall.forward;
55981         }
55982                 
55983         if(newCell){
55984             //Roo.log('next cell after edit');
55985             g.startEditing.defer(100, g, [newCell[0], newCell[1]]);
55986         } else if (forward) {
55987             // tabbed past last
55988             this.fireEvent.defer(100, this, ['tabend',this]);
55989         }
55990     }
55991 });/*
55992  * Based on:
55993  * Ext JS Library 1.1.1
55994  * Copyright(c) 2006-2007, Ext JS, LLC.
55995  *
55996  * Originally Released Under LGPL - original licence link has changed is not relivant.
55997  *
55998  * Fork - LGPL
55999  * <script type="text/javascript">
56000  */
56001  
56002 /**
56003  * @class Roo.grid.EditorGrid
56004  * @extends Roo.grid.Grid
56005  * Class for creating and editable grid.
56006  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered - 
56007  * The container MUST have some type of size defined for the grid to fill. The container will be 
56008  * automatically set to position relative if it isn't already.
56009  * @param {Object} dataSource The data model to bind to
56010  * @param {Object} colModel The column model with info about this grid's columns
56011  */
56012 Roo.grid.EditorGrid = function(container, config){
56013     Roo.grid.EditorGrid.superclass.constructor.call(this, container, config);
56014     this.getGridEl().addClass("xedit-grid");
56015
56016     if(!this.selModel){
56017         this.selModel = new Roo.grid.CellSelectionModel();
56018     }
56019
56020     this.activeEditor = null;
56021
56022         this.addEvents({
56023             /**
56024              * @event beforeedit
56025              * Fires before cell editing is triggered. The edit event object has the following properties <br />
56026              * <ul style="padding:5px;padding-left:16px;">
56027              * <li>grid - This grid</li>
56028              * <li>record - The record being edited</li>
56029              * <li>field - The field name being edited</li>
56030              * <li>value - The value for the field being edited.</li>
56031              * <li>row - The grid row index</li>
56032              * <li>column - The grid column index</li>
56033              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
56034              * </ul>
56035              * @param {Object} e An edit event (see above for description)
56036              */
56037             "beforeedit" : true,
56038             /**
56039              * @event afteredit
56040              * Fires after a cell is edited. <br />
56041              * <ul style="padding:5px;padding-left:16px;">
56042              * <li>grid - This grid</li>
56043              * <li>record - The record being edited</li>
56044              * <li>field - The field name being edited</li>
56045              * <li>value - The value being set</li>
56046              * <li>originalValue - The original value for the field, before the edit.</li>
56047              * <li>row - The grid row index</li>
56048              * <li>column - The grid column index</li>
56049              * </ul>
56050              * @param {Object} e An edit event (see above for description)
56051              */
56052             "afteredit" : true,
56053             /**
56054              * @event validateedit
56055              * Fires after a cell is edited, but before the value is set in the record. 
56056          * You can use this to modify the value being set in the field, Return false
56057              * to cancel the change. The edit event object has the following properties <br />
56058              * <ul style="padding:5px;padding-left:16px;">
56059          * <li>editor - This editor</li>
56060              * <li>grid - This grid</li>
56061              * <li>record - The record being edited</li>
56062              * <li>field - The field name being edited</li>
56063              * <li>value - The value being set</li>
56064              * <li>originalValue - The original value for the field, before the edit.</li>
56065              * <li>row - The grid row index</li>
56066              * <li>column - The grid column index</li>
56067              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
56068              * </ul>
56069              * @param {Object} e An edit event (see above for description)
56070              */
56071             "validateedit" : true
56072         });
56073     this.on("bodyscroll", this.stopEditing,  this);
56074     this.on(this.clicksToEdit == 1 ? "cellclick" : "celldblclick", this.onCellDblClick,  this);
56075 };
56076
56077 Roo.extend(Roo.grid.EditorGrid, Roo.grid.Grid, {
56078     /**
56079      * @cfg {Number} clicksToEdit
56080      * The number of clicks on a cell required to display the cell's editor (defaults to 2)
56081      */
56082     clicksToEdit: 2,
56083
56084     // private
56085     isEditor : true,
56086     // private
56087     trackMouseOver: false, // causes very odd FF errors
56088
56089     onCellDblClick : function(g, row, col){
56090         this.startEditing(row, col);
56091     },
56092
56093     onEditComplete : function(ed, value, startValue){
56094         this.editing = false;
56095         this.activeEditor = null;
56096         ed.un("specialkey", this.selModel.onEditorKey, this.selModel);
56097         var r = ed.record;
56098         var field = this.colModel.getDataIndex(ed.col);
56099         var e = {
56100             grid: this,
56101             record: r,
56102             field: field,
56103             originalValue: startValue,
56104             value: value,
56105             row: ed.row,
56106             column: ed.col,
56107             cancel:false,
56108             editor: ed
56109         };
56110         var cell = Roo.get(this.view.getCell(ed.row,ed.col))
56111         cell.show();
56112           
56113         if(String(value) !== String(startValue)){
56114             
56115             if(this.fireEvent("validateedit", e) !== false && !e.cancel){
56116                 r.set(field, e.value);
56117                 // if we are dealing with a combo box..
56118                 // then we also set the 'name' colum to be the displayField
56119                 if (ed.field.displayField && ed.field.name) {
56120                     r.set(ed.field.name, ed.field.el.dom.value);
56121                 }
56122                 
56123                 delete e.cancel; //?? why!!!
56124                 this.fireEvent("afteredit", e);
56125             }
56126         } else {
56127             this.fireEvent("afteredit", e); // always fire it!
56128         }
56129         this.view.focusCell(ed.row, ed.col);
56130     },
56131
56132     /**
56133      * Starts editing the specified for the specified row/column
56134      * @param {Number} rowIndex
56135      * @param {Number} colIndex
56136      */
56137     startEditing : function(row, col){
56138         this.stopEditing();
56139         if(this.colModel.isCellEditable(col, row)){
56140             this.view.ensureVisible(row, col, true);
56141           
56142             var r = this.dataSource.getAt(row);
56143             var field = this.colModel.getDataIndex(col);
56144             var cell = Roo.get(this.view.getCell(row,col));
56145             var e = {
56146                 grid: this,
56147                 record: r,
56148                 field: field,
56149                 value: r.data[field],
56150                 row: row,
56151                 column: col,
56152                 cancel:false 
56153             };
56154             if(this.fireEvent("beforeedit", e) !== false && !e.cancel){
56155                 this.editing = true;
56156                 var ed = this.colModel.getCellEditor(col, row);
56157                 
56158                 if (!ed) {
56159                     return;
56160                 }
56161                 if(!ed.rendered){
56162                     ed.render(ed.parentEl || document.body);
56163                 }
56164                 ed.field.reset();
56165                
56166                 cell.hide();
56167                 
56168                 (function(){ // complex but required for focus issues in safari, ie and opera
56169                     ed.row = row;
56170                     ed.col = col;
56171                     ed.record = r;
56172                     ed.on("complete",   this.onEditComplete,        this,       {single: true});
56173                     ed.on("specialkey", this.selModel.onEditorKey,  this.selModel);
56174                     this.activeEditor = ed;
56175                     var v = r.data[field];
56176                     ed.startEdit(this.view.getCell(row, col), v);
56177                     // combo's with 'displayField and name set
56178                     if (ed.field.displayField && ed.field.name) {
56179                         ed.field.el.dom.value = r.data[ed.field.name];
56180                     }
56181                     
56182                     
56183                 }).defer(50, this);
56184             }
56185         }
56186     },
56187         
56188     /**
56189      * Stops any active editing
56190      */
56191     stopEditing : function(){
56192         if(this.activeEditor){
56193             this.activeEditor.completeEdit();
56194         }
56195         this.activeEditor = null;
56196     },
56197         
56198          /**
56199      * Called to get grid's drag proxy text, by default returns this.ddText.
56200      * @return {String}
56201      */
56202     getDragDropText : function(){
56203         var count = this.selModel.getSelectedCell() ? 1 : 0;
56204         return String.format(this.ddText, count, count == 1 ? '' : 's');
56205     }
56206         
56207 });/*
56208  * Based on:
56209  * Ext JS Library 1.1.1
56210  * Copyright(c) 2006-2007, Ext JS, LLC.
56211  *
56212  * Originally Released Under LGPL - original licence link has changed is not relivant.
56213  *
56214  * Fork - LGPL
56215  * <script type="text/javascript">
56216  */
56217
56218 // private - not really -- you end up using it !
56219 // This is a support class used internally by the Grid components
56220
56221 /**
56222  * @class Roo.grid.GridEditor
56223  * @extends Roo.Editor
56224  * Class for creating and editable grid elements.
56225  * @param {Object} config any settings (must include field)
56226  */
56227 Roo.grid.GridEditor = function(field, config){
56228     if (!config && field.field) {
56229         config = field;
56230         field = Roo.factory(config.field, Roo.form);
56231     }
56232     Roo.grid.GridEditor.superclass.constructor.call(this, field, config);
56233     field.monitorTab = false;
56234 };
56235
56236 Roo.extend(Roo.grid.GridEditor, Roo.Editor, {
56237     
56238     /**
56239      * @cfg {Roo.form.Field} field Field to wrap (or xtyped)
56240      */
56241     
56242     alignment: "tl-tl",
56243     autoSize: "width",
56244     hideEl : false,
56245     cls: "x-small-editor x-grid-editor",
56246     shim:false,
56247     shadow:"frame"
56248 });/*
56249  * Based on:
56250  * Ext JS Library 1.1.1
56251  * Copyright(c) 2006-2007, Ext JS, LLC.
56252  *
56253  * Originally Released Under LGPL - original licence link has changed is not relivant.
56254  *
56255  * Fork - LGPL
56256  * <script type="text/javascript">
56257  */
56258   
56259
56260   
56261 Roo.grid.PropertyRecord = Roo.data.Record.create([
56262     {name:'name',type:'string'},  'value'
56263 ]);
56264
56265
56266 Roo.grid.PropertyStore = function(grid, source){
56267     this.grid = grid;
56268     this.store = new Roo.data.Store({
56269         recordType : Roo.grid.PropertyRecord
56270     });
56271     this.store.on('update', this.onUpdate,  this);
56272     if(source){
56273         this.setSource(source);
56274     }
56275     Roo.grid.PropertyStore.superclass.constructor.call(this);
56276 };
56277
56278
56279
56280 Roo.extend(Roo.grid.PropertyStore, Roo.util.Observable, {
56281     setSource : function(o){
56282         this.source = o;
56283         this.store.removeAll();
56284         var data = [];
56285         for(var k in o){
56286             if(this.isEditableValue(o[k])){
56287                 data.push(new Roo.grid.PropertyRecord({name: k, value: o[k]}, k));
56288             }
56289         }
56290         this.store.loadRecords({records: data}, {}, true);
56291     },
56292
56293     onUpdate : function(ds, record, type){
56294         if(type == Roo.data.Record.EDIT){
56295             var v = record.data['value'];
56296             var oldValue = record.modified['value'];
56297             if(this.grid.fireEvent('beforepropertychange', this.source, record.id, v, oldValue) !== false){
56298                 this.source[record.id] = v;
56299                 record.commit();
56300                 this.grid.fireEvent('propertychange', this.source, record.id, v, oldValue);
56301             }else{
56302                 record.reject();
56303             }
56304         }
56305     },
56306
56307     getProperty : function(row){
56308        return this.store.getAt(row);
56309     },
56310
56311     isEditableValue: function(val){
56312         if(val && val instanceof Date){
56313             return true;
56314         }else if(typeof val == 'object' || typeof val == 'function'){
56315             return false;
56316         }
56317         return true;
56318     },
56319
56320     setValue : function(prop, value){
56321         this.source[prop] = value;
56322         this.store.getById(prop).set('value', value);
56323     },
56324
56325     getSource : function(){
56326         return this.source;
56327     }
56328 });
56329
56330 Roo.grid.PropertyColumnModel = function(grid, store){
56331     this.grid = grid;
56332     var g = Roo.grid;
56333     g.PropertyColumnModel.superclass.constructor.call(this, [
56334         {header: this.nameText, sortable: true, dataIndex:'name', id: 'name'},
56335         {header: this.valueText, resizable:false, dataIndex: 'value', id: 'value'}
56336     ]);
56337     this.store = store;
56338     this.bselect = Roo.DomHelper.append(document.body, {
56339         tag: 'select', style:'display:none', cls: 'x-grid-editor', children: [
56340             {tag: 'option', value: 'true', html: 'true'},
56341             {tag: 'option', value: 'false', html: 'false'}
56342         ]
56343     });
56344     Roo.id(this.bselect);
56345     var f = Roo.form;
56346     this.editors = {
56347         'date' : new g.GridEditor(new f.DateField({selectOnFocus:true})),
56348         'string' : new g.GridEditor(new f.TextField({selectOnFocus:true})),
56349         'number' : new g.GridEditor(new f.NumberField({selectOnFocus:true, style:'text-align:left;'})),
56350         'int' : new g.GridEditor(new f.NumberField({selectOnFocus:true, allowDecimals:false, style:'text-align:left;'})),
56351         'boolean' : new g.GridEditor(new f.Field({el:this.bselect,selectOnFocus:true}))
56352     };
56353     this.renderCellDelegate = this.renderCell.createDelegate(this);
56354     this.renderPropDelegate = this.renderProp.createDelegate(this);
56355 };
56356
56357 Roo.extend(Roo.grid.PropertyColumnModel, Roo.grid.ColumnModel, {
56358     
56359     
56360     nameText : 'Name',
56361     valueText : 'Value',
56362     
56363     dateFormat : 'm/j/Y',
56364     
56365     
56366     renderDate : function(dateVal){
56367         return dateVal.dateFormat(this.dateFormat);
56368     },
56369
56370     renderBool : function(bVal){
56371         return bVal ? 'true' : 'false';
56372     },
56373
56374     isCellEditable : function(colIndex, rowIndex){
56375         return colIndex == 1;
56376     },
56377
56378     getRenderer : function(col){
56379         return col == 1 ?
56380             this.renderCellDelegate : this.renderPropDelegate;
56381     },
56382
56383     renderProp : function(v){
56384         return this.getPropertyName(v);
56385     },
56386
56387     renderCell : function(val){
56388         var rv = val;
56389         if(val instanceof Date){
56390             rv = this.renderDate(val);
56391         }else if(typeof val == 'boolean'){
56392             rv = this.renderBool(val);
56393         }
56394         return Roo.util.Format.htmlEncode(rv);
56395     },
56396
56397     getPropertyName : function(name){
56398         var pn = this.grid.propertyNames;
56399         return pn && pn[name] ? pn[name] : name;
56400     },
56401
56402     getCellEditor : function(colIndex, rowIndex){
56403         var p = this.store.getProperty(rowIndex);
56404         var n = p.data['name'], val = p.data['value'];
56405         
56406         if(typeof(this.grid.customEditors[n]) == 'string'){
56407             return this.editors[this.grid.customEditors[n]];
56408         }
56409         if(typeof(this.grid.customEditors[n]) != 'undefined'){
56410             return this.grid.customEditors[n];
56411         }
56412         if(val instanceof Date){
56413             return this.editors['date'];
56414         }else if(typeof val == 'number'){
56415             return this.editors['number'];
56416         }else if(typeof val == 'boolean'){
56417             return this.editors['boolean'];
56418         }else{
56419             return this.editors['string'];
56420         }
56421     }
56422 });
56423
56424 /**
56425  * @class Roo.grid.PropertyGrid
56426  * @extends Roo.grid.EditorGrid
56427  * This class represents the  interface of a component based property grid control.
56428  * <br><br>Usage:<pre><code>
56429  var grid = new Roo.grid.PropertyGrid("my-container-id", {
56430       
56431  });
56432  // set any options
56433  grid.render();
56434  * </code></pre>
56435   
56436  * @constructor
56437  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
56438  * The container MUST have some type of size defined for the grid to fill. The container will be
56439  * automatically set to position relative if it isn't already.
56440  * @param {Object} config A config object that sets properties on this grid.
56441  */
56442 Roo.grid.PropertyGrid = function(container, config){
56443     config = config || {};
56444     var store = new Roo.grid.PropertyStore(this);
56445     this.store = store;
56446     var cm = new Roo.grid.PropertyColumnModel(this, store);
56447     store.store.sort('name', 'ASC');
56448     Roo.grid.PropertyGrid.superclass.constructor.call(this, container, Roo.apply({
56449         ds: store.store,
56450         cm: cm,
56451         enableColLock:false,
56452         enableColumnMove:false,
56453         stripeRows:false,
56454         trackMouseOver: false,
56455         clicksToEdit:1
56456     }, config));
56457     this.getGridEl().addClass('x-props-grid');
56458     this.lastEditRow = null;
56459     this.on('columnresize', this.onColumnResize, this);
56460     this.addEvents({
56461          /**
56462              * @event beforepropertychange
56463              * Fires before a property changes (return false to stop?)
56464              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
56465              * @param {String} id Record Id
56466              * @param {String} newval New Value
56467          * @param {String} oldval Old Value
56468              */
56469         "beforepropertychange": true,
56470         /**
56471              * @event propertychange
56472              * Fires after a property changes
56473              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
56474              * @param {String} id Record Id
56475              * @param {String} newval New Value
56476          * @param {String} oldval Old Value
56477              */
56478         "propertychange": true
56479     });
56480     this.customEditors = this.customEditors || {};
56481 };
56482 Roo.extend(Roo.grid.PropertyGrid, Roo.grid.EditorGrid, {
56483     
56484      /**
56485      * @cfg {Object} customEditors map of colnames=> custom editors.
56486      * the custom editor can be one of the standard ones (date|string|number|int|boolean), or a
56487      * grid editor eg. Roo.grid.GridEditor(new Roo.form.TextArea({selectOnFocus:true})),
56488      * false disables editing of the field.
56489          */
56490     
56491       /**
56492      * @cfg {Object} propertyNames map of property Names to their displayed value
56493          */
56494     
56495     render : function(){
56496         Roo.grid.PropertyGrid.superclass.render.call(this);
56497         this.autoSize.defer(100, this);
56498     },
56499
56500     autoSize : function(){
56501         Roo.grid.PropertyGrid.superclass.autoSize.call(this);
56502         if(this.view){
56503             this.view.fitColumns();
56504         }
56505     },
56506
56507     onColumnResize : function(){
56508         this.colModel.setColumnWidth(1, this.container.getWidth(true)-this.colModel.getColumnWidth(0));
56509         this.autoSize();
56510     },
56511     /**
56512      * Sets the data for the Grid
56513      * accepts a Key => Value object of all the elements avaiable.
56514      * @param {Object} data  to appear in grid.
56515      */
56516     setSource : function(source){
56517         this.store.setSource(source);
56518         //this.autoSize();
56519     },
56520     /**
56521      * Gets all the data from the grid.
56522      * @return {Object} data  data stored in grid
56523      */
56524     getSource : function(){
56525         return this.store.getSource();
56526     }
56527 });/*
56528   
56529  * Licence LGPL
56530  
56531  */
56532  
56533 /**
56534  * @class Roo.grid.Calendar
56535  * @extends Roo.util.Grid
56536  * This class extends the Grid to provide a calendar widget
56537  * <br><br>Usage:<pre><code>
56538  var grid = new Roo.grid.Calendar("my-container-id", {
56539      ds: myDataStore,
56540      cm: myColModel,
56541      selModel: mySelectionModel,
56542      autoSizeColumns: true,
56543      monitorWindowResize: false,
56544      trackMouseOver: true
56545      eventstore : real data store..
56546  });
56547  // set any options
56548  grid.render();
56549   
56550   * @constructor
56551  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
56552  * The container MUST have some type of size defined for the grid to fill. The container will be
56553  * automatically set to position relative if it isn't already.
56554  * @param {Object} config A config object that sets properties on this grid.
56555  */
56556 Roo.grid.Calendar = function(container, config){
56557         // initialize the container
56558         this.container = Roo.get(container);
56559         this.container.update("");
56560         this.container.setStyle("overflow", "hidden");
56561     this.container.addClass('x-grid-container');
56562
56563     this.id = this.container.id;
56564
56565     Roo.apply(this, config);
56566     // check and correct shorthanded configs
56567     
56568     var rows = [];
56569     var d =1;
56570     for (var r = 0;r < 6;r++) {
56571         
56572         rows[r]=[];
56573         for (var c =0;c < 7;c++) {
56574             rows[r][c]= '';
56575         }
56576     }
56577     if (this.eventStore) {
56578         this.eventStore= Roo.factory(this.eventStore, Roo.data);
56579         this.eventStore.on('load',this.onLoad, this);
56580         this.eventStore.on('beforeload',this.clearEvents, this);
56581          
56582     }
56583     
56584     this.dataSource = new Roo.data.Store({
56585             proxy: new Roo.data.MemoryProxy(rows),
56586             reader: new Roo.data.ArrayReader({}, [
56587                    'weekday0', 'weekday1', 'weekday2', 'weekday3', 'weekday4', 'weekday5', 'weekday6' ])
56588     });
56589
56590     this.dataSource.load();
56591     this.ds = this.dataSource;
56592     this.ds.xmodule = this.xmodule || false;
56593     
56594     
56595     var cellRender = function(v,x,r)
56596     {
56597         return String.format(
56598             '<div class="fc-day  fc-widget-content"><div>' +
56599                 '<div class="fc-event-container"></div>' +
56600                 '<div class="fc-day-number">{0}</div>'+
56601                 
56602                 '<div class="fc-day-content"><div style="position:relative"></div></div>' +
56603             '</div></div>', v);
56604     
56605     }
56606     
56607     
56608     this.colModel = new Roo.grid.ColumnModel( [
56609         {
56610             xtype: 'ColumnModel',
56611             xns: Roo.grid,
56612             dataIndex : 'weekday0',
56613             header : 'Sunday',
56614             renderer : cellRender
56615         },
56616         {
56617             xtype: 'ColumnModel',
56618             xns: Roo.grid,
56619             dataIndex : 'weekday1',
56620             header : 'Monday',
56621             renderer : cellRender
56622         },
56623         {
56624             xtype: 'ColumnModel',
56625             xns: Roo.grid,
56626             dataIndex : 'weekday2',
56627             header : 'Tuesday',
56628             renderer : cellRender
56629         },
56630         {
56631             xtype: 'ColumnModel',
56632             xns: Roo.grid,
56633             dataIndex : 'weekday3',
56634             header : 'Wednesday',
56635             renderer : cellRender
56636         },
56637         {
56638             xtype: 'ColumnModel',
56639             xns: Roo.grid,
56640             dataIndex : 'weekday4',
56641             header : 'Thursday',
56642             renderer : cellRender
56643         },
56644         {
56645             xtype: 'ColumnModel',
56646             xns: Roo.grid,
56647             dataIndex : 'weekday5',
56648             header : 'Friday',
56649             renderer : cellRender
56650         },
56651         {
56652             xtype: 'ColumnModel',
56653             xns: Roo.grid,
56654             dataIndex : 'weekday6',
56655             header : 'Saturday',
56656             renderer : cellRender
56657         }
56658     ]);
56659     this.cm = this.colModel;
56660     this.cm.xmodule = this.xmodule || false;
56661  
56662         
56663           
56664     //this.selModel = new Roo.grid.CellSelectionModel();
56665     //this.sm = this.selModel;
56666     //this.selModel.init(this);
56667     
56668     
56669     if(this.width){
56670         this.container.setWidth(this.width);
56671     }
56672
56673     if(this.height){
56674         this.container.setHeight(this.height);
56675     }
56676     /** @private */
56677         this.addEvents({
56678         // raw events
56679         /**
56680          * @event click
56681          * The raw click event for the entire grid.
56682          * @param {Roo.EventObject} e
56683          */
56684         "click" : true,
56685         /**
56686          * @event dblclick
56687          * The raw dblclick event for the entire grid.
56688          * @param {Roo.EventObject} e
56689          */
56690         "dblclick" : true,
56691         /**
56692          * @event contextmenu
56693          * The raw contextmenu event for the entire grid.
56694          * @param {Roo.EventObject} e
56695          */
56696         "contextmenu" : true,
56697         /**
56698          * @event mousedown
56699          * The raw mousedown event for the entire grid.
56700          * @param {Roo.EventObject} e
56701          */
56702         "mousedown" : true,
56703         /**
56704          * @event mouseup
56705          * The raw mouseup event for the entire grid.
56706          * @param {Roo.EventObject} e
56707          */
56708         "mouseup" : true,
56709         /**
56710          * @event mouseover
56711          * The raw mouseover event for the entire grid.
56712          * @param {Roo.EventObject} e
56713          */
56714         "mouseover" : true,
56715         /**
56716          * @event mouseout
56717          * The raw mouseout event for the entire grid.
56718          * @param {Roo.EventObject} e
56719          */
56720         "mouseout" : true,
56721         /**
56722          * @event keypress
56723          * The raw keypress event for the entire grid.
56724          * @param {Roo.EventObject} e
56725          */
56726         "keypress" : true,
56727         /**
56728          * @event keydown
56729          * The raw keydown event for the entire grid.
56730          * @param {Roo.EventObject} e
56731          */
56732         "keydown" : true,
56733
56734         // custom events
56735
56736         /**
56737          * @event cellclick
56738          * Fires when a cell is clicked
56739          * @param {Grid} this
56740          * @param {Number} rowIndex
56741          * @param {Number} columnIndex
56742          * @param {Roo.EventObject} e
56743          */
56744         "cellclick" : true,
56745         /**
56746          * @event celldblclick
56747          * Fires when a cell is double clicked
56748          * @param {Grid} this
56749          * @param {Number} rowIndex
56750          * @param {Number} columnIndex
56751          * @param {Roo.EventObject} e
56752          */
56753         "celldblclick" : true,
56754         /**
56755          * @event rowclick
56756          * Fires when a row is clicked
56757          * @param {Grid} this
56758          * @param {Number} rowIndex
56759          * @param {Roo.EventObject} e
56760          */
56761         "rowclick" : true,
56762         /**
56763          * @event rowdblclick
56764          * Fires when a row is double clicked
56765          * @param {Grid} this
56766          * @param {Number} rowIndex
56767          * @param {Roo.EventObject} e
56768          */
56769         "rowdblclick" : true,
56770         /**
56771          * @event headerclick
56772          * Fires when a header is clicked
56773          * @param {Grid} this
56774          * @param {Number} columnIndex
56775          * @param {Roo.EventObject} e
56776          */
56777         "headerclick" : true,
56778         /**
56779          * @event headerdblclick
56780          * Fires when a header cell is double clicked
56781          * @param {Grid} this
56782          * @param {Number} columnIndex
56783          * @param {Roo.EventObject} e
56784          */
56785         "headerdblclick" : true,
56786         /**
56787          * @event rowcontextmenu
56788          * Fires when a row is right clicked
56789          * @param {Grid} this
56790          * @param {Number} rowIndex
56791          * @param {Roo.EventObject} e
56792          */
56793         "rowcontextmenu" : true,
56794         /**
56795          * @event cellcontextmenu
56796          * Fires when a cell is right clicked
56797          * @param {Grid} this
56798          * @param {Number} rowIndex
56799          * @param {Number} cellIndex
56800          * @param {Roo.EventObject} e
56801          */
56802          "cellcontextmenu" : true,
56803         /**
56804          * @event headercontextmenu
56805          * Fires when a header is right clicked
56806          * @param {Grid} this
56807          * @param {Number} columnIndex
56808          * @param {Roo.EventObject} e
56809          */
56810         "headercontextmenu" : true,
56811         /**
56812          * @event bodyscroll
56813          * Fires when the body element is scrolled
56814          * @param {Number} scrollLeft
56815          * @param {Number} scrollTop
56816          */
56817         "bodyscroll" : true,
56818         /**
56819          * @event columnresize
56820          * Fires when the user resizes a column
56821          * @param {Number} columnIndex
56822          * @param {Number} newSize
56823          */
56824         "columnresize" : true,
56825         /**
56826          * @event columnmove
56827          * Fires when the user moves a column
56828          * @param {Number} oldIndex
56829          * @param {Number} newIndex
56830          */
56831         "columnmove" : true,
56832         /**
56833          * @event startdrag
56834          * Fires when row(s) start being dragged
56835          * @param {Grid} this
56836          * @param {Roo.GridDD} dd The drag drop object
56837          * @param {event} e The raw browser event
56838          */
56839         "startdrag" : true,
56840         /**
56841          * @event enddrag
56842          * Fires when a drag operation is complete
56843          * @param {Grid} this
56844          * @param {Roo.GridDD} dd The drag drop object
56845          * @param {event} e The raw browser event
56846          */
56847         "enddrag" : true,
56848         /**
56849          * @event dragdrop
56850          * Fires when dragged row(s) are dropped on a valid DD target
56851          * @param {Grid} this
56852          * @param {Roo.GridDD} dd The drag drop object
56853          * @param {String} targetId The target drag drop object
56854          * @param {event} e The raw browser event
56855          */
56856         "dragdrop" : true,
56857         /**
56858          * @event dragover
56859          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
56860          * @param {Grid} this
56861          * @param {Roo.GridDD} dd The drag drop object
56862          * @param {String} targetId The target drag drop object
56863          * @param {event} e The raw browser event
56864          */
56865         "dragover" : true,
56866         /**
56867          * @event dragenter
56868          *  Fires when the dragged row(s) first cross another DD target while being dragged
56869          * @param {Grid} this
56870          * @param {Roo.GridDD} dd The drag drop object
56871          * @param {String} targetId The target drag drop object
56872          * @param {event} e The raw browser event
56873          */
56874         "dragenter" : true,
56875         /**
56876          * @event dragout
56877          * Fires when the dragged row(s) leave another DD target while being dragged
56878          * @param {Grid} this
56879          * @param {Roo.GridDD} dd The drag drop object
56880          * @param {String} targetId The target drag drop object
56881          * @param {event} e The raw browser event
56882          */
56883         "dragout" : true,
56884         /**
56885          * @event rowclass
56886          * Fires when a row is rendered, so you can change add a style to it.
56887          * @param {GridView} gridview   The grid view
56888          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
56889          */
56890         'rowclass' : true,
56891
56892         /**
56893          * @event render
56894          * Fires when the grid is rendered
56895          * @param {Grid} grid
56896          */
56897         'render' : true,
56898             /**
56899              * @event select
56900              * Fires when a date is selected
56901              * @param {DatePicker} this
56902              * @param {Date} date The selected date
56903              */
56904         'select': true,
56905         /**
56906              * @event monthchange
56907              * Fires when the displayed month changes 
56908              * @param {DatePicker} this
56909              * @param {Date} date The selected month
56910              */
56911         'monthchange': true,
56912         /**
56913              * @event evententer
56914              * Fires when mouse over an event
56915              * @param {Calendar} this
56916              * @param {event} Event
56917              */
56918         'evententer': true,
56919         /**
56920              * @event eventleave
56921              * Fires when the mouse leaves an
56922              * @param {Calendar} this
56923              * @param {event}
56924              */
56925         'eventleave': true,
56926         /**
56927              * @event eventclick
56928              * Fires when the mouse click an
56929              * @param {Calendar} this
56930              * @param {event}
56931              */
56932         'eventclick': true,
56933         /**
56934              * @event eventrender
56935              * Fires before each cell is rendered, so you can modify the contents, like cls / title / qtip
56936              * @param {Calendar} this
56937              * @param {data} data to be modified
56938              */
56939         'eventrender': true
56940         
56941     });
56942
56943     Roo.grid.Grid.superclass.constructor.call(this);
56944     this.on('render', function() {
56945         this.view.el.addClass('x-grid-cal'); 
56946         
56947         (function() { this.setDate(new Date()); }).defer(100,this); //default today..
56948
56949     },this);
56950     
56951     if (!Roo.grid.Calendar.style) {
56952         Roo.grid.Calendar.style = Roo.util.CSS.createStyleSheet({
56953             
56954             
56955             '.x-grid-cal .x-grid-col' :  {
56956                 height: 'auto !important',
56957                 'vertical-align': 'top'
56958             },
56959             '.x-grid-cal  .fc-event-hori' : {
56960                 height: '14px'
56961             }
56962              
56963             
56964         }, Roo.id());
56965     }
56966
56967     
56968     
56969 };
56970 Roo.extend(Roo.grid.Calendar, Roo.grid.Grid, {
56971     /**
56972      * @cfg {Store} eventStore The store that loads events.
56973      */
56974     eventStore : 25,
56975
56976      
56977     activeDate : false,
56978     startDay : 0,
56979     autoWidth : true,
56980     monitorWindowResize : false,
56981
56982     
56983     resizeColumns : function() {
56984         var col = (this.view.el.getWidth() / 7) - 3;
56985         // loop through cols, and setWidth
56986         for(var i =0 ; i < 7 ; i++){
56987             this.cm.setColumnWidth(i, col);
56988         }
56989     },
56990      setDate :function(date) {
56991         
56992         Roo.log('setDate?');
56993         
56994         this.resizeColumns();
56995         var vd = this.activeDate;
56996         this.activeDate = date;
56997 //        if(vd && this.el){
56998 //            var t = date.getTime();
56999 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
57000 //                Roo.log('using add remove');
57001 //                
57002 //                this.fireEvent('monthchange', this, date);
57003 //                
57004 //                this.cells.removeClass("fc-state-highlight");
57005 //                this.cells.each(function(c){
57006 //                   if(c.dateValue == t){
57007 //                       c.addClass("fc-state-highlight");
57008 //                       setTimeout(function(){
57009 //                            try{c.dom.firstChild.focus();}catch(e){}
57010 //                       }, 50);
57011 //                       return false;
57012 //                   }
57013 //                   return true;
57014 //                });
57015 //                return;
57016 //            }
57017 //        }
57018         
57019         var days = date.getDaysInMonth();
57020         
57021         var firstOfMonth = date.getFirstDateOfMonth();
57022         var startingPos = firstOfMonth.getDay()-this.startDay;
57023         
57024         if(startingPos < this.startDay){
57025             startingPos += 7;
57026         }
57027         
57028         var pm = date.add(Date.MONTH, -1);
57029         var prevStart = pm.getDaysInMonth()-startingPos;
57030 //        
57031         
57032         
57033         this.cells = this.view.el.select('.x-grid-row .x-grid-col',true);
57034         
57035         this.textNodes = this.view.el.query('.x-grid-row .x-grid-col .x-grid-cell-text');
57036         //this.cells.addClassOnOver('fc-state-hover');
57037         
57038         var cells = this.cells.elements;
57039         var textEls = this.textNodes;
57040         
57041         //Roo.each(cells, function(cell){
57042         //    cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
57043         //});
57044         
57045         days += startingPos;
57046
57047         // convert everything to numbers so it's fast
57048         var day = 86400000;
57049         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
57050         //Roo.log(d);
57051         //Roo.log(pm);
57052         //Roo.log(prevStart);
57053         
57054         var today = new Date().clearTime().getTime();
57055         var sel = date.clearTime().getTime();
57056         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
57057         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
57058         var ddMatch = this.disabledDatesRE;
57059         var ddText = this.disabledDatesText;
57060         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
57061         var ddaysText = this.disabledDaysText;
57062         var format = this.format;
57063         
57064         var setCellClass = function(cal, cell){
57065             
57066             //Roo.log('set Cell Class');
57067             cell.title = "";
57068             var t = d.getTime();
57069             
57070             //Roo.log(d);
57071             
57072             
57073             cell.dateValue = t;
57074             if(t == today){
57075                 cell.className += " fc-today";
57076                 cell.className += " fc-state-highlight";
57077                 cell.title = cal.todayText;
57078             }
57079             if(t == sel){
57080                 // disable highlight in other month..
57081                 cell.className += " fc-state-highlight";
57082                 
57083             }
57084             // disabling
57085             if(t < min) {
57086                 //cell.className = " fc-state-disabled";
57087                 cell.title = cal.minText;
57088                 return;
57089             }
57090             if(t > max) {
57091                 //cell.className = " fc-state-disabled";
57092                 cell.title = cal.maxText;
57093                 return;
57094             }
57095             if(ddays){
57096                 if(ddays.indexOf(d.getDay()) != -1){
57097                     // cell.title = ddaysText;
57098                    // cell.className = " fc-state-disabled";
57099                 }
57100             }
57101             if(ddMatch && format){
57102                 var fvalue = d.dateFormat(format);
57103                 if(ddMatch.test(fvalue)){
57104                     cell.title = ddText.replace("%0", fvalue);
57105                    cell.className = " fc-state-disabled";
57106                 }
57107             }
57108             
57109             if (!cell.initialClassName) {
57110                 cell.initialClassName = cell.dom.className;
57111             }
57112             
57113             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
57114         };
57115
57116         var i = 0;
57117         
57118         for(; i < startingPos; i++) {
57119             cells[i].dayName =  (++prevStart);
57120             Roo.log(textEls[i]);
57121             d.setDate(d.getDate()+1);
57122             
57123             //cells[i].className = "fc-past fc-other-month";
57124             setCellClass(this, cells[i]);
57125         }
57126         
57127         var intDay = 0;
57128         
57129         for(; i < days; i++){
57130             intDay = i - startingPos + 1;
57131             cells[i].dayName =  (intDay);
57132             d.setDate(d.getDate()+1);
57133             
57134             cells[i].className = ''; // "x-date-active";
57135             setCellClass(this, cells[i]);
57136         }
57137         var extraDays = 0;
57138         
57139         for(; i < 42; i++) {
57140             //textEls[i].innerHTML = (++extraDays);
57141             
57142             d.setDate(d.getDate()+1);
57143             cells[i].dayName = (++extraDays);
57144             cells[i].className = "fc-future fc-other-month";
57145             setCellClass(this, cells[i]);
57146         }
57147         
57148         //this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
57149         
57150         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
57151         
57152         // this will cause all the cells to mis
57153         var rows= [];
57154         var i =0;
57155         for (var r = 0;r < 6;r++) {
57156             for (var c =0;c < 7;c++) {
57157                 this.ds.getAt(r).set('weekday' + c ,cells[i++].dayName );
57158             }    
57159         }
57160         
57161         this.cells = this.view.el.select('.x-grid-row .x-grid-col',true);
57162         for(i=0;i<cells.length;i++) {
57163             
57164             this.cells.elements[i].dayName = cells[i].dayName ;
57165             this.cells.elements[i].className = cells[i].className;
57166             this.cells.elements[i].initialClassName = cells[i].initialClassName ;
57167             this.cells.elements[i].title = cells[i].title ;
57168             this.cells.elements[i].dateValue = cells[i].dateValue ;
57169         }
57170         
57171         
57172         
57173         
57174         //this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
57175         //this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
57176         
57177         ////if(totalRows != 6){
57178             //this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
57179            // this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
57180        // }
57181         
57182         this.fireEvent('monthchange', this, date);
57183         
57184         
57185     },
57186  /**
57187      * Returns the grid's SelectionModel.
57188      * @return {SelectionModel}
57189      */
57190     getSelectionModel : function(){
57191         if(!this.selModel){
57192             this.selModel = new Roo.grid.CellSelectionModel();
57193         }
57194         return this.selModel;
57195     },
57196
57197     load: function() {
57198         this.eventStore.load()
57199         
57200         
57201         
57202     },
57203     
57204     findCell : function(dt) {
57205         dt = dt.clearTime().getTime();
57206         var ret = false;
57207         this.cells.each(function(c){
57208             //Roo.log("check " +c.dateValue + '?=' + dt);
57209             if(c.dateValue == dt){
57210                 ret = c;
57211                 return false;
57212             }
57213             return true;
57214         });
57215         
57216         return ret;
57217     },
57218     
57219     findCells : function(rec) {
57220         var s = rec.data.start_dt.clone().clearTime().getTime();
57221        // Roo.log(s);
57222         var e= rec.data.end_dt.clone().clearTime().getTime();
57223        // Roo.log(e);
57224         var ret = [];
57225         this.cells.each(function(c){
57226              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
57227             
57228             if(c.dateValue > e){
57229                 return ;
57230             }
57231             if(c.dateValue < s){
57232                 return ;
57233             }
57234             ret.push(c);
57235         });
57236         
57237         return ret;    
57238     },
57239     
57240     findBestRow: function(cells)
57241     {
57242         var ret = 0;
57243         
57244         for (var i =0 ; i < cells.length;i++) {
57245             ret  = Math.max(cells[i].rows || 0,ret);
57246         }
57247         return ret;
57248         
57249     },
57250     
57251     
57252     addItem : function(rec)
57253     {
57254         // look for vertical location slot in
57255         var cells = this.findCells(rec);
57256         
57257         rec.row = this.findBestRow(cells);
57258         
57259         // work out the location.
57260         
57261         var crow = false;
57262         var rows = [];
57263         for(var i =0; i < cells.length; i++) {
57264             if (!crow) {
57265                 crow = {
57266                     start : cells[i],
57267                     end :  cells[i]
57268                 };
57269                 continue;
57270             }
57271             if (crow.start.getY() == cells[i].getY()) {
57272                 // on same row.
57273                 crow.end = cells[i];
57274                 continue;
57275             }
57276             // different row.
57277             rows.push(crow);
57278             crow = {
57279                 start: cells[i],
57280                 end : cells[i]
57281             };
57282             
57283         }
57284         
57285         rows.push(crow);
57286         rec.els = [];
57287         rec.rows = rows;
57288         rec.cells = cells;
57289         for (var i = 0; i < cells.length;i++) {
57290             cells[i].rows = Math.max(cells[i].rows || 0 , rec.row + 1 );
57291             
57292         }
57293         
57294         
57295     },
57296     
57297     clearEvents: function() {
57298         
57299         if (!this.eventStore.getCount()) {
57300             return;
57301         }
57302         // reset number of rows in cells.
57303         Roo.each(this.cells.elements, function(c){
57304             c.rows = 0;
57305         });
57306         
57307         this.eventStore.each(function(e) {
57308             this.clearEvent(e);
57309         },this);
57310         
57311     },
57312     
57313     clearEvent : function(ev)
57314     {
57315         if (ev.els) {
57316             Roo.each(ev.els, function(el) {
57317                 el.un('mouseenter' ,this.onEventEnter, this);
57318                 el.un('mouseleave' ,this.onEventLeave, this);
57319                 el.remove();
57320             },this);
57321             ev.els = [];
57322         }
57323     },
57324     
57325     
57326     renderEvent : function(ev,ctr) {
57327         if (!ctr) {
57328              ctr = this.view.el.select('.fc-event-container',true).first();
57329         }
57330         
57331          
57332         this.clearEvent(ev);
57333             //code
57334        
57335         
57336         
57337         ev.els = [];
57338         var cells = ev.cells;
57339         var rows = ev.rows;
57340         this.fireEvent('eventrender', this, ev);
57341         
57342         for(var i =0; i < rows.length; i++) {
57343             
57344             cls = '';
57345             if (i == 0) {
57346                 cls += ' fc-event-start';
57347             }
57348             if ((i+1) == rows.length) {
57349                 cls += ' fc-event-end';
57350             }
57351             
57352             //Roo.log(ev.data);
57353             // how many rows should it span..
57354             var cg = this.eventTmpl.append(ctr,Roo.apply({
57355                 fccls : cls
57356                 
57357             }, ev.data) , true);
57358             
57359             
57360             cg.on('mouseenter' ,this.onEventEnter, this, ev);
57361             cg.on('mouseleave' ,this.onEventLeave, this, ev);
57362             cg.on('click', this.onEventClick, this, ev);
57363             
57364             ev.els.push(cg);
57365             
57366             var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
57367             var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
57368             //Roo.log(cg);
57369              
57370             cg.setXY([sbox.x +2, sbox.y +(ev.row * 20)]);    
57371             cg.setWidth(ebox.right - sbox.x -2);
57372         }
57373     },
57374     
57375     renderEvents: function()
57376     {   
57377         // first make sure there is enough space..
57378         
57379         if (!this.eventTmpl) {
57380             this.eventTmpl = new Roo.Template(
57381                 '<div class="roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable {fccls} {cls}"  style="position: absolute" unselectable="on">' +
57382                     '<div class="fc-event-inner">' +
57383                         '<span class="fc-event-time">{time}</span>' +
57384                         '<span class="fc-event-title" qtip="{qtip}">{title}</span>' +
57385                     '</div>' +
57386                     '<div class="ui-resizable-heandle ui-resizable-e">&nbsp;&nbsp;&nbsp;</div>' +
57387                 '</div>'
57388             );
57389                 
57390         }
57391                
57392         
57393         
57394         this.cells.each(function(c) {
57395             //Roo.log(c.select('.fc-day-content div',true).first());
57396             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, (c.rows || 1) * 20));
57397         });
57398         
57399         var ctr = this.view.el.select('.fc-event-container',true).first();
57400         
57401         var cls;
57402         this.eventStore.each(function(ev){
57403             
57404             this.renderEvent(ev);
57405              
57406              
57407         }, this);
57408         this.view.layout();
57409         
57410     },
57411     
57412     onEventEnter: function (e, el,event,d) {
57413         this.fireEvent('evententer', this, el, event);
57414     },
57415     
57416     onEventLeave: function (e, el,event,d) {
57417         this.fireEvent('eventleave', this, el, event);
57418     },
57419     
57420     onEventClick: function (e, el,event,d) {
57421         this.fireEvent('eventclick', this, el, event);
57422     },
57423     
57424     onMonthChange: function () {
57425         this.store.load();
57426     },
57427     
57428     onLoad: function () {
57429         
57430         //Roo.log('calendar onload');
57431 //         
57432         if(this.eventStore.getCount() > 0){
57433             
57434            
57435             
57436             this.eventStore.each(function(d){
57437                 
57438                 
57439                 // FIXME..
57440                 var add =   d.data;
57441                 if (typeof(add.end_dt) == 'undefined')  {
57442                     Roo.log("Missing End time in calendar data: ");
57443                     Roo.log(d);
57444                     return;
57445                 }
57446                 if (typeof(add.start_dt) == 'undefined')  {
57447                     Roo.log("Missing Start time in calendar data: ");
57448                     Roo.log(d);
57449                     return;
57450                 }
57451                 add.start_dt = typeof(add.start_dt) == 'string' ? Date.parseDate(add.start_dt,'Y-m-d H:i:s') : add.start_dt,
57452                 add.end_dt = typeof(add.end_dt) == 'string' ? Date.parseDate(add.end_dt,'Y-m-d H:i:s') : add.end_dt,
57453                 add.id = add.id || d.id;
57454                 add.title = add.title || '??';
57455                 
57456                 this.addItem(d);
57457                 
57458              
57459             },this);
57460         }
57461         
57462         this.renderEvents();
57463     }
57464     
57465
57466 });
57467 /*
57468  grid : {
57469                 xtype: 'Grid',
57470                 xns: Roo.grid,
57471                 listeners : {
57472                     render : function ()
57473                     {
57474                         _this.grid = this;
57475                         
57476                         if (!this.view.el.hasClass('course-timesheet')) {
57477                             this.view.el.addClass('course-timesheet');
57478                         }
57479                         if (this.tsStyle) {
57480                             this.ds.load({});
57481                             return; 
57482                         }
57483                         Roo.log('width');
57484                         Roo.log(_this.grid.view.el.getWidth());
57485                         
57486                         
57487                         this.tsStyle =  Roo.util.CSS.createStyleSheet({
57488                             '.course-timesheet .x-grid-row' : {
57489                                 height: '80px'
57490                             },
57491                             '.x-grid-row td' : {
57492                                 'vertical-align' : 0
57493                             },
57494                             '.course-edit-link' : {
57495                                 'color' : 'blue',
57496                                 'text-overflow' : 'ellipsis',
57497                                 'overflow' : 'hidden',
57498                                 'white-space' : 'nowrap',
57499                                 'cursor' : 'pointer'
57500                             },
57501                             '.sub-link' : {
57502                                 'color' : 'green'
57503                             },
57504                             '.de-act-sup-link' : {
57505                                 'color' : 'purple',
57506                                 'text-decoration' : 'line-through'
57507                             },
57508                             '.de-act-link' : {
57509                                 'color' : 'red',
57510                                 'text-decoration' : 'line-through'
57511                             },
57512                             '.course-timesheet .course-highlight' : {
57513                                 'border-top-style': 'dashed !important',
57514                                 'border-bottom-bottom': 'dashed !important'
57515                             },
57516                             '.course-timesheet .course-item' : {
57517                                 'font-family'   : 'tahoma, arial, helvetica',
57518                                 'font-size'     : '11px',
57519                                 'overflow'      : 'hidden',
57520                                 'padding-left'  : '10px',
57521                                 'padding-right' : '10px',
57522                                 'padding-top' : '10px' 
57523                             }
57524                             
57525                         }, Roo.id());
57526                                 this.ds.load({});
57527                     }
57528                 },
57529                 autoWidth : true,
57530                 monitorWindowResize : false,
57531                 cellrenderer : function(v,x,r)
57532                 {
57533                     return v;
57534                 },
57535                 sm : {
57536                     xtype: 'CellSelectionModel',
57537                     xns: Roo.grid
57538                 },
57539                 dataSource : {
57540                     xtype: 'Store',
57541                     xns: Roo.data,
57542                     listeners : {
57543                         beforeload : function (_self, options)
57544                         {
57545                             options.params = options.params || {};
57546                             options.params._month = _this.monthField.getValue();
57547                             options.params.limit = 9999;
57548                             options.params['sort'] = 'when_dt';    
57549                             options.params['dir'] = 'ASC';    
57550                             this.proxy.loadResponse = this.loadResponse;
57551                             Roo.log("load?");
57552                             //this.addColumns();
57553                         },
57554                         load : function (_self, records, options)
57555                         {
57556                             _this.grid.view.el.select('.course-edit-link', true).on('click', function() {
57557                                 // if you click on the translation.. you can edit it...
57558                                 var el = Roo.get(this);
57559                                 var id = el.dom.getAttribute('data-id');
57560                                 var d = el.dom.getAttribute('data-date');
57561                                 var t = el.dom.getAttribute('data-time');
57562                                 //var id = this.child('span').dom.textContent;
57563                                 
57564                                 //Roo.log(this);
57565                                 Pman.Dialog.CourseCalendar.show({
57566                                     id : id,
57567                                     when_d : d,
57568                                     when_t : t,
57569                                     productitem_active : id ? 1 : 0
57570                                 }, function() {
57571                                     _this.grid.ds.load({});
57572                                 });
57573                            
57574                            });
57575                            
57576                            _this.panel.fireEvent('resize', [ '', '' ]);
57577                         }
57578                     },
57579                     loadResponse : function(o, success, response){
57580                             // this is overridden on before load..
57581                             
57582                             Roo.log("our code?");       
57583                             //Roo.log(success);
57584                             //Roo.log(response)
57585                             delete this.activeRequest;
57586                             if(!success){
57587                                 this.fireEvent("loadexception", this, o, response);
57588                                 o.request.callback.call(o.request.scope, null, o.request.arg, false);
57589                                 return;
57590                             }
57591                             var result;
57592                             try {
57593                                 result = o.reader.read(response);
57594                             }catch(e){
57595                                 Roo.log("load exception?");
57596                                 this.fireEvent("loadexception", this, o, response, e);
57597                                 o.request.callback.call(o.request.scope, null, o.request.arg, false);
57598                                 return;
57599                             }
57600                             Roo.log("ready...");        
57601                             // loop through result.records;
57602                             // and set this.tdate[date] = [] << array of records..
57603                             _this.tdata  = {};
57604                             Roo.each(result.records, function(r){
57605                                 //Roo.log(r.data);
57606                                 if(typeof(_this.tdata[r.data.when_dt.format('j')]) == 'undefined'){
57607                                     _this.tdata[r.data.when_dt.format('j')] = [];
57608                                 }
57609                                 _this.tdata[r.data.when_dt.format('j')].push(r.data);
57610                             });
57611                             
57612                             //Roo.log(_this.tdata);
57613                             
57614                             result.records = [];
57615                             result.totalRecords = 6;
57616                     
57617                             // let's generate some duumy records for the rows.
57618                             //var st = _this.dateField.getValue();
57619                             
57620                             // work out monday..
57621                             //st = st.add(Date.DAY, -1 * st.format('w'));
57622                             
57623                             var date = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
57624                             
57625                             var firstOfMonth = date.getFirstDayOfMonth();
57626                             var days = date.getDaysInMonth();
57627                             var d = 1;
57628                             var firstAdded = false;
57629                             for (var i = 0; i < result.totalRecords ; i++) {
57630                                 //var d= st.add(Date.DAY, i);
57631                                 var row = {};
57632                                 var added = 0;
57633                                 for(var w = 0 ; w < 7 ; w++){
57634                                     if(!firstAdded && firstOfMonth != w){
57635                                         continue;
57636                                     }
57637                                     if(d > days){
57638                                         continue;
57639                                     }
57640                                     firstAdded = true;
57641                                     var dd = (d > 0 && d < 10) ? "0"+d : d;
57642                                     row['weekday'+w] = String.format(
57643                                                     '<span style="font-size: 16px;"><b>{0}</b></span>'+
57644                                                     '<span class="course-edit-link" style="color:blue;" data-id="0" data-date="{1}"> Add New</span>',
57645                                                     d,
57646                                                     date.format('Y-m-')+dd
57647                                                 );
57648                                     added++;
57649                                     if(typeof(_this.tdata[d]) != 'undefined'){
57650                                         Roo.each(_this.tdata[d], function(r){
57651                                             var is_sub = '';
57652                                             var deactive = '';
57653                                             var id = r.id;
57654                                             var desc = (r.productitem_id_descrip) ? r.productitem_id_descrip : '';
57655                                             if(r.parent_id*1>0){
57656                                                 is_sub = (r.productitem_id_visible*1 < 1) ? 'de-act-sup-link' :'sub-link';
57657                                                 id = r.parent_id;
57658                                             }
57659                                             if(r.productitem_id_visible*1 < 1 && r.parent_id*1 < 1){
57660                                                 deactive = 'de-act-link';
57661                                             }
57662                                             
57663                                             row['weekday'+w] += String.format(
57664                                                     '<br /><span class="course-edit-link {3} {4}" qtip="{5}" data-id="{0}">{2} - {1}</span>',
57665                                                     id, //0
57666                                                     r.product_id_name, //1
57667                                                     r.when_dt.format('h:ia'), //2
57668                                                     is_sub, //3
57669                                                     deactive, //4
57670                                                     desc // 5
57671                                             );
57672                                         });
57673                                     }
57674                                     d++;
57675                                 }
57676                                 
57677                                 // only do this if something added..
57678                                 if(added > 0){ 
57679                                     result.records.push(_this.grid.dataSource.reader.newRow(row));
57680                                 }
57681                                 
57682                                 
57683                                 // push it twice. (second one with an hour..
57684                                 
57685                             }
57686                             //Roo.log(result);
57687                             this.fireEvent("load", this, o, o.request.arg);
57688                             o.request.callback.call(o.request.scope, result, o.request.arg, true);
57689                         },
57690                     sortInfo : {field: 'when_dt', direction : 'ASC' },
57691                     proxy : {
57692                         xtype: 'HttpProxy',
57693                         xns: Roo.data,
57694                         method : 'GET',
57695                         url : baseURL + '/Roo/Shop_course.php'
57696                     },
57697                     reader : {
57698                         xtype: 'JsonReader',
57699                         xns: Roo.data,
57700                         id : 'id',
57701                         fields : [
57702                             {
57703                                 'name': 'id',
57704                                 'type': 'int'
57705                             },
57706                             {
57707                                 'name': 'when_dt',
57708                                 'type': 'string'
57709                             },
57710                             {
57711                                 'name': 'end_dt',
57712                                 'type': 'string'
57713                             },
57714                             {
57715                                 'name': 'parent_id',
57716                                 'type': 'int'
57717                             },
57718                             {
57719                                 'name': 'product_id',
57720                                 'type': 'int'
57721                             },
57722                             {
57723                                 'name': 'productitem_id',
57724                                 'type': 'int'
57725                             },
57726                             {
57727                                 'name': 'guid',
57728                                 'type': 'int'
57729                             }
57730                         ]
57731                     }
57732                 },
57733                 toolbar : {
57734                     xtype: 'Toolbar',
57735                     xns: Roo,
57736                     items : [
57737                         {
57738                             xtype: 'Button',
57739                             xns: Roo.Toolbar,
57740                             listeners : {
57741                                 click : function (_self, e)
57742                                 {
57743                                     var sd = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
57744                                     sd.setMonth(sd.getMonth()-1);
57745                                     _this.monthField.setValue(sd.format('Y-m-d'));
57746                                     _this.grid.ds.load({});
57747                                 }
57748                             },
57749                             text : "Back"
57750                         },
57751                         {
57752                             xtype: 'Separator',
57753                             xns: Roo.Toolbar
57754                         },
57755                         {
57756                             xtype: 'MonthField',
57757                             xns: Roo.form,
57758                             listeners : {
57759                                 render : function (_self)
57760                                 {
57761                                     _this.monthField = _self;
57762                                    // _this.monthField.set  today
57763                                 },
57764                                 select : function (combo, date)
57765                                 {
57766                                     _this.grid.ds.load({});
57767                                 }
57768                             },
57769                             value : (function() { return new Date(); })()
57770                         },
57771                         {
57772                             xtype: 'Separator',
57773                             xns: Roo.Toolbar
57774                         },
57775                         {
57776                             xtype: 'TextItem',
57777                             xns: Roo.Toolbar,
57778                             text : "Blue: in-active, green: in-active sup-event, red: de-active, purple: de-active sup-event"
57779                         },
57780                         {
57781                             xtype: 'Fill',
57782                             xns: Roo.Toolbar
57783                         },
57784                         {
57785                             xtype: 'Button',
57786                             xns: Roo.Toolbar,
57787                             listeners : {
57788                                 click : function (_self, e)
57789                                 {
57790                                     var sd = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
57791                                     sd.setMonth(sd.getMonth()+1);
57792                                     _this.monthField.setValue(sd.format('Y-m-d'));
57793                                     _this.grid.ds.load({});
57794                                 }
57795                             },
57796                             text : "Next"
57797                         }
57798                     ]
57799                 },
57800                  
57801             }
57802         };
57803         
57804         *//*
57805  * Based on:
57806  * Ext JS Library 1.1.1
57807  * Copyright(c) 2006-2007, Ext JS, LLC.
57808  *
57809  * Originally Released Under LGPL - original licence link has changed is not relivant.
57810  *
57811  * Fork - LGPL
57812  * <script type="text/javascript">
57813  */
57814  
57815 /**
57816  * @class Roo.LoadMask
57817  * A simple utility class for generically masking elements while loading data.  If the element being masked has
57818  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
57819  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
57820  * element's UpdateManager load indicator and will be destroyed after the initial load.
57821  * @constructor
57822  * Create a new LoadMask
57823  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
57824  * @param {Object} config The config object
57825  */
57826 Roo.LoadMask = function(el, config){
57827     this.el = Roo.get(el);
57828     Roo.apply(this, config);
57829     if(this.store){
57830         this.store.on('beforeload', this.onBeforeLoad, this);
57831         this.store.on('load', this.onLoad, this);
57832         this.store.on('loadexception', this.onLoadException, this);
57833         this.removeMask = false;
57834     }else{
57835         var um = this.el.getUpdateManager();
57836         um.showLoadIndicator = false; // disable the default indicator
57837         um.on('beforeupdate', this.onBeforeLoad, this);
57838         um.on('update', this.onLoad, this);
57839         um.on('failure', this.onLoad, this);
57840         this.removeMask = true;
57841     }
57842 };
57843
57844 Roo.LoadMask.prototype = {
57845     /**
57846      * @cfg {Boolean} removeMask
57847      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
57848      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
57849      */
57850     /**
57851      * @cfg {String} msg
57852      * The text to display in a centered loading message box (defaults to 'Loading...')
57853      */
57854     msg : 'Loading...',
57855     /**
57856      * @cfg {String} msgCls
57857      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
57858      */
57859     msgCls : 'x-mask-loading',
57860
57861     /**
57862      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
57863      * @type Boolean
57864      */
57865     disabled: false,
57866
57867     /**
57868      * Disables the mask to prevent it from being displayed
57869      */
57870     disable : function(){
57871        this.disabled = true;
57872     },
57873
57874     /**
57875      * Enables the mask so that it can be displayed
57876      */
57877     enable : function(){
57878         this.disabled = false;
57879     },
57880     
57881     onLoadException : function()
57882     {
57883         Roo.log(arguments);
57884         
57885         if (typeof(arguments[3]) != 'undefined') {
57886             Roo.MessageBox.alert("Error loading",arguments[3]);
57887         } 
57888         /*
57889         try {
57890             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
57891                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
57892             }   
57893         } catch(e) {
57894             
57895         }
57896         */
57897     
57898         
57899         
57900         this.el.unmask(this.removeMask);
57901     },
57902     // private
57903     onLoad : function()
57904     {
57905         this.el.unmask(this.removeMask);
57906     },
57907
57908     // private
57909     onBeforeLoad : function(){
57910         if(!this.disabled){
57911             this.el.mask(this.msg, this.msgCls);
57912         }
57913     },
57914
57915     // private
57916     destroy : function(){
57917         if(this.store){
57918             this.store.un('beforeload', this.onBeforeLoad, this);
57919             this.store.un('load', this.onLoad, this);
57920             this.store.un('loadexception', this.onLoadException, this);
57921         }else{
57922             var um = this.el.getUpdateManager();
57923             um.un('beforeupdate', this.onBeforeLoad, this);
57924             um.un('update', this.onLoad, this);
57925             um.un('failure', this.onLoad, this);
57926         }
57927     }
57928 };/*
57929  * Based on:
57930  * Ext JS Library 1.1.1
57931  * Copyright(c) 2006-2007, Ext JS, LLC.
57932  *
57933  * Originally Released Under LGPL - original licence link has changed is not relivant.
57934  *
57935  * Fork - LGPL
57936  * <script type="text/javascript">
57937  */
57938
57939
57940 /**
57941  * @class Roo.XTemplate
57942  * @extends Roo.Template
57943  * Provides a template that can have nested templates for loops or conditionals. The syntax is:
57944 <pre><code>
57945 var t = new Roo.XTemplate(
57946         '&lt;select name="{name}"&gt;',
57947                 '&lt;tpl for="options"&gt;&lt;option value="{value:trim}"&gt;{text:ellipsis(10)}&lt;/option&gt;&lt;/tpl&gt;',
57948         '&lt;/select&gt;'
57949 );
57950  
57951 // then append, applying the master template values
57952  </code></pre>
57953  *
57954  * Supported features:
57955  *
57956  *  Tags:
57957
57958 <pre><code>
57959       {a_variable} - output encoded.
57960       {a_variable.format:("Y-m-d")} - call a method on the variable
57961       {a_variable:raw} - unencoded output
57962       {a_variable:toFixed(1,2)} - Roo.util.Format."toFixed"
57963       {a_variable:this.method_on_template(...)} - call a method on the template object.
57964  
57965 </code></pre>
57966  *  The tpl tag:
57967 <pre><code>
57968         &lt;tpl for="a_variable or condition.."&gt;&lt;/tpl&gt;
57969         &lt;tpl if="a_variable or condition"&gt;&lt;/tpl&gt;
57970         &lt;tpl exec="some javascript"&gt;&lt;/tpl&gt;
57971         &lt;tpl name="named_template"&gt;&lt;/tpl&gt; (experimental)
57972   
57973         &lt;tpl for="."&gt;&lt;/tpl&gt; - just iterate the property..
57974         &lt;tpl for=".."&gt;&lt;/tpl&gt; - iterates with the parent (probably the template) 
57975 </code></pre>
57976  *      
57977  */
57978 Roo.XTemplate = function()
57979 {
57980     Roo.XTemplate.superclass.constructor.apply(this, arguments);
57981     if (this.html) {
57982         this.compile();
57983     }
57984 };
57985
57986
57987 Roo.extend(Roo.XTemplate, Roo.Template, {
57988
57989     /**
57990      * The various sub templates
57991      */
57992     tpls : false,
57993     /**
57994      *
57995      * basic tag replacing syntax
57996      * WORD:WORD()
57997      *
57998      * // you can fake an object call by doing this
57999      *  x.t:(test,tesT) 
58000      * 
58001      */
58002     re : /\{([\w-\.]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
58003
58004     /**
58005      * compile the template
58006      *
58007      * This is not recursive, so I'm not sure how nested templates are really going to be handled..
58008      *
58009      */
58010     compile: function()
58011     {
58012         var s = this.html;
58013      
58014         s = ['<tpl>', s, '</tpl>'].join('');
58015     
58016         var re     = /<tpl\b[^>]*>((?:(?=([^<]+))\2|<(?!tpl\b[^>]*>))*?)<\/tpl>/,
58017             nameRe = /^<tpl\b[^>]*?for="(.*?)"/,
58018             ifRe   = /^<tpl\b[^>]*?if="(.*?)"/,
58019             execRe = /^<tpl\b[^>]*?exec="(.*?)"/,
58020             namedRe = /^<tpl\b[^>]*?name="(\w+)"/,  // named templates..
58021             m,
58022             id     = 0,
58023             tpls   = [];
58024     
58025         while(true == !!(m = s.match(re))){
58026             var forMatch   = m[0].match(nameRe),
58027                 ifMatch   = m[0].match(ifRe),
58028                 execMatch   = m[0].match(execRe),
58029                 namedMatch   = m[0].match(namedRe),
58030                 
58031                 exp  = null, 
58032                 fn   = null,
58033                 exec = null,
58034                 name = forMatch && forMatch[1] ? forMatch[1] : '';
58035                 
58036             if (ifMatch) {
58037                 // if - puts fn into test..
58038                 exp = ifMatch && ifMatch[1] ? ifMatch[1] : null;
58039                 if(exp){
58040                    fn = new Function('values', 'parent', 'with(values){ return '+(Roo.util.Format.htmlDecode(exp))+'; }');
58041                 }
58042             }
58043             
58044             if (execMatch) {
58045                 // exec - calls a function... returns empty if true is  returned.
58046                 exp = execMatch && execMatch[1] ? execMatch[1] : null;
58047                 if(exp){
58048                    exec = new Function('values', 'parent', 'with(values){ '+(Roo.util.Format.htmlDecode(exp))+'; }');
58049                 }
58050             }
58051             
58052             
58053             if (name) {
58054                 // for = 
58055                 switch(name){
58056                     case '.':  name = new Function('values', 'parent', 'with(values){ return values; }'); break;
58057                     case '..': name = new Function('values', 'parent', 'with(values){ return parent; }'); break;
58058                     default:   name = new Function('values', 'parent', 'with(values){ return '+name+'; }');
58059                 }
58060             }
58061             var uid = namedMatch ? namedMatch[1] : id;
58062             
58063             
58064             tpls.push({
58065                 id:     namedMatch ? namedMatch[1] : id,
58066                 target: name,
58067                 exec:   exec,
58068                 test:   fn,
58069                 body:   m[1] || ''
58070             });
58071             if (namedMatch) {
58072                 s = s.replace(m[0], '');
58073             } else { 
58074                 s = s.replace(m[0], '{xtpl'+ id + '}');
58075             }
58076             ++id;
58077         }
58078         this.tpls = [];
58079         for(var i = tpls.length-1; i >= 0; --i){
58080             this.compileTpl(tpls[i]);
58081             this.tpls[tpls[i].id] = tpls[i];
58082         }
58083         this.master = tpls[tpls.length-1];
58084         return this;
58085     },
58086     /**
58087      * same as applyTemplate, except it's done to one of the subTemplates
58088      * when using named templates, you can do:
58089      *
58090      * var str = pl.applySubTemplate('your-name', values);
58091      *
58092      * 
58093      * @param {Number} id of the template
58094      * @param {Object} values to apply to template
58095      * @param {Object} parent (normaly the instance of this object)
58096      */
58097     applySubTemplate : function(id, values, parent)
58098     {
58099         
58100         
58101         var t = this.tpls[id];
58102         
58103         
58104         try { 
58105             if(t.test && !t.test.call(this, values, parent)){
58106                 return '';
58107             }
58108         } catch(e) {
58109             Roo.log("Xtemplate.applySubTemplate 'test': Exception thrown");
58110             Roo.log(e.toString());
58111             Roo.log(t.test);
58112             return ''
58113         }
58114         try { 
58115             
58116             if(t.exec && t.exec.call(this, values, parent)){
58117                 return '';
58118             }
58119         } catch(e) {
58120             Roo.log("Xtemplate.applySubTemplate 'exec': Exception thrown");
58121             Roo.log(e.toString());
58122             Roo.log(t.exec);
58123             return ''
58124         }
58125         try {
58126             var vs = t.target ? t.target.call(this, values, parent) : values;
58127             parent = t.target ? values : parent;
58128             if(t.target && vs instanceof Array){
58129                 var buf = [];
58130                 for(var i = 0, len = vs.length; i < len; i++){
58131                     buf[buf.length] = t.compiled.call(this, vs[i], parent);
58132                 }
58133                 return buf.join('');
58134             }
58135             return t.compiled.call(this, vs, parent);
58136         } catch (e) {
58137             Roo.log("Xtemplate.applySubTemplate : Exception thrown");
58138             Roo.log(e.toString());
58139             Roo.log(t.compiled);
58140             return '';
58141         }
58142     },
58143
58144     compileTpl : function(tpl)
58145     {
58146         var fm = Roo.util.Format;
58147         var useF = this.disableFormats !== true;
58148         var sep = Roo.isGecko ? "+" : ",";
58149         var undef = function(str) {
58150             Roo.log("Property not found :"  + str);
58151             return '';
58152         };
58153         
58154         var fn = function(m, name, format, args)
58155         {
58156             //Roo.log(arguments);
58157             args = args ? args.replace(/\\'/g,"'") : args;
58158             //["{TEST:(a,b,c)}", "TEST", "", "a,b,c", 0, "{TEST:(a,b,c)}"]
58159             if (typeof(format) == 'undefined') {
58160                 format= 'htmlEncode';
58161             }
58162             if (format == 'raw' ) {
58163                 format = false;
58164             }
58165             
58166             if(name.substr(0, 4) == 'xtpl'){
58167                 return "'"+ sep +'this.applySubTemplate('+name.substr(4)+', values, parent)'+sep+"'";
58168             }
58169             
58170             // build an array of options to determine if value is undefined..
58171             
58172             // basically get 'xxxx.yyyy' then do
58173             // (typeof(xxxx) == 'undefined' || typeof(xxx.yyyy) == 'undefined') ?
58174             //    (function () { Roo.log("Property not found"); return ''; })() :
58175             //    ......
58176             
58177             var udef_ar = [];
58178             var lookfor = '';
58179             Roo.each(name.split('.'), function(st) {
58180                 lookfor += (lookfor.length ? '.': '') + st;
58181                 udef_ar.push(  "(typeof(" + lookfor + ") == 'undefined')"  );
58182             });
58183             
58184             var udef_st = '((' + udef_ar.join(" || ") +") ? undef('" + name + "') : "; // .. needs )
58185             
58186             
58187             if(format && useF){
58188                 
58189                 args = args ? ',' + args : "";
58190                  
58191                 if(format.substr(0, 5) != "this."){
58192                     format = "fm." + format + '(';
58193                 }else{
58194                     format = 'this.call("'+ format.substr(5) + '", ';
58195                     args = ", values";
58196                 }
58197                 
58198                 return "'"+ sep +   udef_st   +    format + name + args + "))"+sep+"'";
58199             }
58200              
58201             if (args.length) {
58202                 // called with xxyx.yuu:(test,test)
58203                 // change to ()
58204                 return "'"+ sep + udef_st  + name + '(' +  args + "))"+sep+"'";
58205             }
58206             // raw.. - :raw modifier..
58207             return "'"+ sep + udef_st  + name + ")"+sep+"'";
58208             
58209         };
58210         var body;
58211         // branched to use + in gecko and [].join() in others
58212         if(Roo.isGecko){
58213             body = "tpl.compiled = function(values, parent){  with(values) { return '" +
58214                    tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
58215                     "';};};";
58216         }else{
58217             body = ["tpl.compiled = function(values, parent){  with (values) { return ['"];
58218             body.push(tpl.body.replace(/(\r\n|\n)/g,
58219                             '\\n').replace(/'/g, "\\'").replace(this.re, fn));
58220             body.push("'].join('');};};");
58221             body = body.join('');
58222         }
58223         
58224         Roo.debug && Roo.log(body.replace(/\\n/,'\n'));
58225        
58226         /** eval:var:tpl eval:var:fm eval:var:useF eval:var:undef  */
58227         eval(body);
58228         
58229         return this;
58230     },
58231
58232     applyTemplate : function(values){
58233         return this.master.compiled.call(this, values, {});
58234         //var s = this.subs;
58235     },
58236
58237     apply : function(){
58238         return this.applyTemplate.apply(this, arguments);
58239     }
58240
58241  });
58242
58243 Roo.XTemplate.from = function(el){
58244     el = Roo.getDom(el);
58245     return new Roo.XTemplate(el.value || el.innerHTML);
58246 };