examples/resizable/basic.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
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
619         /**
620          * By default, Ext intelligently decides whether floating elements should be shimmed. If you are using flash,
621          * you may want to set this to true.
622          * @type Boolean
623          */
624         useShims : ((isIE && !isIE7) || (isGecko && isMac)),
625         
626         
627                 
628         /**
629          * Selects a single element as a Roo Element
630          * This is about as close as you can get to jQuery's $('do crazy stuff')
631          * @param {String} selector The selector/xpath query
632          * @param {Node} root (optional) The start of the query (defaults to document).
633          * @return {Roo.Element}
634          */
635         selectNode : function(selector, root) 
636         {
637             var node = Roo.DomQuery.selectNode(selector,root);
638             return node ? Roo.get(node) : new Roo.Element(false);
639         }
640         
641     });
642
643
644 })();
645
646 Roo.namespace("Roo", "Roo.util", "Roo.grid", "Roo.dd", "Roo.tree", "Roo.data",
647                 "Roo.form", "Roo.menu", "Roo.state", "Roo.lib", "Roo.layout", "Roo.app", "Roo.ux");
648 /*
649  * Based on:
650  * Ext JS Library 1.1.1
651  * Copyright(c) 2006-2007, Ext JS, LLC.
652  *
653  * Originally Released Under LGPL - original licence link has changed is not relivant.
654  *
655  * Fork - LGPL
656  * <script type="text/javascript">
657  */
658
659 (function() {    
660     // wrappedn so fnCleanup is not in global scope...
661     if(Roo.isIE) {
662         function fnCleanUp() {
663             var p = Function.prototype;
664             delete p.createSequence;
665             delete p.defer;
666             delete p.createDelegate;
667             delete p.createCallback;
668             delete p.createInterceptor;
669
670             window.detachEvent("onunload", fnCleanUp);
671         }
672         window.attachEvent("onunload", fnCleanUp);
673     }
674 })();
675
676
677 /**
678  * @class Function
679  * These functions are available on every Function object (any JavaScript function).
680  */
681 Roo.apply(Function.prototype, {
682      /**
683      * Creates a callback that passes arguments[0], arguments[1], arguments[2], ...
684      * Call directly on any function. Example: <code>myFunction.createCallback(myarg, myarg2)</code>
685      * Will create a function that is bound to those 2 args.
686      * @return {Function} The new function
687     */
688     createCallback : function(/*args...*/){
689         // make args available, in function below
690         var args = arguments;
691         var method = this;
692         return function() {
693             return method.apply(window, args);
694         };
695     },
696
697     /**
698      * Creates a delegate (callback) that sets the scope to obj.
699      * Call directly on any function. Example: <code>this.myFunction.createDelegate(this)</code>
700      * Will create a function that is automatically scoped to this.
701      * @param {Object} obj (optional) The object for which the scope is set
702      * @param {Array} args (optional) Overrides arguments for the call. (Defaults to the arguments passed by the caller)
703      * @param {Boolean/Number} appendArgs (optional) if True args are appended to call args instead of overriding,
704      *                                             if a number the args are inserted at the specified position
705      * @return {Function} The new function
706      */
707     createDelegate : function(obj, args, appendArgs){
708         var method = this;
709         return function() {
710             var callArgs = args || arguments;
711             if(appendArgs === true){
712                 callArgs = Array.prototype.slice.call(arguments, 0);
713                 callArgs = callArgs.concat(args);
714             }else if(typeof appendArgs == "number"){
715                 callArgs = Array.prototype.slice.call(arguments, 0); // copy arguments first
716                 var applyArgs = [appendArgs, 0].concat(args); // create method call params
717                 Array.prototype.splice.apply(callArgs, applyArgs); // splice them in
718             }
719             return method.apply(obj || window, callArgs);
720         };
721     },
722
723     /**
724      * Calls this function after the number of millseconds specified.
725      * @param {Number} millis The number of milliseconds for the setTimeout call (if 0 the function is executed immediately)
726      * @param {Object} obj (optional) The object for which the scope is set
727      * @param {Array} args (optional) Overrides arguments for the call. (Defaults to the arguments passed by the caller)
728      * @param {Boolean/Number} appendArgs (optional) if True args are appended to call args instead of overriding,
729      *                                             if a number the args are inserted at the specified position
730      * @return {Number} The timeout id that can be used with clearTimeout
731      */
732     defer : function(millis, obj, args, appendArgs){
733         var fn = this.createDelegate(obj, args, appendArgs);
734         if(millis){
735             return setTimeout(fn, millis);
736         }
737         fn();
738         return 0;
739     },
740     /**
741      * Create a combined function call sequence of the original function + the passed function.
742      * The resulting function returns the results of the original function.
743      * The passed fcn is called with the parameters of the original function
744      * @param {Function} fcn The function to sequence
745      * @param {Object} scope (optional) The scope of the passed fcn (Defaults to scope of original function or window)
746      * @return {Function} The new function
747      */
748     createSequence : function(fcn, scope){
749         if(typeof fcn != "function"){
750             return this;
751         }
752         var method = this;
753         return function() {
754             var retval = method.apply(this || window, arguments);
755             fcn.apply(scope || this || window, arguments);
756             return retval;
757         };
758     },
759
760     /**
761      * Creates an interceptor function. The passed fcn is called before the original one. If it returns false, the original one is not called.
762      * The resulting function returns the results of the original function.
763      * The passed fcn is called with the parameters of the original function.
764      * @addon
765      * @param {Function} fcn The function to call before the original
766      * @param {Object} scope (optional) The scope of the passed fcn (Defaults to scope of original function or window)
767      * @return {Function} The new function
768      */
769     createInterceptor : function(fcn, scope){
770         if(typeof fcn != "function"){
771             return this;
772         }
773         var method = this;
774         return function() {
775             fcn.target = this;
776             fcn.method = method;
777             if(fcn.apply(scope || this || window, arguments) === false){
778                 return;
779             }
780             return method.apply(this || window, arguments);
781         };
782     }
783 });
784 /*
785  * Based on:
786  * Ext JS Library 1.1.1
787  * Copyright(c) 2006-2007, Ext JS, LLC.
788  *
789  * Originally Released Under LGPL - original licence link has changed is not relivant.
790  *
791  * Fork - LGPL
792  * <script type="text/javascript">
793  */
794
795 Roo.applyIf(String, {
796     
797     /** @scope String */
798     
799     /**
800      * Escapes the passed string for ' and \
801      * @param {String} string The string to escape
802      * @return {String} The escaped string
803      * @static
804      */
805     escape : function(string) {
806         return string.replace(/('|\\)/g, "\\$1");
807     },
808
809     /**
810      * Pads the left side of a string with a specified character.  This is especially useful
811      * for normalizing number and date strings.  Example usage:
812      * <pre><code>
813 var s = String.leftPad('123', 5, '0');
814 // s now contains the string: '00123'
815 </code></pre>
816      * @param {String} string The original string
817      * @param {Number} size The total length of the output string
818      * @param {String} char (optional) The character with which to pad the original string (defaults to empty string " ")
819      * @return {String} The padded string
820      * @static
821      */
822     leftPad : function (val, size, ch) {
823         var result = new String(val);
824         if(ch === null || ch === undefined || ch === '') {
825             ch = " ";
826         }
827         while (result.length < size) {
828             result = ch + result;
829         }
830         return result;
831     },
832
833     /**
834      * Allows you to define a tokenized string and pass an arbitrary number of arguments to replace the tokens.  Each
835      * token must be unique, and must increment in the format {0}, {1}, etc.  Example usage:
836      * <pre><code>
837 var cls = 'my-class', text = 'Some text';
838 var s = String.format('<div class="{0}">{1}</div>', cls, text);
839 // s now contains the string: '<div class="my-class">Some text</div>'
840 </code></pre>
841      * @param {String} string The tokenized string to be formatted
842      * @param {String} value1 The value to replace token {0}
843      * @param {String} value2 Etc...
844      * @return {String} The formatted string
845      * @static
846      */
847     format : function(format){
848         var args = Array.prototype.slice.call(arguments, 1);
849         return format.replace(/\{(\d+)\}/g, function(m, i){
850             return Roo.util.Format.htmlEncode(args[i]);
851         });
852     }
853 });
854
855 /**
856  * Utility function that allows you to easily switch a string between two alternating values.  The passed value
857  * is compared to the current string, and if they are equal, the other value that was passed in is returned.  If
858  * they are already different, the first value passed in is returned.  Note that this method returns the new value
859  * but does not change the current string.
860  * <pre><code>
861 // alternate sort directions
862 sort = sort.toggle('ASC', 'DESC');
863
864 // instead of conditional logic:
865 sort = (sort == 'ASC' ? 'DESC' : 'ASC');
866 </code></pre>
867  * @param {String} value The value to compare to the current string
868  * @param {String} other The new value to use if the string already equals the first value passed in
869  * @return {String} The new value
870  */
871  
872 String.prototype.toggle = function(value, other){
873     return this == value ? other : value;
874 };/*
875  * Based on:
876  * Ext JS Library 1.1.1
877  * Copyright(c) 2006-2007, Ext JS, LLC.
878  *
879  * Originally Released Under LGPL - original licence link has changed is not relivant.
880  *
881  * Fork - LGPL
882  * <script type="text/javascript">
883  */
884
885  /**
886  * @class Number
887  */
888 Roo.applyIf(Number.prototype, {
889     /**
890      * Checks whether or not the current number is within a desired range.  If the number is already within the
891      * range it is returned, otherwise the min or max value is returned depending on which side of the range is
892      * exceeded.  Note that this method returns the constrained value but does not change the current number.
893      * @param {Number} min The minimum number in the range
894      * @param {Number} max The maximum number in the range
895      * @return {Number} The constrained value if outside the range, otherwise the current value
896      */
897     constrain : function(min, max){
898         return Math.min(Math.max(this, min), max);
899     }
900 });/*
901  * Based on:
902  * Ext JS Library 1.1.1
903  * Copyright(c) 2006-2007, Ext JS, LLC.
904  *
905  * Originally Released Under LGPL - original licence link has changed is not relivant.
906  *
907  * Fork - LGPL
908  * <script type="text/javascript">
909  */
910  /**
911  * @class Array
912  */
913 Roo.applyIf(Array.prototype, {
914     /**
915      * Checks whether or not the specified object exists in the array.
916      * @param {Object} o The object to check for
917      * @return {Number} The index of o in the array (or -1 if it is not found)
918      */
919     indexOf : function(o){
920        for (var i = 0, len = this.length; i < len; i++){
921               if(this[i] == o) return i;
922        }
923            return -1;
924     },
925
926     /**
927      * Removes the specified object from the array.  If the object is not found nothing happens.
928      * @param {Object} o The object to remove
929      */
930     remove : function(o){
931        var index = this.indexOf(o);
932        if(index != -1){
933            this.splice(index, 1);
934        }
935     },
936     /**
937      * Map (JS 1.6 compatibility)
938      * @param {Function} function  to call
939      */
940     map : function(fun )
941     {
942         var len = this.length >>> 0;
943         if (typeof fun != "function")
944             throw new TypeError();
945
946         var res = new Array(len);
947         var thisp = arguments[1];
948         for (var i = 0; i < len; i++)
949         {
950             if (i in this)
951                 res[i] = fun.call(thisp, this[i], i, this);
952         }
953
954         return res;
955     }
956     
957 });
958
959
960  /*
961  * Based on:
962  * Ext JS Library 1.1.1
963  * Copyright(c) 2006-2007, Ext JS, LLC.
964  *
965  * Originally Released Under LGPL - original licence link has changed is not relivant.
966  *
967  * Fork - LGPL
968  * <script type="text/javascript">
969  */
970
971 /**
972  * @class Date
973  *
974  * The date parsing and format syntax is a subset of
975  * <a href="http://www.php.net/date">PHP's date() function</a>, and the formats that are
976  * supported will provide results equivalent to their PHP versions.
977  *
978  * Following is the list of all currently supported formats:
979  *<pre>
980 Sample date:
981 'Wed Jan 10 2007 15:05:01 GMT-0600 (Central Standard Time)'
982
983 Format  Output      Description
984 ------  ----------  --------------------------------------------------------------
985   d      10         Day of the month, 2 digits with leading zeros
986   D      Wed        A textual representation of a day, three letters
987   j      10         Day of the month without leading zeros
988   l      Wednesday  A full textual representation of the day of the week
989   S      th         English ordinal day of month suffix, 2 chars (use with j)
990   w      3          Numeric representation of the day of the week
991   z      9          The julian date, or day of the year (0-365)
992   W      01         ISO-8601 2-digit week number of year, weeks starting on Monday (00-52)
993   F      January    A full textual representation of the month
994   m      01         Numeric representation of a month, with leading zeros
995   M      Jan        Month name abbreviation, three letters
996   n      1          Numeric representation of a month, without leading zeros
997   t      31         Number of days in the given month
998   L      0          Whether it's a leap year (1 if it is a leap year, else 0)
999   Y      2007       A full numeric representation of a year, 4 digits
1000   y      07         A two digit representation of a year
1001   a      pm         Lowercase Ante meridiem and Post meridiem
1002   A      PM         Uppercase Ante meridiem and Post meridiem
1003   g      3          12-hour format of an hour without leading zeros
1004   G      15         24-hour format of an hour without leading zeros
1005   h      03         12-hour format of an hour with leading zeros
1006   H      15         24-hour format of an hour with leading zeros
1007   i      05         Minutes with leading zeros
1008   s      01         Seconds, with leading zeros
1009   O      -0600      Difference to Greenwich time (GMT) in hours (Allows +08, without minutes)
1010   P      -06:00     Difference to Greenwich time (GMT) with colon between hours and minutes
1011   T      CST        Timezone setting of the machine running the code
1012   Z      -21600     Timezone offset in seconds (negative if west of UTC, positive if east)
1013 </pre>
1014  *
1015  * Example usage (note that you must escape format specifiers with '\\' to render them as character literals):
1016  * <pre><code>
1017 var dt = new Date('1/10/2007 03:05:01 PM GMT-0600');
1018 document.write(dt.format('Y-m-d'));                         //2007-01-10
1019 document.write(dt.format('F j, Y, g:i a'));                 //January 10, 2007, 3:05 pm
1020 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
1021  </code></pre>
1022  *
1023  * Here are some standard date/time patterns that you might find helpful.  They
1024  * are not part of the source of Date.js, but to use them you can simply copy this
1025  * block of code into any script that is included after Date.js and they will also become
1026  * globally available on the Date object.  Feel free to add or remove patterns as needed in your code.
1027  * <pre><code>
1028 Date.patterns = {
1029     ISO8601Long:"Y-m-d H:i:s",
1030     ISO8601Short:"Y-m-d",
1031     ShortDate: "n/j/Y",
1032     LongDate: "l, F d, Y",
1033     FullDateTime: "l, F d, Y g:i:s A",
1034     MonthDay: "F d",
1035     ShortTime: "g:i A",
1036     LongTime: "g:i:s A",
1037     SortableDateTime: "Y-m-d\\TH:i:s",
1038     UniversalSortableDateTime: "Y-m-d H:i:sO",
1039     YearMonth: "F, Y"
1040 };
1041 </code></pre>
1042  *
1043  * Example usage:
1044  * <pre><code>
1045 var dt = new Date();
1046 document.write(dt.format(Date.patterns.ShortDate));
1047  </code></pre>
1048  */
1049
1050 /*
1051  * Most of the date-formatting functions below are the excellent work of Baron Schwartz.
1052  * They generate precompiled functions from date formats instead of parsing and
1053  * processing the pattern every time you format a date.  These functions are available
1054  * on every Date object (any javascript function).
1055  *
1056  * The original article and download are here:
1057  * http://www.xaprb.com/blog/2005/12/12/javascript-closures-for-runtime-efficiency/
1058  *
1059  */
1060  
1061  
1062  // was in core
1063 /**
1064  Returns the number of milliseconds between this date and date
1065  @param {Date} date (optional) Defaults to now
1066  @return {Number} The diff in milliseconds
1067  @member Date getElapsed
1068  */
1069 Date.prototype.getElapsed = function(date) {
1070         return Math.abs((date || new Date()).getTime()-this.getTime());
1071 };
1072 // was in date file..
1073
1074
1075 // private
1076 Date.parseFunctions = {count:0};
1077 // private
1078 Date.parseRegexes = [];
1079 // private
1080 Date.formatFunctions = {count:0};
1081
1082 // private
1083 Date.prototype.dateFormat = function(format) {
1084     if (Date.formatFunctions[format] == null) {
1085         Date.createNewFormat(format);
1086     }
1087     var func = Date.formatFunctions[format];
1088     return this[func]();
1089 };
1090
1091
1092 /**
1093  * Formats a date given the supplied format string
1094  * @param {String} format The format string
1095  * @return {String} The formatted date
1096  * @method
1097  */
1098 Date.prototype.format = Date.prototype.dateFormat;
1099
1100 // private
1101 Date.createNewFormat = function(format) {
1102     var funcName = "format" + Date.formatFunctions.count++;
1103     Date.formatFunctions[format] = funcName;
1104     var code = "Date.prototype." + funcName + " = function(){return ";
1105     var special = false;
1106     var ch = '';
1107     for (var i = 0; i < format.length; ++i) {
1108         ch = format.charAt(i);
1109         if (!special && ch == "\\") {
1110             special = true;
1111         }
1112         else if (special) {
1113             special = false;
1114             code += "'" + String.escape(ch) + "' + ";
1115         }
1116         else {
1117             code += Date.getFormatCode(ch);
1118         }
1119     }
1120     /** eval:var:zzzzzzzzzzzzz */
1121     eval(code.substring(0, code.length - 3) + ";}");
1122 };
1123
1124 // private
1125 Date.getFormatCode = function(character) {
1126     switch (character) {
1127     case "d":
1128         return "String.leftPad(this.getDate(), 2, '0') + ";
1129     case "D":
1130         return "Date.dayNames[this.getDay()].substring(0, 3) + ";
1131     case "j":
1132         return "this.getDate() + ";
1133     case "l":
1134         return "Date.dayNames[this.getDay()] + ";
1135     case "S":
1136         return "this.getSuffix() + ";
1137     case "w":
1138         return "this.getDay() + ";
1139     case "z":
1140         return "this.getDayOfYear() + ";
1141     case "W":
1142         return "this.getWeekOfYear() + ";
1143     case "F":
1144         return "Date.monthNames[this.getMonth()] + ";
1145     case "m":
1146         return "String.leftPad(this.getMonth() + 1, 2, '0') + ";
1147     case "M":
1148         return "Date.monthNames[this.getMonth()].substring(0, 3) + ";
1149     case "n":
1150         return "(this.getMonth() + 1) + ";
1151     case "t":
1152         return "this.getDaysInMonth() + ";
1153     case "L":
1154         return "(this.isLeapYear() ? 1 : 0) + ";
1155     case "Y":
1156         return "this.getFullYear() + ";
1157     case "y":
1158         return "('' + this.getFullYear()).substring(2, 4) + ";
1159     case "a":
1160         return "(this.getHours() < 12 ? 'am' : 'pm') + ";
1161     case "A":
1162         return "(this.getHours() < 12 ? 'AM' : 'PM') + ";
1163     case "g":
1164         return "((this.getHours() % 12) ? this.getHours() % 12 : 12) + ";
1165     case "G":
1166         return "this.getHours() + ";
1167     case "h":
1168         return "String.leftPad((this.getHours() % 12) ? this.getHours() % 12 : 12, 2, '0') + ";
1169     case "H":
1170         return "String.leftPad(this.getHours(), 2, '0') + ";
1171     case "i":
1172         return "String.leftPad(this.getMinutes(), 2, '0') + ";
1173     case "s":
1174         return "String.leftPad(this.getSeconds(), 2, '0') + ";
1175     case "O":
1176         return "this.getGMTOffset() + ";
1177     case "P":
1178         return "this.getGMTColonOffset() + ";
1179     case "T":
1180         return "this.getTimezone() + ";
1181     case "Z":
1182         return "(this.getTimezoneOffset() * -60) + ";
1183     default:
1184         return "'" + String.escape(character) + "' + ";
1185     }
1186 };
1187
1188 /**
1189  * Parses the passed string using the specified format. Note that this function expects dates in normal calendar
1190  * format, meaning that months are 1-based (1 = January) and not zero-based like in JavaScript dates.  Any part of
1191  * the date format that is not specified will default to the current date value for that part.  Time parts can also
1192  * be specified, but default to 0.  Keep in mind that the input date string must precisely match the specified format
1193  * string or the parse operation will fail.
1194  * Example Usage:
1195 <pre><code>
1196 //dt = Fri May 25 2007 (current date)
1197 var dt = new Date();
1198
1199 //dt = Thu May 25 2006 (today's month/day in 2006)
1200 dt = Date.parseDate("2006", "Y");
1201
1202 //dt = Sun Jan 15 2006 (all date parts specified)
1203 dt = Date.parseDate("2006-1-15", "Y-m-d");
1204
1205 //dt = Sun Jan 15 2006 15:20:01 GMT-0600 (CST)
1206 dt = Date.parseDate("2006-1-15 3:20:01 PM", "Y-m-d h:i:s A" );
1207 </code></pre>
1208  * @param {String} input The unparsed date as a string
1209  * @param {String} format The format the date is in
1210  * @return {Date} The parsed date
1211  * @static
1212  */
1213 Date.parseDate = function(input, format) {
1214     if (Date.parseFunctions[format] == null) {
1215         Date.createParser(format);
1216     }
1217     var func = Date.parseFunctions[format];
1218     return Date[func](input);
1219 };
1220 /**
1221  * @private
1222  */
1223 Date.createParser = function(format) {
1224     var funcName = "parse" + Date.parseFunctions.count++;
1225     var regexNum = Date.parseRegexes.length;
1226     var currentGroup = 1;
1227     Date.parseFunctions[format] = funcName;
1228
1229     var code = "Date." + funcName + " = function(input){\n"
1230         + "var y = -1, m = -1, d = -1, h = -1, i = -1, s = -1, o, z, v;\n"
1231         + "var d = new Date();\n"
1232         + "y = d.getFullYear();\n"
1233         + "m = d.getMonth();\n"
1234         + "d = d.getDate();\n"
1235         + "var results = input.match(Date.parseRegexes[" + regexNum + "]);\n"
1236         + "if (results && results.length > 0) {";
1237     var regex = "";
1238
1239     var special = false;
1240     var ch = '';
1241     for (var i = 0; i < format.length; ++i) {
1242         ch = format.charAt(i);
1243         if (!special && ch == "\\") {
1244             special = true;
1245         }
1246         else if (special) {
1247             special = false;
1248             regex += String.escape(ch);
1249         }
1250         else {
1251             var obj = Date.formatCodeToRegex(ch, currentGroup);
1252             currentGroup += obj.g;
1253             regex += obj.s;
1254             if (obj.g && obj.c) {
1255                 code += obj.c;
1256             }
1257         }
1258     }
1259
1260     code += "if (y >= 0 && m >= 0 && d > 0 && h >= 0 && i >= 0 && s >= 0)\n"
1261         + "{v = new Date(y, m, d, h, i, s);}\n"
1262         + "else if (y >= 0 && m >= 0 && d > 0 && h >= 0 && i >= 0)\n"
1263         + "{v = new Date(y, m, d, h, i);}\n"
1264         + "else if (y >= 0 && m >= 0 && d > 0 && h >= 0)\n"
1265         + "{v = new Date(y, m, d, h);}\n"
1266         + "else if (y >= 0 && m >= 0 && d > 0)\n"
1267         + "{v = new Date(y, m, d);}\n"
1268         + "else if (y >= 0 && m >= 0)\n"
1269         + "{v = new Date(y, m);}\n"
1270         + "else if (y >= 0)\n"
1271         + "{v = new Date(y);}\n"
1272         + "}return (v && (z || o))?\n" // favour UTC offset over GMT offset
1273         + "    ((z)? v.add(Date.SECOND, (v.getTimezoneOffset() * 60) + (z*1)) :\n" // reset to UTC, then add offset
1274         + "        v.add(Date.HOUR, (v.getGMTOffset() / 100) + (o / -100))) : v\n" // reset to GMT, then add offset
1275         + ";}";
1276
1277     Date.parseRegexes[regexNum] = new RegExp("^" + regex + "$");
1278     /** eval:var:zzzzzzzzzzzzz */
1279     eval(code);
1280 };
1281
1282 // private
1283 Date.formatCodeToRegex = function(character, currentGroup) {
1284     switch (character) {
1285     case "D":
1286         return {g:0,
1287         c:null,
1288         s:"(?:Sun|Mon|Tue|Wed|Thu|Fri|Sat)"};
1289     case "j":
1290         return {g:1,
1291             c:"d = parseInt(results[" + currentGroup + "], 10);\n",
1292             s:"(\\d{1,2})"}; // day of month without leading zeroes
1293     case "d":
1294         return {g:1,
1295             c:"d = parseInt(results[" + currentGroup + "], 10);\n",
1296             s:"(\\d{2})"}; // day of month with leading zeroes
1297     case "l":
1298         return {g:0,
1299             c:null,
1300             s:"(?:" + Date.dayNames.join("|") + ")"};
1301     case "S":
1302         return {g:0,
1303             c:null,
1304             s:"(?:st|nd|rd|th)"};
1305     case "w":
1306         return {g:0,
1307             c:null,
1308             s:"\\d"};
1309     case "z":
1310         return {g:0,
1311             c:null,
1312             s:"(?:\\d{1,3})"};
1313     case "W":
1314         return {g:0,
1315             c:null,
1316             s:"(?:\\d{2})"};
1317     case "F":
1318         return {g:1,
1319             c:"m = parseInt(Date.monthNumbers[results[" + currentGroup + "].substring(0, 3)], 10);\n",
1320             s:"(" + Date.monthNames.join("|") + ")"};
1321     case "M":
1322         return {g:1,
1323             c:"m = parseInt(Date.monthNumbers[results[" + currentGroup + "]], 10);\n",
1324             s:"(Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)"};
1325     case "n":
1326         return {g:1,
1327             c:"m = parseInt(results[" + currentGroup + "], 10) - 1;\n",
1328             s:"(\\d{1,2})"}; // Numeric representation of a month, without leading zeros
1329     case "m":
1330         return {g:1,
1331             c:"m = parseInt(results[" + currentGroup + "], 10) - 1;\n",
1332             s:"(\\d{2})"}; // Numeric representation of a month, with leading zeros
1333     case "t":
1334         return {g:0,
1335             c:null,
1336             s:"\\d{1,2}"};
1337     case "L":
1338         return {g:0,
1339             c:null,
1340             s:"(?:1|0)"};
1341     case "Y":
1342         return {g:1,
1343             c:"y = parseInt(results[" + currentGroup + "], 10);\n",
1344             s:"(\\d{4})"};
1345     case "y":
1346         return {g:1,
1347             c:"var ty = parseInt(results[" + currentGroup + "], 10);\n"
1348                 + "y = ty > Date.y2kYear ? 1900 + ty : 2000 + ty;\n",
1349             s:"(\\d{1,2})"};
1350     case "a":
1351         return {g:1,
1352             c:"if (results[" + currentGroup + "] == 'am') {\n"
1353                 + "if (h == 12) { h = 0; }\n"
1354                 + "} else { if (h < 12) { h += 12; }}",
1355             s:"(am|pm)"};
1356     case "A":
1357         return {g:1,
1358             c:"if (results[" + currentGroup + "] == 'AM') {\n"
1359                 + "if (h == 12) { h = 0; }\n"
1360                 + "} else { if (h < 12) { h += 12; }}",
1361             s:"(AM|PM)"};
1362     case "g":
1363     case "G":
1364         return {g:1,
1365             c:"h = parseInt(results[" + currentGroup + "], 10);\n",
1366             s:"(\\d{1,2})"}; // 12/24-hr format  format of an hour without leading zeroes
1367     case "h":
1368     case "H":
1369         return {g:1,
1370             c:"h = parseInt(results[" + currentGroup + "], 10);\n",
1371             s:"(\\d{2})"}; //  12/24-hr format  format of an hour with leading zeroes
1372     case "i":
1373         return {g:1,
1374             c:"i = parseInt(results[" + currentGroup + "], 10);\n",
1375             s:"(\\d{2})"};
1376     case "s":
1377         return {g:1,
1378             c:"s = parseInt(results[" + currentGroup + "], 10);\n",
1379             s:"(\\d{2})"};
1380     case "O":
1381         return {g:1,
1382             c:[
1383                 "o = results[", currentGroup, "];\n",
1384                 "var sn = o.substring(0,1);\n", // get + / - sign
1385                 "var hr = o.substring(1,3)*1 + Math.floor(o.substring(3,5) / 60);\n", // get hours (performs minutes-to-hour conversion also)
1386                 "var mn = o.substring(3,5) % 60;\n", // get minutes
1387                 "o = ((-12 <= (hr*60 + mn)/60) && ((hr*60 + mn)/60 <= 14))?\n", // -12hrs <= GMT offset <= 14hrs
1388                 "    (sn + String.leftPad(hr, 2, 0) + String.leftPad(mn, 2, 0)) : null;\n"
1389             ].join(""),
1390             s:"([+\-]\\d{2,4})"};
1391     
1392     
1393     case "P":
1394         return {g:1,
1395                 c:[
1396                    "o = results[", currentGroup, "];\n",
1397                    "var sn = o.substring(0,1);\n",
1398                    "var hr = o.substring(1,3)*1 + Math.floor(o.substring(4,6) / 60);\n",
1399                    "var mn = o.substring(4,6) % 60;\n",
1400                    "o = ((-12 <= (hr*60 + mn)/60) && ((hr*60 + mn)/60 <= 14))?\n",
1401                         "    (sn + String.leftPad(hr, 2, 0) + String.leftPad(mn, 2, 0)) : null;\n"
1402             ].join(""),
1403             s:"([+\-]\\d{4})"};
1404     case "T":
1405         return {g:0,
1406             c:null,
1407             s:"[A-Z]{1,4}"}; // timezone abbrev. may be between 1 - 4 chars
1408     case "Z":
1409         return {g:1,
1410             c:"z = results[" + currentGroup + "];\n" // -43200 <= UTC offset <= 50400
1411                   + "z = (-43200 <= z*1 && z*1 <= 50400)? z : null;\n",
1412             s:"([+\-]?\\d{1,5})"}; // leading '+' sign is optional for UTC offset
1413     default:
1414         return {g:0,
1415             c:null,
1416             s:String.escape(character)};
1417     }
1418 };
1419
1420 /**
1421  * Get the timezone abbreviation of the current date (equivalent to the format specifier 'T').
1422  * @return {String} The abbreviated timezone name (e.g. 'CST')
1423  */
1424 Date.prototype.getTimezone = function() {
1425     return this.toString().replace(/^.*? ([A-Z]{1,4})[\-+][0-9]{4} .*$/, "$1");
1426 };
1427
1428 /**
1429  * Get the offset from GMT of the current date (equivalent to the format specifier 'O').
1430  * @return {String} The 4-character offset string prefixed with + or - (e.g. '-0600')
1431  */
1432 Date.prototype.getGMTOffset = function() {
1433     return (this.getTimezoneOffset() > 0 ? "-" : "+")
1434         + String.leftPad(Math.abs(Math.floor(this.getTimezoneOffset() / 60)), 2, "0")
1435         + String.leftPad(this.getTimezoneOffset() % 60, 2, "0");
1436 };
1437
1438 /**
1439  * Get the offset from GMT of the current date (equivalent to the format specifier 'P').
1440  * @return {String} 2-characters representing hours and 2-characters representing minutes
1441  * seperated by a colon and prefixed with + or - (e.g. '-06:00')
1442  */
1443 Date.prototype.getGMTColonOffset = function() {
1444         return (this.getTimezoneOffset() > 0 ? "-" : "+")
1445                 + String.leftPad(Math.abs(Math.floor(this.getTimezoneOffset() / 60)), 2, "0")
1446                 + ":"
1447                 + String.leftPad(this.getTimezoneOffset() %60, 2, "0");
1448 }
1449
1450 /**
1451  * Get the numeric day number of the year, adjusted for leap year.
1452  * @return {Number} 0 through 364 (365 in leap years)
1453  */
1454 Date.prototype.getDayOfYear = function() {
1455     var num = 0;
1456     Date.daysInMonth[1] = this.isLeapYear() ? 29 : 28;
1457     for (var i = 0; i < this.getMonth(); ++i) {
1458         num += Date.daysInMonth[i];
1459     }
1460     return num + this.getDate() - 1;
1461 };
1462
1463 /**
1464  * Get the string representation of the numeric week number of the year
1465  * (equivalent to the format specifier 'W').
1466  * @return {String} '00' through '52'
1467  */
1468 Date.prototype.getWeekOfYear = function() {
1469     // Skip to Thursday of this week
1470     var now = this.getDayOfYear() + (4 - this.getDay());
1471     // Find the first Thursday of the year
1472     var jan1 = new Date(this.getFullYear(), 0, 1);
1473     var then = (7 - jan1.getDay() + 4);
1474     return String.leftPad(((now - then) / 7) + 1, 2, "0");
1475 };
1476
1477 /**
1478  * Whether or not the current date is in a leap year.
1479  * @return {Boolean} True if the current date is in a leap year, else false
1480  */
1481 Date.prototype.isLeapYear = function() {
1482     var year = this.getFullYear();
1483     return ((year & 3) == 0 && (year % 100 || (year % 400 == 0 && year)));
1484 };
1485
1486 /**
1487  * Get the first day of the current month, adjusted for leap year.  The returned value
1488  * is the numeric day index within the week (0-6) which can be used in conjunction with
1489  * the {@link #monthNames} array to retrieve the textual day name.
1490  * Example:
1491  *<pre><code>
1492 var dt = new Date('1/10/2007');
1493 document.write(Date.dayNames[dt.getFirstDayOfMonth()]); //output: 'Monday'
1494 </code></pre>
1495  * @return {Number} The day number (0-6)
1496  */
1497 Date.prototype.getFirstDayOfMonth = function() {
1498     var day = (this.getDay() - (this.getDate() - 1)) % 7;
1499     return (day < 0) ? (day + 7) : day;
1500 };
1501
1502 /**
1503  * Get the last day of the current month, adjusted for leap year.  The returned value
1504  * is the numeric day index within the week (0-6) which can be used in conjunction with
1505  * the {@link #monthNames} array to retrieve the textual day name.
1506  * Example:
1507  *<pre><code>
1508 var dt = new Date('1/10/2007');
1509 document.write(Date.dayNames[dt.getLastDayOfMonth()]); //output: 'Wednesday'
1510 </code></pre>
1511  * @return {Number} The day number (0-6)
1512  */
1513 Date.prototype.getLastDayOfMonth = function() {
1514     var day = (this.getDay() + (Date.daysInMonth[this.getMonth()] - this.getDate())) % 7;
1515     return (day < 0) ? (day + 7) : day;
1516 };
1517
1518
1519 /**
1520  * Get the first date of this date's month
1521  * @return {Date}
1522  */
1523 Date.prototype.getFirstDateOfMonth = function() {
1524     return new Date(this.getFullYear(), this.getMonth(), 1);
1525 };
1526
1527 /**
1528  * Get the last date of this date's month
1529  * @return {Date}
1530  */
1531 Date.prototype.getLastDateOfMonth = function() {
1532     return new Date(this.getFullYear(), this.getMonth(), this.getDaysInMonth());
1533 };
1534 /**
1535  * Get the number of days in the current month, adjusted for leap year.
1536  * @return {Number} The number of days in the month
1537  */
1538 Date.prototype.getDaysInMonth = function() {
1539     Date.daysInMonth[1] = this.isLeapYear() ? 29 : 28;
1540     return Date.daysInMonth[this.getMonth()];
1541 };
1542
1543 /**
1544  * Get the English ordinal suffix of the current day (equivalent to the format specifier 'S').
1545  * @return {String} 'st, 'nd', 'rd' or 'th'
1546  */
1547 Date.prototype.getSuffix = function() {
1548     switch (this.getDate()) {
1549         case 1:
1550         case 21:
1551         case 31:
1552             return "st";
1553         case 2:
1554         case 22:
1555             return "nd";
1556         case 3:
1557         case 23:
1558             return "rd";
1559         default:
1560             return "th";
1561     }
1562 };
1563
1564 // private
1565 Date.daysInMonth = [31,28,31,30,31,30,31,31,30,31,30,31];
1566
1567 /**
1568  * An array of textual month names.
1569  * Override these values for international dates, for example...
1570  * Date.monthNames = ['JanInYourLang', 'FebInYourLang', ...];
1571  * @type Array
1572  * @static
1573  */
1574 Date.monthNames =
1575    ["January",
1576     "February",
1577     "March",
1578     "April",
1579     "May",
1580     "June",
1581     "July",
1582     "August",
1583     "September",
1584     "October",
1585     "November",
1586     "December"];
1587
1588 /**
1589  * An array of textual day names.
1590  * Override these values for international dates, for example...
1591  * Date.dayNames = ['SundayInYourLang', 'MondayInYourLang', ...];
1592  * @type Array
1593  * @static
1594  */
1595 Date.dayNames =
1596    ["Sunday",
1597     "Monday",
1598     "Tuesday",
1599     "Wednesday",
1600     "Thursday",
1601     "Friday",
1602     "Saturday"];
1603
1604 // private
1605 Date.y2kYear = 50;
1606 // private
1607 Date.monthNumbers = {
1608     Jan:0,
1609     Feb:1,
1610     Mar:2,
1611     Apr:3,
1612     May:4,
1613     Jun:5,
1614     Jul:6,
1615     Aug:7,
1616     Sep:8,
1617     Oct:9,
1618     Nov:10,
1619     Dec:11};
1620
1621 /**
1622  * Creates and returns a new Date instance with the exact same date value as the called instance.
1623  * Dates are copied and passed by reference, so if a copied date variable is modified later, the original
1624  * variable will also be changed.  When the intention is to create a new variable that will not
1625  * modify the original instance, you should create a clone.
1626  *
1627  * Example of correctly cloning a date:
1628  * <pre><code>
1629 //wrong way:
1630 var orig = new Date('10/1/2006');
1631 var copy = orig;
1632 copy.setDate(5);
1633 document.write(orig);  //returns 'Thu Oct 05 2006'!
1634
1635 //correct way:
1636 var orig = new Date('10/1/2006');
1637 var copy = orig.clone();
1638 copy.setDate(5);
1639 document.write(orig);  //returns 'Thu Oct 01 2006'
1640 </code></pre>
1641  * @return {Date} The new Date instance
1642  */
1643 Date.prototype.clone = function() {
1644         return new Date(this.getTime());
1645 };
1646
1647 /**
1648  * Clears any time information from this date
1649  @param {Boolean} clone true to create a clone of this date, clear the time and return it
1650  @return {Date} this or the clone
1651  */
1652 Date.prototype.clearTime = function(clone){
1653     if(clone){
1654         return this.clone().clearTime();
1655     }
1656     this.setHours(0);
1657     this.setMinutes(0);
1658     this.setSeconds(0);
1659     this.setMilliseconds(0);
1660     return this;
1661 };
1662
1663 // private
1664 // safari setMonth is broken
1665 if(Roo.isSafari){
1666     Date.brokenSetMonth = Date.prototype.setMonth;
1667         Date.prototype.setMonth = function(num){
1668                 if(num <= -1){
1669                         var n = Math.ceil(-num);
1670                         var back_year = Math.ceil(n/12);
1671                         var month = (n % 12) ? 12 - n % 12 : 0 ;
1672                         this.setFullYear(this.getFullYear() - back_year);
1673                         return Date.brokenSetMonth.call(this, month);
1674                 } else {
1675                         return Date.brokenSetMonth.apply(this, arguments);
1676                 }
1677         };
1678 }
1679
1680 /** Date interval constant 
1681 * @static 
1682 * @type String */
1683 Date.MILLI = "ms";
1684 /** Date interval constant 
1685 * @static 
1686 * @type String */
1687 Date.SECOND = "s";
1688 /** Date interval constant 
1689 * @static 
1690 * @type String */
1691 Date.MINUTE = "mi";
1692 /** Date interval constant 
1693 * @static 
1694 * @type String */
1695 Date.HOUR = "h";
1696 /** Date interval constant 
1697 * @static 
1698 * @type String */
1699 Date.DAY = "d";
1700 /** Date interval constant 
1701 * @static 
1702 * @type String */
1703 Date.MONTH = "mo";
1704 /** Date interval constant 
1705 * @static 
1706 * @type String */
1707 Date.YEAR = "y";
1708
1709 /**
1710  * Provides a convenient method of performing basic date arithmetic.  This method
1711  * does not modify the Date instance being called - it creates and returns
1712  * a new Date instance containing the resulting date value.
1713  *
1714  * Examples:
1715  * <pre><code>
1716 //Basic usage:
1717 var dt = new Date('10/29/2006').add(Date.DAY, 5);
1718 document.write(dt); //returns 'Fri Oct 06 2006 00:00:00'
1719
1720 //Negative values will subtract correctly:
1721 var dt2 = new Date('10/1/2006').add(Date.DAY, -5);
1722 document.write(dt2); //returns 'Tue Sep 26 2006 00:00:00'
1723
1724 //You can even chain several calls together in one line!
1725 var dt3 = new Date('10/1/2006').add(Date.DAY, 5).add(Date.HOUR, 8).add(Date.MINUTE, -30);
1726 document.write(dt3); //returns 'Fri Oct 06 2006 07:30:00'
1727  </code></pre>
1728  *
1729  * @param {String} interval   A valid date interval enum value
1730  * @param {Number} value      The amount to add to the current date
1731  * @return {Date} The new Date instance
1732  */
1733 Date.prototype.add = function(interval, value){
1734   var d = this.clone();
1735   if (!interval || value === 0) return d;
1736   switch(interval.toLowerCase()){
1737     case Date.MILLI:
1738       d.setMilliseconds(this.getMilliseconds() + value);
1739       break;
1740     case Date.SECOND:
1741       d.setSeconds(this.getSeconds() + value);
1742       break;
1743     case Date.MINUTE:
1744       d.setMinutes(this.getMinutes() + value);
1745       break;
1746     case Date.HOUR:
1747       d.setHours(this.getHours() + value);
1748       break;
1749     case Date.DAY:
1750       d.setDate(this.getDate() + value);
1751       break;
1752     case Date.MONTH:
1753       var day = this.getDate();
1754       if(day > 28){
1755           day = Math.min(day, this.getFirstDateOfMonth().add('mo', value).getLastDateOfMonth().getDate());
1756       }
1757       d.setDate(day);
1758       d.setMonth(this.getMonth() + value);
1759       break;
1760     case Date.YEAR:
1761       d.setFullYear(this.getFullYear() + value);
1762       break;
1763   }
1764   return d;
1765 };
1766 /*
1767  * Based on:
1768  * Ext JS Library 1.1.1
1769  * Copyright(c) 2006-2007, Ext JS, LLC.
1770  *
1771  * Originally Released Under LGPL - original licence link has changed is not relivant.
1772  *
1773  * Fork - LGPL
1774  * <script type="text/javascript">
1775  */
1776
1777 /**
1778  * @class Roo.lib.Dom
1779  * @static
1780  * 
1781  * Dom utils (from YIU afaik)
1782  * 
1783  **/
1784 Roo.lib.Dom = {
1785     /**
1786      * Get the view width
1787      * @param {Boolean} full True will get the full document, otherwise it's the view width
1788      * @return {Number} The width
1789      */
1790      
1791     getViewWidth : function(full) {
1792         return full ? this.getDocumentWidth() : this.getViewportWidth();
1793     },
1794     /**
1795      * Get the view height
1796      * @param {Boolean} full True will get the full document, otherwise it's the view height
1797      * @return {Number} The height
1798      */
1799     getViewHeight : function(full) {
1800         return full ? this.getDocumentHeight() : this.getViewportHeight();
1801     },
1802
1803     getDocumentHeight: function() {
1804         var scrollHeight = (document.compatMode != "CSS1Compat") ? document.body.scrollHeight : document.documentElement.scrollHeight;
1805         return Math.max(scrollHeight, this.getViewportHeight());
1806     },
1807
1808     getDocumentWidth: function() {
1809         var scrollWidth = (document.compatMode != "CSS1Compat") ? document.body.scrollWidth : document.documentElement.scrollWidth;
1810         return Math.max(scrollWidth, this.getViewportWidth());
1811     },
1812
1813     getViewportHeight: function() {
1814         var height = self.innerHeight;
1815         var mode = document.compatMode;
1816
1817         if ((mode || Roo.isIE) && !Roo.isOpera) {
1818             height = (mode == "CSS1Compat") ?
1819                      document.documentElement.clientHeight :
1820                      document.body.clientHeight;
1821         }
1822
1823         return height;
1824     },
1825
1826     getViewportWidth: function() {
1827         var width = self.innerWidth;
1828         var mode = document.compatMode;
1829
1830         if (mode || Roo.isIE) {
1831             width = (mode == "CSS1Compat") ?
1832                     document.documentElement.clientWidth :
1833                     document.body.clientWidth;
1834         }
1835         return width;
1836     },
1837
1838     isAncestor : function(p, c) {
1839         p = Roo.getDom(p);
1840         c = Roo.getDom(c);
1841         if (!p || !c) {
1842             return false;
1843         }
1844
1845         if (p.contains && !Roo.isSafari) {
1846             return p.contains(c);
1847         } else if (p.compareDocumentPosition) {
1848             return !!(p.compareDocumentPosition(c) & 16);
1849         } else {
1850             var parent = c.parentNode;
1851             while (parent) {
1852                 if (parent == p) {
1853                     return true;
1854                 }
1855                 else if (!parent.tagName || parent.tagName.toUpperCase() == "HTML") {
1856                     return false;
1857                 }
1858                 parent = parent.parentNode;
1859             }
1860             return false;
1861         }
1862     },
1863
1864     getRegion : function(el) {
1865         return Roo.lib.Region.getRegion(el);
1866     },
1867
1868     getY : function(el) {
1869         return this.getXY(el)[1];
1870     },
1871
1872     getX : function(el) {
1873         return this.getXY(el)[0];
1874     },
1875
1876     getXY : function(el) {
1877         var p, pe, b, scroll, bd = document.body;
1878         el = Roo.getDom(el);
1879         var fly = Roo.lib.AnimBase.fly;
1880         if (el.getBoundingClientRect) {
1881             b = el.getBoundingClientRect();
1882             scroll = fly(document).getScroll();
1883             return [b.left + scroll.left, b.top + scroll.top];
1884         }
1885         var x = 0, y = 0;
1886
1887         p = el;
1888
1889         var hasAbsolute = fly(el).getStyle("position") == "absolute";
1890
1891         while (p) {
1892
1893             x += p.offsetLeft;
1894             y += p.offsetTop;
1895
1896             if (!hasAbsolute && fly(p).getStyle("position") == "absolute") {
1897                 hasAbsolute = true;
1898             }
1899
1900             if (Roo.isGecko) {
1901                 pe = fly(p);
1902
1903                 var bt = parseInt(pe.getStyle("borderTopWidth"), 10) || 0;
1904                 var bl = parseInt(pe.getStyle("borderLeftWidth"), 10) || 0;
1905
1906
1907                 x += bl;
1908                 y += bt;
1909
1910
1911                 if (p != el && pe.getStyle('overflow') != 'visible') {
1912                     x += bl;
1913                     y += bt;
1914                 }
1915             }
1916             p = p.offsetParent;
1917         }
1918
1919         if (Roo.isSafari && hasAbsolute) {
1920             x -= bd.offsetLeft;
1921             y -= bd.offsetTop;
1922         }
1923
1924         if (Roo.isGecko && !hasAbsolute) {
1925             var dbd = fly(bd);
1926             x += parseInt(dbd.getStyle("borderLeftWidth"), 10) || 0;
1927             y += parseInt(dbd.getStyle("borderTopWidth"), 10) || 0;
1928         }
1929
1930         p = el.parentNode;
1931         while (p && p != bd) {
1932             if (!Roo.isOpera || (p.tagName != 'TR' && fly(p).getStyle("display") != "inline")) {
1933                 x -= p.scrollLeft;
1934                 y -= p.scrollTop;
1935             }
1936             p = p.parentNode;
1937         }
1938         return [x, y];
1939     },
1940  
1941   
1942
1943
1944     setXY : function(el, xy) {
1945         el = Roo.fly(el, '_setXY');
1946         el.position();
1947         var pts = el.translatePoints(xy);
1948         if (xy[0] !== false) {
1949             el.dom.style.left = pts.left + "px";
1950         }
1951         if (xy[1] !== false) {
1952             el.dom.style.top = pts.top + "px";
1953         }
1954     },
1955
1956     setX : function(el, x) {
1957         this.setXY(el, [x, false]);
1958     },
1959
1960     setY : function(el, y) {
1961         this.setXY(el, [false, y]);
1962     }
1963 };
1964 /*
1965  * Portions of this file are based on pieces of Yahoo User Interface Library
1966  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
1967  * YUI licensed under the BSD License:
1968  * http://developer.yahoo.net/yui/license.txt
1969  * <script type="text/javascript">
1970  *
1971  */
1972
1973 Roo.lib.Event = function() {
1974     var loadComplete = false;
1975     var listeners = [];
1976     var unloadListeners = [];
1977     var retryCount = 0;
1978     var onAvailStack = [];
1979     var counter = 0;
1980     var lastError = null;
1981
1982     return {
1983         POLL_RETRYS: 200,
1984         POLL_INTERVAL: 20,
1985         EL: 0,
1986         TYPE: 1,
1987         FN: 2,
1988         WFN: 3,
1989         OBJ: 3,
1990         ADJ_SCOPE: 4,
1991         _interval: null,
1992
1993         startInterval: function() {
1994             if (!this._interval) {
1995                 var self = this;
1996                 var callback = function() {
1997                     self._tryPreloadAttach();
1998                 };
1999                 this._interval = setInterval(callback, this.POLL_INTERVAL);
2000
2001             }
2002         },
2003
2004         onAvailable: function(p_id, p_fn, p_obj, p_override) {
2005             onAvailStack.push({ id:         p_id,
2006                 fn:         p_fn,
2007                 obj:        p_obj,
2008                 override:   p_override,
2009                 checkReady: false    });
2010
2011             retryCount = this.POLL_RETRYS;
2012             this.startInterval();
2013         },
2014
2015
2016         addListener: function(el, eventName, fn) {
2017             el = Roo.getDom(el);
2018             if (!el || !fn) {
2019                 return false;
2020             }
2021
2022             if ("unload" == eventName) {
2023                 unloadListeners[unloadListeners.length] =
2024                 [el, eventName, fn];
2025                 return true;
2026             }
2027
2028             var wrappedFn = function(e) {
2029                 return fn(Roo.lib.Event.getEvent(e));
2030             };
2031
2032             var li = [el, eventName, fn, wrappedFn];
2033
2034             var index = listeners.length;
2035             listeners[index] = li;
2036
2037             this.doAdd(el, eventName, wrappedFn, false);
2038             return true;
2039
2040         },
2041
2042
2043         removeListener: function(el, eventName, fn) {
2044             var i, len;
2045
2046             el = Roo.getDom(el);
2047
2048             if(!fn) {
2049                 return this.purgeElement(el, false, eventName);
2050             }
2051
2052
2053             if ("unload" == eventName) {
2054
2055                 for (i = 0,len = unloadListeners.length; i < len; i++) {
2056                     var li = unloadListeners[i];
2057                     if (li &&
2058                         li[0] == el &&
2059                         li[1] == eventName &&
2060                         li[2] == fn) {
2061                         unloadListeners.splice(i, 1);
2062                         return true;
2063                     }
2064                 }
2065
2066                 return false;
2067             }
2068
2069             var cacheItem = null;
2070
2071
2072             var index = arguments[3];
2073
2074             if ("undefined" == typeof index) {
2075                 index = this._getCacheIndex(el, eventName, fn);
2076             }
2077
2078             if (index >= 0) {
2079                 cacheItem = listeners[index];
2080             }
2081
2082             if (!el || !cacheItem) {
2083                 return false;
2084             }
2085
2086             this.doRemove(el, eventName, cacheItem[this.WFN], false);
2087
2088             delete listeners[index][this.WFN];
2089             delete listeners[index][this.FN];
2090             listeners.splice(index, 1);
2091
2092             return true;
2093
2094         },
2095
2096
2097         getTarget: function(ev, resolveTextNode) {
2098             ev = ev.browserEvent || ev;
2099             var t = ev.target || ev.srcElement;
2100             return this.resolveTextNode(t);
2101         },
2102
2103
2104         resolveTextNode: function(node) {
2105             if (Roo.isSafari && node && 3 == node.nodeType) {
2106                 return node.parentNode;
2107             } else {
2108                 return node;
2109             }
2110         },
2111
2112
2113         getPageX: function(ev) {
2114             ev = ev.browserEvent || ev;
2115             var x = ev.pageX;
2116             if (!x && 0 !== x) {
2117                 x = ev.clientX || 0;
2118
2119                 if (Roo.isIE) {
2120                     x += this.getScroll()[1];
2121                 }
2122             }
2123
2124             return x;
2125         },
2126
2127
2128         getPageY: function(ev) {
2129             ev = ev.browserEvent || ev;
2130             var y = ev.pageY;
2131             if (!y && 0 !== y) {
2132                 y = ev.clientY || 0;
2133
2134                 if (Roo.isIE) {
2135                     y += this.getScroll()[0];
2136                 }
2137             }
2138
2139
2140             return y;
2141         },
2142
2143
2144         getXY: function(ev) {
2145             ev = ev.browserEvent || ev;
2146             return [this.getPageX(ev), this.getPageY(ev)];
2147         },
2148
2149
2150         getRelatedTarget: function(ev) {
2151             ev = ev.browserEvent || ev;
2152             var t = ev.relatedTarget;
2153             if (!t) {
2154                 if (ev.type == "mouseout") {
2155                     t = ev.toElement;
2156                 } else if (ev.type == "mouseover") {
2157                     t = ev.fromElement;
2158                 }
2159             }
2160
2161             return this.resolveTextNode(t);
2162         },
2163
2164
2165         getTime: function(ev) {
2166             ev = ev.browserEvent || ev;
2167             if (!ev.time) {
2168                 var t = new Date().getTime();
2169                 try {
2170                     ev.time = t;
2171                 } catch(ex) {
2172                     this.lastError = ex;
2173                     return t;
2174                 }
2175             }
2176
2177             return ev.time;
2178         },
2179
2180
2181         stopEvent: function(ev) {
2182             this.stopPropagation(ev);
2183             this.preventDefault(ev);
2184         },
2185
2186
2187         stopPropagation: function(ev) {
2188             ev = ev.browserEvent || ev;
2189             if (ev.stopPropagation) {
2190                 ev.stopPropagation();
2191             } else {
2192                 ev.cancelBubble = true;
2193             }
2194         },
2195
2196
2197         preventDefault: function(ev) {
2198             ev = ev.browserEvent || ev;
2199             if(ev.preventDefault) {
2200                 ev.preventDefault();
2201             } else {
2202                 ev.returnValue = false;
2203             }
2204         },
2205
2206
2207         getEvent: function(e) {
2208             var ev = e || window.event;
2209             if (!ev) {
2210                 var c = this.getEvent.caller;
2211                 while (c) {
2212                     ev = c.arguments[0];
2213                     if (ev && Event == ev.constructor) {
2214                         break;
2215                     }
2216                     c = c.caller;
2217                 }
2218             }
2219             return ev;
2220         },
2221
2222
2223         getCharCode: function(ev) {
2224             ev = ev.browserEvent || ev;
2225             return ev.charCode || ev.keyCode || 0;
2226         },
2227
2228
2229         _getCacheIndex: function(el, eventName, fn) {
2230             for (var i = 0,len = listeners.length; i < len; ++i) {
2231                 var li = listeners[i];
2232                 if (li &&
2233                     li[this.FN] == fn &&
2234                     li[this.EL] == el &&
2235                     li[this.TYPE] == eventName) {
2236                     return i;
2237                 }
2238             }
2239
2240             return -1;
2241         },
2242
2243
2244         elCache: {},
2245
2246
2247         getEl: function(id) {
2248             return document.getElementById(id);
2249         },
2250
2251
2252         clearCache: function() {
2253         },
2254
2255
2256         _load: function(e) {
2257             loadComplete = true;
2258             var EU = Roo.lib.Event;
2259
2260
2261             if (Roo.isIE) {
2262                 EU.doRemove(window, "load", EU._load);
2263             }
2264         },
2265
2266
2267         _tryPreloadAttach: function() {
2268
2269             if (this.locked) {
2270                 return false;
2271             }
2272
2273             this.locked = true;
2274
2275
2276             var tryAgain = !loadComplete;
2277             if (!tryAgain) {
2278                 tryAgain = (retryCount > 0);
2279             }
2280
2281
2282             var notAvail = [];
2283             for (var i = 0,len = onAvailStack.length; i < len; ++i) {
2284                 var item = onAvailStack[i];
2285                 if (item) {
2286                     var el = this.getEl(item.id);
2287
2288                     if (el) {
2289                         if (!item.checkReady ||
2290                             loadComplete ||
2291                             el.nextSibling ||
2292                             (document && document.body)) {
2293
2294                             var scope = el;
2295                             if (item.override) {
2296                                 if (item.override === true) {
2297                                     scope = item.obj;
2298                                 } else {
2299                                     scope = item.override;
2300                                 }
2301                             }
2302                             item.fn.call(scope, item.obj);
2303                             onAvailStack[i] = null;
2304                         }
2305                     } else {
2306                         notAvail.push(item);
2307                     }
2308                 }
2309             }
2310
2311             retryCount = (notAvail.length === 0) ? 0 : retryCount - 1;
2312
2313             if (tryAgain) {
2314
2315                 this.startInterval();
2316             } else {
2317                 clearInterval(this._interval);
2318                 this._interval = null;
2319             }
2320
2321             this.locked = false;
2322
2323             return true;
2324
2325         },
2326
2327
2328         purgeElement: function(el, recurse, eventName) {
2329             var elListeners = this.getListeners(el, eventName);
2330             if (elListeners) {
2331                 for (var i = 0,len = elListeners.length; i < len; ++i) {
2332                     var l = elListeners[i];
2333                     this.removeListener(el, l.type, l.fn);
2334                 }
2335             }
2336
2337             if (recurse && el && el.childNodes) {
2338                 for (i = 0,len = el.childNodes.length; i < len; ++i) {
2339                     this.purgeElement(el.childNodes[i], recurse, eventName);
2340                 }
2341             }
2342         },
2343
2344
2345         getListeners: function(el, eventName) {
2346             var results = [], searchLists;
2347             if (!eventName) {
2348                 searchLists = [listeners, unloadListeners];
2349             } else if (eventName == "unload") {
2350                 searchLists = [unloadListeners];
2351             } else {
2352                 searchLists = [listeners];
2353             }
2354
2355             for (var j = 0; j < searchLists.length; ++j) {
2356                 var searchList = searchLists[j];
2357                 if (searchList && searchList.length > 0) {
2358                     for (var i = 0,len = searchList.length; i < len; ++i) {
2359                         var l = searchList[i];
2360                         if (l && l[this.EL] === el &&
2361                             (!eventName || eventName === l[this.TYPE])) {
2362                             results.push({
2363                                 type:   l[this.TYPE],
2364                                 fn:     l[this.FN],
2365                                 obj:    l[this.OBJ],
2366                                 adjust: l[this.ADJ_SCOPE],
2367                                 index:  i
2368                             });
2369                         }
2370                     }
2371                 }
2372             }
2373
2374             return (results.length) ? results : null;
2375         },
2376
2377
2378         _unload: function(e) {
2379
2380             var EU = Roo.lib.Event, i, j, l, len, index;
2381
2382             for (i = 0,len = unloadListeners.length; i < len; ++i) {
2383                 l = unloadListeners[i];
2384                 if (l) {
2385                     var scope = window;
2386                     if (l[EU.ADJ_SCOPE]) {
2387                         if (l[EU.ADJ_SCOPE] === true) {
2388                             scope = l[EU.OBJ];
2389                         } else {
2390                             scope = l[EU.ADJ_SCOPE];
2391                         }
2392                     }
2393                     l[EU.FN].call(scope, EU.getEvent(e), l[EU.OBJ]);
2394                     unloadListeners[i] = null;
2395                     l = null;
2396                     scope = null;
2397                 }
2398             }
2399
2400             unloadListeners = null;
2401
2402             if (listeners && listeners.length > 0) {
2403                 j = listeners.length;
2404                 while (j) {
2405                     index = j - 1;
2406                     l = listeners[index];
2407                     if (l) {
2408                         EU.removeListener(l[EU.EL], l[EU.TYPE],
2409                                 l[EU.FN], index);
2410                     }
2411                     j = j - 1;
2412                 }
2413                 l = null;
2414
2415                 EU.clearCache();
2416             }
2417
2418             EU.doRemove(window, "unload", EU._unload);
2419
2420         },
2421
2422
2423         getScroll: function() {
2424             var dd = document.documentElement, db = document.body;
2425             if (dd && (dd.scrollTop || dd.scrollLeft)) {
2426                 return [dd.scrollTop, dd.scrollLeft];
2427             } else if (db) {
2428                 return [db.scrollTop, db.scrollLeft];
2429             } else {
2430                 return [0, 0];
2431             }
2432         },
2433
2434
2435         doAdd: function () {
2436             if (window.addEventListener) {
2437                 return function(el, eventName, fn, capture) {
2438                     el.addEventListener(eventName, fn, (capture));
2439                 };
2440             } else if (window.attachEvent) {
2441                 return function(el, eventName, fn, capture) {
2442                     el.attachEvent("on" + eventName, fn);
2443                 };
2444             } else {
2445                 return function() {
2446                 };
2447             }
2448         }(),
2449
2450
2451         doRemove: function() {
2452             if (window.removeEventListener) {
2453                 return function (el, eventName, fn, capture) {
2454                     el.removeEventListener(eventName, fn, (capture));
2455                 };
2456             } else if (window.detachEvent) {
2457                 return function (el, eventName, fn) {
2458                     el.detachEvent("on" + eventName, fn);
2459                 };
2460             } else {
2461                 return function() {
2462                 };
2463             }
2464         }()
2465     };
2466     
2467 }();
2468 (function() {     
2469    
2470     var E = Roo.lib.Event;
2471     E.on = E.addListener;
2472     E.un = E.removeListener;
2473
2474     if (document && document.body) {
2475         E._load();
2476     } else {
2477         E.doAdd(window, "load", E._load);
2478     }
2479     E.doAdd(window, "unload", E._unload);
2480     E._tryPreloadAttach();
2481 })();
2482
2483 /*
2484  * Portions of this file are based on pieces of Yahoo User Interface Library
2485  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
2486  * YUI licensed under the BSD License:
2487  * http://developer.yahoo.net/yui/license.txt
2488  * <script type="text/javascript">
2489  *
2490  */
2491
2492 (function() {
2493     /**
2494      * @class Roo.lib.Ajax
2495      *
2496      */
2497     Roo.lib.Ajax = {
2498         /**
2499          * @static 
2500          */
2501         request : function(method, uri, cb, data, options) {
2502             if(options){
2503                 var hs = options.headers;
2504                 if(hs){
2505                     for(var h in hs){
2506                         if(hs.hasOwnProperty(h)){
2507                             this.initHeader(h, hs[h], false);
2508                         }
2509                     }
2510                 }
2511                 if(options.xmlData){
2512                     this.initHeader('Content-Type', 'text/xml', false);
2513                     method = 'POST';
2514                     data = options.xmlData;
2515                 }
2516             }
2517
2518             return this.asyncRequest(method, uri, cb, data);
2519         },
2520
2521         serializeForm : function(form) {
2522             if(typeof form == 'string') {
2523                 form = (document.getElementById(form) || document.forms[form]);
2524             }
2525
2526             var el, name, val, disabled, data = '', hasSubmit = false;
2527             for (var i = 0; i < form.elements.length; i++) {
2528                 el = form.elements[i];
2529                 disabled = form.elements[i].disabled;
2530                 name = form.elements[i].name;
2531                 val = form.elements[i].value;
2532
2533                 if (!disabled && name){
2534                     switch (el.type)
2535                             {
2536                         case 'select-one':
2537                         case 'select-multiple':
2538                             for (var j = 0; j < el.options.length; j++) {
2539                                 if (el.options[j].selected) {
2540                                     if (Roo.isIE) {
2541                                         data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(el.options[j].attributes['value'].specified ? el.options[j].value : el.options[j].text) + '&';
2542                                     }
2543                                     else {
2544                                         data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(el.options[j].hasAttribute('value') ? el.options[j].value : el.options[j].text) + '&';
2545                                     }
2546                                 }
2547                             }
2548                             break;
2549                         case 'radio':
2550                         case 'checkbox':
2551                             if (el.checked) {
2552                                 data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(val) + '&';
2553                             }
2554                             break;
2555                         case 'file':
2556
2557                         case undefined:
2558
2559                         case 'reset':
2560
2561                         case 'button':
2562
2563                             break;
2564                         case 'submit':
2565                             if(hasSubmit == false) {
2566                                 data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(val) + '&';
2567                                 hasSubmit = true;
2568                             }
2569                             break;
2570                         default:
2571                             data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(val) + '&';
2572                             break;
2573                     }
2574                 }
2575             }
2576             data = data.substr(0, data.length - 1);
2577             return data;
2578         },
2579
2580         headers:{},
2581
2582         hasHeaders:false,
2583
2584         useDefaultHeader:true,
2585
2586         defaultPostHeader:'application/x-www-form-urlencoded',
2587
2588         useDefaultXhrHeader:true,
2589
2590         defaultXhrHeader:'XMLHttpRequest',
2591
2592         hasDefaultHeaders:true,
2593
2594         defaultHeaders:{},
2595
2596         poll:{},
2597
2598         timeout:{},
2599
2600         pollInterval:50,
2601
2602         transactionId:0,
2603
2604         setProgId:function(id)
2605         {
2606             this.activeX.unshift(id);
2607         },
2608
2609         setDefaultPostHeader:function(b)
2610         {
2611             this.useDefaultHeader = b;
2612         },
2613
2614         setDefaultXhrHeader:function(b)
2615         {
2616             this.useDefaultXhrHeader = b;
2617         },
2618
2619         setPollingInterval:function(i)
2620         {
2621             if (typeof i == 'number' && isFinite(i)) {
2622                 this.pollInterval = i;
2623             }
2624         },
2625
2626         createXhrObject:function(transactionId)
2627         {
2628             var obj,http;
2629             try
2630             {
2631
2632                 http = new XMLHttpRequest();
2633
2634                 obj = { conn:http, tId:transactionId };
2635             }
2636             catch(e)
2637             {
2638                 for (var i = 0; i < this.activeX.length; ++i) {
2639                     try
2640                     {
2641
2642                         http = new ActiveXObject(this.activeX[i]);
2643
2644                         obj = { conn:http, tId:transactionId };
2645                         break;
2646                     }
2647                     catch(e) {
2648                     }
2649                 }
2650             }
2651             finally
2652             {
2653                 return obj;
2654             }
2655         },
2656
2657         getConnectionObject:function()
2658         {
2659             var o;
2660             var tId = this.transactionId;
2661
2662             try
2663             {
2664                 o = this.createXhrObject(tId);
2665                 if (o) {
2666                     this.transactionId++;
2667                 }
2668             }
2669             catch(e) {
2670             }
2671             finally
2672             {
2673                 return o;
2674             }
2675         },
2676
2677         asyncRequest:function(method, uri, callback, postData)
2678         {
2679             var o = this.getConnectionObject();
2680
2681             if (!o) {
2682                 return null;
2683             }
2684             else {
2685                 o.conn.open(method, uri, true);
2686
2687                 if (this.useDefaultXhrHeader) {
2688                     if (!this.defaultHeaders['X-Requested-With']) {
2689                         this.initHeader('X-Requested-With', this.defaultXhrHeader, true);
2690                     }
2691                 }
2692
2693                 if(postData && this.useDefaultHeader){
2694                     this.initHeader('Content-Type', this.defaultPostHeader);
2695                 }
2696
2697                  if (this.hasDefaultHeaders || this.hasHeaders) {
2698                     this.setHeader(o);
2699                 }
2700
2701                 this.handleReadyState(o, callback);
2702                 o.conn.send(postData || null);
2703
2704                 return o;
2705             }
2706         },
2707
2708         handleReadyState:function(o, callback)
2709         {
2710             var oConn = this;
2711
2712             if (callback && callback.timeout) {
2713                 
2714                 this.timeout[o.tId] = window.setTimeout(function() {
2715                     oConn.abort(o, callback, true);
2716                 }, callback.timeout);
2717             }
2718
2719             this.poll[o.tId] = window.setInterval(
2720                     function() {
2721                         if (o.conn && o.conn.readyState == 4) {
2722                             window.clearInterval(oConn.poll[o.tId]);
2723                             delete oConn.poll[o.tId];
2724
2725                             if(callback && callback.timeout) {
2726                                 window.clearTimeout(oConn.timeout[o.tId]);
2727                                 delete oConn.timeout[o.tId];
2728                             }
2729
2730                             oConn.handleTransactionResponse(o, callback);
2731                         }
2732                     }
2733                     , this.pollInterval);
2734         },
2735
2736         handleTransactionResponse:function(o, callback, isAbort)
2737         {
2738
2739             if (!callback) {
2740                 this.releaseObject(o);
2741                 return;
2742             }
2743
2744             var httpStatus, responseObject;
2745
2746             try
2747             {
2748                 if (o.conn.status !== undefined && o.conn.status != 0) {
2749                     httpStatus = o.conn.status;
2750                 }
2751                 else {
2752                     httpStatus = 13030;
2753                 }
2754             }
2755             catch(e) {
2756
2757
2758                 httpStatus = 13030;
2759             }
2760
2761             if (httpStatus >= 200 && httpStatus < 300) {
2762                 responseObject = this.createResponseObject(o, callback.argument);
2763                 if (callback.success) {
2764                     if (!callback.scope) {
2765                         callback.success(responseObject);
2766                     }
2767                     else {
2768
2769
2770                         callback.success.apply(callback.scope, [responseObject]);
2771                     }
2772                 }
2773             }
2774             else {
2775                 switch (httpStatus) {
2776
2777                     case 12002:
2778                     case 12029:
2779                     case 12030:
2780                     case 12031:
2781                     case 12152:
2782                     case 13030:
2783                         responseObject = this.createExceptionObject(o.tId, callback.argument, (isAbort ? isAbort : false));
2784                         if (callback.failure) {
2785                             if (!callback.scope) {
2786                                 callback.failure(responseObject);
2787                             }
2788                             else {
2789                                 callback.failure.apply(callback.scope, [responseObject]);
2790                             }
2791                         }
2792                         break;
2793                     default:
2794                         responseObject = this.createResponseObject(o, callback.argument);
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                 }
2804             }
2805
2806             this.releaseObject(o);
2807             responseObject = null;
2808         },
2809
2810         createResponseObject:function(o, callbackArg)
2811         {
2812             var obj = {};
2813             var headerObj = {};
2814
2815             try
2816             {
2817                 var headerStr = o.conn.getAllResponseHeaders();
2818                 var header = headerStr.split('\n');
2819                 for (var i = 0; i < header.length; i++) {
2820                     var delimitPos = header[i].indexOf(':');
2821                     if (delimitPos != -1) {
2822                         headerObj[header[i].substring(0, delimitPos)] = header[i].substring(delimitPos + 2);
2823                     }
2824                 }
2825             }
2826             catch(e) {
2827             }
2828
2829             obj.tId = o.tId;
2830             obj.status = o.conn.status;
2831             obj.statusText = o.conn.statusText;
2832             obj.getResponseHeader = headerObj;
2833             obj.getAllResponseHeaders = headerStr;
2834             obj.responseText = o.conn.responseText;
2835             obj.responseXML = o.conn.responseXML;
2836
2837             if (typeof callbackArg !== undefined) {
2838                 obj.argument = callbackArg;
2839             }
2840
2841             return obj;
2842         },
2843
2844         createExceptionObject:function(tId, callbackArg, isAbort)
2845         {
2846             var COMM_CODE = 0;
2847             var COMM_ERROR = 'communication failure';
2848             var ABORT_CODE = -1;
2849             var ABORT_ERROR = 'transaction aborted';
2850
2851             var obj = {};
2852
2853             obj.tId = tId;
2854             if (isAbort) {
2855                 obj.status = ABORT_CODE;
2856                 obj.statusText = ABORT_ERROR;
2857             }
2858             else {
2859                 obj.status = COMM_CODE;
2860                 obj.statusText = COMM_ERROR;
2861             }
2862
2863             if (callbackArg) {
2864                 obj.argument = callbackArg;
2865             }
2866
2867             return obj;
2868         },
2869
2870         initHeader:function(label, value, isDefault)
2871         {
2872             var headerObj = (isDefault) ? this.defaultHeaders : this.headers;
2873
2874             if (headerObj[label] === undefined) {
2875                 headerObj[label] = value;
2876             }
2877             else {
2878
2879
2880                 headerObj[label] = value + "," + headerObj[label];
2881             }
2882
2883             if (isDefault) {
2884                 this.hasDefaultHeaders = true;
2885             }
2886             else {
2887                 this.hasHeaders = true;
2888             }
2889         },
2890
2891
2892         setHeader:function(o)
2893         {
2894             if (this.hasDefaultHeaders) {
2895                 for (var prop in this.defaultHeaders) {
2896                     if (this.defaultHeaders.hasOwnProperty(prop)) {
2897                         o.conn.setRequestHeader(prop, this.defaultHeaders[prop]);
2898                     }
2899                 }
2900             }
2901
2902             if (this.hasHeaders) {
2903                 for (var prop in this.headers) {
2904                     if (this.headers.hasOwnProperty(prop)) {
2905                         o.conn.setRequestHeader(prop, this.headers[prop]);
2906                     }
2907                 }
2908                 this.headers = {};
2909                 this.hasHeaders = false;
2910             }
2911         },
2912
2913         resetDefaultHeaders:function() {
2914             delete this.defaultHeaders;
2915             this.defaultHeaders = {};
2916             this.hasDefaultHeaders = false;
2917         },
2918
2919         abort:function(o, callback, isTimeout)
2920         {
2921             if(this.isCallInProgress(o)) {
2922                 o.conn.abort();
2923                 window.clearInterval(this.poll[o.tId]);
2924                 delete this.poll[o.tId];
2925                 if (isTimeout) {
2926                     delete this.timeout[o.tId];
2927                 }
2928
2929                 this.handleTransactionResponse(o, callback, true);
2930
2931                 return true;
2932             }
2933             else {
2934                 return false;
2935             }
2936         },
2937
2938
2939         isCallInProgress:function(o)
2940         {
2941             if (o && o.conn) {
2942                 return o.conn.readyState != 4 && o.conn.readyState != 0;
2943             }
2944             else {
2945
2946                 return false;
2947             }
2948         },
2949
2950
2951         releaseObject:function(o)
2952         {
2953
2954             o.conn = null;
2955
2956             o = null;
2957         },
2958
2959         activeX:[
2960         'MSXML2.XMLHTTP.3.0',
2961         'MSXML2.XMLHTTP',
2962         'Microsoft.XMLHTTP'
2963         ]
2964
2965
2966     };
2967 })();/*
2968  * Portions of this file are based on pieces of Yahoo User Interface Library
2969  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
2970  * YUI licensed under the BSD License:
2971  * http://developer.yahoo.net/yui/license.txt
2972  * <script type="text/javascript">
2973  *
2974  */
2975
2976 Roo.lib.Region = function(t, r, b, l) {
2977     this.top = t;
2978     this[1] = t;
2979     this.right = r;
2980     this.bottom = b;
2981     this.left = l;
2982     this[0] = l;
2983 };
2984
2985
2986 Roo.lib.Region.prototype = {
2987     contains : function(region) {
2988         return ( region.left >= this.left &&
2989                  region.right <= this.right &&
2990                  region.top >= this.top &&
2991                  region.bottom <= this.bottom    );
2992
2993     },
2994
2995     getArea : function() {
2996         return ( (this.bottom - this.top) * (this.right - this.left) );
2997     },
2998
2999     intersect : function(region) {
3000         var t = Math.max(this.top, region.top);
3001         var r = Math.min(this.right, region.right);
3002         var b = Math.min(this.bottom, region.bottom);
3003         var l = Math.max(this.left, region.left);
3004
3005         if (b >= t && r >= l) {
3006             return new Roo.lib.Region(t, r, b, l);
3007         } else {
3008             return null;
3009         }
3010     },
3011     union : function(region) {
3012         var t = Math.min(this.top, region.top);
3013         var r = Math.max(this.right, region.right);
3014         var b = Math.max(this.bottom, region.bottom);
3015         var l = Math.min(this.left, region.left);
3016
3017         return new Roo.lib.Region(t, r, b, l);
3018     },
3019
3020     adjust : function(t, l, b, r) {
3021         this.top += t;
3022         this.left += l;
3023         this.right += r;
3024         this.bottom += b;
3025         return this;
3026     }
3027 };
3028
3029 Roo.lib.Region.getRegion = function(el) {
3030     var p = Roo.lib.Dom.getXY(el);
3031
3032     var t = p[1];
3033     var r = p[0] + el.offsetWidth;
3034     var b = p[1] + el.offsetHeight;
3035     var l = p[0];
3036
3037     return new Roo.lib.Region(t, r, b, l);
3038 };
3039 /*
3040  * Portions of this file are based on pieces of Yahoo User Interface Library
3041  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3042  * YUI licensed under the BSD License:
3043  * http://developer.yahoo.net/yui/license.txt
3044  * <script type="text/javascript">
3045  *
3046  */
3047 //@@dep Roo.lib.Region
3048
3049
3050 Roo.lib.Point = function(x, y) {
3051     if (x instanceof Array) {
3052         y = x[1];
3053         x = x[0];
3054     }
3055     this.x = this.right = this.left = this[0] = x;
3056     this.y = this.top = this.bottom = this[1] = y;
3057 };
3058
3059 Roo.lib.Point.prototype = new Roo.lib.Region();
3060 /*
3061  * Portions of this file are based on pieces of Yahoo User Interface Library
3062  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3063  * YUI licensed under the BSD License:
3064  * http://developer.yahoo.net/yui/license.txt
3065  * <script type="text/javascript">
3066  *
3067  */
3068  
3069 (function() {   
3070
3071     Roo.lib.Anim = {
3072         scroll : function(el, args, duration, easing, cb, scope) {
3073             this.run(el, args, duration, easing, cb, scope, Roo.lib.Scroll);
3074         },
3075
3076         motion : function(el, args, duration, easing, cb, scope) {
3077             this.run(el, args, duration, easing, cb, scope, Roo.lib.Motion);
3078         },
3079
3080         color : function(el, args, duration, easing, cb, scope) {
3081             this.run(el, args, duration, easing, cb, scope, Roo.lib.ColorAnim);
3082         },
3083
3084         run : function(el, args, duration, easing, cb, scope, type) {
3085             type = type || Roo.lib.AnimBase;
3086             if (typeof easing == "string") {
3087                 easing = Roo.lib.Easing[easing];
3088             }
3089             var anim = new type(el, args, duration, easing);
3090             anim.animateX(function() {
3091                 Roo.callback(cb, scope);
3092             });
3093             return anim;
3094         }
3095     };
3096 })();/*
3097  * Portions of this file are based on pieces of Yahoo User Interface Library
3098  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3099  * YUI licensed under the BSD License:
3100  * http://developer.yahoo.net/yui/license.txt
3101  * <script type="text/javascript">
3102  *
3103  */
3104
3105 (function() {    
3106     var libFlyweight;
3107     
3108     function fly(el) {
3109         if (!libFlyweight) {
3110             libFlyweight = new Roo.Element.Flyweight();
3111         }
3112         libFlyweight.dom = el;
3113         return libFlyweight;
3114     }
3115
3116     // since this uses fly! - it cant be in DOM (which does not have fly yet..)
3117     
3118    
3119     
3120     Roo.lib.AnimBase = function(el, attributes, duration, method) {
3121         if (el) {
3122             this.init(el, attributes, duration, method);
3123         }
3124     };
3125
3126     Roo.lib.AnimBase.fly = fly;
3127     
3128     
3129     
3130     Roo.lib.AnimBase.prototype = {
3131
3132         toString: function() {
3133             var el = this.getEl();
3134             var id = el.id || el.tagName;
3135             return ("Anim " + id);
3136         },
3137
3138         patterns: {
3139             noNegatives:        /width|height|opacity|padding/i,
3140             offsetAttribute:  /^((width|height)|(top|left))$/,
3141             defaultUnit:        /width|height|top$|bottom$|left$|right$/i,
3142             offsetUnit:         /\d+(em|%|en|ex|pt|in|cm|mm|pc)$/i
3143         },
3144
3145
3146         doMethod: function(attr, start, end) {
3147             return this.method(this.currentFrame, start, end - start, this.totalFrames);
3148         },
3149
3150
3151         setAttribute: function(attr, val, unit) {
3152             if (this.patterns.noNegatives.test(attr)) {
3153                 val = (val > 0) ? val : 0;
3154             }
3155
3156             Roo.fly(this.getEl(), '_anim').setStyle(attr, val + unit);
3157         },
3158
3159
3160         getAttribute: function(attr) {
3161             var el = this.getEl();
3162             var val = fly(el).getStyle(attr);
3163
3164             if (val !== 'auto' && !this.patterns.offsetUnit.test(val)) {
3165                 return parseFloat(val);
3166             }
3167
3168             var a = this.patterns.offsetAttribute.exec(attr) || [];
3169             var pos = !!( a[3] );
3170             var box = !!( a[2] );
3171
3172
3173             if (box || (fly(el).getStyle('position') == 'absolute' && pos)) {
3174                 val = el['offset' + a[0].charAt(0).toUpperCase() + a[0].substr(1)];
3175             } else {
3176                 val = 0;
3177             }
3178
3179             return val;
3180         },
3181
3182
3183         getDefaultUnit: function(attr) {
3184             if (this.patterns.defaultUnit.test(attr)) {
3185                 return 'px';
3186             }
3187
3188             return '';
3189         },
3190
3191         animateX : function(callback, scope) {
3192             var f = function() {
3193                 this.onComplete.removeListener(f);
3194                 if (typeof callback == "function") {
3195                     callback.call(scope || this, this);
3196                 }
3197             };
3198             this.onComplete.addListener(f, this);
3199             this.animate();
3200         },
3201
3202
3203         setRuntimeAttribute: function(attr) {
3204             var start;
3205             var end;
3206             var attributes = this.attributes;
3207
3208             this.runtimeAttributes[attr] = {};
3209
3210             var isset = function(prop) {
3211                 return (typeof prop !== 'undefined');
3212             };
3213
3214             if (!isset(attributes[attr]['to']) && !isset(attributes[attr]['by'])) {
3215                 return false;
3216             }
3217
3218             start = ( isset(attributes[attr]['from']) ) ? attributes[attr]['from'] : this.getAttribute(attr);
3219
3220
3221             if (isset(attributes[attr]['to'])) {
3222                 end = attributes[attr]['to'];
3223             } else if (isset(attributes[attr]['by'])) {
3224                 if (start.constructor == Array) {
3225                     end = [];
3226                     for (var i = 0, len = start.length; i < len; ++i) {
3227                         end[i] = start[i] + attributes[attr]['by'][i];
3228                     }
3229                 } else {
3230                     end = start + attributes[attr]['by'];
3231                 }
3232             }
3233
3234             this.runtimeAttributes[attr].start = start;
3235             this.runtimeAttributes[attr].end = end;
3236
3237
3238             this.runtimeAttributes[attr].unit = ( isset(attributes[attr].unit) ) ? attributes[attr]['unit'] : this.getDefaultUnit(attr);
3239         },
3240
3241
3242         init: function(el, attributes, duration, method) {
3243
3244             var isAnimated = false;
3245
3246
3247             var startTime = null;
3248
3249
3250             var actualFrames = 0;
3251
3252
3253             el = Roo.getDom(el);
3254
3255
3256             this.attributes = attributes || {};
3257
3258
3259             this.duration = duration || 1;
3260
3261
3262             this.method = method || Roo.lib.Easing.easeNone;
3263
3264
3265             this.useSeconds = true;
3266
3267
3268             this.currentFrame = 0;
3269
3270
3271             this.totalFrames = Roo.lib.AnimMgr.fps;
3272
3273
3274             this.getEl = function() {
3275                 return el;
3276             };
3277
3278
3279             this.isAnimated = function() {
3280                 return isAnimated;
3281             };
3282
3283
3284             this.getStartTime = function() {
3285                 return startTime;
3286             };
3287
3288             this.runtimeAttributes = {};
3289
3290
3291             this.animate = function() {
3292                 if (this.isAnimated()) {
3293                     return false;
3294                 }
3295
3296                 this.currentFrame = 0;
3297
3298                 this.totalFrames = ( this.useSeconds ) ? Math.ceil(Roo.lib.AnimMgr.fps * this.duration) : this.duration;
3299
3300                 Roo.lib.AnimMgr.registerElement(this);
3301             };
3302
3303
3304             this.stop = function(finish) {
3305                 if (finish) {
3306                     this.currentFrame = this.totalFrames;
3307                     this._onTween.fire();
3308                 }
3309                 Roo.lib.AnimMgr.stop(this);
3310             };
3311
3312             var onStart = function() {
3313                 this.onStart.fire();
3314
3315                 this.runtimeAttributes = {};
3316                 for (var attr in this.attributes) {
3317                     this.setRuntimeAttribute(attr);
3318                 }
3319
3320                 isAnimated = true;
3321                 actualFrames = 0;
3322                 startTime = new Date();
3323             };
3324
3325
3326             var onTween = function() {
3327                 var data = {
3328                     duration: new Date() - this.getStartTime(),
3329                     currentFrame: this.currentFrame
3330                 };
3331
3332                 data.toString = function() {
3333                     return (
3334                             'duration: ' + data.duration +
3335                             ', currentFrame: ' + data.currentFrame
3336                             );
3337                 };
3338
3339                 this.onTween.fire(data);
3340
3341                 var runtimeAttributes = this.runtimeAttributes;
3342
3343                 for (var attr in runtimeAttributes) {
3344                     this.setAttribute(attr, this.doMethod(attr, runtimeAttributes[attr].start, runtimeAttributes[attr].end), runtimeAttributes[attr].unit);
3345                 }
3346
3347                 actualFrames += 1;
3348             };
3349
3350             var onComplete = function() {
3351                 var actual_duration = (new Date() - startTime) / 1000 ;
3352
3353                 var data = {
3354                     duration: actual_duration,
3355                     frames: actualFrames,
3356                     fps: actualFrames / actual_duration
3357                 };
3358
3359                 data.toString = function() {
3360                     return (
3361                             'duration: ' + data.duration +
3362                             ', frames: ' + data.frames +
3363                             ', fps: ' + data.fps
3364                             );
3365                 };
3366
3367                 isAnimated = false;
3368                 actualFrames = 0;
3369                 this.onComplete.fire(data);
3370             };
3371
3372
3373             this._onStart = new Roo.util.Event(this);
3374             this.onStart = new Roo.util.Event(this);
3375             this.onTween = new Roo.util.Event(this);
3376             this._onTween = new Roo.util.Event(this);
3377             this.onComplete = new Roo.util.Event(this);
3378             this._onComplete = new Roo.util.Event(this);
3379             this._onStart.addListener(onStart);
3380             this._onTween.addListener(onTween);
3381             this._onComplete.addListener(onComplete);
3382         }
3383     };
3384 })();
3385 /*
3386  * Portions of this file are based on pieces of Yahoo User Interface Library
3387  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3388  * YUI licensed under the BSD License:
3389  * http://developer.yahoo.net/yui/license.txt
3390  * <script type="text/javascript">
3391  *
3392  */
3393
3394 Roo.lib.AnimMgr = new function() {
3395
3396         var thread = null;
3397
3398
3399         var queue = [];
3400
3401
3402         var tweenCount = 0;
3403
3404
3405         this.fps = 1000;
3406
3407
3408         this.delay = 1;
3409
3410
3411         this.registerElement = function(tween) {
3412             queue[queue.length] = tween;
3413             tweenCount += 1;
3414             tween._onStart.fire();
3415             this.start();
3416         };
3417
3418
3419         this.unRegister = function(tween, index) {
3420             tween._onComplete.fire();
3421             index = index || getIndex(tween);
3422             if (index != -1) {
3423                 queue.splice(index, 1);
3424             }
3425
3426             tweenCount -= 1;
3427             if (tweenCount <= 0) {
3428                 this.stop();
3429             }
3430         };
3431
3432
3433         this.start = function() {
3434             if (thread === null) {
3435                 thread = setInterval(this.run, this.delay);
3436             }
3437         };
3438
3439
3440         this.stop = function(tween) {
3441             if (!tween) {
3442                 clearInterval(thread);
3443
3444                 for (var i = 0, len = queue.length; i < len; ++i) {
3445                     if (queue[0].isAnimated()) {
3446                         this.unRegister(queue[0], 0);
3447                     }
3448                 }
3449
3450                 queue = [];
3451                 thread = null;
3452                 tweenCount = 0;
3453             }
3454             else {
3455                 this.unRegister(tween);
3456             }
3457         };
3458
3459
3460         this.run = function() {
3461             for (var i = 0, len = queue.length; i < len; ++i) {
3462                 var tween = queue[i];
3463                 if (!tween || !tween.isAnimated()) {
3464                     continue;
3465                 }
3466
3467                 if (tween.currentFrame < tween.totalFrames || tween.totalFrames === null)
3468                 {
3469                     tween.currentFrame += 1;
3470
3471                     if (tween.useSeconds) {
3472                         correctFrame(tween);
3473                     }
3474                     tween._onTween.fire();
3475                 }
3476                 else {
3477                     Roo.lib.AnimMgr.stop(tween, i);
3478                 }
3479             }
3480         };
3481
3482         var getIndex = function(anim) {
3483             for (var i = 0, len = queue.length; i < len; ++i) {
3484                 if (queue[i] == anim) {
3485                     return i;
3486                 }
3487             }
3488             return -1;
3489         };
3490
3491
3492         var correctFrame = function(tween) {
3493             var frames = tween.totalFrames;
3494             var frame = tween.currentFrame;
3495             var expected = (tween.currentFrame * tween.duration * 1000 / tween.totalFrames);
3496             var elapsed = (new Date() - tween.getStartTime());
3497             var tweak = 0;
3498
3499             if (elapsed < tween.duration * 1000) {
3500                 tweak = Math.round((elapsed / expected - 1) * tween.currentFrame);
3501             } else {
3502                 tweak = frames - (frame + 1);
3503             }
3504             if (tweak > 0 && isFinite(tweak)) {
3505                 if (tween.currentFrame + tweak >= frames) {
3506                     tweak = frames - (frame + 1);
3507                 }
3508
3509                 tween.currentFrame += tweak;
3510             }
3511         };
3512     };/*
3513  * Portions of this file are based on pieces of Yahoo User Interface Library
3514  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3515  * YUI licensed under the BSD License:
3516  * http://developer.yahoo.net/yui/license.txt
3517  * <script type="text/javascript">
3518  *
3519  */
3520 Roo.lib.Bezier = new function() {
3521
3522         this.getPosition = function(points, t) {
3523             var n = points.length;
3524             var tmp = [];
3525
3526             for (var i = 0; i < n; ++i) {
3527                 tmp[i] = [points[i][0], points[i][1]];
3528             }
3529
3530             for (var j = 1; j < n; ++j) {
3531                 for (i = 0; i < n - j; ++i) {
3532                     tmp[i][0] = (1 - t) * tmp[i][0] + t * tmp[parseInt(i + 1, 10)][0];
3533                     tmp[i][1] = (1 - t) * tmp[i][1] + t * tmp[parseInt(i + 1, 10)][1];
3534                 }
3535             }
3536
3537             return [ tmp[0][0], tmp[0][1] ];
3538
3539         };
3540     };/*
3541  * Portions of this file are based on pieces of Yahoo User Interface Library
3542  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3543  * YUI licensed under the BSD License:
3544  * http://developer.yahoo.net/yui/license.txt
3545  * <script type="text/javascript">
3546  *
3547  */
3548 (function() {
3549
3550     Roo.lib.ColorAnim = function(el, attributes, duration, method) {
3551         Roo.lib.ColorAnim.superclass.constructor.call(this, el, attributes, duration, method);
3552     };
3553
3554     Roo.extend(Roo.lib.ColorAnim, Roo.lib.AnimBase);
3555
3556     var fly = Roo.lib.AnimBase.fly;
3557     var Y = Roo.lib;
3558     var superclass = Y.ColorAnim.superclass;
3559     var proto = Y.ColorAnim.prototype;
3560
3561     proto.toString = function() {
3562         var el = this.getEl();
3563         var id = el.id || el.tagName;
3564         return ("ColorAnim " + id);
3565     };
3566
3567     proto.patterns.color = /color$/i;
3568     proto.patterns.rgb = /^rgb\(([0-9]+)\s*,\s*([0-9]+)\s*,\s*([0-9]+)\)$/i;
3569     proto.patterns.hex = /^#?([0-9A-F]{2})([0-9A-F]{2})([0-9A-F]{2})$/i;
3570     proto.patterns.hex3 = /^#?([0-9A-F]{1})([0-9A-F]{1})([0-9A-F]{1})$/i;
3571     proto.patterns.transparent = /^transparent|rgba\(0, 0, 0, 0\)$/;
3572
3573
3574     proto.parseColor = function(s) {
3575         if (s.length == 3) {
3576             return s;
3577         }
3578
3579         var c = this.patterns.hex.exec(s);
3580         if (c && c.length == 4) {
3581             return [ parseInt(c[1], 16), parseInt(c[2], 16), parseInt(c[3], 16) ];
3582         }
3583
3584         c = this.patterns.rgb.exec(s);
3585         if (c && c.length == 4) {
3586             return [ parseInt(c[1], 10), parseInt(c[2], 10), parseInt(c[3], 10) ];
3587         }
3588
3589         c = this.patterns.hex3.exec(s);
3590         if (c && c.length == 4) {
3591             return [ parseInt(c[1] + c[1], 16), parseInt(c[2] + c[2], 16), parseInt(c[3] + c[3], 16) ];
3592         }
3593
3594         return null;
3595     };
3596     // since this uses fly! - it cant be in ColorAnim (which does not have fly yet..)
3597     proto.getAttribute = function(attr) {
3598         var el = this.getEl();
3599         if (this.patterns.color.test(attr)) {
3600             var val = fly(el).getStyle(attr);
3601
3602             if (this.patterns.transparent.test(val)) {
3603                 var parent = el.parentNode;
3604                 val = fly(parent).getStyle(attr);
3605
3606                 while (parent && this.patterns.transparent.test(val)) {
3607                     parent = parent.parentNode;
3608                     val = fly(parent).getStyle(attr);
3609                     if (parent.tagName.toUpperCase() == 'HTML') {
3610                         val = '#fff';
3611                     }
3612                 }
3613             }
3614         } else {
3615             val = superclass.getAttribute.call(this, attr);
3616         }
3617
3618         return val;
3619     };
3620     proto.getAttribute = function(attr) {
3621         var el = this.getEl();
3622         if (this.patterns.color.test(attr)) {
3623             var val = fly(el).getStyle(attr);
3624
3625             if (this.patterns.transparent.test(val)) {
3626                 var parent = el.parentNode;
3627                 val = fly(parent).getStyle(attr);
3628
3629                 while (parent && this.patterns.transparent.test(val)) {
3630                     parent = parent.parentNode;
3631                     val = fly(parent).getStyle(attr);
3632                     if (parent.tagName.toUpperCase() == 'HTML') {
3633                         val = '#fff';
3634                     }
3635                 }
3636             }
3637         } else {
3638             val = superclass.getAttribute.call(this, attr);
3639         }
3640
3641         return val;
3642     };
3643
3644     proto.doMethod = function(attr, start, end) {
3645         var val;
3646
3647         if (this.patterns.color.test(attr)) {
3648             val = [];
3649             for (var i = 0, len = start.length; i < len; ++i) {
3650                 val[i] = superclass.doMethod.call(this, attr, start[i], end[i]);
3651             }
3652
3653             val = 'rgb(' + Math.floor(val[0]) + ',' + Math.floor(val[1]) + ',' + Math.floor(val[2]) + ')';
3654         }
3655         else {
3656             val = superclass.doMethod.call(this, attr, start, end);
3657         }
3658
3659         return val;
3660     };
3661
3662     proto.setRuntimeAttribute = function(attr) {
3663         superclass.setRuntimeAttribute.call(this, attr);
3664
3665         if (this.patterns.color.test(attr)) {
3666             var attributes = this.attributes;
3667             var start = this.parseColor(this.runtimeAttributes[attr].start);
3668             var end = this.parseColor(this.runtimeAttributes[attr].end);
3669
3670             if (typeof attributes[attr]['to'] === 'undefined' && typeof attributes[attr]['by'] !== 'undefined') {
3671                 end = this.parseColor(attributes[attr].by);
3672
3673                 for (var i = 0, len = start.length; i < len; ++i) {
3674                     end[i] = start[i] + end[i];
3675                 }
3676             }
3677
3678             this.runtimeAttributes[attr].start = start;
3679             this.runtimeAttributes[attr].end = end;
3680         }
3681     };
3682 })();
3683
3684 /*
3685  * Portions of this file are based on pieces of Yahoo User Interface Library
3686  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3687  * YUI licensed under the BSD License:
3688  * http://developer.yahoo.net/yui/license.txt
3689  * <script type="text/javascript">
3690  *
3691  */
3692 Roo.lib.Easing = {
3693
3694
3695     easeNone: function (t, b, c, d) {
3696         return c * t / d + b;
3697     },
3698
3699
3700     easeIn: function (t, b, c, d) {
3701         return c * (t /= d) * t + b;
3702     },
3703
3704
3705     easeOut: function (t, b, c, d) {
3706         return -c * (t /= d) * (t - 2) + b;
3707     },
3708
3709
3710     easeBoth: function (t, b, c, d) {
3711         if ((t /= d / 2) < 1) {
3712             return c / 2 * t * t + b;
3713         }
3714
3715         return -c / 2 * ((--t) * (t - 2) - 1) + b;
3716     },
3717
3718
3719     easeInStrong: function (t, b, c, d) {
3720         return c * (t /= d) * t * t * t + b;
3721     },
3722
3723
3724     easeOutStrong: function (t, b, c, d) {
3725         return -c * ((t = t / d - 1) * t * t * t - 1) + b;
3726     },
3727
3728
3729     easeBothStrong: function (t, b, c, d) {
3730         if ((t /= d / 2) < 1) {
3731             return c / 2 * t * t * t * t + b;
3732         }
3733
3734         return -c / 2 * ((t -= 2) * t * t * t - 2) + b;
3735     },
3736
3737
3738
3739     elasticIn: function (t, b, c, d, a, p) {
3740         if (t == 0) {
3741             return b;
3742         }
3743         if ((t /= d) == 1) {
3744             return b + c;
3745         }
3746         if (!p) {
3747             p = d * .3;
3748         }
3749
3750         if (!a || a < Math.abs(c)) {
3751             a = c;
3752             var s = p / 4;
3753         }
3754         else {
3755             var s = p / (2 * Math.PI) * Math.asin(c / a);
3756         }
3757
3758         return -(a * Math.pow(2, 10 * (t -= 1)) * Math.sin((t * d - s) * (2 * Math.PI) / p)) + b;
3759     },
3760
3761
3762     elasticOut: function (t, b, c, d, a, p) {
3763         if (t == 0) {
3764             return b;
3765         }
3766         if ((t /= d) == 1) {
3767             return b + c;
3768         }
3769         if (!p) {
3770             p = d * .3;
3771         }
3772
3773         if (!a || a < Math.abs(c)) {
3774             a = c;
3775             var s = p / 4;
3776         }
3777         else {
3778             var s = p / (2 * Math.PI) * Math.asin(c / a);
3779         }
3780
3781         return a * Math.pow(2, -10 * t) * Math.sin((t * d - s) * (2 * Math.PI) / p) + c + b;
3782     },
3783
3784
3785     elasticBoth: function (t, b, c, d, a, p) {
3786         if (t == 0) {
3787             return b;
3788         }
3789
3790         if ((t /= d / 2) == 2) {
3791             return b + c;
3792         }
3793
3794         if (!p) {
3795             p = d * (.3 * 1.5);
3796         }
3797
3798         if (!a || a < Math.abs(c)) {
3799             a = c;
3800             var s = p / 4;
3801         }
3802         else {
3803             var s = p / (2 * Math.PI) * Math.asin(c / a);
3804         }
3805
3806         if (t < 1) {
3807             return -.5 * (a * Math.pow(2, 10 * (t -= 1)) *
3808                           Math.sin((t * d - s) * (2 * Math.PI) / p)) + b;
3809         }
3810         return a * Math.pow(2, -10 * (t -= 1)) *
3811                Math.sin((t * d - s) * (2 * Math.PI) / p) * .5 + c + b;
3812     },
3813
3814
3815
3816     backIn: function (t, b, c, d, s) {
3817         if (typeof s == 'undefined') {
3818             s = 1.70158;
3819         }
3820         return c * (t /= d) * t * ((s + 1) * t - s) + b;
3821     },
3822
3823
3824     backOut: function (t, b, c, d, s) {
3825         if (typeof s == 'undefined') {
3826             s = 1.70158;
3827         }
3828         return c * ((t = t / d - 1) * t * ((s + 1) * t + s) + 1) + b;
3829     },
3830
3831
3832     backBoth: function (t, b, c, d, s) {
3833         if (typeof s == 'undefined') {
3834             s = 1.70158;
3835         }
3836
3837         if ((t /= d / 2 ) < 1) {
3838             return c / 2 * (t * t * (((s *= (1.525)) + 1) * t - s)) + b;
3839         }
3840         return c / 2 * ((t -= 2) * t * (((s *= (1.525)) + 1) * t + s) + 2) + b;
3841     },
3842
3843
3844     bounceIn: function (t, b, c, d) {
3845         return c - Roo.lib.Easing.bounceOut(d - t, 0, c, d) + b;
3846     },
3847
3848
3849     bounceOut: function (t, b, c, d) {
3850         if ((t /= d) < (1 / 2.75)) {
3851             return c * (7.5625 * t * t) + b;
3852         } else if (t < (2 / 2.75)) {
3853             return c * (7.5625 * (t -= (1.5 / 2.75)) * t + .75) + b;
3854         } else if (t < (2.5 / 2.75)) {
3855             return c * (7.5625 * (t -= (2.25 / 2.75)) * t + .9375) + b;
3856         }
3857         return c * (7.5625 * (t -= (2.625 / 2.75)) * t + .984375) + b;
3858     },
3859
3860
3861     bounceBoth: function (t, b, c, d) {
3862         if (t < d / 2) {
3863             return Roo.lib.Easing.bounceIn(t * 2, 0, c, d) * .5 + b;
3864         }
3865         return Roo.lib.Easing.bounceOut(t * 2 - d, 0, c, d) * .5 + c * .5 + b;
3866     }
3867 };/*
3868  * Portions of this file are based on pieces of Yahoo User Interface Library
3869  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3870  * YUI licensed under the BSD License:
3871  * http://developer.yahoo.net/yui/license.txt
3872  * <script type="text/javascript">
3873  *
3874  */
3875     (function() {
3876         Roo.lib.Motion = function(el, attributes, duration, method) {
3877             if (el) {
3878                 Roo.lib.Motion.superclass.constructor.call(this, el, attributes, duration, method);
3879             }
3880         };
3881
3882         Roo.extend(Roo.lib.Motion, Roo.lib.ColorAnim);
3883
3884
3885         var Y = Roo.lib;
3886         var superclass = Y.Motion.superclass;
3887         var proto = Y.Motion.prototype;
3888
3889         proto.toString = function() {
3890             var el = this.getEl();
3891             var id = el.id || el.tagName;
3892             return ("Motion " + id);
3893         };
3894
3895         proto.patterns.points = /^points$/i;
3896
3897         proto.setAttribute = function(attr, val, unit) {
3898             if (this.patterns.points.test(attr)) {
3899                 unit = unit || 'px';
3900                 superclass.setAttribute.call(this, 'left', val[0], unit);
3901                 superclass.setAttribute.call(this, 'top', val[1], unit);
3902             } else {
3903                 superclass.setAttribute.call(this, attr, val, unit);
3904             }
3905         };
3906
3907         proto.getAttribute = function(attr) {
3908             if (this.patterns.points.test(attr)) {
3909                 var val = [
3910                         superclass.getAttribute.call(this, 'left'),
3911                         superclass.getAttribute.call(this, 'top')
3912                         ];
3913             } else {
3914                 val = superclass.getAttribute.call(this, attr);
3915             }
3916
3917             return val;
3918         };
3919
3920         proto.doMethod = function(attr, start, end) {
3921             var val = null;
3922
3923             if (this.patterns.points.test(attr)) {
3924                 var t = this.method(this.currentFrame, 0, 100, this.totalFrames) / 100;
3925                 val = Y.Bezier.getPosition(this.runtimeAttributes[attr], t);
3926             } else {
3927                 val = superclass.doMethod.call(this, attr, start, end);
3928             }
3929             return val;
3930         };
3931
3932         proto.setRuntimeAttribute = function(attr) {
3933             if (this.patterns.points.test(attr)) {
3934                 var el = this.getEl();
3935                 var attributes = this.attributes;
3936                 var start;
3937                 var control = attributes['points']['control'] || [];
3938                 var end;
3939                 var i, len;
3940
3941                 if (control.length > 0 && !(control[0] instanceof Array)) {
3942                     control = [control];
3943                 } else {
3944                     var tmp = [];
3945                     for (i = 0,len = control.length; i < len; ++i) {
3946                         tmp[i] = control[i];
3947                     }
3948                     control = tmp;
3949                 }
3950
3951                 Roo.fly(el).position();
3952
3953                 if (isset(attributes['points']['from'])) {
3954                     Roo.lib.Dom.setXY(el, attributes['points']['from']);
3955                 }
3956                 else {
3957                     Roo.lib.Dom.setXY(el, Roo.lib.Dom.getXY(el));
3958                 }
3959
3960                 start = this.getAttribute('points');
3961
3962
3963                 if (isset(attributes['points']['to'])) {
3964                     end = translateValues.call(this, attributes['points']['to'], start);
3965
3966                     var pageXY = Roo.lib.Dom.getXY(this.getEl());
3967                     for (i = 0,len = control.length; i < len; ++i) {
3968                         control[i] = translateValues.call(this, control[i], start);
3969                     }
3970
3971
3972                 } else if (isset(attributes['points']['by'])) {
3973                     end = [ start[0] + attributes['points']['by'][0], start[1] + attributes['points']['by'][1] ];
3974
3975                     for (i = 0,len = control.length; i < len; ++i) {
3976                         control[i] = [ start[0] + control[i][0], start[1] + control[i][1] ];
3977                     }
3978                 }
3979
3980                 this.runtimeAttributes[attr] = [start];
3981
3982                 if (control.length > 0) {
3983                     this.runtimeAttributes[attr] = this.runtimeAttributes[attr].concat(control);
3984                 }
3985
3986                 this.runtimeAttributes[attr][this.runtimeAttributes[attr].length] = end;
3987             }
3988             else {
3989                 superclass.setRuntimeAttribute.call(this, attr);
3990             }
3991         };
3992
3993         var translateValues = function(val, start) {
3994             var pageXY = Roo.lib.Dom.getXY(this.getEl());
3995             val = [ val[0] - pageXY[0] + start[0], val[1] - pageXY[1] + start[1] ];
3996
3997             return val;
3998         };
3999
4000         var isset = function(prop) {
4001             return (typeof prop !== 'undefined');
4002         };
4003     })();
4004 /*
4005  * Portions of this file are based on pieces of Yahoo User Interface Library
4006  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
4007  * YUI licensed under the BSD License:
4008  * http://developer.yahoo.net/yui/license.txt
4009  * <script type="text/javascript">
4010  *
4011  */
4012     (function() {
4013         Roo.lib.Scroll = function(el, attributes, duration, method) {
4014             if (el) {
4015                 Roo.lib.Scroll.superclass.constructor.call(this, el, attributes, duration, method);
4016             }
4017         };
4018
4019         Roo.extend(Roo.lib.Scroll, Roo.lib.ColorAnim);
4020
4021
4022         var Y = Roo.lib;
4023         var superclass = Y.Scroll.superclass;
4024         var proto = Y.Scroll.prototype;
4025
4026         proto.toString = function() {
4027             var el = this.getEl();
4028             var id = el.id || el.tagName;
4029             return ("Scroll " + id);
4030         };
4031
4032         proto.doMethod = function(attr, start, end) {
4033             var val = null;
4034
4035             if (attr == 'scroll') {
4036                 val = [
4037                         this.method(this.currentFrame, start[0], end[0] - start[0], this.totalFrames),
4038                         this.method(this.currentFrame, start[1], end[1] - start[1], this.totalFrames)
4039                         ];
4040
4041             } else {
4042                 val = superclass.doMethod.call(this, attr, start, end);
4043             }
4044             return val;
4045         };
4046
4047         proto.getAttribute = function(attr) {
4048             var val = null;
4049             var el = this.getEl();
4050
4051             if (attr == 'scroll') {
4052                 val = [ el.scrollLeft, el.scrollTop ];
4053             } else {
4054                 val = superclass.getAttribute.call(this, attr);
4055             }
4056
4057             return val;
4058         };
4059
4060         proto.setAttribute = function(attr, val, unit) {
4061             var el = this.getEl();
4062
4063             if (attr == 'scroll') {
4064                 el.scrollLeft = val[0];
4065                 el.scrollTop = val[1];
4066             } else {
4067                 superclass.setAttribute.call(this, attr, val, unit);
4068             }
4069         };
4070     })();
4071 /*
4072  * Based on:
4073  * Ext JS Library 1.1.1
4074  * Copyright(c) 2006-2007, Ext JS, LLC.
4075  *
4076  * Originally Released Under LGPL - original licence link has changed is not relivant.
4077  *
4078  * Fork - LGPL
4079  * <script type="text/javascript">
4080  */
4081
4082
4083 // nasty IE9 hack - what a pile of crap that is..
4084
4085  if (typeof Range != "undefined" && typeof Range.prototype.createContextualFragment == "undefined") {
4086     Range.prototype.createContextualFragment = function (html) {
4087         var doc = window.document;
4088         var container = doc.createElement("div");
4089         container.innerHTML = html;
4090         var frag = doc.createDocumentFragment(), n;
4091         while ((n = container.firstChild)) {
4092             frag.appendChild(n);
4093         }
4094         return frag;
4095     };
4096 }
4097
4098 /**
4099  * @class Roo.DomHelper
4100  * Utility class for working with DOM and/or Templates. It transparently supports using HTML fragments or DOM.
4101  * 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>.
4102  * @singleton
4103  */
4104 Roo.DomHelper = function(){
4105     var tempTableEl = null;
4106     var emptyTags = /^(?:br|frame|hr|img|input|link|meta|range|spacer|wbr|area|param|col)$/i;
4107     var tableRe = /^table|tbody|tr|td$/i;
4108     var xmlns = {};
4109     // build as innerHTML where available
4110     /** @ignore */
4111     var createHtml = function(o){
4112         if(typeof o == 'string'){
4113             return o;
4114         }
4115         var b = "";
4116         if(!o.tag){
4117             o.tag = "div";
4118         }
4119         b += "<" + o.tag;
4120         for(var attr in o){
4121             if(attr == "tag" || attr == "children" || attr == "cn" || attr == "html" || typeof o[attr] == "function") continue;
4122             if(attr == "style"){
4123                 var s = o["style"];
4124                 if(typeof s == "function"){
4125                     s = s.call();
4126                 }
4127                 if(typeof s == "string"){
4128                     b += ' style="' + s + '"';
4129                 }else if(typeof s == "object"){
4130                     b += ' style="';
4131                     for(var key in s){
4132                         if(typeof s[key] != "function"){
4133                             b += key + ":" + s[key] + ";";
4134                         }
4135                     }
4136                     b += '"';
4137                 }
4138             }else{
4139                 if(attr == "cls"){
4140                     b += ' class="' + o["cls"] + '"';
4141                 }else if(attr == "htmlFor"){
4142                     b += ' for="' + o["htmlFor"] + '"';
4143                 }else{
4144                     b += " " + attr + '="' + o[attr] + '"';
4145                 }
4146             }
4147         }
4148         if(emptyTags.test(o.tag)){
4149             b += "/>";
4150         }else{
4151             b += ">";
4152             var cn = o.children || o.cn;
4153             if(cn){
4154                 //http://bugs.kde.org/show_bug.cgi?id=71506
4155                 if((cn instanceof Array) || (Roo.isSafari && typeof(cn.join) == "function")){
4156                     for(var i = 0, len = cn.length; i < len; i++) {
4157                         b += createHtml(cn[i], b);
4158                     }
4159                 }else{
4160                     b += createHtml(cn, b);
4161                 }
4162             }
4163             if(o.html){
4164                 b += o.html;
4165             }
4166             b += "</" + o.tag + ">";
4167         }
4168         return b;
4169     };
4170
4171     // build as dom
4172     /** @ignore */
4173     var createDom = function(o, parentNode){
4174          
4175         // defininition craeted..
4176         var ns = false;
4177         if (o.ns && o.ns != 'html') {
4178                
4179             if (o.xmlns && typeof(xmlns[o.ns]) == 'undefined') {
4180                 xmlns[o.ns] = o.xmlns;
4181                 ns = o.xmlns;
4182             }
4183             if (typeof(xmlns[o.ns]) == 'undefined') {
4184                 console.log("Trying to create namespace element " + o.ns + ", however no xmlns was sent to builder previously");
4185             }
4186             ns = xmlns[o.ns];
4187         }
4188         
4189         
4190         if (typeof(o) == 'string') {
4191             return parentNode.appendChild(document.createTextNode(o));
4192         }
4193         o.tag = o.tag || div;
4194         if (o.ns && Roo.isIE) {
4195             ns = false;
4196             o.tag = o.ns + ':' + o.tag;
4197             
4198         }
4199         var el = ns ? document.createElementNS( ns, o.tag||'div') :  document.createElement(o.tag||'div');
4200         var useSet = el.setAttribute ? true : false; // In IE some elements don't have setAttribute
4201         for(var attr in o){
4202             
4203             if(attr == "tag" || attr == "ns" ||attr == "xmlns" ||attr == "children" || attr == "cn" || attr == "html" || 
4204                     attr == "style" || typeof o[attr] == "function") continue;
4205                     
4206             if(attr=="cls" && Roo.isIE){
4207                 el.className = o["cls"];
4208             }else{
4209                 if(useSet) el.setAttribute(attr=="cls" ? 'class' : attr, o[attr]);
4210                 else el[attr] = o[attr];
4211             }
4212         }
4213         Roo.DomHelper.applyStyles(el, o.style);
4214         var cn = o.children || o.cn;
4215         if(cn){
4216             //http://bugs.kde.org/show_bug.cgi?id=71506
4217              if((cn instanceof Array) || (Roo.isSafari && typeof(cn.join) == "function")){
4218                 for(var i = 0, len = cn.length; i < len; i++) {
4219                     createDom(cn[i], el);
4220                 }
4221             }else{
4222                 createDom(cn, el);
4223             }
4224         }
4225         if(o.html){
4226             el.innerHTML = o.html;
4227         }
4228         if(parentNode){
4229            parentNode.appendChild(el);
4230         }
4231         return el;
4232     };
4233
4234     var ieTable = function(depth, s, h, e){
4235         tempTableEl.innerHTML = [s, h, e].join('');
4236         var i = -1, el = tempTableEl;
4237         while(++i < depth){
4238             el = el.firstChild;
4239         }
4240         return el;
4241     };
4242
4243     // kill repeat to save bytes
4244     var ts = '<table>',
4245         te = '</table>',
4246         tbs = ts+'<tbody>',
4247         tbe = '</tbody>'+te,
4248         trs = tbs + '<tr>',
4249         tre = '</tr>'+tbe;
4250
4251     /**
4252      * @ignore
4253      * Nasty code for IE's broken table implementation
4254      */
4255     var insertIntoTable = function(tag, where, el, html){
4256         if(!tempTableEl){
4257             tempTableEl = document.createElement('div');
4258         }
4259         var node;
4260         var before = null;
4261         if(tag == 'td'){
4262             if(where == 'afterbegin' || where == 'beforeend'){ // INTO a TD
4263                 return;
4264             }
4265             if(where == 'beforebegin'){
4266                 before = el;
4267                 el = el.parentNode;
4268             } else{
4269                 before = el.nextSibling;
4270                 el = el.parentNode;
4271             }
4272             node = ieTable(4, trs, html, tre);
4273         }
4274         else if(tag == 'tr'){
4275             if(where == 'beforebegin'){
4276                 before = el;
4277                 el = el.parentNode;
4278                 node = ieTable(3, tbs, html, tbe);
4279             } else if(where == 'afterend'){
4280                 before = el.nextSibling;
4281                 el = el.parentNode;
4282                 node = ieTable(3, tbs, html, tbe);
4283             } else{ // INTO a TR
4284                 if(where == 'afterbegin'){
4285                     before = el.firstChild;
4286                 }
4287                 node = ieTable(4, trs, html, tre);
4288             }
4289         } else if(tag == 'tbody'){
4290             if(where == 'beforebegin'){
4291                 before = el;
4292                 el = el.parentNode;
4293                 node = ieTable(2, ts, html, te);
4294             } else if(where == 'afterend'){
4295                 before = el.nextSibling;
4296                 el = el.parentNode;
4297                 node = ieTable(2, ts, html, te);
4298             } else{
4299                 if(where == 'afterbegin'){
4300                     before = el.firstChild;
4301                 }
4302                 node = ieTable(3, tbs, html, tbe);
4303             }
4304         } else{ // TABLE
4305             if(where == 'beforebegin' || where == 'afterend'){ // OUTSIDE the table
4306                 return;
4307             }
4308             if(where == 'afterbegin'){
4309                 before = el.firstChild;
4310             }
4311             node = ieTable(2, ts, html, te);
4312         }
4313         el.insertBefore(node, before);
4314         return node;
4315     };
4316
4317     return {
4318     /** True to force the use of DOM instead of html fragments @type Boolean */
4319     useDom : false,
4320
4321     /**
4322      * Returns the markup for the passed Element(s) config
4323      * @param {Object} o The Dom object spec (and children)
4324      * @return {String}
4325      */
4326     markup : function(o){
4327         return createHtml(o);
4328     },
4329
4330     /**
4331      * Applies a style specification to an element
4332      * @param {String/HTMLElement} el The element to apply styles to
4333      * @param {String/Object/Function} styles A style specification string eg "width:100px", or object in the form {width:"100px"}, or
4334      * a function which returns such a specification.
4335      */
4336     applyStyles : function(el, styles){
4337         if(styles){
4338            el = Roo.fly(el);
4339            if(typeof styles == "string"){
4340                var re = /\s?([a-z\-]*)\:\s?([^;]*);?/gi;
4341                var matches;
4342                while ((matches = re.exec(styles)) != null){
4343                    el.setStyle(matches[1], matches[2]);
4344                }
4345            }else if (typeof styles == "object"){
4346                for (var style in styles){
4347                   el.setStyle(style, styles[style]);
4348                }
4349            }else if (typeof styles == "function"){
4350                 Roo.DomHelper.applyStyles(el, styles.call());
4351            }
4352         }
4353     },
4354
4355     /**
4356      * Inserts an HTML fragment into the Dom
4357      * @param {String} where Where to insert the html in relation to el - beforeBegin, afterBegin, beforeEnd, afterEnd.
4358      * @param {HTMLElement} el The context element
4359      * @param {String} html The HTML fragmenet
4360      * @return {HTMLElement} The new node
4361      */
4362     insertHtml : function(where, el, html){
4363         where = where.toLowerCase();
4364         if(el.insertAdjacentHTML){
4365             if(tableRe.test(el.tagName)){
4366                 var rs;
4367                 if(rs = insertIntoTable(el.tagName.toLowerCase(), where, el, html)){
4368                     return rs;
4369                 }
4370             }
4371             switch(where){
4372                 case "beforebegin":
4373                     el.insertAdjacentHTML('BeforeBegin', html);
4374                     return el.previousSibling;
4375                 case "afterbegin":
4376                     el.insertAdjacentHTML('AfterBegin', html);
4377                     return el.firstChild;
4378                 case "beforeend":
4379                     el.insertAdjacentHTML('BeforeEnd', html);
4380                     return el.lastChild;
4381                 case "afterend":
4382                     el.insertAdjacentHTML('AfterEnd', html);
4383                     return el.nextSibling;
4384             }
4385             throw 'Illegal insertion point -> "' + where + '"';
4386         }
4387         var range = el.ownerDocument.createRange();
4388         var frag;
4389         switch(where){
4390              case "beforebegin":
4391                 range.setStartBefore(el);
4392                 frag = range.createContextualFragment(html);
4393                 el.parentNode.insertBefore(frag, el);
4394                 return el.previousSibling;
4395              case "afterbegin":
4396                 if(el.firstChild){
4397                     range.setStartBefore(el.firstChild);
4398                     frag = range.createContextualFragment(html);
4399                     el.insertBefore(frag, el.firstChild);
4400                     return el.firstChild;
4401                 }else{
4402                     el.innerHTML = html;
4403                     return el.firstChild;
4404                 }
4405             case "beforeend":
4406                 if(el.lastChild){
4407                     range.setStartAfter(el.lastChild);
4408                     frag = range.createContextualFragment(html);
4409                     el.appendChild(frag);
4410                     return el.lastChild;
4411                 }else{
4412                     el.innerHTML = html;
4413                     return el.lastChild;
4414                 }
4415             case "afterend":
4416                 range.setStartAfter(el);
4417                 frag = range.createContextualFragment(html);
4418                 el.parentNode.insertBefore(frag, el.nextSibling);
4419                 return el.nextSibling;
4420             }
4421             throw 'Illegal insertion point -> "' + where + '"';
4422     },
4423
4424     /**
4425      * Creates new Dom element(s) and inserts them before el
4426      * @param {String/HTMLElement/Element} el The context element
4427      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
4428      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4429      * @return {HTMLElement/Roo.Element} The new node
4430      */
4431     insertBefore : function(el, o, returnElement){
4432         return this.doInsert(el, o, returnElement, "beforeBegin");
4433     },
4434
4435     /**
4436      * Creates new Dom element(s) and inserts them after el
4437      * @param {String/HTMLElement/Element} el The context element
4438      * @param {Object} o The Dom object spec (and children)
4439      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4440      * @return {HTMLElement/Roo.Element} The new node
4441      */
4442     insertAfter : function(el, o, returnElement){
4443         return this.doInsert(el, o, returnElement, "afterEnd", "nextSibling");
4444     },
4445
4446     /**
4447      * Creates new Dom element(s) and inserts them as the first child of el
4448      * @param {String/HTMLElement/Element} el The context element
4449      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
4450      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4451      * @return {HTMLElement/Roo.Element} The new node
4452      */
4453     insertFirst : function(el, o, returnElement){
4454         return this.doInsert(el, o, returnElement, "afterBegin");
4455     },
4456
4457     // private
4458     doInsert : function(el, o, returnElement, pos, sibling){
4459         el = Roo.getDom(el);
4460         var newNode;
4461         if(this.useDom || o.ns){
4462             newNode = createDom(o, null);
4463             el.parentNode.insertBefore(newNode, sibling ? el[sibling] : el);
4464         }else{
4465             var html = createHtml(o);
4466             newNode = this.insertHtml(pos, el, html);
4467         }
4468         return returnElement ? Roo.get(newNode, true) : newNode;
4469     },
4470
4471     /**
4472      * Creates new Dom element(s) and appends them to el
4473      * @param {String/HTMLElement/Element} el The context element
4474      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
4475      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4476      * @return {HTMLElement/Roo.Element} The new node
4477      */
4478     append : function(el, o, returnElement){
4479         el = Roo.getDom(el);
4480         var newNode;
4481         if(this.useDom || o.ns){
4482             newNode = createDom(o, null);
4483             el.appendChild(newNode);
4484         }else{
4485             var html = createHtml(o);
4486             newNode = this.insertHtml("beforeEnd", el, html);
4487         }
4488         return returnElement ? Roo.get(newNode, true) : newNode;
4489     },
4490
4491     /**
4492      * Creates new Dom element(s) and overwrites the contents of el with them
4493      * @param {String/HTMLElement/Element} el The context element
4494      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
4495      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4496      * @return {HTMLElement/Roo.Element} The new node
4497      */
4498     overwrite : function(el, o, returnElement){
4499         el = Roo.getDom(el);
4500         if (o.ns) {
4501           
4502             while (el.childNodes.length) {
4503                 el.removeChild(el.firstChild);
4504             }
4505             createDom(o, el);
4506         } else {
4507             el.innerHTML = createHtml(o);   
4508         }
4509         
4510         return returnElement ? Roo.get(el.firstChild, true) : el.firstChild;
4511     },
4512
4513     /**
4514      * Creates a new Roo.DomHelper.Template from the Dom object spec
4515      * @param {Object} o The Dom object spec (and children)
4516      * @return {Roo.DomHelper.Template} The new template
4517      */
4518     createTemplate : function(o){
4519         var html = createHtml(o);
4520         return new Roo.Template(html);
4521     }
4522     };
4523 }();
4524 /*
4525  * Based on:
4526  * Ext JS Library 1.1.1
4527  * Copyright(c) 2006-2007, Ext JS, LLC.
4528  *
4529  * Originally Released Under LGPL - original licence link has changed is not relivant.
4530  *
4531  * Fork - LGPL
4532  * <script type="text/javascript">
4533  */
4534  
4535 /**
4536 * @class Roo.Template
4537 * Represents an HTML fragment template. Templates can be precompiled for greater performance.
4538 * For a list of available format functions, see {@link Roo.util.Format}.<br />
4539 * Usage:
4540 <pre><code>
4541 var t = new Roo.Template({
4542     html :  '&lt;div name="{id}"&gt;' + 
4543         '&lt;span class="{cls}"&gt;{name:trim} {someval:this.myformat}{value:ellipsis(10)}&lt;/span&gt;' +
4544         '&lt;/div&gt;',
4545     myformat: function (value, allValues) {
4546         return 'XX' + value;
4547     }
4548 });
4549 t.append('some-element', {id: 'myid', cls: 'myclass', name: 'foo', value: 'bar'});
4550 </code></pre>
4551 * For more information see this blog post with examples:
4552 *  <a href="http://www.cnitblog.com/seeyeah/archive/2011/12/30/38728.html/">DomHelper
4553      - Create Elements using DOM, HTML fragments and Templates</a>. 
4554 * @constructor
4555 * @param {Object} cfg - Configuration object.
4556 */
4557 Roo.Template = function(cfg){
4558     // BC!
4559     if(cfg instanceof Array){
4560         cfg = cfg.join("");
4561     }else if(arguments.length > 1){
4562         cfg = Array.prototype.join.call(arguments, "");
4563     }
4564     
4565     
4566     if (typeof(cfg) == 'object') {
4567         Roo.apply(this,cfg)
4568     } else {
4569         // bc
4570         this.html = cfg;
4571     }
4572     if (this.url) {
4573         this.load();
4574     }
4575     
4576 };
4577 Roo.Template.prototype = {
4578     
4579     /**
4580      * @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..
4581      *                    it should be fixed so that template is observable...
4582      */
4583     url : false,
4584     /**
4585      * @cfg {String} html  The HTML fragment or an array of fragments to join("") or multiple arguments to join("")
4586      */
4587     html : '',
4588     /**
4589      * Returns an HTML fragment of this template with the specified values applied.
4590      * @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'})
4591      * @return {String} The HTML fragment
4592      */
4593     applyTemplate : function(values){
4594         try {
4595            
4596             if(this.compiled){
4597                 return this.compiled(values);
4598             }
4599             var useF = this.disableFormats !== true;
4600             var fm = Roo.util.Format, tpl = this;
4601             var fn = function(m, name, format, args){
4602                 if(format && useF){
4603                     if(format.substr(0, 5) == "this."){
4604                         return tpl.call(format.substr(5), values[name], values);
4605                     }else{
4606                         if(args){
4607                             // quoted values are required for strings in compiled templates, 
4608                             // but for non compiled we need to strip them
4609                             // quoted reversed for jsmin
4610                             var re = /^\s*['"](.*)["']\s*$/;
4611                             args = args.split(',');
4612                             for(var i = 0, len = args.length; i < len; i++){
4613                                 args[i] = args[i].replace(re, "$1");
4614                             }
4615                             args = [values[name]].concat(args);
4616                         }else{
4617                             args = [values[name]];
4618                         }
4619                         return fm[format].apply(fm, args);
4620                     }
4621                 }else{
4622                     return values[name] !== undefined ? values[name] : "";
4623                 }
4624             };
4625             return this.html.replace(this.re, fn);
4626         } catch (e) {
4627             Roo.log(e);
4628             throw e;
4629         }
4630          
4631     },
4632     
4633     loading : false,
4634       
4635     load : function ()
4636     {
4637          
4638         if (this.loading) {
4639             return;
4640         }
4641         var _t = this;
4642         
4643         this.loading = true;
4644         this.compiled = false;
4645         
4646         var cx = new Roo.data.Connection();
4647         cx.request({
4648             url : this.url,
4649             method : 'GET',
4650             success : function (response) {
4651                 _t.loading = false;
4652                 _t.html = response.responseText;
4653                 _t.url = false;
4654                 _t.compile();
4655              },
4656             failure : function(response) {
4657                 Roo.log("Template failed to load from " + _t.url);
4658                 _t.loading = false;
4659             }
4660         });
4661     },
4662
4663     /**
4664      * Sets the HTML used as the template and optionally compiles it.
4665      * @param {String} html
4666      * @param {Boolean} compile (optional) True to compile the template (defaults to undefined)
4667      * @return {Roo.Template} this
4668      */
4669     set : function(html, compile){
4670         this.html = html;
4671         this.compiled = null;
4672         if(compile){
4673             this.compile();
4674         }
4675         return this;
4676     },
4677     
4678     /**
4679      * True to disable format functions (defaults to false)
4680      * @type Boolean
4681      */
4682     disableFormats : false,
4683     
4684     /**
4685     * The regular expression used to match template variables 
4686     * @type RegExp
4687     * @property 
4688     */
4689     re : /\{([\w-]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
4690     
4691     /**
4692      * Compiles the template into an internal function, eliminating the RegEx overhead.
4693      * @return {Roo.Template} this
4694      */
4695     compile : function(){
4696         var fm = Roo.util.Format;
4697         var useF = this.disableFormats !== true;
4698         var sep = Roo.isGecko ? "+" : ",";
4699         var fn = function(m, name, format, args){
4700             if(format && useF){
4701                 args = args ? ',' + args : "";
4702                 if(format.substr(0, 5) != "this."){
4703                     format = "fm." + format + '(';
4704                 }else{
4705                     format = 'this.call("'+ format.substr(5) + '", ';
4706                     args = ", values";
4707                 }
4708             }else{
4709                 args= ''; format = "(values['" + name + "'] == undefined ? '' : ";
4710             }
4711             return "'"+ sep + format + "values['" + name + "']" + args + ")"+sep+"'";
4712         };
4713         var body;
4714         // branched to use + in gecko and [].join() in others
4715         if(Roo.isGecko){
4716             body = "this.compiled = function(values){ return '" +
4717                    this.html.replace(/\\/g, '\\\\').replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
4718                     "';};";
4719         }else{
4720             body = ["this.compiled = function(values){ return ['"];
4721             body.push(this.html.replace(/\\/g, '\\\\').replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn));
4722             body.push("'].join('');};");
4723             body = body.join('');
4724         }
4725         /**
4726          * eval:var:values
4727          * eval:var:fm
4728          */
4729         eval(body);
4730         return this;
4731     },
4732     
4733     // private function used to call members
4734     call : function(fnName, value, allValues){
4735         return this[fnName](value, allValues);
4736     },
4737     
4738     /**
4739      * Applies the supplied values to the template and inserts the new node(s) as the first child of el.
4740      * @param {String/HTMLElement/Roo.Element} el The context element
4741      * @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'})
4742      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4743      * @return {HTMLElement/Roo.Element} The new node or Element
4744      */
4745     insertFirst: function(el, values, returnElement){
4746         return this.doInsert('afterBegin', el, values, returnElement);
4747     },
4748
4749     /**
4750      * Applies the supplied values to the template and inserts the new node(s) before el.
4751      * @param {String/HTMLElement/Roo.Element} el The context element
4752      * @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'})
4753      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4754      * @return {HTMLElement/Roo.Element} The new node or Element
4755      */
4756     insertBefore: function(el, values, returnElement){
4757         return this.doInsert('beforeBegin', el, values, returnElement);
4758     },
4759
4760     /**
4761      * Applies the supplied values to the template and inserts the new node(s) after el.
4762      * @param {String/HTMLElement/Roo.Element} el The context element
4763      * @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'})
4764      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4765      * @return {HTMLElement/Roo.Element} The new node or Element
4766      */
4767     insertAfter : function(el, values, returnElement){
4768         return this.doInsert('afterEnd', el, values, returnElement);
4769     },
4770     
4771     /**
4772      * Applies the supplied values to the template and appends the new node(s) to el.
4773      * @param {String/HTMLElement/Roo.Element} el The context element
4774      * @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'})
4775      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4776      * @return {HTMLElement/Roo.Element} The new node or Element
4777      */
4778     append : function(el, values, returnElement){
4779         return this.doInsert('beforeEnd', el, values, returnElement);
4780     },
4781
4782     doInsert : function(where, el, values, returnEl){
4783         el = Roo.getDom(el);
4784         var newNode = Roo.DomHelper.insertHtml(where, el, this.applyTemplate(values));
4785         return returnEl ? Roo.get(newNode, true) : newNode;
4786     },
4787
4788     /**
4789      * Applies the supplied values to the template and overwrites the content of el with the new node(s).
4790      * @param {String/HTMLElement/Roo.Element} el The context element
4791      * @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'})
4792      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4793      * @return {HTMLElement/Roo.Element} The new node or Element
4794      */
4795     overwrite : function(el, values, returnElement){
4796         el = Roo.getDom(el);
4797         el.innerHTML = this.applyTemplate(values);
4798         return returnElement ? Roo.get(el.firstChild, true) : el.firstChild;
4799     }
4800 };
4801 /**
4802  * Alias for {@link #applyTemplate}
4803  * @method
4804  */
4805 Roo.Template.prototype.apply = Roo.Template.prototype.applyTemplate;
4806
4807 // backwards compat
4808 Roo.DomHelper.Template = Roo.Template;
4809
4810 /**
4811  * Creates a template from the passed element's value (<i>display:none</i> textarea, preferred) or innerHTML.
4812  * @param {String/HTMLElement} el A DOM element or its id
4813  * @returns {Roo.Template} The created template
4814  * @static
4815  */
4816 Roo.Template.from = function(el){
4817     el = Roo.getDom(el);
4818     return new Roo.Template(el.value || el.innerHTML);
4819 };/*
4820  * Based on:
4821  * Ext JS Library 1.1.1
4822  * Copyright(c) 2006-2007, Ext JS, LLC.
4823  *
4824  * Originally Released Under LGPL - original licence link has changed is not relivant.
4825  *
4826  * Fork - LGPL
4827  * <script type="text/javascript">
4828  */
4829  
4830
4831 /*
4832  * This is code is also distributed under MIT license for use
4833  * with jQuery and prototype JavaScript libraries.
4834  */
4835 /**
4836  * @class Roo.DomQuery
4837 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).
4838 <p>
4839 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>
4840
4841 <p>
4842 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.
4843 </p>
4844 <h4>Element Selectors:</h4>
4845 <ul class="list">
4846     <li> <b>*</b> any element</li>
4847     <li> <b>E</b> an element with the tag E</li>
4848     <li> <b>E F</b> All descendent elements of E that have the tag F</li>
4849     <li> <b>E > F</b> or <b>E/F</b> all direct children elements of E that have the tag F</li>
4850     <li> <b>E + F</b> all elements with the tag F that are immediately preceded by an element with the tag E</li>
4851     <li> <b>E ~ F</b> all elements with the tag F that are preceded by a sibling element with the tag E</li>
4852 </ul>
4853 <h4>Attribute Selectors:</h4>
4854 <p>The use of @ and quotes are optional. For example, div[@foo='bar'] is also a valid attribute selector.</p>
4855 <ul class="list">
4856     <li> <b>E[foo]</b> has an attribute "foo"</li>
4857     <li> <b>E[foo=bar]</b> has an attribute "foo" that equals "bar"</li>
4858     <li> <b>E[foo^=bar]</b> has an attribute "foo" that starts with "bar"</li>
4859     <li> <b>E[foo$=bar]</b> has an attribute "foo" that ends with "bar"</li>
4860     <li> <b>E[foo*=bar]</b> has an attribute "foo" that contains the substring "bar"</li>
4861     <li> <b>E[foo%=2]</b> has an attribute "foo" that is evenly divisible by 2</li>
4862     <li> <b>E[foo!=bar]</b> has an attribute "foo" that does not equal "bar"</li>
4863 </ul>
4864 <h4>Pseudo Classes:</h4>
4865 <ul class="list">
4866     <li> <b>E:first-child</b> E is the first child of its parent</li>
4867     <li> <b>E:last-child</b> E is the last child of its parent</li>
4868     <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>
4869     <li> <b>E:nth-child(odd)</b> E is an odd child of its parent</li>
4870     <li> <b>E:nth-child(even)</b> E is an even child of its parent</li>
4871     <li> <b>E:only-child</b> E is the only child of its parent</li>
4872     <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>
4873     <li> <b>E:first</b> the first E in the resultset</li>
4874     <li> <b>E:last</b> the last E in the resultset</li>
4875     <li> <b>E:nth(<i>n</i>)</b> the <i>n</i>th E in the resultset (1 based)</li>
4876     <li> <b>E:odd</b> shortcut for :nth-child(odd)</li>
4877     <li> <b>E:even</b> shortcut for :nth-child(even)</li>
4878     <li> <b>E:contains(foo)</b> E's innerHTML contains the substring "foo"</li>
4879     <li> <b>E:nodeValue(foo)</b> E contains a textNode with a nodeValue that equals "foo"</li>
4880     <li> <b>E:not(S)</b> an E element that does not match simple selector S</li>
4881     <li> <b>E:has(S)</b> an E element that has a descendent that matches simple selector S</li>
4882     <li> <b>E:next(S)</b> an E element whose next sibling matches simple selector S</li>
4883     <li> <b>E:prev(S)</b> an E element whose previous sibling matches simple selector S</li>
4884 </ul>
4885 <h4>CSS Value Selectors:</h4>
4886 <ul class="list">
4887     <li> <b>E{display=none}</b> css value "display" that equals "none"</li>
4888     <li> <b>E{display^=none}</b> css value "display" that starts with "none"</li>
4889     <li> <b>E{display$=none}</b> css value "display" that ends with "none"</li>
4890     <li> <b>E{display*=none}</b> css value "display" that contains the substring "none"</li>
4891     <li> <b>E{display%=2}</b> css value "display" that is evenly divisible by 2</li>
4892     <li> <b>E{display!=none}</b> css value "display" that does not equal "none"</li>
4893 </ul>
4894  * @singleton
4895  */
4896 Roo.DomQuery = function(){
4897     var cache = {}, simpleCache = {}, valueCache = {};
4898     var nonSpace = /\S/;
4899     var trimRe = /^\s+|\s+$/g;
4900     var tplRe = /\{(\d+)\}/g;
4901     var modeRe = /^(\s?[\/>+~]\s?|\s|$)/;
4902     var tagTokenRe = /^(#)?([\w-\*]+)/;
4903     var nthRe = /(\d*)n\+?(\d*)/, nthRe2 = /\D/;
4904
4905     function child(p, index){
4906         var i = 0;
4907         var n = p.firstChild;
4908         while(n){
4909             if(n.nodeType == 1){
4910                if(++i == index){
4911                    return n;
4912                }
4913             }
4914             n = n.nextSibling;
4915         }
4916         return null;
4917     };
4918
4919     function next(n){
4920         while((n = n.nextSibling) && n.nodeType != 1);
4921         return n;
4922     };
4923
4924     function prev(n){
4925         while((n = n.previousSibling) && n.nodeType != 1);
4926         return n;
4927     };
4928
4929     function children(d){
4930         var n = d.firstChild, ni = -1;
4931             while(n){
4932                 var nx = n.nextSibling;
4933                 if(n.nodeType == 3 && !nonSpace.test(n.nodeValue)){
4934                     d.removeChild(n);
4935                 }else{
4936                     n.nodeIndex = ++ni;
4937                 }
4938                 n = nx;
4939             }
4940             return this;
4941         };
4942
4943     function byClassName(c, a, v){
4944         if(!v){
4945             return c;
4946         }
4947         var r = [], ri = -1, cn;
4948         for(var i = 0, ci; ci = c[i]; i++){
4949             if((' '+ci.className+' ').indexOf(v) != -1){
4950                 r[++ri] = ci;
4951             }
4952         }
4953         return r;
4954     };
4955
4956     function attrValue(n, attr){
4957         if(!n.tagName && typeof n.length != "undefined"){
4958             n = n[0];
4959         }
4960         if(!n){
4961             return null;
4962         }
4963         if(attr == "for"){
4964             return n.htmlFor;
4965         }
4966         if(attr == "class" || attr == "className"){
4967             return n.className;
4968         }
4969         return n.getAttribute(attr) || n[attr];
4970
4971     };
4972
4973     function getNodes(ns, mode, tagName){
4974         var result = [], ri = -1, cs;
4975         if(!ns){
4976             return result;
4977         }
4978         tagName = tagName || "*";
4979         if(typeof ns.getElementsByTagName != "undefined"){
4980             ns = [ns];
4981         }
4982         if(!mode){
4983             for(var i = 0, ni; ni = ns[i]; i++){
4984                 cs = ni.getElementsByTagName(tagName);
4985                 for(var j = 0, ci; ci = cs[j]; j++){
4986                     result[++ri] = ci;
4987                 }
4988             }
4989         }else if(mode == "/" || mode == ">"){
4990             var utag = tagName.toUpperCase();
4991             for(var i = 0, ni, cn; ni = ns[i]; i++){
4992                 cn = ni.children || ni.childNodes;
4993                 for(var j = 0, cj; cj = cn[j]; j++){
4994                     if(cj.nodeName == utag || cj.nodeName == tagName  || tagName == '*'){
4995                         result[++ri] = cj;
4996                     }
4997                 }
4998             }
4999         }else if(mode == "+"){
5000             var utag = tagName.toUpperCase();
5001             for(var i = 0, n; n = ns[i]; i++){
5002                 while((n = n.nextSibling) && n.nodeType != 1);
5003                 if(n && (n.nodeName == utag || n.nodeName == tagName || tagName == '*')){
5004                     result[++ri] = n;
5005                 }
5006             }
5007         }else if(mode == "~"){
5008             for(var i = 0, n; n = ns[i]; i++){
5009                 while((n = n.nextSibling) && (n.nodeType != 1 || (tagName == '*' || n.tagName.toLowerCase()!=tagName)));
5010                 if(n){
5011                     result[++ri] = n;
5012                 }
5013             }
5014         }
5015         return result;
5016     };
5017
5018     function concat(a, b){
5019         if(b.slice){
5020             return a.concat(b);
5021         }
5022         for(var i = 0, l = b.length; i < l; i++){
5023             a[a.length] = b[i];
5024         }
5025         return a;
5026     }
5027
5028     function byTag(cs, tagName){
5029         if(cs.tagName || cs == document){
5030             cs = [cs];
5031         }
5032         if(!tagName){
5033             return cs;
5034         }
5035         var r = [], ri = -1;
5036         tagName = tagName.toLowerCase();
5037         for(var i = 0, ci; ci = cs[i]; i++){
5038             if(ci.nodeType == 1 && ci.tagName.toLowerCase()==tagName){
5039                 r[++ri] = ci;
5040             }
5041         }
5042         return r;
5043     };
5044
5045     function byId(cs, attr, id){
5046         if(cs.tagName || cs == document){
5047             cs = [cs];
5048         }
5049         if(!id){
5050             return cs;
5051         }
5052         var r = [], ri = -1;
5053         for(var i = 0,ci; ci = cs[i]; i++){
5054             if(ci && ci.id == id){
5055                 r[++ri] = ci;
5056                 return r;
5057             }
5058         }
5059         return r;
5060     };
5061
5062     function byAttribute(cs, attr, value, op, custom){
5063         var r = [], ri = -1, st = custom=="{";
5064         var f = Roo.DomQuery.operators[op];
5065         for(var i = 0, ci; ci = cs[i]; i++){
5066             var a;
5067             if(st){
5068                 a = Roo.DomQuery.getStyle(ci, attr);
5069             }
5070             else if(attr == "class" || attr == "className"){
5071                 a = ci.className;
5072             }else if(attr == "for"){
5073                 a = ci.htmlFor;
5074             }else if(attr == "href"){
5075                 a = ci.getAttribute("href", 2);
5076             }else{
5077                 a = ci.getAttribute(attr);
5078             }
5079             if((f && f(a, value)) || (!f && a)){
5080                 r[++ri] = ci;
5081             }
5082         }
5083         return r;
5084     };
5085
5086     function byPseudo(cs, name, value){
5087         return Roo.DomQuery.pseudos[name](cs, value);
5088     };
5089
5090     // This is for IE MSXML which does not support expandos.
5091     // IE runs the same speed using setAttribute, however FF slows way down
5092     // and Safari completely fails so they need to continue to use expandos.
5093     var isIE = window.ActiveXObject ? true : false;
5094
5095     // this eval is stop the compressor from
5096     // renaming the variable to something shorter
5097     
5098     /** eval:var:batch */
5099     var batch = 30803; 
5100
5101     var key = 30803;
5102
5103     function nodupIEXml(cs){
5104         var d = ++key;
5105         cs[0].setAttribute("_nodup", d);
5106         var r = [cs[0]];
5107         for(var i = 1, len = cs.length; i < len; i++){
5108             var c = cs[i];
5109             if(!c.getAttribute("_nodup") != d){
5110                 c.setAttribute("_nodup", d);
5111                 r[r.length] = c;
5112             }
5113         }
5114         for(var i = 0, len = cs.length; i < len; i++){
5115             cs[i].removeAttribute("_nodup");
5116         }
5117         return r;
5118     }
5119
5120     function nodup(cs){
5121         if(!cs){
5122             return [];
5123         }
5124         var len = cs.length, c, i, r = cs, cj, ri = -1;
5125         if(!len || typeof cs.nodeType != "undefined" || len == 1){
5126             return cs;
5127         }
5128         if(isIE && typeof cs[0].selectSingleNode != "undefined"){
5129             return nodupIEXml(cs);
5130         }
5131         var d = ++key;
5132         cs[0]._nodup = d;
5133         for(i = 1; c = cs[i]; i++){
5134             if(c._nodup != d){
5135                 c._nodup = d;
5136             }else{
5137                 r = [];
5138                 for(var j = 0; j < i; j++){
5139                     r[++ri] = cs[j];
5140                 }
5141                 for(j = i+1; cj = cs[j]; j++){
5142                     if(cj._nodup != d){
5143                         cj._nodup = d;
5144                         r[++ri] = cj;
5145                     }
5146                 }
5147                 return r;
5148             }
5149         }
5150         return r;
5151     }
5152
5153     function quickDiffIEXml(c1, c2){
5154         var d = ++key;
5155         for(var i = 0, len = c1.length; i < len; i++){
5156             c1[i].setAttribute("_qdiff", d);
5157         }
5158         var r = [];
5159         for(var i = 0, len = c2.length; i < len; i++){
5160             if(c2[i].getAttribute("_qdiff") != d){
5161                 r[r.length] = c2[i];
5162             }
5163         }
5164         for(var i = 0, len = c1.length; i < len; i++){
5165            c1[i].removeAttribute("_qdiff");
5166         }
5167         return r;
5168     }
5169
5170     function quickDiff(c1, c2){
5171         var len1 = c1.length;
5172         if(!len1){
5173             return c2;
5174         }
5175         if(isIE && c1[0].selectSingleNode){
5176             return quickDiffIEXml(c1, c2);
5177         }
5178         var d = ++key;
5179         for(var i = 0; i < len1; i++){
5180             c1[i]._qdiff = d;
5181         }
5182         var r = [];
5183         for(var i = 0, len = c2.length; i < len; i++){
5184             if(c2[i]._qdiff != d){
5185                 r[r.length] = c2[i];
5186             }
5187         }
5188         return r;
5189     }
5190
5191     function quickId(ns, mode, root, id){
5192         if(ns == root){
5193            var d = root.ownerDocument || root;
5194            return d.getElementById(id);
5195         }
5196         ns = getNodes(ns, mode, "*");
5197         return byId(ns, null, id);
5198     }
5199
5200     return {
5201         getStyle : function(el, name){
5202             return Roo.fly(el).getStyle(name);
5203         },
5204         /**
5205          * Compiles a selector/xpath query into a reusable function. The returned function
5206          * takes one parameter "root" (optional), which is the context node from where the query should start.
5207          * @param {String} selector The selector/xpath query
5208          * @param {String} type (optional) Either "select" (the default) or "simple" for a simple selector match
5209          * @return {Function}
5210          */
5211         compile : function(path, type){
5212             type = type || "select";
5213             
5214             var fn = ["var f = function(root){\n var mode; ++batch; var n = root || document;\n"];
5215             var q = path, mode, lq;
5216             var tk = Roo.DomQuery.matchers;
5217             var tklen = tk.length;
5218             var mm;
5219
5220             // accept leading mode switch
5221             var lmode = q.match(modeRe);
5222             if(lmode && lmode[1]){
5223                 fn[fn.length] = 'mode="'+lmode[1].replace(trimRe, "")+'";';
5224                 q = q.replace(lmode[1], "");
5225             }
5226             // strip leading slashes
5227             while(path.substr(0, 1)=="/"){
5228                 path = path.substr(1);
5229             }
5230
5231             while(q && lq != q){
5232                 lq = q;
5233                 var tm = q.match(tagTokenRe);
5234                 if(type == "select"){
5235                     if(tm){
5236                         if(tm[1] == "#"){
5237                             fn[fn.length] = 'n = quickId(n, mode, root, "'+tm[2]+'");';
5238                         }else{
5239                             fn[fn.length] = 'n = getNodes(n, mode, "'+tm[2]+'");';
5240                         }
5241                         q = q.replace(tm[0], "");
5242                     }else if(q.substr(0, 1) != '@'){
5243                         fn[fn.length] = 'n = getNodes(n, mode, "*");';
5244                     }
5245                 }else{
5246                     if(tm){
5247                         if(tm[1] == "#"){
5248                             fn[fn.length] = 'n = byId(n, null, "'+tm[2]+'");';
5249                         }else{
5250                             fn[fn.length] = 'n = byTag(n, "'+tm[2]+'");';
5251                         }
5252                         q = q.replace(tm[0], "");
5253                     }
5254                 }
5255                 while(!(mm = q.match(modeRe))){
5256                     var matched = false;
5257                     for(var j = 0; j < tklen; j++){
5258                         var t = tk[j];
5259                         var m = q.match(t.re);
5260                         if(m){
5261                             fn[fn.length] = t.select.replace(tplRe, function(x, i){
5262                                                     return m[i];
5263                                                 });
5264                             q = q.replace(m[0], "");
5265                             matched = true;
5266                             break;
5267                         }
5268                     }
5269                     // prevent infinite loop on bad selector
5270                     if(!matched){
5271                         throw 'Error parsing selector, parsing failed at "' + q + '"';
5272                     }
5273                 }
5274                 if(mm[1]){
5275                     fn[fn.length] = 'mode="'+mm[1].replace(trimRe, "")+'";';
5276                     q = q.replace(mm[1], "");
5277                 }
5278             }
5279             fn[fn.length] = "return nodup(n);\n}";
5280             
5281              /** 
5282               * list of variables that need from compression as they are used by eval.
5283              *  eval:var:batch 
5284              *  eval:var:nodup
5285              *  eval:var:byTag
5286              *  eval:var:ById
5287              *  eval:var:getNodes
5288              *  eval:var:quickId
5289              *  eval:var:mode
5290              *  eval:var:root
5291              *  eval:var:n
5292              *  eval:var:byClassName
5293              *  eval:var:byPseudo
5294              *  eval:var:byAttribute
5295              *  eval:var:attrValue
5296              * 
5297              **/ 
5298             eval(fn.join(""));
5299             return f;
5300         },
5301
5302         /**
5303          * Selects a group of elements.
5304          * @param {String} selector The selector/xpath query (can be a comma separated list of selectors)
5305          * @param {Node} root (optional) The start of the query (defaults to document).
5306          * @return {Array}
5307          */
5308         select : function(path, root, type){
5309             if(!root || root == document){
5310                 root = document;
5311             }
5312             if(typeof root == "string"){
5313                 root = document.getElementById(root);
5314             }
5315             var paths = path.split(",");
5316             var results = [];
5317             for(var i = 0, len = paths.length; i < len; i++){
5318                 var p = paths[i].replace(trimRe, "");
5319                 if(!cache[p]){
5320                     cache[p] = Roo.DomQuery.compile(p);
5321                     if(!cache[p]){
5322                         throw p + " is not a valid selector";
5323                     }
5324                 }
5325                 var result = cache[p](root);
5326                 if(result && result != document){
5327                     results = results.concat(result);
5328                 }
5329             }
5330             if(paths.length > 1){
5331                 return nodup(results);
5332             }
5333             return results;
5334         },
5335
5336         /**
5337          * Selects a single element.
5338          * @param {String} selector The selector/xpath query
5339          * @param {Node} root (optional) The start of the query (defaults to document).
5340          * @return {Element}
5341          */
5342         selectNode : function(path, root){
5343             return Roo.DomQuery.select(path, root)[0];
5344         },
5345
5346         /**
5347          * Selects the value of a node, optionally replacing null with the defaultValue.
5348          * @param {String} selector The selector/xpath query
5349          * @param {Node} root (optional) The start of the query (defaults to document).
5350          * @param {String} defaultValue
5351          */
5352         selectValue : function(path, root, defaultValue){
5353             path = path.replace(trimRe, "");
5354             if(!valueCache[path]){
5355                 valueCache[path] = Roo.DomQuery.compile(path, "select");
5356             }
5357             var n = valueCache[path](root);
5358             n = n[0] ? n[0] : n;
5359             var v = (n && n.firstChild ? n.firstChild.nodeValue : null);
5360             return ((v === null||v === undefined||v==='') ? defaultValue : v);
5361         },
5362
5363         /**
5364          * Selects the value of a node, parsing integers and floats.
5365          * @param {String} selector The selector/xpath query
5366          * @param {Node} root (optional) The start of the query (defaults to document).
5367          * @param {Number} defaultValue
5368          * @return {Number}
5369          */
5370         selectNumber : function(path, root, defaultValue){
5371             var v = Roo.DomQuery.selectValue(path, root, defaultValue || 0);
5372             return parseFloat(v);
5373         },
5374
5375         /**
5376          * Returns true if the passed element(s) match the passed simple selector (e.g. div.some-class or span:first-child)
5377          * @param {String/HTMLElement/Array} el An element id, element or array of elements
5378          * @param {String} selector The simple selector to test
5379          * @return {Boolean}
5380          */
5381         is : function(el, ss){
5382             if(typeof el == "string"){
5383                 el = document.getElementById(el);
5384             }
5385             var isArray = (el instanceof Array);
5386             var result = Roo.DomQuery.filter(isArray ? el : [el], ss);
5387             return isArray ? (result.length == el.length) : (result.length > 0);
5388         },
5389
5390         /**
5391          * Filters an array of elements to only include matches of a simple selector (e.g. div.some-class or span:first-child)
5392          * @param {Array} el An array of elements to filter
5393          * @param {String} selector The simple selector to test
5394          * @param {Boolean} nonMatches If true, it returns the elements that DON'T match
5395          * the selector instead of the ones that match
5396          * @return {Array}
5397          */
5398         filter : function(els, ss, nonMatches){
5399             ss = ss.replace(trimRe, "");
5400             if(!simpleCache[ss]){
5401                 simpleCache[ss] = Roo.DomQuery.compile(ss, "simple");
5402             }
5403             var result = simpleCache[ss](els);
5404             return nonMatches ? quickDiff(result, els) : result;
5405         },
5406
5407         /**
5408          * Collection of matching regular expressions and code snippets.
5409          */
5410         matchers : [{
5411                 re: /^\.([\w-]+)/,
5412                 select: 'n = byClassName(n, null, " {1} ");'
5413             }, {
5414                 re: /^\:([\w-]+)(?:\(((?:[^\s>\/]*|.*?))\))?/,
5415                 select: 'n = byPseudo(n, "{1}", "{2}");'
5416             },{
5417                 re: /^(?:([\[\{])(?:@)?([\w-]+)\s?(?:(=|.=)\s?['"]?(.*?)["']?)?[\]\}])/,
5418                 select: 'n = byAttribute(n, "{2}", "{4}", "{3}", "{1}");'
5419             }, {
5420                 re: /^#([\w-]+)/,
5421                 select: 'n = byId(n, null, "{1}");'
5422             },{
5423                 re: /^@([\w-]+)/,
5424                 select: 'return {firstChild:{nodeValue:attrValue(n, "{1}")}};'
5425             }
5426         ],
5427
5428         /**
5429          * Collection of operator comparison functions. The default operators are =, !=, ^=, $=, *=, %=, |= and ~=.
5430          * 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;.
5431          */
5432         operators : {
5433             "=" : function(a, v){
5434                 return a == v;
5435             },
5436             "!=" : function(a, v){
5437                 return a != v;
5438             },
5439             "^=" : function(a, v){
5440                 return a && a.substr(0, v.length) == v;
5441             },
5442             "$=" : function(a, v){
5443                 return a && a.substr(a.length-v.length) == v;
5444             },
5445             "*=" : function(a, v){
5446                 return a && a.indexOf(v) !== -1;
5447             },
5448             "%=" : function(a, v){
5449                 return (a % v) == 0;
5450             },
5451             "|=" : function(a, v){
5452                 return a && (a == v || a.substr(0, v.length+1) == v+'-');
5453             },
5454             "~=" : function(a, v){
5455                 return a && (' '+a+' ').indexOf(' '+v+' ') != -1;
5456             }
5457         },
5458
5459         /**
5460          * Collection of "pseudo class" processors. Each processor is passed the current nodeset (array)
5461          * and the argument (if any) supplied in the selector.
5462          */
5463         pseudos : {
5464             "first-child" : function(c){
5465                 var r = [], ri = -1, n;
5466                 for(var i = 0, ci; ci = n = c[i]; i++){
5467                     while((n = n.previousSibling) && n.nodeType != 1);
5468                     if(!n){
5469                         r[++ri] = ci;
5470                     }
5471                 }
5472                 return r;
5473             },
5474
5475             "last-child" : function(c){
5476                 var r = [], ri = -1, n;
5477                 for(var i = 0, ci; ci = n = c[i]; i++){
5478                     while((n = n.nextSibling) && n.nodeType != 1);
5479                     if(!n){
5480                         r[++ri] = ci;
5481                     }
5482                 }
5483                 return r;
5484             },
5485
5486             "nth-child" : function(c, a) {
5487                 var r = [], ri = -1;
5488                 var m = nthRe.exec(a == "even" && "2n" || a == "odd" && "2n+1" || !nthRe2.test(a) && "n+" + a || a);
5489                 var f = (m[1] || 1) - 0, l = m[2] - 0;
5490                 for(var i = 0, n; n = c[i]; i++){
5491                     var pn = n.parentNode;
5492                     if (batch != pn._batch) {
5493                         var j = 0;
5494                         for(var cn = pn.firstChild; cn; cn = cn.nextSibling){
5495                             if(cn.nodeType == 1){
5496                                cn.nodeIndex = ++j;
5497                             }
5498                         }
5499                         pn._batch = batch;
5500                     }
5501                     if (f == 1) {
5502                         if (l == 0 || n.nodeIndex == l){
5503                             r[++ri] = n;
5504                         }
5505                     } else if ((n.nodeIndex + l) % f == 0){
5506                         r[++ri] = n;
5507                     }
5508                 }
5509
5510                 return r;
5511             },
5512
5513             "only-child" : function(c){
5514                 var r = [], ri = -1;;
5515                 for(var i = 0, ci; ci = c[i]; i++){
5516                     if(!prev(ci) && !next(ci)){
5517                         r[++ri] = ci;
5518                     }
5519                 }
5520                 return r;
5521             },
5522
5523             "empty" : function(c){
5524                 var r = [], ri = -1;
5525                 for(var i = 0, ci; ci = c[i]; i++){
5526                     var cns = ci.childNodes, j = 0, cn, empty = true;
5527                     while(cn = cns[j]){
5528                         ++j;
5529                         if(cn.nodeType == 1 || cn.nodeType == 3){
5530                             empty = false;
5531                             break;
5532                         }
5533                     }
5534                     if(empty){
5535                         r[++ri] = ci;
5536                     }
5537                 }
5538                 return r;
5539             },
5540
5541             "contains" : function(c, v){
5542                 var r = [], ri = -1;
5543                 for(var i = 0, ci; ci = c[i]; i++){
5544                     if((ci.textContent||ci.innerText||'').indexOf(v) != -1){
5545                         r[++ri] = ci;
5546                     }
5547                 }
5548                 return r;
5549             },
5550
5551             "nodeValue" : function(c, v){
5552                 var r = [], ri = -1;
5553                 for(var i = 0, ci; ci = c[i]; i++){
5554                     if(ci.firstChild && ci.firstChild.nodeValue == v){
5555                         r[++ri] = ci;
5556                     }
5557                 }
5558                 return r;
5559             },
5560
5561             "checked" : function(c){
5562                 var r = [], ri = -1;
5563                 for(var i = 0, ci; ci = c[i]; i++){
5564                     if(ci.checked == true){
5565                         r[++ri] = ci;
5566                     }
5567                 }
5568                 return r;
5569             },
5570
5571             "not" : function(c, ss){
5572                 return Roo.DomQuery.filter(c, ss, true);
5573             },
5574
5575             "odd" : function(c){
5576                 return this["nth-child"](c, "odd");
5577             },
5578
5579             "even" : function(c){
5580                 return this["nth-child"](c, "even");
5581             },
5582
5583             "nth" : function(c, a){
5584                 return c[a-1] || [];
5585             },
5586
5587             "first" : function(c){
5588                 return c[0] || [];
5589             },
5590
5591             "last" : function(c){
5592                 return c[c.length-1] || [];
5593             },
5594
5595             "has" : function(c, ss){
5596                 var s = Roo.DomQuery.select;
5597                 var r = [], ri = -1;
5598                 for(var i = 0, ci; ci = c[i]; i++){
5599                     if(s(ss, ci).length > 0){
5600                         r[++ri] = ci;
5601                     }
5602                 }
5603                 return r;
5604             },
5605
5606             "next" : function(c, ss){
5607                 var is = Roo.DomQuery.is;
5608                 var r = [], ri = -1;
5609                 for(var i = 0, ci; ci = c[i]; i++){
5610                     var n = next(ci);
5611                     if(n && is(n, ss)){
5612                         r[++ri] = ci;
5613                     }
5614                 }
5615                 return r;
5616             },
5617
5618             "prev" : function(c, ss){
5619                 var is = Roo.DomQuery.is;
5620                 var r = [], ri = -1;
5621                 for(var i = 0, ci; ci = c[i]; i++){
5622                     var n = prev(ci);
5623                     if(n && is(n, ss)){
5624                         r[++ri] = ci;
5625                     }
5626                 }
5627                 return r;
5628             }
5629         }
5630     };
5631 }();
5632
5633 /**
5634  * Selects an array of DOM nodes by CSS/XPath selector. Shorthand of {@link Roo.DomQuery#select}
5635  * @param {String} path The selector/xpath query
5636  * @param {Node} root (optional) The start of the query (defaults to document).
5637  * @return {Array}
5638  * @member Roo
5639  * @method query
5640  */
5641 Roo.query = Roo.DomQuery.select;
5642 /*
5643  * Based on:
5644  * Ext JS Library 1.1.1
5645  * Copyright(c) 2006-2007, Ext JS, LLC.
5646  *
5647  * Originally Released Under LGPL - original licence link has changed is not relivant.
5648  *
5649  * Fork - LGPL
5650  * <script type="text/javascript">
5651  */
5652
5653 /**
5654  * @class Roo.util.Observable
5655  * Base class that provides a common interface for publishing events. Subclasses are expected to
5656  * to have a property "events" with all the events defined.<br>
5657  * For example:
5658  * <pre><code>
5659  Employee = function(name){
5660     this.name = name;
5661     this.addEvents({
5662         "fired" : true,
5663         "quit" : true
5664     });
5665  }
5666  Roo.extend(Employee, Roo.util.Observable);
5667 </code></pre>
5668  * @param {Object} config properties to use (incuding events / listeners)
5669  */
5670
5671 Roo.util.Observable = function(cfg){
5672     
5673     cfg = cfg|| {};
5674     this.addEvents(cfg.events || {});
5675     if (cfg.events) {
5676         delete cfg.events; // make sure
5677     }
5678      
5679     Roo.apply(this, cfg);
5680     
5681     if(this.listeners){
5682         this.on(this.listeners);
5683         delete this.listeners;
5684     }
5685 };
5686 Roo.util.Observable.prototype = {
5687     /** 
5688  * @cfg {Object} listeners  list of events and functions to call for this object, 
5689  * For example :
5690  * <pre><code>
5691     listeners :  { 
5692        'click' : function(e) {
5693            ..... 
5694         } ,
5695         .... 
5696     } 
5697   </code></pre>
5698  */
5699     
5700     
5701     /**
5702      * Fires the specified event with the passed parameters (minus the event name).
5703      * @param {String} eventName
5704      * @param {Object...} args Variable number of parameters are passed to handlers
5705      * @return {Boolean} returns false if any of the handlers return false otherwise it returns true
5706      */
5707     fireEvent : function(){
5708         var ce = this.events[arguments[0].toLowerCase()];
5709         if(typeof ce == "object"){
5710             return ce.fire.apply(ce, Array.prototype.slice.call(arguments, 1));
5711         }else{
5712             return true;
5713         }
5714     },
5715
5716     // private
5717     filterOptRe : /^(?:scope|delay|buffer|single)$/,
5718
5719     /**
5720      * Appends an event handler to this component
5721      * @param {String}   eventName The type of event to listen for
5722      * @param {Function} handler The method the event invokes
5723      * @param {Object}   scope (optional) The scope in which to execute the handler
5724      * function. The handler function's "this" context.
5725      * @param {Object}   options (optional) An object containing handler configuration
5726      * properties. This may contain any of the following properties:<ul>
5727      * <li>scope {Object} The scope in which to execute the handler function. The handler function's "this" context.</li>
5728      * <li>delay {Number} The number of milliseconds to delay the invocation of the handler after te event fires.</li>
5729      * <li>single {Boolean} True to add a handler to handle just the next firing of the event, and then remove itself.</li>
5730      * <li>buffer {Number} Causes the handler to be scheduled to run in an {@link Roo.util.DelayedTask} delayed
5731      * by the specified number of milliseconds. If the event fires again within that time, the original
5732      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>
5733      * </ul><br>
5734      * <p>
5735      * <b>Combining Options</b><br>
5736      * Using the options argument, it is possible to combine different types of listeners:<br>
5737      * <br>
5738      * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)
5739                 <pre><code>
5740                 el.on('click', this.onClick, this, {
5741                         single: true,
5742                 delay: 100,
5743                 forumId: 4
5744                 });
5745                 </code></pre>
5746      * <p>
5747      * <b>Attaching multiple handlers in 1 call</b><br>
5748      * The method also allows for a single argument to be passed which is a config object containing properties
5749      * which specify multiple handlers.
5750      * <pre><code>
5751                 el.on({
5752                         'click': {
5753                         fn: this.onClick,
5754                         scope: this,
5755                         delay: 100
5756                 }, 
5757                 'mouseover': {
5758                         fn: this.onMouseOver,
5759                         scope: this
5760                 },
5761                 'mouseout': {
5762                         fn: this.onMouseOut,
5763                         scope: this
5764                 }
5765                 });
5766                 </code></pre>
5767      * <p>
5768      * Or a shorthand syntax which passes the same scope object to all handlers:
5769         <pre><code>
5770                 el.on({
5771                         'click': this.onClick,
5772                 'mouseover': this.onMouseOver,
5773                 'mouseout': this.onMouseOut,
5774                 scope: this
5775                 });
5776                 </code></pre>
5777      */
5778     addListener : function(eventName, fn, scope, o){
5779         if(typeof eventName == "object"){
5780             o = eventName;
5781             for(var e in o){
5782                 if(this.filterOptRe.test(e)){
5783                     continue;
5784                 }
5785                 if(typeof o[e] == "function"){
5786                     // shared options
5787                     this.addListener(e, o[e], o.scope,  o);
5788                 }else{
5789                     // individual options
5790                     this.addListener(e, o[e].fn, o[e].scope, o[e]);
5791                 }
5792             }
5793             return;
5794         }
5795         o = (!o || typeof o == "boolean") ? {} : o;
5796         eventName = eventName.toLowerCase();
5797         var ce = this.events[eventName] || true;
5798         if(typeof ce == "boolean"){
5799             ce = new Roo.util.Event(this, eventName);
5800             this.events[eventName] = ce;
5801         }
5802         ce.addListener(fn, scope, o);
5803     },
5804
5805     /**
5806      * Removes a listener
5807      * @param {String}   eventName     The type of event to listen for
5808      * @param {Function} handler        The handler to remove
5809      * @param {Object}   scope  (optional) The scope (this object) for the handler
5810      */
5811     removeListener : function(eventName, fn, scope){
5812         var ce = this.events[eventName.toLowerCase()];
5813         if(typeof ce == "object"){
5814             ce.removeListener(fn, scope);
5815         }
5816     },
5817
5818     /**
5819      * Removes all listeners for this object
5820      */
5821     purgeListeners : function(){
5822         for(var evt in this.events){
5823             if(typeof this.events[evt] == "object"){
5824                  this.events[evt].clearListeners();
5825             }
5826         }
5827     },
5828
5829     relayEvents : function(o, events){
5830         var createHandler = function(ename){
5831             return function(){
5832                 return this.fireEvent.apply(this, Roo.combine(ename, Array.prototype.slice.call(arguments, 0)));
5833             };
5834         };
5835         for(var i = 0, len = events.length; i < len; i++){
5836             var ename = events[i];
5837             if(!this.events[ename]){ this.events[ename] = true; };
5838             o.on(ename, createHandler(ename), this);
5839         }
5840     },
5841
5842     /**
5843      * Used to define events on this Observable
5844      * @param {Object} object The object with the events defined
5845      */
5846     addEvents : function(o){
5847         if(!this.events){
5848             this.events = {};
5849         }
5850         Roo.applyIf(this.events, o);
5851     },
5852
5853     /**
5854      * Checks to see if this object has any listeners for a specified event
5855      * @param {String} eventName The name of the event to check for
5856      * @return {Boolean} True if the event is being listened for, else false
5857      */
5858     hasListener : function(eventName){
5859         var e = this.events[eventName];
5860         return typeof e == "object" && e.listeners.length > 0;
5861     }
5862 };
5863 /**
5864  * Appends an event handler to this element (shorthand for addListener)
5865  * @param {String}   eventName     The type of event to listen for
5866  * @param {Function} handler        The method the event invokes
5867  * @param {Object}   scope (optional) The scope in which to execute the handler
5868  * function. The handler function's "this" context.
5869  * @param {Object}   options  (optional)
5870  * @method
5871  */
5872 Roo.util.Observable.prototype.on = Roo.util.Observable.prototype.addListener;
5873 /**
5874  * Removes a listener (shorthand for removeListener)
5875  * @param {String}   eventName     The type of event to listen for
5876  * @param {Function} handler        The handler to remove
5877  * @param {Object}   scope  (optional) The scope (this object) for the handler
5878  * @method
5879  */
5880 Roo.util.Observable.prototype.un = Roo.util.Observable.prototype.removeListener;
5881
5882 /**
5883  * Starts capture on the specified Observable. All events will be passed
5884  * to the supplied function with the event name + standard signature of the event
5885  * <b>before</b> the event is fired. If the supplied function returns false,
5886  * the event will not fire.
5887  * @param {Observable} o The Observable to capture
5888  * @param {Function} fn The function to call
5889  * @param {Object} scope (optional) The scope (this object) for the fn
5890  * @static
5891  */
5892 Roo.util.Observable.capture = function(o, fn, scope){
5893     o.fireEvent = o.fireEvent.createInterceptor(fn, scope);
5894 };
5895
5896 /**
5897  * Removes <b>all</b> added captures from the Observable.
5898  * @param {Observable} o The Observable to release
5899  * @static
5900  */
5901 Roo.util.Observable.releaseCapture = function(o){
5902     o.fireEvent = Roo.util.Observable.prototype.fireEvent;
5903 };
5904
5905 (function(){
5906
5907     var createBuffered = function(h, o, scope){
5908         var task = new Roo.util.DelayedTask();
5909         return function(){
5910             task.delay(o.buffer, h, scope, Array.prototype.slice.call(arguments, 0));
5911         };
5912     };
5913
5914     var createSingle = function(h, e, fn, scope){
5915         return function(){
5916             e.removeListener(fn, scope);
5917             return h.apply(scope, arguments);
5918         };
5919     };
5920
5921     var createDelayed = function(h, o, scope){
5922         return function(){
5923             var args = Array.prototype.slice.call(arguments, 0);
5924             setTimeout(function(){
5925                 h.apply(scope, args);
5926             }, o.delay || 10);
5927         };
5928     };
5929
5930     Roo.util.Event = function(obj, name){
5931         this.name = name;
5932         this.obj = obj;
5933         this.listeners = [];
5934     };
5935
5936     Roo.util.Event.prototype = {
5937         addListener : function(fn, scope, options){
5938             var o = options || {};
5939             scope = scope || this.obj;
5940             if(!this.isListening(fn, scope)){
5941                 var l = {fn: fn, scope: scope, options: o};
5942                 var h = fn;
5943                 if(o.delay){
5944                     h = createDelayed(h, o, scope);
5945                 }
5946                 if(o.single){
5947                     h = createSingle(h, this, fn, scope);
5948                 }
5949                 if(o.buffer){
5950                     h = createBuffered(h, o, scope);
5951                 }
5952                 l.fireFn = h;
5953                 if(!this.firing){ // if we are currently firing this event, don't disturb the listener loop
5954                     this.listeners.push(l);
5955                 }else{
5956                     this.listeners = this.listeners.slice(0);
5957                     this.listeners.push(l);
5958                 }
5959             }
5960         },
5961
5962         findListener : function(fn, scope){
5963             scope = scope || this.obj;
5964             var ls = this.listeners;
5965             for(var i = 0, len = ls.length; i < len; i++){
5966                 var l = ls[i];
5967                 if(l.fn == fn && l.scope == scope){
5968                     return i;
5969                 }
5970             }
5971             return -1;
5972         },
5973
5974         isListening : function(fn, scope){
5975             return this.findListener(fn, scope) != -1;
5976         },
5977
5978         removeListener : function(fn, scope){
5979             var index;
5980             if((index = this.findListener(fn, scope)) != -1){
5981                 if(!this.firing){
5982                     this.listeners.splice(index, 1);
5983                 }else{
5984                     this.listeners = this.listeners.slice(0);
5985                     this.listeners.splice(index, 1);
5986                 }
5987                 return true;
5988             }
5989             return false;
5990         },
5991
5992         clearListeners : function(){
5993             this.listeners = [];
5994         },
5995
5996         fire : function(){
5997             var ls = this.listeners, scope, len = ls.length;
5998             if(len > 0){
5999                 this.firing = true;
6000                 var args = Array.prototype.slice.call(arguments, 0);
6001                 for(var i = 0; i < len; i++){
6002                     var l = ls[i];
6003                     if(l.fireFn.apply(l.scope||this.obj||window, arguments) === false){
6004                         this.firing = false;
6005                         return false;
6006                     }
6007                 }
6008                 this.firing = false;
6009             }
6010             return true;
6011         }
6012     };
6013 })();/*
6014  * Based on:
6015  * Ext JS Library 1.1.1
6016  * Copyright(c) 2006-2007, Ext JS, LLC.
6017  *
6018  * Originally Released Under LGPL - original licence link has changed is not relivant.
6019  *
6020  * Fork - LGPL
6021  * <script type="text/javascript">
6022  */
6023
6024 /**
6025  * @class Roo.EventManager
6026  * Registers event handlers that want to receive a normalized EventObject instead of the standard browser event and provides 
6027  * several useful events directly.
6028  * See {@link Roo.EventObject} for more details on normalized event objects.
6029  * @singleton
6030  */
6031 Roo.EventManager = function(){
6032     var docReadyEvent, docReadyProcId, docReadyState = false;
6033     var resizeEvent, resizeTask, textEvent, textSize;
6034     var E = Roo.lib.Event;
6035     var D = Roo.lib.Dom;
6036
6037
6038     var fireDocReady = function(){
6039         if(!docReadyState){
6040             docReadyState = true;
6041             Roo.isReady = true;
6042             if(docReadyProcId){
6043                 clearInterval(docReadyProcId);
6044             }
6045             if(Roo.isGecko || Roo.isOpera) {
6046                 document.removeEventListener("DOMContentLoaded", fireDocReady, false);
6047             }
6048             if(Roo.isIE){
6049                 var defer = document.getElementById("ie-deferred-loader");
6050                 if(defer){
6051                     defer.onreadystatechange = null;
6052                     defer.parentNode.removeChild(defer);
6053                 }
6054             }
6055             if(docReadyEvent){
6056                 docReadyEvent.fire();
6057                 docReadyEvent.clearListeners();
6058             }
6059         }
6060     };
6061     
6062     var initDocReady = function(){
6063         docReadyEvent = new Roo.util.Event();
6064         if(Roo.isGecko || Roo.isOpera) {
6065             document.addEventListener("DOMContentLoaded", fireDocReady, false);
6066         }else if(Roo.isIE){
6067             document.write("<s"+'cript id="ie-deferred-loader" defer="defer" src="/'+'/:"></s'+"cript>");
6068             var defer = document.getElementById("ie-deferred-loader");
6069             defer.onreadystatechange = function(){
6070                 if(this.readyState == "complete"){
6071                     fireDocReady();
6072                 }
6073             };
6074         }else if(Roo.isSafari){ 
6075             docReadyProcId = setInterval(function(){
6076                 var rs = document.readyState;
6077                 if(rs == "complete") {
6078                     fireDocReady();     
6079                  }
6080             }, 10);
6081         }
6082         // no matter what, make sure it fires on load
6083         E.on(window, "load", fireDocReady);
6084     };
6085
6086     var createBuffered = function(h, o){
6087         var task = new Roo.util.DelayedTask(h);
6088         return function(e){
6089             // create new event object impl so new events don't wipe out properties
6090             e = new Roo.EventObjectImpl(e);
6091             task.delay(o.buffer, h, null, [e]);
6092         };
6093     };
6094
6095     var createSingle = function(h, el, ename, fn){
6096         return function(e){
6097             Roo.EventManager.removeListener(el, ename, fn);
6098             h(e);
6099         };
6100     };
6101
6102     var createDelayed = function(h, o){
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             setTimeout(function(){
6107                 h(e);
6108             }, o.delay || 10);
6109         };
6110     };
6111
6112     var listen = function(element, ename, opt, fn, scope){
6113         var o = (!opt || typeof opt == "boolean") ? {} : opt;
6114         fn = fn || o.fn; scope = scope || o.scope;
6115         var el = Roo.getDom(element);
6116         if(!el){
6117             throw "Error listening for \"" + ename + '\". Element "' + element + '" doesn\'t exist.';
6118         }
6119         var h = function(e){
6120             e = Roo.EventObject.setEvent(e);
6121             var t;
6122             if(o.delegate){
6123                 t = e.getTarget(o.delegate, el);
6124                 if(!t){
6125                     return;
6126                 }
6127             }else{
6128                 t = e.target;
6129             }
6130             if(o.stopEvent === true){
6131                 e.stopEvent();
6132             }
6133             if(o.preventDefault === true){
6134                e.preventDefault();
6135             }
6136             if(o.stopPropagation === true){
6137                 e.stopPropagation();
6138             }
6139
6140             if(o.normalized === false){
6141                 e = e.browserEvent;
6142             }
6143
6144             fn.call(scope || el, e, t, o);
6145         };
6146         if(o.delay){
6147             h = createDelayed(h, o);
6148         }
6149         if(o.single){
6150             h = createSingle(h, el, ename, fn);
6151         }
6152         if(o.buffer){
6153             h = createBuffered(h, o);
6154         }
6155         fn._handlers = fn._handlers || [];
6156         fn._handlers.push([Roo.id(el), ename, h]);
6157
6158         E.on(el, ename, h);
6159         if(ename == "mousewheel" && el.addEventListener){ // workaround for jQuery
6160             el.addEventListener("DOMMouseScroll", h, false);
6161             E.on(window, 'unload', function(){
6162                 el.removeEventListener("DOMMouseScroll", h, false);
6163             });
6164         }
6165         if(ename == "mousedown" && el == document){ // fix stopped mousedowns on the document
6166             Roo.EventManager.stoppedMouseDownEvent.addListener(h);
6167         }
6168         return h;
6169     };
6170
6171     var stopListening = function(el, ename, fn){
6172         var id = Roo.id(el), hds = fn._handlers, hd = fn;
6173         if(hds){
6174             for(var i = 0, len = hds.length; i < len; i++){
6175                 var h = hds[i];
6176                 if(h[0] == id && h[1] == ename){
6177                     hd = h[2];
6178                     hds.splice(i, 1);
6179                     break;
6180                 }
6181             }
6182         }
6183         E.un(el, ename, hd);
6184         el = Roo.getDom(el);
6185         if(ename == "mousewheel" && el.addEventListener){
6186             el.removeEventListener("DOMMouseScroll", hd, false);
6187         }
6188         if(ename == "mousedown" && el == document){ // fix stopped mousedowns on the document
6189             Roo.EventManager.stoppedMouseDownEvent.removeListener(hd);
6190         }
6191     };
6192
6193     var propRe = /^(?:scope|delay|buffer|single|stopEvent|preventDefault|stopPropagation|normalized|args|delegate)$/;
6194     
6195     var pub = {
6196         
6197         
6198         /** 
6199          * Fix for doc tools
6200          * @scope Roo.EventManager
6201          */
6202         
6203         
6204         /** 
6205          * This is no longer needed and is deprecated. Places a simple wrapper around an event handler to override the browser event
6206          * object with a Roo.EventObject
6207          * @param {Function} fn        The method the event invokes
6208          * @param {Object}   scope    An object that becomes the scope of the handler
6209          * @param {boolean}  override If true, the obj passed in becomes
6210          *                             the execution scope of the listener
6211          * @return {Function} The wrapped function
6212          * @deprecated
6213          */
6214         wrap : function(fn, scope, override){
6215             return function(e){
6216                 Roo.EventObject.setEvent(e);
6217                 fn.call(override ? scope || window : window, Roo.EventObject, scope);
6218             };
6219         },
6220         
6221         /**
6222      * Appends an event handler to an element (shorthand for addListener)
6223      * @param {String/HTMLElement}   element        The html element or id to assign the
6224      * @param {String}   eventName The type of event to listen for
6225      * @param {Function} handler The method the event invokes
6226      * @param {Object}   scope (optional) The scope in which to execute the handler
6227      * function. The handler function's "this" context.
6228      * @param {Object}   options (optional) An object containing handler configuration
6229      * properties. This may contain any of the following properties:<ul>
6230      * <li>scope {Object} The scope in which to execute the handler function. The handler function's "this" context.</li>
6231      * <li>delegate {String} A simple selector to filter the target or look for a descendant of the target</li>
6232      * <li>stopEvent {Boolean} True to stop the event. That is stop propagation, and prevent the default action.</li>
6233      * <li>preventDefault {Boolean} True to prevent the default action</li>
6234      * <li>stopPropagation {Boolean} True to prevent event propagation</li>
6235      * <li>normalized {Boolean} False to pass a browser event to the handler function instead of an Roo.EventObject</li>
6236      * <li>delay {Number} The number of milliseconds to delay the invocation of the handler after te event fires.</li>
6237      * <li>single {Boolean} True to add a handler to handle just the next firing of the event, and then remove itself.</li>
6238      * <li>buffer {Number} Causes the handler to be scheduled to run in an {@link Roo.util.DelayedTask} delayed
6239      * by the specified number of milliseconds. If the event fires again within that time, the original
6240      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>
6241      * </ul><br>
6242      * <p>
6243      * <b>Combining Options</b><br>
6244      * Using the options argument, it is possible to combine different types of listeners:<br>
6245      * <br>
6246      * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)<div style="margin: 5px 20px 20px;">
6247      * Code:<pre><code>
6248 el.on('click', this.onClick, this, {
6249     single: true,
6250     delay: 100,
6251     stopEvent : true,
6252     forumId: 4
6253 });</code></pre>
6254      * <p>
6255      * <b>Attaching multiple handlers in 1 call</b><br>
6256       * The method also allows for a single argument to be passed which is a config object containing properties
6257      * which specify multiple handlers.
6258      * <p>
6259      * Code:<pre><code>
6260 el.on({
6261     'click' : {
6262         fn: this.onClick
6263         scope: this,
6264         delay: 100
6265     },
6266     'mouseover' : {
6267         fn: this.onMouseOver
6268         scope: this
6269     },
6270     'mouseout' : {
6271         fn: this.onMouseOut
6272         scope: this
6273     }
6274 });</code></pre>
6275      * <p>
6276      * Or a shorthand syntax:<br>
6277      * Code:<pre><code>
6278 el.on({
6279     'click' : this.onClick,
6280     'mouseover' : this.onMouseOver,
6281     'mouseout' : this.onMouseOut
6282     scope: this
6283 });</code></pre>
6284      */
6285         addListener : function(element, eventName, fn, scope, options){
6286             if(typeof eventName == "object"){
6287                 var o = eventName;
6288                 for(var e in o){
6289                     if(propRe.test(e)){
6290                         continue;
6291                     }
6292                     if(typeof o[e] == "function"){
6293                         // shared options
6294                         listen(element, e, o, o[e], o.scope);
6295                     }else{
6296                         // individual options
6297                         listen(element, e, o[e]);
6298                     }
6299                 }
6300                 return;
6301             }
6302             return listen(element, eventName, options, fn, scope);
6303         },
6304         
6305         /**
6306          * Removes an event handler
6307          *
6308          * @param {String/HTMLElement}   element        The id or html element to remove the 
6309          *                             event from
6310          * @param {String}   eventName     The type of event
6311          * @param {Function} fn
6312          * @return {Boolean} True if a listener was actually removed
6313          */
6314         removeListener : function(element, eventName, fn){
6315             return stopListening(element, eventName, fn);
6316         },
6317         
6318         /**
6319          * Fires when the document is ready (before onload and before images are loaded). Can be 
6320          * accessed shorthanded Roo.onReady().
6321          * @param {Function} fn        The method the event invokes
6322          * @param {Object}   scope    An  object that becomes the scope of the handler
6323          * @param {boolean}  options
6324          */
6325         onDocumentReady : function(fn, scope, options){
6326             if(docReadyState){ // if it already fired
6327                 docReadyEvent.addListener(fn, scope, options);
6328                 docReadyEvent.fire();
6329                 docReadyEvent.clearListeners();
6330                 return;
6331             }
6332             if(!docReadyEvent){
6333                 initDocReady();
6334             }
6335             docReadyEvent.addListener(fn, scope, options);
6336         },
6337         
6338         /**
6339          * Fires when the window is resized and provides resize event buffering (50 milliseconds), passes new viewport width and height to handlers.
6340          * @param {Function} fn        The method the event invokes
6341          * @param {Object}   scope    An object that becomes the scope of the handler
6342          * @param {boolean}  options
6343          */
6344         onWindowResize : function(fn, scope, options){
6345             if(!resizeEvent){
6346                 resizeEvent = new Roo.util.Event();
6347                 resizeTask = new Roo.util.DelayedTask(function(){
6348                     resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
6349                 });
6350                 E.on(window, "resize", function(){
6351                     if(Roo.isIE){
6352                         resizeTask.delay(50);
6353                     }else{
6354                         resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
6355                     }
6356                 });
6357             }
6358             resizeEvent.addListener(fn, scope, options);
6359         },
6360
6361         /**
6362          * Fires when the user changes the active text size. Handler gets called with 2 params, the old size and the new size.
6363          * @param {Function} fn        The method the event invokes
6364          * @param {Object}   scope    An object that becomes the scope of the handler
6365          * @param {boolean}  options
6366          */
6367         onTextResize : function(fn, scope, options){
6368             if(!textEvent){
6369                 textEvent = new Roo.util.Event();
6370                 var textEl = new Roo.Element(document.createElement('div'));
6371                 textEl.dom.className = 'x-text-resize';
6372                 textEl.dom.innerHTML = 'X';
6373                 textEl.appendTo(document.body);
6374                 textSize = textEl.dom.offsetHeight;
6375                 setInterval(function(){
6376                     if(textEl.dom.offsetHeight != textSize){
6377                         textEvent.fire(textSize, textSize = textEl.dom.offsetHeight);
6378                     }
6379                 }, this.textResizeInterval);
6380             }
6381             textEvent.addListener(fn, scope, options);
6382         },
6383
6384         /**
6385          * Removes the passed window resize listener.
6386          * @param {Function} fn        The method the event invokes
6387          * @param {Object}   scope    The scope of handler
6388          */
6389         removeResizeListener : function(fn, scope){
6390             if(resizeEvent){
6391                 resizeEvent.removeListener(fn, scope);
6392             }
6393         },
6394
6395         // private
6396         fireResize : function(){
6397             if(resizeEvent){
6398                 resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
6399             }   
6400         },
6401         /**
6402          * Url used for onDocumentReady with using SSL (defaults to Roo.SSL_SECURE_URL)
6403          */
6404         ieDeferSrc : false,
6405         /**
6406          * The frequency, in milliseconds, to check for text resize events (defaults to 50)
6407          */
6408         textResizeInterval : 50
6409     };
6410     
6411     /**
6412      * Fix for doc tools
6413      * @scopeAlias pub=Roo.EventManager
6414      */
6415     
6416      /**
6417      * Appends an event handler to an element (shorthand for addListener)
6418      * @param {String/HTMLElement}   element        The html element or id to assign the
6419      * @param {String}   eventName The type of event to listen for
6420      * @param {Function} handler The method the event invokes
6421      * @param {Object}   scope (optional) The scope in which to execute the handler
6422      * function. The handler function's "this" context.
6423      * @param {Object}   options (optional) An object containing handler configuration
6424      * properties. This may contain any of the following properties:<ul>
6425      * <li>scope {Object} The scope in which to execute the handler function. The handler function's "this" context.</li>
6426      * <li>delegate {String} A simple selector to filter the target or look for a descendant of the target</li>
6427      * <li>stopEvent {Boolean} True to stop the event. That is stop propagation, and prevent the default action.</li>
6428      * <li>preventDefault {Boolean} True to prevent the default action</li>
6429      * <li>stopPropagation {Boolean} True to prevent event propagation</li>
6430      * <li>normalized {Boolean} False to pass a browser event to the handler function instead of an Roo.EventObject</li>
6431      * <li>delay {Number} The number of milliseconds to delay the invocation of the handler after te event fires.</li>
6432      * <li>single {Boolean} True to add a handler to handle just the next firing of the event, and then remove itself.</li>
6433      * <li>buffer {Number} Causes the handler to be scheduled to run in an {@link Roo.util.DelayedTask} delayed
6434      * by the specified number of milliseconds. If the event fires again within that time, the original
6435      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>
6436      * </ul><br>
6437      * <p>
6438      * <b>Combining Options</b><br>
6439      * Using the options argument, it is possible to combine different types of listeners:<br>
6440      * <br>
6441      * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)<div style="margin: 5px 20px 20px;">
6442      * Code:<pre><code>
6443 el.on('click', this.onClick, this, {
6444     single: true,
6445     delay: 100,
6446     stopEvent : true,
6447     forumId: 4
6448 });</code></pre>
6449      * <p>
6450      * <b>Attaching multiple handlers in 1 call</b><br>
6451       * The method also allows for a single argument to be passed which is a config object containing properties
6452      * which specify multiple handlers.
6453      * <p>
6454      * Code:<pre><code>
6455 el.on({
6456     'click' : {
6457         fn: this.onClick
6458         scope: this,
6459         delay: 100
6460     },
6461     'mouseover' : {
6462         fn: this.onMouseOver
6463         scope: this
6464     },
6465     'mouseout' : {
6466         fn: this.onMouseOut
6467         scope: this
6468     }
6469 });</code></pre>
6470      * <p>
6471      * Or a shorthand syntax:<br>
6472      * Code:<pre><code>
6473 el.on({
6474     'click' : this.onClick,
6475     'mouseover' : this.onMouseOver,
6476     'mouseout' : this.onMouseOut
6477     scope: this
6478 });</code></pre>
6479      */
6480     pub.on = pub.addListener;
6481     pub.un = pub.removeListener;
6482
6483     pub.stoppedMouseDownEvent = new Roo.util.Event();
6484     return pub;
6485 }();
6486 /**
6487   * Fires when the document is ready (before onload and before images are loaded).  Shorthand of {@link Roo.EventManager#onDocumentReady}.
6488   * @param {Function} fn        The method the event invokes
6489   * @param {Object}   scope    An  object that becomes the scope of the handler
6490   * @param {boolean}  override If true, the obj passed in becomes
6491   *                             the execution scope of the listener
6492   * @member Roo
6493   * @method onReady
6494  */
6495 Roo.onReady = Roo.EventManager.onDocumentReady;
6496
6497 Roo.onReady(function(){
6498     var bd = Roo.get(document.body);
6499     if(!bd){ return; }
6500
6501     var cls = [
6502             Roo.isIE ? "roo-ie"
6503             : Roo.isGecko ? "roo-gecko"
6504             : Roo.isOpera ? "roo-opera"
6505             : Roo.isSafari ? "roo-safari" : ""];
6506
6507     if(Roo.isMac){
6508         cls.push("roo-mac");
6509     }
6510     if(Roo.isLinux){
6511         cls.push("roo-linux");
6512     }
6513     if(Roo.isBorderBox){
6514         cls.push('roo-border-box');
6515     }
6516     if(Roo.isStrict){ // add to the parent to allow for selectors like ".ext-strict .ext-ie"
6517         var p = bd.dom.parentNode;
6518         if(p){
6519             p.className += ' roo-strict';
6520         }
6521     }
6522     bd.addClass(cls.join(' '));
6523 });
6524
6525 /**
6526  * @class Roo.EventObject
6527  * EventObject exposes the Yahoo! UI Event functionality directly on the object
6528  * passed to your event handler. It exists mostly for convenience. It also fixes the annoying null checks automatically to cleanup your code 
6529  * Example:
6530  * <pre><code>
6531  function handleClick(e){ // e is not a standard event object, it is a Roo.EventObject
6532     e.preventDefault();
6533     var target = e.getTarget();
6534     ...
6535  }
6536  var myDiv = Roo.get("myDiv");
6537  myDiv.on("click", handleClick);
6538  //or
6539  Roo.EventManager.on("myDiv", 'click', handleClick);
6540  Roo.EventManager.addListener("myDiv", 'click', handleClick);
6541  </code></pre>
6542  * @singleton
6543  */
6544 Roo.EventObject = function(){
6545     
6546     var E = Roo.lib.Event;
6547     
6548     // safari keypress events for special keys return bad keycodes
6549     var safariKeys = {
6550         63234 : 37, // left
6551         63235 : 39, // right
6552         63232 : 38, // up
6553         63233 : 40, // down
6554         63276 : 33, // page up
6555         63277 : 34, // page down
6556         63272 : 46, // delete
6557         63273 : 36, // home
6558         63275 : 35  // end
6559     };
6560
6561     // normalize button clicks
6562     var btnMap = Roo.isIE ? {1:0,4:1,2:2} :
6563                 (Roo.isSafari ? {1:0,2:1,3:2} : {0:0,1:1,2:2});
6564
6565     Roo.EventObjectImpl = function(e){
6566         if(e){
6567             this.setEvent(e.browserEvent || e);
6568         }
6569     };
6570     Roo.EventObjectImpl.prototype = {
6571         /**
6572          * Used to fix doc tools.
6573          * @scope Roo.EventObject.prototype
6574          */
6575             
6576
6577         
6578         
6579         /** The normal browser event */
6580         browserEvent : null,
6581         /** The button pressed in a mouse event */
6582         button : -1,
6583         /** True if the shift key was down during the event */
6584         shiftKey : false,
6585         /** True if the control key was down during the event */
6586         ctrlKey : false,
6587         /** True if the alt key was down during the event */
6588         altKey : false,
6589
6590         /** Key constant 
6591         * @type Number */
6592         BACKSPACE : 8,
6593         /** Key constant 
6594         * @type Number */
6595         TAB : 9,
6596         /** Key constant 
6597         * @type Number */
6598         RETURN : 13,
6599         /** Key constant 
6600         * @type Number */
6601         ENTER : 13,
6602         /** Key constant 
6603         * @type Number */
6604         SHIFT : 16,
6605         /** Key constant 
6606         * @type Number */
6607         CONTROL : 17,
6608         /** Key constant 
6609         * @type Number */
6610         ESC : 27,
6611         /** Key constant 
6612         * @type Number */
6613         SPACE : 32,
6614         /** Key constant 
6615         * @type Number */
6616         PAGEUP : 33,
6617         /** Key constant 
6618         * @type Number */
6619         PAGEDOWN : 34,
6620         /** Key constant 
6621         * @type Number */
6622         END : 35,
6623         /** Key constant 
6624         * @type Number */
6625         HOME : 36,
6626         /** Key constant 
6627         * @type Number */
6628         LEFT : 37,
6629         /** Key constant 
6630         * @type Number */
6631         UP : 38,
6632         /** Key constant 
6633         * @type Number */
6634         RIGHT : 39,
6635         /** Key constant 
6636         * @type Number */
6637         DOWN : 40,
6638         /** Key constant 
6639         * @type Number */
6640         DELETE : 46,
6641         /** Key constant 
6642         * @type Number */
6643         F5 : 116,
6644
6645            /** @private */
6646         setEvent : function(e){
6647             if(e == this || (e && e.browserEvent)){ // already wrapped
6648                 return e;
6649             }
6650             this.browserEvent = e;
6651             if(e){
6652                 // normalize buttons
6653                 this.button = e.button ? btnMap[e.button] : (e.which ? e.which-1 : -1);
6654                 if(e.type == 'click' && this.button == -1){
6655                     this.button = 0;
6656                 }
6657                 this.type = e.type;
6658                 this.shiftKey = e.shiftKey;
6659                 // mac metaKey behaves like ctrlKey
6660                 this.ctrlKey = e.ctrlKey || e.metaKey;
6661                 this.altKey = e.altKey;
6662                 // in getKey these will be normalized for the mac
6663                 this.keyCode = e.keyCode;
6664                 // keyup warnings on firefox.
6665                 this.charCode = (e.type == 'keyup' || e.type == 'keydown') ? 0 : e.charCode;
6666                 // cache the target for the delayed and or buffered events
6667                 this.target = E.getTarget(e);
6668                 // same for XY
6669                 this.xy = E.getXY(e);
6670             }else{
6671                 this.button = -1;
6672                 this.shiftKey = false;
6673                 this.ctrlKey = false;
6674                 this.altKey = false;
6675                 this.keyCode = 0;
6676                 this.charCode =0;
6677                 this.target = null;
6678                 this.xy = [0, 0];
6679             }
6680             return this;
6681         },
6682
6683         /**
6684          * Stop the event (preventDefault and stopPropagation)
6685          */
6686         stopEvent : function(){
6687             if(this.browserEvent){
6688                 if(this.browserEvent.type == 'mousedown'){
6689                     Roo.EventManager.stoppedMouseDownEvent.fire(this);
6690                 }
6691                 E.stopEvent(this.browserEvent);
6692             }
6693         },
6694
6695         /**
6696          * Prevents the browsers default handling of the event.
6697          */
6698         preventDefault : function(){
6699             if(this.browserEvent){
6700                 E.preventDefault(this.browserEvent);
6701             }
6702         },
6703
6704         /** @private */
6705         isNavKeyPress : function(){
6706             var k = this.keyCode;
6707             k = Roo.isSafari ? (safariKeys[k] || k) : k;
6708             return (k >= 33 && k <= 40) || k == this.RETURN || k == this.TAB || k == this.ESC;
6709         },
6710
6711         isSpecialKey : function(){
6712             var k = this.keyCode;
6713             return (this.type == 'keypress' && this.ctrlKey) || k == 9 || k == 13  || k == 40 || k == 27 ||
6714             (k == 16) || (k == 17) ||
6715             (k >= 18 && k <= 20) ||
6716             (k >= 33 && k <= 35) ||
6717             (k >= 36 && k <= 39) ||
6718             (k >= 44 && k <= 45);
6719         },
6720         /**
6721          * Cancels bubbling of the event.
6722          */
6723         stopPropagation : function(){
6724             if(this.browserEvent){
6725                 if(this.type == 'mousedown'){
6726                     Roo.EventManager.stoppedMouseDownEvent.fire(this);
6727                 }
6728                 E.stopPropagation(this.browserEvent);
6729             }
6730         },
6731
6732         /**
6733          * Gets the key code for the event.
6734          * @return {Number}
6735          */
6736         getCharCode : function(){
6737             return this.charCode || this.keyCode;
6738         },
6739
6740         /**
6741          * Returns a normalized keyCode for the event.
6742          * @return {Number} The key code
6743          */
6744         getKey : function(){
6745             var k = this.keyCode || this.charCode;
6746             return Roo.isSafari ? (safariKeys[k] || k) : k;
6747         },
6748
6749         /**
6750          * Gets the x coordinate of the event.
6751          * @return {Number}
6752          */
6753         getPageX : function(){
6754             return this.xy[0];
6755         },
6756
6757         /**
6758          * Gets the y coordinate of the event.
6759          * @return {Number}
6760          */
6761         getPageY : function(){
6762             return this.xy[1];
6763         },
6764
6765         /**
6766          * Gets the time of the event.
6767          * @return {Number}
6768          */
6769         getTime : function(){
6770             if(this.browserEvent){
6771                 return E.getTime(this.browserEvent);
6772             }
6773             return null;
6774         },
6775
6776         /**
6777          * Gets the page coordinates of the event.
6778          * @return {Array} The xy values like [x, y]
6779          */
6780         getXY : function(){
6781             return this.xy;
6782         },
6783
6784         /**
6785          * Gets the target for the event.
6786          * @param {String} selector (optional) A simple selector to filter the target or look for an ancestor of the target
6787          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
6788                 search as a number or element (defaults to 10 || document.body)
6789          * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
6790          * @return {HTMLelement}
6791          */
6792         getTarget : function(selector, maxDepth, returnEl){
6793             return selector ? Roo.fly(this.target).findParent(selector, maxDepth, returnEl) : this.target;
6794         },
6795         /**
6796          * Gets the related target.
6797          * @return {HTMLElement}
6798          */
6799         getRelatedTarget : function(){
6800             if(this.browserEvent){
6801                 return E.getRelatedTarget(this.browserEvent);
6802             }
6803             return null;
6804         },
6805
6806         /**
6807          * Normalizes mouse wheel delta across browsers
6808          * @return {Number} The delta
6809          */
6810         getWheelDelta : function(){
6811             var e = this.browserEvent;
6812             var delta = 0;
6813             if(e.wheelDelta){ /* IE/Opera. */
6814                 delta = e.wheelDelta/120;
6815             }else if(e.detail){ /* Mozilla case. */
6816                 delta = -e.detail/3;
6817             }
6818             return delta;
6819         },
6820
6821         /**
6822          * Returns true if the control, meta, shift or alt key was pressed during this event.
6823          * @return {Boolean}
6824          */
6825         hasModifier : function(){
6826             return !!((this.ctrlKey || this.altKey) || this.shiftKey);
6827         },
6828
6829         /**
6830          * Returns true if the target of this event equals el or is a child of el
6831          * @param {String/HTMLElement/Element} el
6832          * @param {Boolean} related (optional) true to test if the related target is within el instead of the target
6833          * @return {Boolean}
6834          */
6835         within : function(el, related){
6836             var t = this[related ? "getRelatedTarget" : "getTarget"]();
6837             return t && Roo.fly(el).contains(t);
6838         },
6839
6840         getPoint : function(){
6841             return new Roo.lib.Point(this.xy[0], this.xy[1]);
6842         }
6843     };
6844
6845     return new Roo.EventObjectImpl();
6846 }();
6847             
6848     /*
6849  * Based on:
6850  * Ext JS Library 1.1.1
6851  * Copyright(c) 2006-2007, Ext JS, LLC.
6852  *
6853  * Originally Released Under LGPL - original licence link has changed is not relivant.
6854  *
6855  * Fork - LGPL
6856  * <script type="text/javascript">
6857  */
6858
6859  
6860 // was in Composite Element!??!?!
6861  
6862 (function(){
6863     var D = Roo.lib.Dom;
6864     var E = Roo.lib.Event;
6865     var A = Roo.lib.Anim;
6866
6867     // local style camelizing for speed
6868     var propCache = {};
6869     var camelRe = /(-[a-z])/gi;
6870     var camelFn = function(m, a){ return a.charAt(1).toUpperCase(); };
6871     var view = document.defaultView;
6872
6873 /**
6874  * @class Roo.Element
6875  * Represents an Element in the DOM.<br><br>
6876  * Usage:<br>
6877 <pre><code>
6878 var el = Roo.get("my-div");
6879
6880 // or with getEl
6881 var el = getEl("my-div");
6882
6883 // or with a DOM element
6884 var el = Roo.get(myDivElement);
6885 </code></pre>
6886  * Using Roo.get() or getEl() instead of calling the constructor directly ensures you get the same object
6887  * each call instead of constructing a new one.<br><br>
6888  * <b>Animations</b><br />
6889  * Many of the functions for manipulating an element have an optional "animate" parameter. The animate parameter
6890  * should either be a boolean (true) or an object literal with animation options. The animation options are:
6891 <pre>
6892 Option    Default   Description
6893 --------- --------  ---------------------------------------------
6894 duration  .35       The duration of the animation in seconds
6895 easing    easeOut   The YUI easing method
6896 callback  none      A function to execute when the anim completes
6897 scope     this      The scope (this) of the callback function
6898 </pre>
6899 * Also, the Anim object being used for the animation will be set on your options object as "anim", which allows you to stop or
6900 * manipulate the animation. Here's an example:
6901 <pre><code>
6902 var el = Roo.get("my-div");
6903
6904 // no animation
6905 el.setWidth(100);
6906
6907 // default animation
6908 el.setWidth(100, true);
6909
6910 // animation with some options set
6911 el.setWidth(100, {
6912     duration: 1,
6913     callback: this.foo,
6914     scope: this
6915 });
6916
6917 // using the "anim" property to get the Anim object
6918 var opt = {
6919     duration: 1,
6920     callback: this.foo,
6921     scope: this
6922 };
6923 el.setWidth(100, opt);
6924 ...
6925 if(opt.anim.isAnimated()){
6926     opt.anim.stop();
6927 }
6928 </code></pre>
6929 * <b> Composite (Collections of) Elements</b><br />
6930  * For working with collections of Elements, see <a href="Roo.CompositeElement.html">Roo.CompositeElement</a>
6931  * @constructor Create a new Element directly.
6932  * @param {String/HTMLElement} element
6933  * @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).
6934  */
6935     Roo.Element = function(element, forceNew){
6936         var dom = typeof element == "string" ?
6937                 document.getElementById(element) : element;
6938         if(!dom){ // invalid id/element
6939             return null;
6940         }
6941         var id = dom.id;
6942         if(forceNew !== true && id && Roo.Element.cache[id]){ // element object already exists
6943             return Roo.Element.cache[id];
6944         }
6945
6946         /**
6947          * The DOM element
6948          * @type HTMLElement
6949          */
6950         this.dom = dom;
6951
6952         /**
6953          * The DOM element ID
6954          * @type String
6955          */
6956         this.id = id || Roo.id(dom);
6957     };
6958
6959     var El = Roo.Element;
6960
6961     El.prototype = {
6962         /**
6963          * The element's default display mode  (defaults to "")
6964          * @type String
6965          */
6966         originalDisplay : "",
6967
6968         visibilityMode : 1,
6969         /**
6970          * The default unit to append to CSS values where a unit isn't provided (defaults to px).
6971          * @type String
6972          */
6973         defaultUnit : "px",
6974         /**
6975          * Sets the element's visibility mode. When setVisible() is called it
6976          * will use this to determine whether to set the visibility or the display property.
6977          * @param visMode Element.VISIBILITY or Element.DISPLAY
6978          * @return {Roo.Element} this
6979          */
6980         setVisibilityMode : function(visMode){
6981             this.visibilityMode = visMode;
6982             return this;
6983         },
6984         /**
6985          * Convenience method for setVisibilityMode(Element.DISPLAY)
6986          * @param {String} display (optional) What to set display to when visible
6987          * @return {Roo.Element} this
6988          */
6989         enableDisplayMode : function(display){
6990             this.setVisibilityMode(El.DISPLAY);
6991             if(typeof display != "undefined") this.originalDisplay = display;
6992             return this;
6993         },
6994
6995         /**
6996          * 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)
6997          * @param {String} selector The simple selector to test
6998          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
6999                 search as a number or element (defaults to 10 || document.body)
7000          * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
7001          * @return {HTMLElement} The matching DOM node (or null if no match was found)
7002          */
7003         findParent : function(simpleSelector, maxDepth, returnEl){
7004             var p = this.dom, b = document.body, depth = 0, dq = Roo.DomQuery, stopEl;
7005             maxDepth = maxDepth || 50;
7006             if(typeof maxDepth != "number"){
7007                 stopEl = Roo.getDom(maxDepth);
7008                 maxDepth = 10;
7009             }
7010             while(p && p.nodeType == 1 && depth < maxDepth && p != b && p != stopEl){
7011                 if(dq.is(p, simpleSelector)){
7012                     return returnEl ? Roo.get(p) : p;
7013                 }
7014                 depth++;
7015                 p = p.parentNode;
7016             }
7017             return null;
7018         },
7019
7020
7021         /**
7022          * Looks at parent nodes for a match of the passed simple selector (e.g. div.some-class or span:first-child)
7023          * @param {String} selector The simple selector to test
7024          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
7025                 search as a number or element (defaults to 10 || document.body)
7026          * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
7027          * @return {HTMLElement} The matching DOM node (or null if no match was found)
7028          */
7029         findParentNode : function(simpleSelector, maxDepth, returnEl){
7030             var p = Roo.fly(this.dom.parentNode, '_internal');
7031             return p ? p.findParent(simpleSelector, maxDepth, returnEl) : null;
7032         },
7033
7034         /**
7035          * Walks up the dom looking for a parent node that matches the passed simple selector (e.g. div.some-class or span:first-child).
7036          * This is a shortcut for findParentNode() that always returns an Roo.Element.
7037          * @param {String} selector The simple selector to test
7038          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
7039                 search as a number or element (defaults to 10 || document.body)
7040          * @return {Roo.Element} The matching DOM node (or null if no match was found)
7041          */
7042         up : function(simpleSelector, maxDepth){
7043             return this.findParentNode(simpleSelector, maxDepth, true);
7044         },
7045
7046
7047
7048         /**
7049          * Returns true if this element matches the passed simple selector (e.g. div.some-class or span:first-child)
7050          * @param {String} selector The simple selector to test
7051          * @return {Boolean} True if this element matches the selector, else false
7052          */
7053         is : function(simpleSelector){
7054             return Roo.DomQuery.is(this.dom, simpleSelector);
7055         },
7056
7057         /**
7058          * Perform animation on this element.
7059          * @param {Object} args The YUI animation control args
7060          * @param {Float} duration (optional) How long the animation lasts in seconds (defaults to .35)
7061          * @param {Function} onComplete (optional) Function to call when animation completes
7062          * @param {String} easing (optional) Easing method to use (defaults to 'easeOut')
7063          * @param {String} animType (optional) 'run' is the default. Can also be 'color', 'motion', or 'scroll'
7064          * @return {Roo.Element} this
7065          */
7066         animate : function(args, duration, onComplete, easing, animType){
7067             this.anim(args, {duration: duration, callback: onComplete, easing: easing}, animType);
7068             return this;
7069         },
7070
7071         /*
7072          * @private Internal animation call
7073          */
7074         anim : function(args, opt, animType, defaultDur, defaultEase, cb){
7075             animType = animType || 'run';
7076             opt = opt || {};
7077             var anim = Roo.lib.Anim[animType](
7078                 this.dom, args,
7079                 (opt.duration || defaultDur) || .35,
7080                 (opt.easing || defaultEase) || 'easeOut',
7081                 function(){
7082                     Roo.callback(cb, this);
7083                     Roo.callback(opt.callback, opt.scope || this, [this, opt]);
7084                 },
7085                 this
7086             );
7087             opt.anim = anim;
7088             return anim;
7089         },
7090
7091         // private legacy anim prep
7092         preanim : function(a, i){
7093             return !a[i] ? false : (typeof a[i] == "object" ? a[i]: {duration: a[i+1], callback: a[i+2], easing: a[i+3]});
7094         },
7095
7096         /**
7097          * Removes worthless text nodes
7098          * @param {Boolean} forceReclean (optional) By default the element
7099          * keeps track if it has been cleaned already so
7100          * you can call this over and over. However, if you update the element and
7101          * need to force a reclean, you can pass true.
7102          */
7103         clean : function(forceReclean){
7104             if(this.isCleaned && forceReclean !== true){
7105                 return this;
7106             }
7107             var ns = /\S/;
7108             var d = this.dom, n = d.firstChild, ni = -1;
7109             while(n){
7110                 var nx = n.nextSibling;
7111                 if(n.nodeType == 3 && !ns.test(n.nodeValue)){
7112                     d.removeChild(n);
7113                 }else{
7114                     n.nodeIndex = ++ni;
7115                 }
7116                 n = nx;
7117             }
7118             this.isCleaned = true;
7119             return this;
7120         },
7121
7122         // private
7123         calcOffsetsTo : function(el){
7124             el = Roo.get(el);
7125             var d = el.dom;
7126             var restorePos = false;
7127             if(el.getStyle('position') == 'static'){
7128                 el.position('relative');
7129                 restorePos = true;
7130             }
7131             var x = 0, y =0;
7132             var op = this.dom;
7133             while(op && op != d && op.tagName != 'HTML'){
7134                 x+= op.offsetLeft;
7135                 y+= op.offsetTop;
7136                 op = op.offsetParent;
7137             }
7138             if(restorePos){
7139                 el.position('static');
7140             }
7141             return [x, y];
7142         },
7143
7144         /**
7145          * Scrolls this element into view within the passed container.
7146          * @param {String/HTMLElement/Element} container (optional) The container element to scroll (defaults to document.body)
7147          * @param {Boolean} hscroll (optional) False to disable horizontal scroll (defaults to true)
7148          * @return {Roo.Element} this
7149          */
7150         scrollIntoView : function(container, hscroll){
7151             var c = Roo.getDom(container) || document.body;
7152             var el = this.dom;
7153
7154             var o = this.calcOffsetsTo(c),
7155                 l = o[0],
7156                 t = o[1],
7157                 b = t+el.offsetHeight,
7158                 r = l+el.offsetWidth;
7159
7160             var ch = c.clientHeight;
7161             var ct = parseInt(c.scrollTop, 10);
7162             var cl = parseInt(c.scrollLeft, 10);
7163             var cb = ct + ch;
7164             var cr = cl + c.clientWidth;
7165
7166             if(t < ct){
7167                 c.scrollTop = t;
7168             }else if(b > cb){
7169                 c.scrollTop = b-ch;
7170             }
7171
7172             if(hscroll !== false){
7173                 if(l < cl){
7174                     c.scrollLeft = l;
7175                 }else if(r > cr){
7176                     c.scrollLeft = r-c.clientWidth;
7177                 }
7178             }
7179             return this;
7180         },
7181
7182         // private
7183         scrollChildIntoView : function(child, hscroll){
7184             Roo.fly(child, '_scrollChildIntoView').scrollIntoView(this, hscroll);
7185         },
7186
7187         /**
7188          * Measures the element's content height and updates height to match. Note: this function uses setTimeout so
7189          * the new height may not be available immediately.
7190          * @param {Boolean} animate (optional) Animate the transition (defaults to false)
7191          * @param {Float} duration (optional) Length of the animation in seconds (defaults to .35)
7192          * @param {Function} onComplete (optional) Function to call when animation completes
7193          * @param {String} easing (optional) Easing method to use (defaults to easeOut)
7194          * @return {Roo.Element} this
7195          */
7196         autoHeight : function(animate, duration, onComplete, easing){
7197             var oldHeight = this.getHeight();
7198             this.clip();
7199             this.setHeight(1); // force clipping
7200             setTimeout(function(){
7201                 var height = parseInt(this.dom.scrollHeight, 10); // parseInt for Safari
7202                 if(!animate){
7203                     this.setHeight(height);
7204                     this.unclip();
7205                     if(typeof onComplete == "function"){
7206                         onComplete();
7207                     }
7208                 }else{
7209                     this.setHeight(oldHeight); // restore original height
7210                     this.setHeight(height, animate, duration, function(){
7211                         this.unclip();
7212                         if(typeof onComplete == "function") onComplete();
7213                     }.createDelegate(this), easing);
7214                 }
7215             }.createDelegate(this), 0);
7216             return this;
7217         },
7218
7219         /**
7220          * Returns true if this element is an ancestor of the passed element
7221          * @param {HTMLElement/String} el The element to check
7222          * @return {Boolean} True if this element is an ancestor of el, else false
7223          */
7224         contains : function(el){
7225             if(!el){return false;}
7226             return D.isAncestor(this.dom, el.dom ? el.dom : el);
7227         },
7228
7229         /**
7230          * Checks whether the element is currently visible using both visibility and display properties.
7231          * @param {Boolean} deep (optional) True to walk the dom and see if parent elements are hidden (defaults to false)
7232          * @return {Boolean} True if the element is currently visible, else false
7233          */
7234         isVisible : function(deep) {
7235             var vis = !(this.getStyle("visibility") == "hidden" || this.getStyle("display") == "none");
7236             if(deep !== true || !vis){
7237                 return vis;
7238             }
7239             var p = this.dom.parentNode;
7240             while(p && p.tagName.toLowerCase() != "body"){
7241                 if(!Roo.fly(p, '_isVisible').isVisible()){
7242                     return false;
7243                 }
7244                 p = p.parentNode;
7245             }
7246             return true;
7247         },
7248
7249         /**
7250          * Creates a {@link Roo.CompositeElement} for child nodes based on the passed CSS selector (the selector should not contain an id).
7251          * @param {String} selector The CSS selector
7252          * @param {Boolean} unique (optional) True to create a unique Roo.Element for each child (defaults to false, which creates a single shared flyweight object)
7253          * @return {CompositeElement/CompositeElementLite} The composite element
7254          */
7255         select : function(selector, unique){
7256             return El.select(selector, unique, this.dom);
7257         },
7258
7259         /**
7260          * Selects child nodes based on the passed CSS selector (the selector should not contain an id).
7261          * @param {String} selector The CSS selector
7262          * @return {Array} An array of the matched nodes
7263          */
7264         query : function(selector, unique){
7265             return Roo.DomQuery.select(selector, this.dom);
7266         },
7267
7268         /**
7269          * Selects a single child at any depth below this element based on the passed CSS selector (the selector should not contain an id).
7270          * @param {String} selector The CSS selector
7271          * @param {Boolean} returnDom (optional) True to return the DOM node instead of Roo.Element (defaults to false)
7272          * @return {HTMLElement/Roo.Element} The child Roo.Element (or DOM node if returnDom = true)
7273          */
7274         child : function(selector, returnDom){
7275             var n = Roo.DomQuery.selectNode(selector, this.dom);
7276             return returnDom ? n : Roo.get(n);
7277         },
7278
7279         /**
7280          * Selects a single *direct* child based on the passed CSS selector (the selector should not contain an id).
7281          * @param {String} selector The CSS selector
7282          * @param {Boolean} returnDom (optional) True to return the DOM node instead of Roo.Element (defaults to false)
7283          * @return {HTMLElement/Roo.Element} The child Roo.Element (or DOM node if returnDom = true)
7284          */
7285         down : function(selector, returnDom){
7286             var n = Roo.DomQuery.selectNode(" > " + selector, this.dom);
7287             return returnDom ? n : Roo.get(n);
7288         },
7289
7290         /**
7291          * Initializes a {@link Roo.dd.DD} drag drop object for this element.
7292          * @param {String} group The group the DD object is member of
7293          * @param {Object} config The DD config object
7294          * @param {Object} overrides An object containing methods to override/implement on the DD object
7295          * @return {Roo.dd.DD} The DD object
7296          */
7297         initDD : function(group, config, overrides){
7298             var dd = new Roo.dd.DD(Roo.id(this.dom), group, config);
7299             return Roo.apply(dd, overrides);
7300         },
7301
7302         /**
7303          * Initializes a {@link Roo.dd.DDProxy} object for this element.
7304          * @param {String} group The group the DDProxy object is member of
7305          * @param {Object} config The DDProxy config object
7306          * @param {Object} overrides An object containing methods to override/implement on the DDProxy object
7307          * @return {Roo.dd.DDProxy} The DDProxy object
7308          */
7309         initDDProxy : function(group, config, overrides){
7310             var dd = new Roo.dd.DDProxy(Roo.id(this.dom), group, config);
7311             return Roo.apply(dd, overrides);
7312         },
7313
7314         /**
7315          * Initializes a {@link Roo.dd.DDTarget} object for this element.
7316          * @param {String} group The group the DDTarget object is member of
7317          * @param {Object} config The DDTarget config object
7318          * @param {Object} overrides An object containing methods to override/implement on the DDTarget object
7319          * @return {Roo.dd.DDTarget} The DDTarget object
7320          */
7321         initDDTarget : function(group, config, overrides){
7322             var dd = new Roo.dd.DDTarget(Roo.id(this.dom), group, config);
7323             return Roo.apply(dd, overrides);
7324         },
7325
7326         /**
7327          * Sets the visibility of the element (see details). If the visibilityMode is set to Element.DISPLAY, it will use
7328          * the display property to hide the element, otherwise it uses visibility. The default is to hide and show using the visibility property.
7329          * @param {Boolean} visible Whether the element is visible
7330          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7331          * @return {Roo.Element} this
7332          */
7333          setVisible : function(visible, animate){
7334             if(!animate || !A){
7335                 if(this.visibilityMode == El.DISPLAY){
7336                     this.setDisplayed(visible);
7337                 }else{
7338                     this.fixDisplay();
7339                     this.dom.style.visibility = visible ? "visible" : "hidden";
7340                 }
7341             }else{
7342                 // closure for composites
7343                 var dom = this.dom;
7344                 var visMode = this.visibilityMode;
7345                 if(visible){
7346                     this.setOpacity(.01);
7347                     this.setVisible(true);
7348                 }
7349                 this.anim({opacity: { to: (visible?1:0) }},
7350                       this.preanim(arguments, 1),
7351                       null, .35, 'easeIn', function(){
7352                          if(!visible){
7353                              if(visMode == El.DISPLAY){
7354                                  dom.style.display = "none";
7355                              }else{
7356                                  dom.style.visibility = "hidden";
7357                              }
7358                              Roo.get(dom).setOpacity(1);
7359                          }
7360                      });
7361             }
7362             return this;
7363         },
7364
7365         /**
7366          * Returns true if display is not "none"
7367          * @return {Boolean}
7368          */
7369         isDisplayed : function() {
7370             return this.getStyle("display") != "none";
7371         },
7372
7373         /**
7374          * Toggles the element's visibility or display, depending on visibility mode.
7375          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7376          * @return {Roo.Element} this
7377          */
7378         toggle : function(animate){
7379             this.setVisible(!this.isVisible(), this.preanim(arguments, 0));
7380             return this;
7381         },
7382
7383         /**
7384          * Sets the CSS display property. Uses originalDisplay if the specified value is a boolean true.
7385          * @param {Boolean} value Boolean value to display the element using its default display, or a string to set the display directly
7386          * @return {Roo.Element} this
7387          */
7388         setDisplayed : function(value) {
7389             if(typeof value == "boolean"){
7390                value = value ? this.originalDisplay : "none";
7391             }
7392             this.setStyle("display", value);
7393             return this;
7394         },
7395
7396         /**
7397          * Tries to focus the element. Any exceptions are caught and ignored.
7398          * @return {Roo.Element} this
7399          */
7400         focus : function() {
7401             try{
7402                 this.dom.focus();
7403             }catch(e){}
7404             return this;
7405         },
7406
7407         /**
7408          * Tries to blur the element. Any exceptions are caught and ignored.
7409          * @return {Roo.Element} this
7410          */
7411         blur : function() {
7412             try{
7413                 this.dom.blur();
7414             }catch(e){}
7415             return this;
7416         },
7417
7418         /**
7419          * Adds one or more CSS classes to the element. Duplicate classes are automatically filtered out.
7420          * @param {String/Array} className The CSS class to add, or an array of classes
7421          * @return {Roo.Element} this
7422          */
7423         addClass : function(className){
7424             if(className instanceof Array){
7425                 for(var i = 0, len = className.length; i < len; i++) {
7426                     this.addClass(className[i]);
7427                 }
7428             }else{
7429                 if(className && !this.hasClass(className)){
7430                     this.dom.className = this.dom.className + " " + className;
7431                 }
7432             }
7433             return this;
7434         },
7435
7436         /**
7437          * Adds one or more CSS classes to this element and removes the same class(es) from all siblings.
7438          * @param {String/Array} className The CSS class to add, or an array of classes
7439          * @return {Roo.Element} this
7440          */
7441         radioClass : function(className){
7442             var siblings = this.dom.parentNode.childNodes;
7443             for(var i = 0; i < siblings.length; i++) {
7444                 var s = siblings[i];
7445                 if(s.nodeType == 1){
7446                     Roo.get(s).removeClass(className);
7447                 }
7448             }
7449             this.addClass(className);
7450             return this;
7451         },
7452
7453         /**
7454          * Removes one or more CSS classes from the element.
7455          * @param {String/Array} className The CSS class to remove, or an array of classes
7456          * @return {Roo.Element} this
7457          */
7458         removeClass : function(className){
7459             if(!className || !this.dom.className){
7460                 return this;
7461             }
7462             if(className instanceof Array){
7463                 for(var i = 0, len = className.length; i < len; i++) {
7464                     this.removeClass(className[i]);
7465                 }
7466             }else{
7467                 if(this.hasClass(className)){
7468                     var re = this.classReCache[className];
7469                     if (!re) {
7470                        re = new RegExp('(?:^|\\s+)' + className + '(?:\\s+|$)', "g");
7471                        this.classReCache[className] = re;
7472                     }
7473                     this.dom.className =
7474                         this.dom.className.replace(re, " ");
7475                 }
7476             }
7477             return this;
7478         },
7479
7480         // private
7481         classReCache: {},
7482
7483         /**
7484          * Toggles the specified CSS class on this element (removes it if it already exists, otherwise adds it).
7485          * @param {String} className The CSS class to toggle
7486          * @return {Roo.Element} this
7487          */
7488         toggleClass : function(className){
7489             if(this.hasClass(className)){
7490                 this.removeClass(className);
7491             }else{
7492                 this.addClass(className);
7493             }
7494             return this;
7495         },
7496
7497         /**
7498          * Checks if the specified CSS class exists on this element's DOM node.
7499          * @param {String} className The CSS class to check for
7500          * @return {Boolean} True if the class exists, else false
7501          */
7502         hasClass : function(className){
7503             return className && (' '+this.dom.className+' ').indexOf(' '+className+' ') != -1;
7504         },
7505
7506         /**
7507          * Replaces a CSS class on the element with another.  If the old name does not exist, the new name will simply be added.
7508          * @param {String} oldClassName The CSS class to replace
7509          * @param {String} newClassName The replacement CSS class
7510          * @return {Roo.Element} this
7511          */
7512         replaceClass : function(oldClassName, newClassName){
7513             this.removeClass(oldClassName);
7514             this.addClass(newClassName);
7515             return this;
7516         },
7517
7518         /**
7519          * Returns an object with properties matching the styles requested.
7520          * For example, el.getStyles('color', 'font-size', 'width') might return
7521          * {'color': '#FFFFFF', 'font-size': '13px', 'width': '100px'}.
7522          * @param {String} style1 A style name
7523          * @param {String} style2 A style name
7524          * @param {String} etc.
7525          * @return {Object} The style object
7526          */
7527         getStyles : function(){
7528             var a = arguments, len = a.length, r = {};
7529             for(var i = 0; i < len; i++){
7530                 r[a[i]] = this.getStyle(a[i]);
7531             }
7532             return r;
7533         },
7534
7535         /**
7536          * Normalizes currentStyle and computedStyle. This is not YUI getStyle, it is an optimised version.
7537          * @param {String} property The style property whose value is returned.
7538          * @return {String} The current value of the style property for this element.
7539          */
7540         getStyle : function(){
7541             return view && view.getComputedStyle ?
7542                 function(prop){
7543                     var el = this.dom, v, cs, camel;
7544                     if(prop == 'float'){
7545                         prop = "cssFloat";
7546                     }
7547                     if(el.style && (v = el.style[prop])){
7548                         return v;
7549                     }
7550                     if(cs = view.getComputedStyle(el, "")){
7551                         if(!(camel = propCache[prop])){
7552                             camel = propCache[prop] = prop.replace(camelRe, camelFn);
7553                         }
7554                         return cs[camel];
7555                     }
7556                     return null;
7557                 } :
7558                 function(prop){
7559                     var el = this.dom, v, cs, camel;
7560                     if(prop == 'opacity'){
7561                         if(typeof el.style.filter == 'string'){
7562                             var m = el.style.filter.match(/alpha\(opacity=(.*)\)/i);
7563                             if(m){
7564                                 var fv = parseFloat(m[1]);
7565                                 if(!isNaN(fv)){
7566                                     return fv ? fv / 100 : 0;
7567                                 }
7568                             }
7569                         }
7570                         return 1;
7571                     }else if(prop == 'float'){
7572                         prop = "styleFloat";
7573                     }
7574                     if(!(camel = propCache[prop])){
7575                         camel = propCache[prop] = prop.replace(camelRe, camelFn);
7576                     }
7577                     if(v = el.style[camel]){
7578                         return v;
7579                     }
7580                     if(cs = el.currentStyle){
7581                         return cs[camel];
7582                     }
7583                     return null;
7584                 };
7585         }(),
7586
7587         /**
7588          * Wrapper for setting style properties, also takes single object parameter of multiple styles.
7589          * @param {String/Object} property The style property to be set, or an object of multiple styles.
7590          * @param {String} value (optional) The value to apply to the given property, or null if an object was passed.
7591          * @return {Roo.Element} this
7592          */
7593         setStyle : function(prop, value){
7594             if(typeof prop == "string"){
7595                 
7596                 if (prop == 'float') {
7597                     this.setStyle(Roo.isIE ? 'styleFloat'  : 'cssFloat', value);
7598                     return this;
7599                 }
7600                 
7601                 var camel;
7602                 if(!(camel = propCache[prop])){
7603                     camel = propCache[prop] = prop.replace(camelRe, camelFn);
7604                 }
7605                 
7606                 if(camel == 'opacity') {
7607                     this.setOpacity(value);
7608                 }else{
7609                     this.dom.style[camel] = value;
7610                 }
7611             }else{
7612                 for(var style in prop){
7613                     if(typeof prop[style] != "function"){
7614                        this.setStyle(style, prop[style]);
7615                     }
7616                 }
7617             }
7618             return this;
7619         },
7620
7621         /**
7622          * More flexible version of {@link #setStyle} for setting style properties.
7623          * @param {String/Object/Function} styles A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
7624          * a function which returns such a specification.
7625          * @return {Roo.Element} this
7626          */
7627         applyStyles : function(style){
7628             Roo.DomHelper.applyStyles(this.dom, style);
7629             return this;
7630         },
7631
7632         /**
7633           * 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).
7634           * @return {Number} The X position of the element
7635           */
7636         getX : function(){
7637             return D.getX(this.dom);
7638         },
7639
7640         /**
7641           * 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).
7642           * @return {Number} The Y position of the element
7643           */
7644         getY : function(){
7645             return D.getY(this.dom);
7646         },
7647
7648         /**
7649           * 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).
7650           * @return {Array} The XY position of the element
7651           */
7652         getXY : function(){
7653             return D.getXY(this.dom);
7654         },
7655
7656         /**
7657          * 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).
7658          * @param {Number} The X position of the element
7659          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7660          * @return {Roo.Element} this
7661          */
7662         setX : function(x, animate){
7663             if(!animate || !A){
7664                 D.setX(this.dom, x);
7665             }else{
7666                 this.setXY([x, this.getY()], this.preanim(arguments, 1));
7667             }
7668             return this;
7669         },
7670
7671         /**
7672          * 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).
7673          * @param {Number} The Y position of the element
7674          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7675          * @return {Roo.Element} this
7676          */
7677         setY : function(y, animate){
7678             if(!animate || !A){
7679                 D.setY(this.dom, y);
7680             }else{
7681                 this.setXY([this.getX(), y], this.preanim(arguments, 1));
7682             }
7683             return this;
7684         },
7685
7686         /**
7687          * Sets the element's left position directly using CSS style (instead of {@link #setX}).
7688          * @param {String} left The left CSS property value
7689          * @return {Roo.Element} this
7690          */
7691         setLeft : function(left){
7692             this.setStyle("left", this.addUnits(left));
7693             return this;
7694         },
7695
7696         /**
7697          * Sets the element's top position directly using CSS style (instead of {@link #setY}).
7698          * @param {String} top The top CSS property value
7699          * @return {Roo.Element} this
7700          */
7701         setTop : function(top){
7702             this.setStyle("top", this.addUnits(top));
7703             return this;
7704         },
7705
7706         /**
7707          * Sets the element's CSS right style.
7708          * @param {String} right The right CSS property value
7709          * @return {Roo.Element} this
7710          */
7711         setRight : function(right){
7712             this.setStyle("right", this.addUnits(right));
7713             return this;
7714         },
7715
7716         /**
7717          * Sets the element's CSS bottom style.
7718          * @param {String} bottom The bottom CSS property value
7719          * @return {Roo.Element} this
7720          */
7721         setBottom : function(bottom){
7722             this.setStyle("bottom", this.addUnits(bottom));
7723             return this;
7724         },
7725
7726         /**
7727          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
7728          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7729          * @param {Array} pos Contains X & Y [x, y] values for new position (coordinates are page-based)
7730          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7731          * @return {Roo.Element} this
7732          */
7733         setXY : function(pos, animate){
7734             if(!animate || !A){
7735                 D.setXY(this.dom, pos);
7736             }else{
7737                 this.anim({points: {to: pos}}, this.preanim(arguments, 1), 'motion');
7738             }
7739             return this;
7740         },
7741
7742         /**
7743          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
7744          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7745          * @param {Number} x X value for new position (coordinates are page-based)
7746          * @param {Number} y Y value for new position (coordinates are page-based)
7747          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7748          * @return {Roo.Element} this
7749          */
7750         setLocation : function(x, y, animate){
7751             this.setXY([x, y], this.preanim(arguments, 2));
7752             return this;
7753         },
7754
7755         /**
7756          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
7757          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7758          * @param {Number} x X value for new position (coordinates are page-based)
7759          * @param {Number} y Y value for new position (coordinates are page-based)
7760          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7761          * @return {Roo.Element} this
7762          */
7763         moveTo : function(x, y, animate){
7764             this.setXY([x, y], this.preanim(arguments, 2));
7765             return this;
7766         },
7767
7768         /**
7769          * Returns the region of the given element.
7770          * The element must be part of the DOM tree to have a region (display:none or elements not appended return false).
7771          * @return {Region} A Roo.lib.Region containing "top, left, bottom, right" member data.
7772          */
7773         getRegion : function(){
7774             return D.getRegion(this.dom);
7775         },
7776
7777         /**
7778          * Returns the offset height of the element
7779          * @param {Boolean} contentHeight (optional) true to get the height minus borders and padding
7780          * @return {Number} The element's height
7781          */
7782         getHeight : function(contentHeight){
7783             var h = this.dom.offsetHeight || 0;
7784             return contentHeight !== true ? h : h-this.getBorderWidth("tb")-this.getPadding("tb");
7785         },
7786
7787         /**
7788          * Returns the offset width of the element
7789          * @param {Boolean} contentWidth (optional) true to get the width minus borders and padding
7790          * @return {Number} The element's width
7791          */
7792         getWidth : function(contentWidth){
7793             var w = this.dom.offsetWidth || 0;
7794             return contentWidth !== true ? w : w-this.getBorderWidth("lr")-this.getPadding("lr");
7795         },
7796
7797         /**
7798          * Returns either the offsetHeight or the height of this element based on CSS height adjusted by padding or borders
7799          * when needed to simulate offsetHeight when offsets aren't available. This may not work on display:none elements
7800          * if a height has not been set using CSS.
7801          * @return {Number}
7802          */
7803         getComputedHeight : function(){
7804             var h = Math.max(this.dom.offsetHeight, this.dom.clientHeight);
7805             if(!h){
7806                 h = parseInt(this.getStyle('height'), 10) || 0;
7807                 if(!this.isBorderBox()){
7808                     h += this.getFrameWidth('tb');
7809                 }
7810             }
7811             return h;
7812         },
7813
7814         /**
7815          * Returns either the offsetWidth or the width of this element based on CSS width adjusted by padding or borders
7816          * when needed to simulate offsetWidth when offsets aren't available. This may not work on display:none elements
7817          * if a width has not been set using CSS.
7818          * @return {Number}
7819          */
7820         getComputedWidth : function(){
7821             var w = Math.max(this.dom.offsetWidth, this.dom.clientWidth);
7822             if(!w){
7823                 w = parseInt(this.getStyle('width'), 10) || 0;
7824                 if(!this.isBorderBox()){
7825                     w += this.getFrameWidth('lr');
7826                 }
7827             }
7828             return w;
7829         },
7830
7831         /**
7832          * Returns the size of the element.
7833          * @param {Boolean} contentSize (optional) true to get the width/size minus borders and padding
7834          * @return {Object} An object containing the element's size {width: (element width), height: (element height)}
7835          */
7836         getSize : function(contentSize){
7837             return {width: this.getWidth(contentSize), height: this.getHeight(contentSize)};
7838         },
7839
7840         /**
7841          * Returns the width and height of the viewport.
7842          * @return {Object} An object containing the viewport's size {width: (viewport width), height: (viewport height)}
7843          */
7844         getViewSize : function(){
7845             var d = this.dom, doc = document, aw = 0, ah = 0;
7846             if(d == doc || d == doc.body){
7847                 return {width : D.getViewWidth(), height: D.getViewHeight()};
7848             }else{
7849                 return {
7850                     width : d.clientWidth,
7851                     height: d.clientHeight
7852                 };
7853             }
7854         },
7855
7856         /**
7857          * Returns the value of the "value" attribute
7858          * @param {Boolean} asNumber true to parse the value as a number
7859          * @return {String/Number}
7860          */
7861         getValue : function(asNumber){
7862             return asNumber ? parseInt(this.dom.value, 10) : this.dom.value;
7863         },
7864
7865         // private
7866         adjustWidth : function(width){
7867             if(typeof width == "number"){
7868                 if(this.autoBoxAdjust && !this.isBorderBox()){
7869                    width -= (this.getBorderWidth("lr") + this.getPadding("lr"));
7870                 }
7871                 if(width < 0){
7872                     width = 0;
7873                 }
7874             }
7875             return width;
7876         },
7877
7878         // private
7879         adjustHeight : function(height){
7880             if(typeof height == "number"){
7881                if(this.autoBoxAdjust && !this.isBorderBox()){
7882                    height -= (this.getBorderWidth("tb") + this.getPadding("tb"));
7883                }
7884                if(height < 0){
7885                    height = 0;
7886                }
7887             }
7888             return height;
7889         },
7890
7891         /**
7892          * Set the width of the element
7893          * @param {Number} width The new width
7894          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7895          * @return {Roo.Element} this
7896          */
7897         setWidth : function(width, animate){
7898             width = this.adjustWidth(width);
7899             if(!animate || !A){
7900                 this.dom.style.width = this.addUnits(width);
7901             }else{
7902                 this.anim({width: {to: width}}, this.preanim(arguments, 1));
7903             }
7904             return this;
7905         },
7906
7907         /**
7908          * Set the height of the element
7909          * @param {Number} height The new height
7910          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7911          * @return {Roo.Element} this
7912          */
7913          setHeight : function(height, animate){
7914             height = this.adjustHeight(height);
7915             if(!animate || !A){
7916                 this.dom.style.height = this.addUnits(height);
7917             }else{
7918                 this.anim({height: {to: height}}, this.preanim(arguments, 1));
7919             }
7920             return this;
7921         },
7922
7923         /**
7924          * Set the size of the element. If animation is true, both width an height will be animated concurrently.
7925          * @param {Number} width The new width
7926          * @param {Number} height The new height
7927          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7928          * @return {Roo.Element} this
7929          */
7930          setSize : function(width, height, animate){
7931             if(typeof width == "object"){ // in case of object from getSize()
7932                 height = width.height; width = width.width;
7933             }
7934             width = this.adjustWidth(width); height = this.adjustHeight(height);
7935             if(!animate || !A){
7936                 this.dom.style.width = this.addUnits(width);
7937                 this.dom.style.height = this.addUnits(height);
7938             }else{
7939                 this.anim({width: {to: width}, height: {to: height}}, this.preanim(arguments, 2));
7940             }
7941             return this;
7942         },
7943
7944         /**
7945          * Sets the element's position and size in one shot. If animation is true then width, height, x and y will be animated concurrently.
7946          * @param {Number} x X value for new position (coordinates are page-based)
7947          * @param {Number} y Y value for new position (coordinates are page-based)
7948          * @param {Number} width The new width
7949          * @param {Number} height The new height
7950          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7951          * @return {Roo.Element} this
7952          */
7953         setBounds : function(x, y, width, height, animate){
7954             if(!animate || !A){
7955                 this.setSize(width, height);
7956                 this.setLocation(x, y);
7957             }else{
7958                 width = this.adjustWidth(width); height = this.adjustHeight(height);
7959                 this.anim({points: {to: [x, y]}, width: {to: width}, height: {to: height}},
7960                               this.preanim(arguments, 4), 'motion');
7961             }
7962             return this;
7963         },
7964
7965         /**
7966          * 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.
7967          * @param {Roo.lib.Region} region The region to fill
7968          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7969          * @return {Roo.Element} this
7970          */
7971         setRegion : function(region, animate){
7972             this.setBounds(region.left, region.top, region.right-region.left, region.bottom-region.top, this.preanim(arguments, 1));
7973             return this;
7974         },
7975
7976         /**
7977          * Appends an event handler
7978          *
7979          * @param {String}   eventName     The type of event to append
7980          * @param {Function} fn        The method the event invokes
7981          * @param {Object} scope       (optional) The scope (this object) of the fn
7982          * @param {Object}   options   (optional)An object with standard {@link Roo.EventManager#addListener} options
7983          */
7984         addListener : function(eventName, fn, scope, options){
7985             if (this.dom) {
7986                 Roo.EventManager.on(this.dom,  eventName, fn, scope || this, options);
7987             }
7988         },
7989
7990         /**
7991          * Removes an event handler from this element
7992          * @param {String} eventName the type of event to remove
7993          * @param {Function} fn the method the event invokes
7994          * @return {Roo.Element} this
7995          */
7996         removeListener : function(eventName, fn){
7997             Roo.EventManager.removeListener(this.dom,  eventName, fn);
7998             return this;
7999         },
8000
8001         /**
8002          * Removes all previous added listeners from this element
8003          * @return {Roo.Element} this
8004          */
8005         removeAllListeners : function(){
8006             E.purgeElement(this.dom);
8007             return this;
8008         },
8009
8010         relayEvent : function(eventName, observable){
8011             this.on(eventName, function(e){
8012                 observable.fireEvent(eventName, e);
8013             });
8014         },
8015
8016         /**
8017          * Set the opacity of the element
8018          * @param {Float} opacity The new opacity. 0 = transparent, .5 = 50% visibile, 1 = fully visible, etc
8019          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8020          * @return {Roo.Element} this
8021          */
8022          setOpacity : function(opacity, animate){
8023             if(!animate || !A){
8024                 var s = this.dom.style;
8025                 if(Roo.isIE){
8026                     s.zoom = 1;
8027                     s.filter = (s.filter || '').replace(/alpha\([^\)]*\)/gi,"") +
8028                                (opacity == 1 ? "" : "alpha(opacity=" + opacity * 100 + ")");
8029                 }else{
8030                     s.opacity = opacity;
8031                 }
8032             }else{
8033                 this.anim({opacity: {to: opacity}}, this.preanim(arguments, 1), null, .35, 'easeIn');
8034             }
8035             return this;
8036         },
8037
8038         /**
8039          * Gets the left X coordinate
8040          * @param {Boolean} local True to get the local css position instead of page coordinate
8041          * @return {Number}
8042          */
8043         getLeft : function(local){
8044             if(!local){
8045                 return this.getX();
8046             }else{
8047                 return parseInt(this.getStyle("left"), 10) || 0;
8048             }
8049         },
8050
8051         /**
8052          * Gets the right X coordinate of the element (element X position + element width)
8053          * @param {Boolean} local True to get the local css position instead of page coordinate
8054          * @return {Number}
8055          */
8056         getRight : function(local){
8057             if(!local){
8058                 return this.getX() + this.getWidth();
8059             }else{
8060                 return (this.getLeft(true) + this.getWidth()) || 0;
8061             }
8062         },
8063
8064         /**
8065          * Gets the top Y coordinate
8066          * @param {Boolean} local True to get the local css position instead of page coordinate
8067          * @return {Number}
8068          */
8069         getTop : function(local) {
8070             if(!local){
8071                 return this.getY();
8072             }else{
8073                 return parseInt(this.getStyle("top"), 10) || 0;
8074             }
8075         },
8076
8077         /**
8078          * Gets the bottom Y coordinate of the element (element Y position + element height)
8079          * @param {Boolean} local True to get the local css position instead of page coordinate
8080          * @return {Number}
8081          */
8082         getBottom : function(local){
8083             if(!local){
8084                 return this.getY() + this.getHeight();
8085             }else{
8086                 return (this.getTop(true) + this.getHeight()) || 0;
8087             }
8088         },
8089
8090         /**
8091         * Initializes positioning on this element. If a desired position is not passed, it will make the
8092         * the element positioned relative IF it is not already positioned.
8093         * @param {String} pos (optional) Positioning to use "relative", "absolute" or "fixed"
8094         * @param {Number} zIndex (optional) The zIndex to apply
8095         * @param {Number} x (optional) Set the page X position
8096         * @param {Number} y (optional) Set the page Y position
8097         */
8098         position : function(pos, zIndex, x, y){
8099             if(!pos){
8100                if(this.getStyle('position') == 'static'){
8101                    this.setStyle('position', 'relative');
8102                }
8103             }else{
8104                 this.setStyle("position", pos);
8105             }
8106             if(zIndex){
8107                 this.setStyle("z-index", zIndex);
8108             }
8109             if(x !== undefined && y !== undefined){
8110                 this.setXY([x, y]);
8111             }else if(x !== undefined){
8112                 this.setX(x);
8113             }else if(y !== undefined){
8114                 this.setY(y);
8115             }
8116         },
8117
8118         /**
8119         * Clear positioning back to the default when the document was loaded
8120         * @param {String} value (optional) The value to use for the left,right,top,bottom, defaults to '' (empty string). You could use 'auto'.
8121         * @return {Roo.Element} this
8122          */
8123         clearPositioning : function(value){
8124             value = value ||'';
8125             this.setStyle({
8126                 "left": value,
8127                 "right": value,
8128                 "top": value,
8129                 "bottom": value,
8130                 "z-index": "",
8131                 "position" : "static"
8132             });
8133             return this;
8134         },
8135
8136         /**
8137         * Gets an object with all CSS positioning properties. Useful along with setPostioning to get
8138         * snapshot before performing an update and then restoring the element.
8139         * @return {Object}
8140         */
8141         getPositioning : function(){
8142             var l = this.getStyle("left");
8143             var t = this.getStyle("top");
8144             return {
8145                 "position" : this.getStyle("position"),
8146                 "left" : l,
8147                 "right" : l ? "" : this.getStyle("right"),
8148                 "top" : t,
8149                 "bottom" : t ? "" : this.getStyle("bottom"),
8150                 "z-index" : this.getStyle("z-index")
8151             };
8152         },
8153
8154         /**
8155          * Gets the width of the border(s) for the specified side(s)
8156          * @param {String} side Can be t, l, r, b or any combination of those to add multiple values. For example,
8157          * passing lr would get the border (l)eft width + the border (r)ight width.
8158          * @return {Number} The width of the sides passed added together
8159          */
8160         getBorderWidth : function(side){
8161             return this.addStyles(side, El.borders);
8162         },
8163
8164         /**
8165          * Gets the width of the padding(s) for the specified side(s)
8166          * @param {String} side Can be t, l, r, b or any combination of those to add multiple values. For example,
8167          * passing lr would get the padding (l)eft + the padding (r)ight.
8168          * @return {Number} The padding of the sides passed added together
8169          */
8170         getPadding : function(side){
8171             return this.addStyles(side, El.paddings);
8172         },
8173
8174         /**
8175         * Set positioning with an object returned by getPositioning().
8176         * @param {Object} posCfg
8177         * @return {Roo.Element} this
8178          */
8179         setPositioning : function(pc){
8180             this.applyStyles(pc);
8181             if(pc.right == "auto"){
8182                 this.dom.style.right = "";
8183             }
8184             if(pc.bottom == "auto"){
8185                 this.dom.style.bottom = "";
8186             }
8187             return this;
8188         },
8189
8190         // private
8191         fixDisplay : function(){
8192             if(this.getStyle("display") == "none"){
8193                 this.setStyle("visibility", "hidden");
8194                 this.setStyle("display", this.originalDisplay); // first try reverting to default
8195                 if(this.getStyle("display") == "none"){ // if that fails, default to block
8196                     this.setStyle("display", "block");
8197                 }
8198             }
8199         },
8200
8201         /**
8202          * Quick set left and top adding default units
8203          * @param {String} left The left CSS property value
8204          * @param {String} top The top CSS property value
8205          * @return {Roo.Element} this
8206          */
8207          setLeftTop : function(left, top){
8208             this.dom.style.left = this.addUnits(left);
8209             this.dom.style.top = this.addUnits(top);
8210             return this;
8211         },
8212
8213         /**
8214          * Move this element relative to its current position.
8215          * @param {String} direction Possible values are: "l","left" - "r","right" - "t","top","up" - "b","bottom","down".
8216          * @param {Number} distance How far to move the element in pixels
8217          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8218          * @return {Roo.Element} this
8219          */
8220          move : function(direction, distance, animate){
8221             var xy = this.getXY();
8222             direction = direction.toLowerCase();
8223             switch(direction){
8224                 case "l":
8225                 case "left":
8226                     this.moveTo(xy[0]-distance, xy[1], this.preanim(arguments, 2));
8227                     break;
8228                case "r":
8229                case "right":
8230                     this.moveTo(xy[0]+distance, xy[1], this.preanim(arguments, 2));
8231                     break;
8232                case "t":
8233                case "top":
8234                case "up":
8235                     this.moveTo(xy[0], xy[1]-distance, this.preanim(arguments, 2));
8236                     break;
8237                case "b":
8238                case "bottom":
8239                case "down":
8240                     this.moveTo(xy[0], xy[1]+distance, this.preanim(arguments, 2));
8241                     break;
8242             }
8243             return this;
8244         },
8245
8246         /**
8247          *  Store the current overflow setting and clip overflow on the element - use {@link #unclip} to remove
8248          * @return {Roo.Element} this
8249          */
8250         clip : function(){
8251             if(!this.isClipped){
8252                this.isClipped = true;
8253                this.originalClip = {
8254                    "o": this.getStyle("overflow"),
8255                    "x": this.getStyle("overflow-x"),
8256                    "y": this.getStyle("overflow-y")
8257                };
8258                this.setStyle("overflow", "hidden");
8259                this.setStyle("overflow-x", "hidden");
8260                this.setStyle("overflow-y", "hidden");
8261             }
8262             return this;
8263         },
8264
8265         /**
8266          *  Return clipping (overflow) to original clipping before clip() was called
8267          * @return {Roo.Element} this
8268          */
8269         unclip : function(){
8270             if(this.isClipped){
8271                 this.isClipped = false;
8272                 var o = this.originalClip;
8273                 if(o.o){this.setStyle("overflow", o.o);}
8274                 if(o.x){this.setStyle("overflow-x", o.x);}
8275                 if(o.y){this.setStyle("overflow-y", o.y);}
8276             }
8277             return this;
8278         },
8279
8280
8281         /**
8282          * Gets the x,y coordinates specified by the anchor position on the element.
8283          * @param {String} anchor (optional) The specified anchor position (defaults to "c").  See {@link #alignTo} for details on supported anchor positions.
8284          * @param {Object} size (optional) An object containing the size to use for calculating anchor position
8285          *                       {width: (target width), height: (target height)} (defaults to the element's current size)
8286          * @param {Boolean} local (optional) True to get the local (element top/left-relative) anchor position instead of page coordinates
8287          * @return {Array} [x, y] An array containing the element's x and y coordinates
8288          */
8289         getAnchorXY : function(anchor, local, s){
8290             //Passing a different size is useful for pre-calculating anchors,
8291             //especially for anchored animations that change the el size.
8292
8293             var w, h, vp = false;
8294             if(!s){
8295                 var d = this.dom;
8296                 if(d == document.body || d == document){
8297                     vp = true;
8298                     w = D.getViewWidth(); h = D.getViewHeight();
8299                 }else{
8300                     w = this.getWidth(); h = this.getHeight();
8301                 }
8302             }else{
8303                 w = s.width;  h = s.height;
8304             }
8305             var x = 0, y = 0, r = Math.round;
8306             switch((anchor || "tl").toLowerCase()){
8307                 case "c":
8308                     x = r(w*.5);
8309                     y = r(h*.5);
8310                 break;
8311                 case "t":
8312                     x = r(w*.5);
8313                     y = 0;
8314                 break;
8315                 case "l":
8316                     x = 0;
8317                     y = r(h*.5);
8318                 break;
8319                 case "r":
8320                     x = w;
8321                     y = r(h*.5);
8322                 break;
8323                 case "b":
8324                     x = r(w*.5);
8325                     y = h;
8326                 break;
8327                 case "tl":
8328                     x = 0;
8329                     y = 0;
8330                 break;
8331                 case "bl":
8332                     x = 0;
8333                     y = h;
8334                 break;
8335                 case "br":
8336                     x = w;
8337                     y = h;
8338                 break;
8339                 case "tr":
8340                     x = w;
8341                     y = 0;
8342                 break;
8343             }
8344             if(local === true){
8345                 return [x, y];
8346             }
8347             if(vp){
8348                 var sc = this.getScroll();
8349                 return [x + sc.left, y + sc.top];
8350             }
8351             //Add the element's offset xy
8352             var o = this.getXY();
8353             return [x+o[0], y+o[1]];
8354         },
8355
8356         /**
8357          * Gets the x,y coordinates to align this element with another element. See {@link #alignTo} for more info on the
8358          * supported position values.
8359          * @param {String/HTMLElement/Roo.Element} element The element to align to.
8360          * @param {String} position The position to align to.
8361          * @param {Array} offsets (optional) Offset the positioning by [x, y]
8362          * @return {Array} [x, y]
8363          */
8364         getAlignToXY : function(el, p, o){
8365             el = Roo.get(el);
8366             var d = this.dom;
8367             if(!el.dom){
8368                 throw "Element.alignTo with an element that doesn't exist";
8369             }
8370             var c = false; //constrain to viewport
8371             var p1 = "", p2 = "";
8372             o = o || [0,0];
8373
8374             if(!p){
8375                 p = "tl-bl";
8376             }else if(p == "?"){
8377                 p = "tl-bl?";
8378             }else if(p.indexOf("-") == -1){
8379                 p = "tl-" + p;
8380             }
8381             p = p.toLowerCase();
8382             var m = p.match(/^([a-z]+)-([a-z]+)(\?)?$/);
8383             if(!m){
8384                throw "Element.alignTo with an invalid alignment " + p;
8385             }
8386             p1 = m[1]; p2 = m[2]; c = !!m[3];
8387
8388             //Subtract the aligned el's internal xy from the target's offset xy
8389             //plus custom offset to get the aligned el's new offset xy
8390             var a1 = this.getAnchorXY(p1, true);
8391             var a2 = el.getAnchorXY(p2, false);
8392             var x = a2[0] - a1[0] + o[0];
8393             var y = a2[1] - a1[1] + o[1];
8394             if(c){
8395                 //constrain the aligned el to viewport if necessary
8396                 var w = this.getWidth(), h = this.getHeight(), r = el.getRegion();
8397                 // 5px of margin for ie
8398                 var dw = D.getViewWidth()-5, dh = D.getViewHeight()-5;
8399
8400                 //If we are at a viewport boundary and the aligned el is anchored on a target border that is
8401                 //perpendicular to the vp border, allow the aligned el to slide on that border,
8402                 //otherwise swap the aligned el to the opposite border of the target.
8403                 var p1y = p1.charAt(0), p1x = p1.charAt(p1.length-1);
8404                var p2y = p2.charAt(0), p2x = p2.charAt(p2.length-1);
8405                var swapY = ((p1y=="t" && p2y=="b") || (p1y=="b" && p2y=="t"));
8406                var swapX = ((p1x=="r" && p2x=="l") || (p1x=="l" && p2x=="r"));
8407
8408                var doc = document;
8409                var scrollX = (doc.documentElement.scrollLeft || doc.body.scrollLeft || 0)+5;
8410                var scrollY = (doc.documentElement.scrollTop || doc.body.scrollTop || 0)+5;
8411
8412                if((x+w) > dw + scrollX){
8413                     x = swapX ? r.left-w : dw+scrollX-w;
8414                 }
8415                if(x < scrollX){
8416                    x = swapX ? r.right : scrollX;
8417                }
8418                if((y+h) > dh + scrollY){
8419                     y = swapY ? r.top-h : dh+scrollY-h;
8420                 }
8421                if (y < scrollY){
8422                    y = swapY ? r.bottom : scrollY;
8423                }
8424             }
8425             return [x,y];
8426         },
8427
8428         // private
8429         getConstrainToXY : function(){
8430             var os = {top:0, left:0, bottom:0, right: 0};
8431
8432             return function(el, local, offsets, proposedXY){
8433                 el = Roo.get(el);
8434                 offsets = offsets ? Roo.applyIf(offsets, os) : os;
8435
8436                 var vw, vh, vx = 0, vy = 0;
8437                 if(el.dom == document.body || el.dom == document){
8438                     vw = Roo.lib.Dom.getViewWidth();
8439                     vh = Roo.lib.Dom.getViewHeight();
8440                 }else{
8441                     vw = el.dom.clientWidth;
8442                     vh = el.dom.clientHeight;
8443                     if(!local){
8444                         var vxy = el.getXY();
8445                         vx = vxy[0];
8446                         vy = vxy[1];
8447                     }
8448                 }
8449
8450                 var s = el.getScroll();
8451
8452                 vx += offsets.left + s.left;
8453                 vy += offsets.top + s.top;
8454
8455                 vw -= offsets.right;
8456                 vh -= offsets.bottom;
8457
8458                 var vr = vx+vw;
8459                 var vb = vy+vh;
8460
8461                 var xy = proposedXY || (!local ? this.getXY() : [this.getLeft(true), this.getTop(true)]);
8462                 var x = xy[0], y = xy[1];
8463                 var w = this.dom.offsetWidth, h = this.dom.offsetHeight;
8464
8465                 // only move it if it needs it
8466                 var moved = false;
8467
8468                 // first validate right/bottom
8469                 if((x + w) > vr){
8470                     x = vr - w;
8471                     moved = true;
8472                 }
8473                 if((y + h) > vb){
8474                     y = vb - h;
8475                     moved = true;
8476                 }
8477                 // then make sure top/left isn't negative
8478                 if(x < vx){
8479                     x = vx;
8480                     moved = true;
8481                 }
8482                 if(y < vy){
8483                     y = vy;
8484                     moved = true;
8485                 }
8486                 return moved ? [x, y] : false;
8487             };
8488         }(),
8489
8490         // private
8491         adjustForConstraints : function(xy, parent, offsets){
8492             return this.getConstrainToXY(parent || document, false, offsets, xy) ||  xy;
8493         },
8494
8495         /**
8496          * Aligns this element with another element relative to the specified anchor points. If the other element is the
8497          * document it aligns it to the viewport.
8498          * The position parameter is optional, and can be specified in any one of the following formats:
8499          * <ul>
8500          *   <li><b>Blank</b>: Defaults to aligning the element's top-left corner to the target's bottom-left corner ("tl-bl").</li>
8501          *   <li><b>One anchor (deprecated)</b>: The passed anchor position is used as the target element's anchor point.
8502          *       The element being aligned will position its top-left corner (tl) to that point.  <i>This method has been
8503          *       deprecated in favor of the newer two anchor syntax below</i>.</li>
8504          *   <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
8505          *       element's anchor point, and the second value is used as the target's anchor point.</li>
8506          * </ul>
8507          * In addition to the anchor points, the position parameter also supports the "?" character.  If "?" is passed at the end of
8508          * the position string, the element will attempt to align as specified, but the position will be adjusted to constrain to
8509          * the viewport if necessary.  Note that the element being aligned might be swapped to align to a different position than
8510          * that specified in order to enforce the viewport constraints.
8511          * Following are all of the supported anchor positions:
8512     <pre>
8513     Value  Description
8514     -----  -----------------------------
8515     tl     The top left corner (default)
8516     t      The center of the top edge
8517     tr     The top right corner
8518     l      The center of the left edge
8519     c      In the center of the element
8520     r      The center of the right edge
8521     bl     The bottom left corner
8522     b      The center of the bottom edge
8523     br     The bottom right corner
8524     </pre>
8525     Example Usage:
8526     <pre><code>
8527     // align el to other-el using the default positioning ("tl-bl", non-constrained)
8528     el.alignTo("other-el");
8529
8530     // align the top left corner of el with the top right corner of other-el (constrained to viewport)
8531     el.alignTo("other-el", "tr?");
8532
8533     // align the bottom right corner of el with the center left edge of other-el
8534     el.alignTo("other-el", "br-l?");
8535
8536     // align the center of el with the bottom left corner of other-el and
8537     // adjust the x position by -6 pixels (and the y position by 0)
8538     el.alignTo("other-el", "c-bl", [-6, 0]);
8539     </code></pre>
8540          * @param {String/HTMLElement/Roo.Element} element The element to align to.
8541          * @param {String} position The position to align to.
8542          * @param {Array} offsets (optional) Offset the positioning by [x, y]
8543          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8544          * @return {Roo.Element} this
8545          */
8546         alignTo : function(element, position, offsets, animate){
8547             var xy = this.getAlignToXY(element, position, offsets);
8548             this.setXY(xy, this.preanim(arguments, 3));
8549             return this;
8550         },
8551
8552         /**
8553          * Anchors an element to another element and realigns it when the window is resized.
8554          * @param {String/HTMLElement/Roo.Element} element The element to align to.
8555          * @param {String} position The position to align to.
8556          * @param {Array} offsets (optional) Offset the positioning by [x, y]
8557          * @param {Boolean/Object} animate (optional) True for the default animation or a standard Element animation config object
8558          * @param {Boolean/Number} monitorScroll (optional) True to monitor body scroll and reposition. If this parameter
8559          * is a number, it is used as the buffer delay (defaults to 50ms).
8560          * @param {Function} callback The function to call after the animation finishes
8561          * @return {Roo.Element} this
8562          */
8563         anchorTo : function(el, alignment, offsets, animate, monitorScroll, callback){
8564             var action = function(){
8565                 this.alignTo(el, alignment, offsets, animate);
8566                 Roo.callback(callback, this);
8567             };
8568             Roo.EventManager.onWindowResize(action, this);
8569             var tm = typeof monitorScroll;
8570             if(tm != 'undefined'){
8571                 Roo.EventManager.on(window, 'scroll', action, this,
8572                     {buffer: tm == 'number' ? monitorScroll : 50});
8573             }
8574             action.call(this); // align immediately
8575             return this;
8576         },
8577         /**
8578          * Clears any opacity settings from this element. Required in some cases for IE.
8579          * @return {Roo.Element} this
8580          */
8581         clearOpacity : function(){
8582             if (window.ActiveXObject) {
8583                 if(typeof this.dom.style.filter == 'string' && (/alpha/i).test(this.dom.style.filter)){
8584                     this.dom.style.filter = "";
8585                 }
8586             } else {
8587                 this.dom.style.opacity = "";
8588                 this.dom.style["-moz-opacity"] = "";
8589                 this.dom.style["-khtml-opacity"] = "";
8590             }
8591             return this;
8592         },
8593
8594         /**
8595          * Hide this element - Uses display mode to determine whether to use "display" or "visibility". See {@link #setVisible}.
8596          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8597          * @return {Roo.Element} this
8598          */
8599         hide : function(animate){
8600             this.setVisible(false, this.preanim(arguments, 0));
8601             return this;
8602         },
8603
8604         /**
8605         * Show this element - Uses display mode to determine whether to use "display" or "visibility". See {@link #setVisible}.
8606         * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8607          * @return {Roo.Element} this
8608          */
8609         show : function(animate){
8610             this.setVisible(true, this.preanim(arguments, 0));
8611             return this;
8612         },
8613
8614         /**
8615          * @private Test if size has a unit, otherwise appends the default
8616          */
8617         addUnits : function(size){
8618             return Roo.Element.addUnits(size, this.defaultUnit);
8619         },
8620
8621         /**
8622          * Temporarily enables offsets (width,height,x,y) for an element with display:none, use endMeasure() when done.
8623          * @return {Roo.Element} this
8624          */
8625         beginMeasure : function(){
8626             var el = this.dom;
8627             if(el.offsetWidth || el.offsetHeight){
8628                 return this; // offsets work already
8629             }
8630             var changed = [];
8631             var p = this.dom, b = document.body; // start with this element
8632             while((!el.offsetWidth && !el.offsetHeight) && p && p.tagName && p != b){
8633                 var pe = Roo.get(p);
8634                 if(pe.getStyle('display') == 'none'){
8635                     changed.push({el: p, visibility: pe.getStyle("visibility")});
8636                     p.style.visibility = "hidden";
8637                     p.style.display = "block";
8638                 }
8639                 p = p.parentNode;
8640             }
8641             this._measureChanged = changed;
8642             return this;
8643
8644         },
8645
8646         /**
8647          * Restores displays to before beginMeasure was called
8648          * @return {Roo.Element} this
8649          */
8650         endMeasure : function(){
8651             var changed = this._measureChanged;
8652             if(changed){
8653                 for(var i = 0, len = changed.length; i < len; i++) {
8654                     var r = changed[i];
8655                     r.el.style.visibility = r.visibility;
8656                     r.el.style.display = "none";
8657                 }
8658                 this._measureChanged = null;
8659             }
8660             return this;
8661         },
8662
8663         /**
8664         * Update the innerHTML of this element, optionally searching for and processing scripts
8665         * @param {String} html The new HTML
8666         * @param {Boolean} loadScripts (optional) true to look for and process scripts
8667         * @param {Function} callback For async script loading you can be noticed when the update completes
8668         * @return {Roo.Element} this
8669          */
8670         update : function(html, loadScripts, callback){
8671             if(typeof html == "undefined"){
8672                 html = "";
8673             }
8674             if(loadScripts !== true){
8675                 this.dom.innerHTML = html;
8676                 if(typeof callback == "function"){
8677                     callback();
8678                 }
8679                 return this;
8680             }
8681             var id = Roo.id();
8682             var dom = this.dom;
8683
8684             html += '<span id="' + id + '"></span>';
8685
8686             E.onAvailable(id, function(){
8687                 var hd = document.getElementsByTagName("head")[0];
8688                 var re = /(?:<script([^>]*)?>)((\n|\r|.)*?)(?:<\/script>)/ig;
8689                 var srcRe = /\ssrc=([\'\"])(.*?)\1/i;
8690                 var typeRe = /\stype=([\'\"])(.*?)\1/i;
8691
8692                 var match;
8693                 while(match = re.exec(html)){
8694                     var attrs = match[1];
8695                     var srcMatch = attrs ? attrs.match(srcRe) : false;
8696                     if(srcMatch && srcMatch[2]){
8697                        var s = document.createElement("script");
8698                        s.src = srcMatch[2];
8699                        var typeMatch = attrs.match(typeRe);
8700                        if(typeMatch && typeMatch[2]){
8701                            s.type = typeMatch[2];
8702                        }
8703                        hd.appendChild(s);
8704                     }else if(match[2] && match[2].length > 0){
8705                         if(window.execScript) {
8706                            window.execScript(match[2]);
8707                         } else {
8708                             /**
8709                              * eval:var:id
8710                              * eval:var:dom
8711                              * eval:var:html
8712                              * 
8713                              */
8714                            window.eval(match[2]);
8715                         }
8716                     }
8717                 }
8718                 var el = document.getElementById(id);
8719                 if(el){el.parentNode.removeChild(el);}
8720                 if(typeof callback == "function"){
8721                     callback();
8722                 }
8723             });
8724             dom.innerHTML = html.replace(/(?:<script.*?>)((\n|\r|.)*?)(?:<\/script>)/ig, "");
8725             return this;
8726         },
8727
8728         /**
8729          * Direct access to the UpdateManager update() method (takes the same parameters).
8730          * @param {String/Function} url The url for this request or a function to call to get the url
8731          * @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}
8732          * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
8733          * @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.
8734          * @return {Roo.Element} this
8735          */
8736         load : function(){
8737             var um = this.getUpdateManager();
8738             um.update.apply(um, arguments);
8739             return this;
8740         },
8741
8742         /**
8743         * Gets this element's UpdateManager
8744         * @return {Roo.UpdateManager} The UpdateManager
8745         */
8746         getUpdateManager : function(){
8747             if(!this.updateManager){
8748                 this.updateManager = new Roo.UpdateManager(this);
8749             }
8750             return this.updateManager;
8751         },
8752
8753         /**
8754          * Disables text selection for this element (normalized across browsers)
8755          * @return {Roo.Element} this
8756          */
8757         unselectable : function(){
8758             this.dom.unselectable = "on";
8759             this.swallowEvent("selectstart", true);
8760             this.applyStyles("-moz-user-select:none;-khtml-user-select:none;");
8761             this.addClass("x-unselectable");
8762             return this;
8763         },
8764
8765         /**
8766         * Calculates the x, y to center this element on the screen
8767         * @return {Array} The x, y values [x, y]
8768         */
8769         getCenterXY : function(){
8770             return this.getAlignToXY(document, 'c-c');
8771         },
8772
8773         /**
8774         * Centers the Element in either the viewport, or another Element.
8775         * @param {String/HTMLElement/Roo.Element} centerIn (optional) The element in which to center the element.
8776         */
8777         center : function(centerIn){
8778             this.alignTo(centerIn || document, 'c-c');
8779             return this;
8780         },
8781
8782         /**
8783          * Tests various css rules/browsers to determine if this element uses a border box
8784          * @return {Boolean}
8785          */
8786         isBorderBox : function(){
8787             return noBoxAdjust[this.dom.tagName.toLowerCase()] || Roo.isBorderBox;
8788         },
8789
8790         /**
8791          * Return a box {x, y, width, height} that can be used to set another elements
8792          * size/location to match this element.
8793          * @param {Boolean} contentBox (optional) If true a box for the content of the element is returned.
8794          * @param {Boolean} local (optional) If true the element's left and top are returned instead of page x/y.
8795          * @return {Object} box An object in the format {x, y, width, height}
8796          */
8797         getBox : function(contentBox, local){
8798             var xy;
8799             if(!local){
8800                 xy = this.getXY();
8801             }else{
8802                 var left = parseInt(this.getStyle("left"), 10) || 0;
8803                 var top = parseInt(this.getStyle("top"), 10) || 0;
8804                 xy = [left, top];
8805             }
8806             var el = this.dom, w = el.offsetWidth, h = el.offsetHeight, bx;
8807             if(!contentBox){
8808                 bx = {x: xy[0], y: xy[1], 0: xy[0], 1: xy[1], width: w, height: h};
8809             }else{
8810                 var l = this.getBorderWidth("l")+this.getPadding("l");
8811                 var r = this.getBorderWidth("r")+this.getPadding("r");
8812                 var t = this.getBorderWidth("t")+this.getPadding("t");
8813                 var b = this.getBorderWidth("b")+this.getPadding("b");
8814                 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)};
8815             }
8816             bx.right = bx.x + bx.width;
8817             bx.bottom = bx.y + bx.height;
8818             return bx;
8819         },
8820
8821         /**
8822          * Returns the sum width of the padding and borders for the passed "sides". See getBorderWidth()
8823          for more information about the sides.
8824          * @param {String} sides
8825          * @return {Number}
8826          */
8827         getFrameWidth : function(sides, onlyContentBox){
8828             return onlyContentBox && Roo.isBorderBox ? 0 : (this.getPadding(sides) + this.getBorderWidth(sides));
8829         },
8830
8831         /**
8832          * 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.
8833          * @param {Object} box The box to fill {x, y, width, height}
8834          * @param {Boolean} adjust (optional) Whether to adjust for box-model issues automatically
8835          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8836          * @return {Roo.Element} this
8837          */
8838         setBox : function(box, adjust, animate){
8839             var w = box.width, h = box.height;
8840             if((adjust && !this.autoBoxAdjust) && !this.isBorderBox()){
8841                w -= (this.getBorderWidth("lr") + this.getPadding("lr"));
8842                h -= (this.getBorderWidth("tb") + this.getPadding("tb"));
8843             }
8844             this.setBounds(box.x, box.y, w, h, this.preanim(arguments, 2));
8845             return this;
8846         },
8847
8848         /**
8849          * Forces the browser to repaint this element
8850          * @return {Roo.Element} this
8851          */
8852          repaint : function(){
8853             var dom = this.dom;
8854             this.addClass("x-repaint");
8855             setTimeout(function(){
8856                 Roo.get(dom).removeClass("x-repaint");
8857             }, 1);
8858             return this;
8859         },
8860
8861         /**
8862          * Returns an object with properties top, left, right and bottom representing the margins of this element unless sides is passed,
8863          * then it returns the calculated width of the sides (see getPadding)
8864          * @param {String} sides (optional) Any combination of l, r, t, b to get the sum of those sides
8865          * @return {Object/Number}
8866          */
8867         getMargins : function(side){
8868             if(!side){
8869                 return {
8870                     top: parseInt(this.getStyle("margin-top"), 10) || 0,
8871                     left: parseInt(this.getStyle("margin-left"), 10) || 0,
8872                     bottom: parseInt(this.getStyle("margin-bottom"), 10) || 0,
8873                     right: parseInt(this.getStyle("margin-right"), 10) || 0
8874                 };
8875             }else{
8876                 return this.addStyles(side, El.margins);
8877              }
8878         },
8879
8880         // private
8881         addStyles : function(sides, styles){
8882             var val = 0, v, w;
8883             for(var i = 0, len = sides.length; i < len; i++){
8884                 v = this.getStyle(styles[sides.charAt(i)]);
8885                 if(v){
8886                      w = parseInt(v, 10);
8887                      if(w){ val += w; }
8888                 }
8889             }
8890             return val;
8891         },
8892
8893         /**
8894          * Creates a proxy element of this element
8895          * @param {String/Object} config The class name of the proxy element or a DomHelper config object
8896          * @param {String/HTMLElement} renderTo (optional) The element or element id to render the proxy to (defaults to document.body)
8897          * @param {Boolean} matchBox (optional) True to align and size the proxy to this element now (defaults to false)
8898          * @return {Roo.Element} The new proxy element
8899          */
8900         createProxy : function(config, renderTo, matchBox){
8901             if(renderTo){
8902                 renderTo = Roo.getDom(renderTo);
8903             }else{
8904                 renderTo = document.body;
8905             }
8906             config = typeof config == "object" ?
8907                 config : {tag : "div", cls: config};
8908             var proxy = Roo.DomHelper.append(renderTo, config, true);
8909             if(matchBox){
8910                proxy.setBox(this.getBox());
8911             }
8912             return proxy;
8913         },
8914
8915         /**
8916          * Puts a mask over this element to disable user interaction. Requires core.css.
8917          * This method can only be applied to elements which accept child nodes.
8918          * @param {String} msg (optional) A message to display in the mask
8919          * @param {String} msgCls (optional) A css class to apply to the msg element
8920          * @return {Element} The mask  element
8921          */
8922         mask : function(msg, msgCls)
8923         {
8924             if(this.getStyle("position") == "static"){
8925                 this.setStyle("position", "relative");
8926             }
8927             if(!this._mask){
8928                 this._mask = Roo.DomHelper.append(this.dom, {cls:"roo-el-mask"}, true);
8929             }
8930             this.addClass("x-masked");
8931             this._mask.setDisplayed(true);
8932             
8933             // we wander
8934             var z = 0;
8935             var dom = this.dom
8936             while (dom && dom.style) {
8937                 if (!isNaN(parseInt(dom.style.zIndex))) {
8938                     z = Math.max(z, parseInt(dom.style.zIndex));
8939                 }
8940                 dom = dom.parentNode;
8941             }
8942             // if we are masking the body - then it hides everything..
8943             if (this.dom == document.body) {
8944                 z = 1000000;
8945                 this._mask.setWidth(Roo.lib.Dom.getDocumentWidth());
8946                 this._mask.setHeight(Roo.lib.Dom.getDocumentHeight());
8947             }
8948            
8949             if(typeof msg == 'string'){
8950                 if(!this._maskMsg){
8951                     this._maskMsg = Roo.DomHelper.append(this.dom, {cls:"roo-el-mask-msg", cn:{tag:'div'}}, true);
8952                 }
8953                 var mm = this._maskMsg;
8954                 mm.dom.className = msgCls ? "roo-el-mask-msg " + msgCls : "roo-el-mask-msg";
8955                 mm.dom.firstChild.innerHTML = msg;
8956                 mm.setDisplayed(true);
8957                 mm.center(this);
8958                 mm.setStyle('z-index', z + 102);
8959             }
8960             if(Roo.isIE && !(Roo.isIE7 && Roo.isStrict) && this.getStyle('height') == 'auto'){ // ie will not expand full height automatically
8961                 this._mask.setHeight(this.getHeight());
8962             }
8963             this._mask.setStyle('z-index', z + 100);
8964             
8965             return this._mask;
8966         },
8967
8968         /**
8969          * Removes a previously applied mask. If removeEl is true the mask overlay is destroyed, otherwise
8970          * it is cached for reuse.
8971          */
8972         unmask : function(removeEl){
8973             if(this._mask){
8974                 if(removeEl === true){
8975                     this._mask.remove();
8976                     delete this._mask;
8977                     if(this._maskMsg){
8978                         this._maskMsg.remove();
8979                         delete this._maskMsg;
8980                     }
8981                 }else{
8982                     this._mask.setDisplayed(false);
8983                     if(this._maskMsg){
8984                         this._maskMsg.setDisplayed(false);
8985                     }
8986                 }
8987             }
8988             this.removeClass("x-masked");
8989         },
8990
8991         /**
8992          * Returns true if this element is masked
8993          * @return {Boolean}
8994          */
8995         isMasked : function(){
8996             return this._mask && this._mask.isVisible();
8997         },
8998
8999         /**
9000          * Creates an iframe shim for this element to keep selects and other windowed objects from
9001          * showing through.
9002          * @return {Roo.Element} The new shim element
9003          */
9004         createShim : function(){
9005             var el = document.createElement('iframe');
9006             el.frameBorder = 'no';
9007             el.className = 'roo-shim';
9008             if(Roo.isIE && Roo.isSecure){
9009                 el.src = Roo.SSL_SECURE_URL;
9010             }
9011             var shim = Roo.get(this.dom.parentNode.insertBefore(el, this.dom));
9012             shim.autoBoxAdjust = false;
9013             return shim;
9014         },
9015
9016         /**
9017          * Removes this element from the DOM and deletes it from the cache
9018          */
9019         remove : function(){
9020             if(this.dom.parentNode){
9021                 this.dom.parentNode.removeChild(this.dom);
9022             }
9023             delete El.cache[this.dom.id];
9024         },
9025
9026         /**
9027          * Sets up event handlers to add and remove a css class when the mouse is over this element
9028          * @param {String} className
9029          * @param {Boolean} preventFlicker (optional) If set to true, it prevents flickering by filtering
9030          * mouseout events for children elements
9031          * @return {Roo.Element} this
9032          */
9033         addClassOnOver : function(className, preventFlicker){
9034             this.on("mouseover", function(){
9035                 Roo.fly(this, '_internal').addClass(className);
9036             }, this.dom);
9037             var removeFn = function(e){
9038                 if(preventFlicker !== true || !e.within(this, true)){
9039                     Roo.fly(this, '_internal').removeClass(className);
9040                 }
9041             };
9042             this.on("mouseout", removeFn, this.dom);
9043             return this;
9044         },
9045
9046         /**
9047          * Sets up event handlers to add and remove a css class when this element has the focus
9048          * @param {String} className
9049          * @return {Roo.Element} this
9050          */
9051         addClassOnFocus : function(className){
9052             this.on("focus", function(){
9053                 Roo.fly(this, '_internal').addClass(className);
9054             }, this.dom);
9055             this.on("blur", function(){
9056                 Roo.fly(this, '_internal').removeClass(className);
9057             }, this.dom);
9058             return this;
9059         },
9060         /**
9061          * 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)
9062          * @param {String} className
9063          * @return {Roo.Element} this
9064          */
9065         addClassOnClick : function(className){
9066             var dom = this.dom;
9067             this.on("mousedown", function(){
9068                 Roo.fly(dom, '_internal').addClass(className);
9069                 var d = Roo.get(document);
9070                 var fn = function(){
9071                     Roo.fly(dom, '_internal').removeClass(className);
9072                     d.removeListener("mouseup", fn);
9073                 };
9074                 d.on("mouseup", fn);
9075             });
9076             return this;
9077         },
9078
9079         /**
9080          * Stops the specified event from bubbling and optionally prevents the default action
9081          * @param {String} eventName
9082          * @param {Boolean} preventDefault (optional) true to prevent the default action too
9083          * @return {Roo.Element} this
9084          */
9085         swallowEvent : function(eventName, preventDefault){
9086             var fn = function(e){
9087                 e.stopPropagation();
9088                 if(preventDefault){
9089                     e.preventDefault();
9090                 }
9091             };
9092             if(eventName instanceof Array){
9093                 for(var i = 0, len = eventName.length; i < len; i++){
9094                      this.on(eventName[i], fn);
9095                 }
9096                 return this;
9097             }
9098             this.on(eventName, fn);
9099             return this;
9100         },
9101
9102         /**
9103          * @private
9104          */
9105       fitToParentDelegate : Roo.emptyFn, // keep a reference to the fitToParent delegate
9106
9107         /**
9108          * Sizes this element to its parent element's dimensions performing
9109          * neccessary box adjustments.
9110          * @param {Boolean} monitorResize (optional) If true maintains the fit when the browser window is resized.
9111          * @param {String/HTMLElment/Element} targetParent (optional) The target parent, default to the parentNode.
9112          * @return {Roo.Element} this
9113          */
9114         fitToParent : function(monitorResize, targetParent) {
9115           Roo.EventManager.removeResizeListener(this.fitToParentDelegate); // always remove previous fitToParent delegate from onWindowResize
9116           this.fitToParentDelegate = Roo.emptyFn; // remove reference to previous delegate
9117           if (monitorResize === true && !this.dom.parentNode) { // check if this Element still exists
9118             return;
9119           }
9120           var p = Roo.get(targetParent || this.dom.parentNode);
9121           this.setSize(p.getComputedWidth() - p.getFrameWidth('lr'), p.getComputedHeight() - p.getFrameWidth('tb'));
9122           if (monitorResize === true) {
9123             this.fitToParentDelegate = this.fitToParent.createDelegate(this, [true, targetParent]);
9124             Roo.EventManager.onWindowResize(this.fitToParentDelegate);
9125           }
9126           return this;
9127         },
9128
9129         /**
9130          * Gets the next sibling, skipping text nodes
9131          * @return {HTMLElement} The next sibling or null
9132          */
9133         getNextSibling : function(){
9134             var n = this.dom.nextSibling;
9135             while(n && n.nodeType != 1){
9136                 n = n.nextSibling;
9137             }
9138             return n;
9139         },
9140
9141         /**
9142          * Gets the previous sibling, skipping text nodes
9143          * @return {HTMLElement} The previous sibling or null
9144          */
9145         getPrevSibling : function(){
9146             var n = this.dom.previousSibling;
9147             while(n && n.nodeType != 1){
9148                 n = n.previousSibling;
9149             }
9150             return n;
9151         },
9152
9153
9154         /**
9155          * Appends the passed element(s) to this element
9156          * @param {String/HTMLElement/Array/Element/CompositeElement} el
9157          * @return {Roo.Element} this
9158          */
9159         appendChild: function(el){
9160             el = Roo.get(el);
9161             el.appendTo(this);
9162             return this;
9163         },
9164
9165         /**
9166          * Creates the passed DomHelper config and appends it to this element or optionally inserts it before the passed child element.
9167          * @param {Object} config DomHelper element config object.  If no tag is specified (e.g., {tag:'input'}) then a div will be
9168          * automatically generated with the specified attributes.
9169          * @param {HTMLElement} insertBefore (optional) a child element of this element
9170          * @param {Boolean} returnDom (optional) true to return the dom node instead of creating an Element
9171          * @return {Roo.Element} The new child element
9172          */
9173         createChild: function(config, insertBefore, returnDom){
9174             config = config || {tag:'div'};
9175             if(insertBefore){
9176                 return Roo.DomHelper.insertBefore(insertBefore, config, returnDom !== true);
9177             }
9178             return Roo.DomHelper[!this.dom.firstChild ? 'overwrite' : 'append'](this.dom, config,  returnDom !== true);
9179         },
9180
9181         /**
9182          * Appends this element to the passed element
9183          * @param {String/HTMLElement/Element} el The new parent element
9184          * @return {Roo.Element} this
9185          */
9186         appendTo: function(el){
9187             el = Roo.getDom(el);
9188             el.appendChild(this.dom);
9189             return this;
9190         },
9191
9192         /**
9193          * Inserts this element before the passed element in the DOM
9194          * @param {String/HTMLElement/Element} el The element to insert before
9195          * @return {Roo.Element} this
9196          */
9197         insertBefore: function(el){
9198             el = Roo.getDom(el);
9199             el.parentNode.insertBefore(this.dom, el);
9200             return this;
9201         },
9202
9203         /**
9204          * Inserts this element after the passed element in the DOM
9205          * @param {String/HTMLElement/Element} el The element to insert after
9206          * @return {Roo.Element} this
9207          */
9208         insertAfter: function(el){
9209             el = Roo.getDom(el);
9210             el.parentNode.insertBefore(this.dom, el.nextSibling);
9211             return this;
9212         },
9213
9214         /**
9215          * Inserts (or creates) an element (or DomHelper config) as the first child of the this element
9216          * @param {String/HTMLElement/Element/Object} el The id or element to insert or a DomHelper config to create and insert
9217          * @return {Roo.Element} The new child
9218          */
9219         insertFirst: function(el, returnDom){
9220             el = el || {};
9221             if(typeof el == 'object' && !el.nodeType){ // dh config
9222                 return this.createChild(el, this.dom.firstChild, returnDom);
9223             }else{
9224                 el = Roo.getDom(el);
9225                 this.dom.insertBefore(el, this.dom.firstChild);
9226                 return !returnDom ? Roo.get(el) : el;
9227             }
9228         },
9229
9230         /**
9231          * Inserts (or creates) the passed element (or DomHelper config) as a sibling of this element
9232          * @param {String/HTMLElement/Element/Object} el The id or element to insert or a DomHelper config to create and insert
9233          * @param {String} where (optional) 'before' or 'after' defaults to before
9234          * @param {Boolean} returnDom (optional) True to return the raw DOM element instead of Roo.Element
9235          * @return {Roo.Element} the inserted Element
9236          */
9237         insertSibling: function(el, where, returnDom){
9238             where = where ? where.toLowerCase() : 'before';
9239             el = el || {};
9240             var rt, refNode = where == 'before' ? this.dom : this.dom.nextSibling;
9241
9242             if(typeof el == 'object' && !el.nodeType){ // dh config
9243                 if(where == 'after' && !this.dom.nextSibling){
9244                     rt = Roo.DomHelper.append(this.dom.parentNode, el, !returnDom);
9245                 }else{
9246                     rt = Roo.DomHelper[where == 'after' ? 'insertAfter' : 'insertBefore'](this.dom, el, !returnDom);
9247                 }
9248
9249             }else{
9250                 rt = this.dom.parentNode.insertBefore(Roo.getDom(el),
9251                             where == 'before' ? this.dom : this.dom.nextSibling);
9252                 if(!returnDom){
9253                     rt = Roo.get(rt);
9254                 }
9255             }
9256             return rt;
9257         },
9258
9259         /**
9260          * Creates and wraps this element with another element
9261          * @param {Object} config (optional) DomHelper element config object for the wrapper element or null for an empty div
9262          * @param {Boolean} returnDom (optional) True to return the raw DOM element instead of Roo.Element
9263          * @return {HTMLElement/Element} The newly created wrapper element
9264          */
9265         wrap: function(config, returnDom){
9266             if(!config){
9267                 config = {tag: "div"};
9268             }
9269             var newEl = Roo.DomHelper.insertBefore(this.dom, config, !returnDom);
9270             newEl.dom ? newEl.dom.appendChild(this.dom) : newEl.appendChild(this.dom);
9271             return newEl;
9272         },
9273
9274         /**
9275          * Replaces the passed element with this element
9276          * @param {String/HTMLElement/Element} el The element to replace
9277          * @return {Roo.Element} this
9278          */
9279         replace: function(el){
9280             el = Roo.get(el);
9281             this.insertBefore(el);
9282             el.remove();
9283             return this;
9284         },
9285
9286         /**
9287          * Inserts an html fragment into this element
9288          * @param {String} where Where to insert the html in relation to the this element - beforeBegin, afterBegin, beforeEnd, afterEnd.
9289          * @param {String} html The HTML fragment
9290          * @param {Boolean} returnEl True to return an Roo.Element
9291          * @return {HTMLElement/Roo.Element} The inserted node (or nearest related if more than 1 inserted)
9292          */
9293         insertHtml : function(where, html, returnEl){
9294             var el = Roo.DomHelper.insertHtml(where, this.dom, html);
9295             return returnEl ? Roo.get(el) : el;
9296         },
9297
9298         /**
9299          * Sets the passed attributes as attributes of this element (a style attribute can be a string, object or function)
9300          * @param {Object} o The object with the attributes
9301          * @param {Boolean} useSet (optional) false to override the default setAttribute to use expandos.
9302          * @return {Roo.Element} this
9303          */
9304         set : function(o, useSet){
9305             var el = this.dom;
9306             useSet = typeof useSet == 'undefined' ? (el.setAttribute ? true : false) : useSet;
9307             for(var attr in o){
9308                 if(attr == "style" || typeof o[attr] == "function") continue;
9309                 if(attr=="cls"){
9310                     el.className = o["cls"];
9311                 }else{
9312                     if(useSet) el.setAttribute(attr, o[attr]);
9313                     else el[attr] = o[attr];
9314                 }
9315             }
9316             if(o.style){
9317                 Roo.DomHelper.applyStyles(el, o.style);
9318             }
9319             return this;
9320         },
9321
9322         /**
9323          * Convenience method for constructing a KeyMap
9324          * @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:
9325          *                                  {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
9326          * @param {Function} fn The function to call
9327          * @param {Object} scope (optional) The scope of the function
9328          * @return {Roo.KeyMap} The KeyMap created
9329          */
9330         addKeyListener : function(key, fn, scope){
9331             var config;
9332             if(typeof key != "object" || key instanceof Array){
9333                 config = {
9334                     key: key,
9335                     fn: fn,
9336                     scope: scope
9337                 };
9338             }else{
9339                 config = {
9340                     key : key.key,
9341                     shift : key.shift,
9342                     ctrl : key.ctrl,
9343                     alt : key.alt,
9344                     fn: fn,
9345                     scope: scope
9346                 };
9347             }
9348             return new Roo.KeyMap(this, config);
9349         },
9350
9351         /**
9352          * Creates a KeyMap for this element
9353          * @param {Object} config The KeyMap config. See {@link Roo.KeyMap} for more details
9354          * @return {Roo.KeyMap} The KeyMap created
9355          */
9356         addKeyMap : function(config){
9357             return new Roo.KeyMap(this, config);
9358         },
9359
9360         /**
9361          * Returns true if this element is scrollable.
9362          * @return {Boolean}
9363          */
9364          isScrollable : function(){
9365             var dom = this.dom;
9366             return dom.scrollHeight > dom.clientHeight || dom.scrollWidth > dom.clientWidth;
9367         },
9368
9369         /**
9370          * 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().
9371          * @param {String} side Either "left" for scrollLeft values or "top" for scrollTop values.
9372          * @param {Number} value The new scroll value
9373          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9374          * @return {Element} this
9375          */
9376
9377         scrollTo : function(side, value, animate){
9378             var prop = side.toLowerCase() == "left" ? "scrollLeft" : "scrollTop";
9379             if(!animate || !A){
9380                 this.dom[prop] = value;
9381             }else{
9382                 var to = prop == "scrollLeft" ? [value, this.dom.scrollTop] : [this.dom.scrollLeft, value];
9383                 this.anim({scroll: {"to": to}}, this.preanim(arguments, 2), 'scroll');
9384             }
9385             return this;
9386         },
9387
9388         /**
9389          * Scrolls this element the specified direction. Does bounds checking to make sure the scroll is
9390          * within this element's scrollable range.
9391          * @param {String} direction Possible values are: "l","left" - "r","right" - "t","top","up" - "b","bottom","down".
9392          * @param {Number} distance How far to scroll the element in pixels
9393          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9394          * @return {Boolean} Returns true if a scroll was triggered or false if the element
9395          * was scrolled as far as it could go.
9396          */
9397          scroll : function(direction, distance, animate){
9398              if(!this.isScrollable()){
9399                  return;
9400              }
9401              var el = this.dom;
9402              var l = el.scrollLeft, t = el.scrollTop;
9403              var w = el.scrollWidth, h = el.scrollHeight;
9404              var cw = el.clientWidth, ch = el.clientHeight;
9405              direction = direction.toLowerCase();
9406              var scrolled = false;
9407              var a = this.preanim(arguments, 2);
9408              switch(direction){
9409                  case "l":
9410                  case "left":
9411                      if(w - l > cw){
9412                          var v = Math.min(l + distance, w-cw);
9413                          this.scrollTo("left", v, a);
9414                          scrolled = true;
9415                      }
9416                      break;
9417                 case "r":
9418                 case "right":
9419                      if(l > 0){
9420                          var v = Math.max(l - distance, 0);
9421                          this.scrollTo("left", v, a);
9422                          scrolled = true;
9423                      }
9424                      break;
9425                 case "t":
9426                 case "top":
9427                 case "up":
9428                      if(t > 0){
9429                          var v = Math.max(t - distance, 0);
9430                          this.scrollTo("top", v, a);
9431                          scrolled = true;
9432                      }
9433                      break;
9434                 case "b":
9435                 case "bottom":
9436                 case "down":
9437                      if(h - t > ch){
9438                          var v = Math.min(t + distance, h-ch);
9439                          this.scrollTo("top", v, a);
9440                          scrolled = true;
9441                      }
9442                      break;
9443              }
9444              return scrolled;
9445         },
9446
9447         /**
9448          * Translates the passed page coordinates into left/top css values for this element
9449          * @param {Number/Array} x The page x or an array containing [x, y]
9450          * @param {Number} y The page y
9451          * @return {Object} An object with left and top properties. e.g. {left: (value), top: (value)}
9452          */
9453         translatePoints : function(x, y){
9454             if(typeof x == 'object' || x instanceof Array){
9455                 y = x[1]; x = x[0];
9456             }
9457             var p = this.getStyle('position');
9458             var o = this.getXY();
9459
9460             var l = parseInt(this.getStyle('left'), 10);
9461             var t = parseInt(this.getStyle('top'), 10);
9462
9463             if(isNaN(l)){
9464                 l = (p == "relative") ? 0 : this.dom.offsetLeft;
9465             }
9466             if(isNaN(t)){
9467                 t = (p == "relative") ? 0 : this.dom.offsetTop;
9468             }
9469
9470             return {left: (x - o[0] + l), top: (y - o[1] + t)};
9471         },
9472
9473         /**
9474          * Returns the current scroll position of the element.
9475          * @return {Object} An object containing the scroll position in the format {left: (scrollLeft), top: (scrollTop)}
9476          */
9477         getScroll : function(){
9478             var d = this.dom, doc = document;
9479             if(d == doc || d == doc.body){
9480                 var l = window.pageXOffset || doc.documentElement.scrollLeft || doc.body.scrollLeft || 0;
9481                 var t = window.pageYOffset || doc.documentElement.scrollTop || doc.body.scrollTop || 0;
9482                 return {left: l, top: t};
9483             }else{
9484                 return {left: d.scrollLeft, top: d.scrollTop};
9485             }
9486         },
9487
9488         /**
9489          * Return the CSS color for the specified CSS attribute. rgb, 3 digit (like #fff) and valid values
9490          * are convert to standard 6 digit hex color.
9491          * @param {String} attr The css attribute
9492          * @param {String} defaultValue The default value to use when a valid color isn't found
9493          * @param {String} prefix (optional) defaults to #. Use an empty string when working with
9494          * YUI color anims.
9495          */
9496         getColor : function(attr, defaultValue, prefix){
9497             var v = this.getStyle(attr);
9498             if(!v || v == "transparent" || v == "inherit") {
9499                 return defaultValue;
9500             }
9501             var color = typeof prefix == "undefined" ? "#" : prefix;
9502             if(v.substr(0, 4) == "rgb("){
9503                 var rvs = v.slice(4, v.length -1).split(",");
9504                 for(var i = 0; i < 3; i++){
9505                     var h = parseInt(rvs[i]).toString(16);
9506                     if(h < 16){
9507                         h = "0" + h;
9508                     }
9509                     color += h;
9510                 }
9511             } else {
9512                 if(v.substr(0, 1) == "#"){
9513                     if(v.length == 4) {
9514                         for(var i = 1; i < 4; i++){
9515                             var c = v.charAt(i);
9516                             color +=  c + c;
9517                         }
9518                     }else if(v.length == 7){
9519                         color += v.substr(1);
9520                     }
9521                 }
9522             }
9523             return(color.length > 5 ? color.toLowerCase() : defaultValue);
9524         },
9525
9526         /**
9527          * Wraps the specified element with a special markup/CSS block that renders by default as a gray container with a
9528          * gradient background, rounded corners and a 4-way shadow.
9529          * @param {String} class (optional) A base CSS class to apply to the containing wrapper element (defaults to 'x-box').
9530          * Note that there are a number of CSS rules that are dependent on this name to make the overall effect work,
9531          * so if you supply an alternate base class, make sure you also supply all of the necessary rules.
9532          * @return {Roo.Element} this
9533          */
9534         boxWrap : function(cls){
9535             cls = cls || 'x-box';
9536             var el = Roo.get(this.insertHtml('beforeBegin', String.format('<div class="{0}">'+El.boxMarkup+'</div>', cls)));
9537             el.child('.'+cls+'-mc').dom.appendChild(this.dom);
9538             return el;
9539         },
9540
9541         /**
9542          * Returns the value of a namespaced attribute from the element's underlying DOM node.
9543          * @param {String} namespace The namespace in which to look for the attribute
9544          * @param {String} name The attribute name
9545          * @return {String} The attribute value
9546          */
9547         getAttributeNS : Roo.isIE ? function(ns, name){
9548             var d = this.dom;
9549             var type = typeof d[ns+":"+name];
9550             if(type != 'undefined' && type != 'unknown'){
9551                 return d[ns+":"+name];
9552             }
9553             return d[name];
9554         } : function(ns, name){
9555             var d = this.dom;
9556             return d.getAttributeNS(ns, name) || d.getAttribute(ns+":"+name) || d.getAttribute(name) || d[name];
9557         },
9558         
9559         
9560         /**
9561          * Sets or Returns the value the dom attribute value
9562          * @param {String} name The attribute name
9563          * @param {String} value (optional) The value to set the attribute to
9564          * @return {String} The attribute value
9565          */
9566         attr : function(name){
9567             if (arguments.length > 1) {
9568                 this.dom.setAttribute(name, arguments[1]);
9569                 return arguments[1];
9570             }
9571             if (!this.dom.hasAttribute(name)) {
9572                 return undefined;
9573             }
9574             return this.dom.getAttribute(name);
9575         }
9576         
9577         
9578         
9579     };
9580
9581     var ep = El.prototype;
9582
9583     /**
9584      * Appends an event handler (Shorthand for addListener)
9585      * @param {String}   eventName     The type of event to append
9586      * @param {Function} fn        The method the event invokes
9587      * @param {Object} scope       (optional) The scope (this object) of the fn
9588      * @param {Object}   options   (optional)An object with standard {@link Roo.EventManager#addListener} options
9589      * @method
9590      */
9591     ep.on = ep.addListener;
9592         // backwards compat
9593     ep.mon = ep.addListener;
9594
9595     /**
9596      * Removes an event handler from this element (shorthand for removeListener)
9597      * @param {String} eventName the type of event to remove
9598      * @param {Function} fn the method the event invokes
9599      * @return {Roo.Element} this
9600      * @method
9601      */
9602     ep.un = ep.removeListener;
9603
9604     /**
9605      * true to automatically adjust width and height settings for box-model issues (default to true)
9606      */
9607     ep.autoBoxAdjust = true;
9608
9609     // private
9610     El.unitPattern = /\d+(px|em|%|en|ex|pt|in|cm|mm|pc)$/i;
9611
9612     // private
9613     El.addUnits = function(v, defaultUnit){
9614         if(v === "" || v == "auto"){
9615             return v;
9616         }
9617         if(v === undefined){
9618             return '';
9619         }
9620         if(typeof v == "number" || !El.unitPattern.test(v)){
9621             return v + (defaultUnit || 'px');
9622         }
9623         return v;
9624     };
9625
9626     // special markup used throughout Roo when box wrapping elements
9627     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>';
9628     /**
9629      * Visibility mode constant - Use visibility to hide element
9630      * @static
9631      * @type Number
9632      */
9633     El.VISIBILITY = 1;
9634     /**
9635      * Visibility mode constant - Use display to hide element
9636      * @static
9637      * @type Number
9638      */
9639     El.DISPLAY = 2;
9640
9641     El.borders = {l: "border-left-width", r: "border-right-width", t: "border-top-width", b: "border-bottom-width"};
9642     El.paddings = {l: "padding-left", r: "padding-right", t: "padding-top", b: "padding-bottom"};
9643     El.margins = {l: "margin-left", r: "margin-right", t: "margin-top", b: "margin-bottom"};
9644
9645
9646
9647     /**
9648      * @private
9649      */
9650     El.cache = {};
9651
9652     var docEl;
9653
9654     /**
9655      * Static method to retrieve Element objects. Uses simple caching to consistently return the same object.
9656      * Automatically fixes if an object was recreated with the same id via AJAX or DOM.
9657      * @param {String/HTMLElement/Element} el The id of the node, a DOM Node or an existing Element.
9658      * @return {Element} The Element object
9659      * @static
9660      */
9661     El.get = function(el){
9662         var ex, elm, id;
9663         if(!el){ return null; }
9664         if(typeof el == "string"){ // element id
9665             if(!(elm = document.getElementById(el))){
9666                 return null;
9667             }
9668             if(ex = El.cache[el]){
9669                 ex.dom = elm;
9670             }else{
9671                 ex = El.cache[el] = new El(elm);
9672             }
9673             return ex;
9674         }else if(el.tagName){ // dom element
9675             if(!(id = el.id)){
9676                 id = Roo.id(el);
9677             }
9678             if(ex = El.cache[id]){
9679                 ex.dom = el;
9680             }else{
9681                 ex = El.cache[id] = new El(el);
9682             }
9683             return ex;
9684         }else if(el instanceof El){
9685             if(el != docEl){
9686                 el.dom = document.getElementById(el.id) || el.dom; // refresh dom element in case no longer valid,
9687                                                               // catch case where it hasn't been appended
9688                 El.cache[el.id] = el; // in case it was created directly with Element(), let's cache it
9689             }
9690             return el;
9691         }else if(el.isComposite){
9692             return el;
9693         }else if(el instanceof Array){
9694             return El.select(el);
9695         }else if(el == document){
9696             // create a bogus element object representing the document object
9697             if(!docEl){
9698                 var f = function(){};
9699                 f.prototype = El.prototype;
9700                 docEl = new f();
9701                 docEl.dom = document;
9702             }
9703             return docEl;
9704         }
9705         return null;
9706     };
9707
9708     // private
9709     El.uncache = function(el){
9710         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
9711             if(a[i]){
9712                 delete El.cache[a[i].id || a[i]];
9713             }
9714         }
9715     };
9716
9717     // private
9718     // Garbage collection - uncache elements/purge listeners on orphaned elements
9719     // so we don't hold a reference and cause the browser to retain them
9720     El.garbageCollect = function(){
9721         if(!Roo.enableGarbageCollector){
9722             clearInterval(El.collectorThread);
9723             return;
9724         }
9725         for(var eid in El.cache){
9726             var el = El.cache[eid], d = el.dom;
9727             // -------------------------------------------------------
9728             // Determining what is garbage:
9729             // -------------------------------------------------------
9730             // !d
9731             // dom node is null, definitely garbage
9732             // -------------------------------------------------------
9733             // !d.parentNode
9734             // no parentNode == direct orphan, definitely garbage
9735             // -------------------------------------------------------
9736             // !d.offsetParent && !document.getElementById(eid)
9737             // display none elements have no offsetParent so we will
9738             // also try to look it up by it's id. However, check
9739             // offsetParent first so we don't do unneeded lookups.
9740             // This enables collection of elements that are not orphans
9741             // directly, but somewhere up the line they have an orphan
9742             // parent.
9743             // -------------------------------------------------------
9744             if(!d || !d.parentNode || (!d.offsetParent && !document.getElementById(eid))){
9745                 delete El.cache[eid];
9746                 if(d && Roo.enableListenerCollection){
9747                     E.purgeElement(d);
9748                 }
9749             }
9750         }
9751     }
9752     El.collectorThreadId = setInterval(El.garbageCollect, 30000);
9753
9754
9755     // dom is optional
9756     El.Flyweight = function(dom){
9757         this.dom = dom;
9758     };
9759     El.Flyweight.prototype = El.prototype;
9760
9761     El._flyweights = {};
9762     /**
9763      * Gets the globally shared flyweight Element, with the passed node as the active element. Do not store a reference to this element -
9764      * the dom node can be overwritten by other code.
9765      * @param {String/HTMLElement} el The dom node or id
9766      * @param {String} named (optional) Allows for creation of named reusable flyweights to
9767      *                                  prevent conflicts (e.g. internally Roo uses "_internal")
9768      * @static
9769      * @return {Element} The shared Element object
9770      */
9771     El.fly = function(el, named){
9772         named = named || '_global';
9773         el = Roo.getDom(el);
9774         if(!el){
9775             return null;
9776         }
9777         if(!El._flyweights[named]){
9778             El._flyweights[named] = new El.Flyweight();
9779         }
9780         El._flyweights[named].dom = el;
9781         return El._flyweights[named];
9782     };
9783
9784     /**
9785      * Static method to retrieve Element objects. Uses simple caching to consistently return the same object.
9786      * Automatically fixes if an object was recreated with the same id via AJAX or DOM.
9787      * Shorthand of {@link Roo.Element#get}
9788      * @param {String/HTMLElement/Element} el The id of the node, a DOM Node or an existing Element.
9789      * @return {Element} The Element object
9790      * @member Roo
9791      * @method get
9792      */
9793     Roo.get = El.get;
9794     /**
9795      * Gets the globally shared flyweight Element, with the passed node as the active element. Do not store a reference to this element -
9796      * the dom node can be overwritten by other code.
9797      * Shorthand of {@link Roo.Element#fly}
9798      * @param {String/HTMLElement} el The dom node or id
9799      * @param {String} named (optional) Allows for creation of named reusable flyweights to
9800      *                                  prevent conflicts (e.g. internally Roo uses "_internal")
9801      * @static
9802      * @return {Element} The shared Element object
9803      * @member Roo
9804      * @method fly
9805      */
9806     Roo.fly = El.fly;
9807
9808     // speedy lookup for elements never to box adjust
9809     var noBoxAdjust = Roo.isStrict ? {
9810         select:1
9811     } : {
9812         input:1, select:1, textarea:1
9813     };
9814     if(Roo.isIE || Roo.isGecko){
9815         noBoxAdjust['button'] = 1;
9816     }
9817
9818
9819     Roo.EventManager.on(window, 'unload', function(){
9820         delete El.cache;
9821         delete El._flyweights;
9822     });
9823 })();
9824
9825
9826
9827
9828 if(Roo.DomQuery){
9829     Roo.Element.selectorFunction = Roo.DomQuery.select;
9830 }
9831
9832 Roo.Element.select = function(selector, unique, root){
9833     var els;
9834     if(typeof selector == "string"){
9835         els = Roo.Element.selectorFunction(selector, root);
9836     }else if(selector.length !== undefined){
9837         els = selector;
9838     }else{
9839         throw "Invalid selector";
9840     }
9841     if(unique === true){
9842         return new Roo.CompositeElement(els);
9843     }else{
9844         return new Roo.CompositeElementLite(els);
9845     }
9846 };
9847 /**
9848  * Selects elements based on the passed CSS selector to enable working on them as 1.
9849  * @param {String/Array} selector The CSS selector or an array of elements
9850  * @param {Boolean} unique (optional) true to create a unique Roo.Element for each element (defaults to a shared flyweight object)
9851  * @param {HTMLElement/String} root (optional) The root element of the query or id of the root
9852  * @return {CompositeElementLite/CompositeElement}
9853  * @member Roo
9854  * @method select
9855  */
9856 Roo.select = Roo.Element.select;
9857
9858
9859
9860
9861
9862
9863
9864
9865
9866
9867
9868
9869
9870
9871 /*
9872  * Based on:
9873  * Ext JS Library 1.1.1
9874  * Copyright(c) 2006-2007, Ext JS, LLC.
9875  *
9876  * Originally Released Under LGPL - original licence link has changed is not relivant.
9877  *
9878  * Fork - LGPL
9879  * <script type="text/javascript">
9880  */
9881
9882
9883
9884 //Notifies Element that fx methods are available
9885 Roo.enableFx = true;
9886
9887 /**
9888  * @class Roo.Fx
9889  * <p>A class to provide basic animation and visual effects support.  <b>Note:</b> This class is automatically applied
9890  * to the {@link Roo.Element} interface when included, so all effects calls should be performed via Element.
9891  * Conversely, since the effects are not actually defined in Element, Roo.Fx <b>must</b> be included in order for the 
9892  * Element effects to work.</p><br/>
9893  *
9894  * <p>It is important to note that although the Fx methods and many non-Fx Element methods support "method chaining" in that
9895  * they return the Element object itself as the method return value, it is not always possible to mix the two in a single
9896  * method chain.  The Fx methods use an internal effects queue so that each effect can be properly timed and sequenced.
9897  * Non-Fx methods, on the other hand, have no such internal queueing and will always execute immediately.  For this reason,
9898  * while it may be possible to mix certain Fx and non-Fx method calls in a single chain, it may not always provide the
9899  * expected results and should be done with care.</p><br/>
9900  *
9901  * <p>Motion effects support 8-way anchoring, meaning that you can choose one of 8 different anchor points on the Element
9902  * that will serve as either the start or end point of the animation.  Following are all of the supported anchor positions:</p>
9903 <pre>
9904 Value  Description
9905 -----  -----------------------------
9906 tl     The top left corner
9907 t      The center of the top edge
9908 tr     The top right corner
9909 l      The center of the left edge
9910 r      The center of the right edge
9911 bl     The bottom left corner
9912 b      The center of the bottom edge
9913 br     The bottom right corner
9914 </pre>
9915  * <b>Although some Fx methods accept specific custom config parameters, the ones shown in the Config Options section
9916  * below are common options that can be passed to any Fx method.</b>
9917  * @cfg {Function} callback A function called when the effect is finished
9918  * @cfg {Object} scope The scope of the effect function
9919  * @cfg {String} easing A valid Easing value for the effect
9920  * @cfg {String} afterCls A css class to apply after the effect
9921  * @cfg {Number} duration The length of time (in seconds) that the effect should last
9922  * @cfg {Boolean} remove Whether the Element should be removed from the DOM and destroyed after the effect finishes
9923  * @cfg {Boolean} useDisplay Whether to use the <i>display</i> CSS property instead of <i>visibility</i> when hiding Elements (only applies to 
9924  * effects that end with the element being visually hidden, ignored otherwise)
9925  * @cfg {String/Object/Function} afterStyle A style specification string, e.g. "width:100px", or an object in the form {width:"100px"}, or
9926  * a function which returns such a specification that will be applied to the Element after the effect finishes
9927  * @cfg {Boolean} block Whether the effect should block other effects from queueing while it runs
9928  * @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
9929  * @cfg {Boolean} stopFx Whether subsequent effects should be stopped and removed after the current effect finishes
9930  */
9931 Roo.Fx = {
9932         /**
9933          * Slides the element into view.  An anchor point can be optionally passed to set the point of
9934          * origin for the slide effect.  This function automatically handles wrapping the element with
9935          * a fixed-size container if needed.  See the Fx class overview for valid anchor point options.
9936          * Usage:
9937          *<pre><code>
9938 // default: slide the element in from the top
9939 el.slideIn();
9940
9941 // custom: slide the element in from the right with a 2-second duration
9942 el.slideIn('r', { duration: 2 });
9943
9944 // common config options shown with default values
9945 el.slideIn('t', {
9946     easing: 'easeOut',
9947     duration: .5
9948 });
9949 </code></pre>
9950          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to top: 't')
9951          * @param {Object} options (optional) Object literal with any of the Fx config options
9952          * @return {Roo.Element} The Element
9953          */
9954     slideIn : function(anchor, o){
9955         var el = this.getFxEl();
9956         o = o || {};
9957
9958         el.queueFx(o, function(){
9959
9960             anchor = anchor || "t";
9961
9962             // fix display to visibility
9963             this.fixDisplay();
9964
9965             // restore values after effect
9966             var r = this.getFxRestore();
9967             var b = this.getBox();
9968             // fixed size for slide
9969             this.setSize(b);
9970
9971             // wrap if needed
9972             var wrap = this.fxWrap(r.pos, o, "hidden");
9973
9974             var st = this.dom.style;
9975             st.visibility = "visible";
9976             st.position = "absolute";
9977
9978             // clear out temp styles after slide and unwrap
9979             var after = function(){
9980                 el.fxUnwrap(wrap, r.pos, o);
9981                 st.width = r.width;
9982                 st.height = r.height;
9983                 el.afterFx(o);
9984             };
9985             // time to calc the positions
9986             var a, pt = {to: [b.x, b.y]}, bw = {to: b.width}, bh = {to: b.height};
9987
9988             switch(anchor.toLowerCase()){
9989                 case "t":
9990                     wrap.setSize(b.width, 0);
9991                     st.left = st.bottom = "0";
9992                     a = {height: bh};
9993                 break;
9994                 case "l":
9995                     wrap.setSize(0, b.height);
9996                     st.right = st.top = "0";
9997                     a = {width: bw};
9998                 break;
9999                 case "r":
10000                     wrap.setSize(0, b.height);
10001                     wrap.setX(b.right);
10002                     st.left = st.top = "0";
10003                     a = {width: bw, points: pt};
10004                 break;
10005                 case "b":
10006                     wrap.setSize(b.width, 0);
10007                     wrap.setY(b.bottom);
10008                     st.left = st.top = "0";
10009                     a = {height: bh, points: pt};
10010                 break;
10011                 case "tl":
10012                     wrap.setSize(0, 0);
10013                     st.right = st.bottom = "0";
10014                     a = {width: bw, height: bh};
10015                 break;
10016                 case "bl":
10017                     wrap.setSize(0, 0);
10018                     wrap.setY(b.y+b.height);
10019                     st.right = st.top = "0";
10020                     a = {width: bw, height: bh, points: pt};
10021                 break;
10022                 case "br":
10023                     wrap.setSize(0, 0);
10024                     wrap.setXY([b.right, b.bottom]);
10025                     st.left = st.top = "0";
10026                     a = {width: bw, height: bh, points: pt};
10027                 break;
10028                 case "tr":
10029                     wrap.setSize(0, 0);
10030                     wrap.setX(b.x+b.width);
10031                     st.left = st.bottom = "0";
10032                     a = {width: bw, height: bh, points: pt};
10033                 break;
10034             }
10035             this.dom.style.visibility = "visible";
10036             wrap.show();
10037
10038             arguments.callee.anim = wrap.fxanim(a,
10039                 o,
10040                 'motion',
10041                 .5,
10042                 'easeOut', after);
10043         });
10044         return this;
10045     },
10046     
10047         /**
10048          * Slides the element out of view.  An anchor point can be optionally passed to set the end point
10049          * for the slide effect.  When the effect is completed, the element will be hidden (visibility = 
10050          * 'hidden') but block elements will still take up space in the document.  The element must be removed
10051          * from the DOM using the 'remove' config option if desired.  This function automatically handles 
10052          * wrapping the element with a fixed-size container if needed.  See the Fx class overview for valid anchor point options.
10053          * Usage:
10054          *<pre><code>
10055 // default: slide the element out to the top
10056 el.slideOut();
10057
10058 // custom: slide the element out to the right with a 2-second duration
10059 el.slideOut('r', { duration: 2 });
10060
10061 // common config options shown with default values
10062 el.slideOut('t', {
10063     easing: 'easeOut',
10064     duration: .5,
10065     remove: false,
10066     useDisplay: false
10067 });
10068 </code></pre>
10069          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to top: 't')
10070          * @param {Object} options (optional) Object literal with any of the Fx config options
10071          * @return {Roo.Element} The Element
10072          */
10073     slideOut : function(anchor, o){
10074         var el = this.getFxEl();
10075         o = o || {};
10076
10077         el.queueFx(o, function(){
10078
10079             anchor = anchor || "t";
10080
10081             // restore values after effect
10082             var r = this.getFxRestore();
10083             
10084             var b = this.getBox();
10085             // fixed size for slide
10086             this.setSize(b);
10087
10088             // wrap if needed
10089             var wrap = this.fxWrap(r.pos, o, "visible");
10090
10091             var st = this.dom.style;
10092             st.visibility = "visible";
10093             st.position = "absolute";
10094
10095             wrap.setSize(b);
10096
10097             var after = function(){
10098                 if(o.useDisplay){
10099                     el.setDisplayed(false);
10100                 }else{
10101                     el.hide();
10102                 }
10103
10104                 el.fxUnwrap(wrap, r.pos, o);
10105
10106                 st.width = r.width;
10107                 st.height = r.height;
10108
10109                 el.afterFx(o);
10110             };
10111
10112             var a, zero = {to: 0};
10113             switch(anchor.toLowerCase()){
10114                 case "t":
10115                     st.left = st.bottom = "0";
10116                     a = {height: zero};
10117                 break;
10118                 case "l":
10119                     st.right = st.top = "0";
10120                     a = {width: zero};
10121                 break;
10122                 case "r":
10123                     st.left = st.top = "0";
10124                     a = {width: zero, points: {to:[b.right, b.y]}};
10125                 break;
10126                 case "b":
10127                     st.left = st.top = "0";
10128                     a = {height: zero, points: {to:[b.x, b.bottom]}};
10129                 break;
10130                 case "tl":
10131                     st.right = st.bottom = "0";
10132                     a = {width: zero, height: zero};
10133                 break;
10134                 case "bl":
10135                     st.right = st.top = "0";
10136                     a = {width: zero, height: zero, points: {to:[b.x, b.bottom]}};
10137                 break;
10138                 case "br":
10139                     st.left = st.top = "0";
10140                     a = {width: zero, height: zero, points: {to:[b.x+b.width, b.bottom]}};
10141                 break;
10142                 case "tr":
10143                     st.left = st.bottom = "0";
10144                     a = {width: zero, height: zero, points: {to:[b.right, b.y]}};
10145                 break;
10146             }
10147
10148             arguments.callee.anim = wrap.fxanim(a,
10149                 o,
10150                 'motion',
10151                 .5,
10152                 "easeOut", after);
10153         });
10154         return this;
10155     },
10156
10157         /**
10158          * Fades the element out while slowly expanding it in all directions.  When the effect is completed, the 
10159          * element will be hidden (visibility = 'hidden') but block elements will still take up space in the document. 
10160          * The element must be removed from the DOM using the 'remove' config option if desired.
10161          * Usage:
10162          *<pre><code>
10163 // default
10164 el.puff();
10165
10166 // common config options shown with default values
10167 el.puff({
10168     easing: 'easeOut',
10169     duration: .5,
10170     remove: false,
10171     useDisplay: false
10172 });
10173 </code></pre>
10174          * @param {Object} options (optional) Object literal with any of the Fx config options
10175          * @return {Roo.Element} The Element
10176          */
10177     puff : function(o){
10178         var el = this.getFxEl();
10179         o = o || {};
10180
10181         el.queueFx(o, function(){
10182             this.clearOpacity();
10183             this.show();
10184
10185             // restore values after effect
10186             var r = this.getFxRestore();
10187             var st = this.dom.style;
10188
10189             var after = function(){
10190                 if(o.useDisplay){
10191                     el.setDisplayed(false);
10192                 }else{
10193                     el.hide();
10194                 }
10195
10196                 el.clearOpacity();
10197
10198                 el.setPositioning(r.pos);
10199                 st.width = r.width;
10200                 st.height = r.height;
10201                 st.fontSize = '';
10202                 el.afterFx(o);
10203             };
10204
10205             var width = this.getWidth();
10206             var height = this.getHeight();
10207
10208             arguments.callee.anim = this.fxanim({
10209                     width : {to: this.adjustWidth(width * 2)},
10210                     height : {to: this.adjustHeight(height * 2)},
10211                     points : {by: [-(width * .5), -(height * .5)]},
10212                     opacity : {to: 0},
10213                     fontSize: {to:200, unit: "%"}
10214                 },
10215                 o,
10216                 'motion',
10217                 .5,
10218                 "easeOut", after);
10219         });
10220         return this;
10221     },
10222
10223         /**
10224          * Blinks the element as if it was clicked and then collapses on its center (similar to switching off a television).
10225          * When the effect is completed, the element will be hidden (visibility = 'hidden') but block elements will still 
10226          * take up space in the document. The element must be removed from the DOM using the 'remove' config option if desired.
10227          * Usage:
10228          *<pre><code>
10229 // default
10230 el.switchOff();
10231
10232 // all config options shown with default values
10233 el.switchOff({
10234     easing: 'easeIn',
10235     duration: .3,
10236     remove: false,
10237     useDisplay: false
10238 });
10239 </code></pre>
10240          * @param {Object} options (optional) Object literal with any of the Fx config options
10241          * @return {Roo.Element} The Element
10242          */
10243     switchOff : function(o){
10244         var el = this.getFxEl();
10245         o = o || {};
10246
10247         el.queueFx(o, function(){
10248             this.clearOpacity();
10249             this.clip();
10250
10251             // restore values after effect
10252             var r = this.getFxRestore();
10253             var st = this.dom.style;
10254
10255             var after = function(){
10256                 if(o.useDisplay){
10257                     el.setDisplayed(false);
10258                 }else{
10259                     el.hide();
10260                 }
10261
10262                 el.clearOpacity();
10263                 el.setPositioning(r.pos);
10264                 st.width = r.width;
10265                 st.height = r.height;
10266
10267                 el.afterFx(o);
10268             };
10269
10270             this.fxanim({opacity:{to:0.3}}, null, null, .1, null, function(){
10271                 this.clearOpacity();
10272                 (function(){
10273                     this.fxanim({
10274                         height:{to:1},
10275                         points:{by:[0, this.getHeight() * .5]}
10276                     }, o, 'motion', 0.3, 'easeIn', after);
10277                 }).defer(100, this);
10278             });
10279         });
10280         return this;
10281     },
10282
10283     /**
10284      * Highlights the Element by setting a color (applies to the background-color by default, but can be
10285      * changed using the "attr" config option) and then fading back to the original color. If no original
10286      * color is available, you should provide the "endColor" config option which will be cleared after the animation.
10287      * Usage:
10288 <pre><code>
10289 // default: highlight background to yellow
10290 el.highlight();
10291
10292 // custom: highlight foreground text to blue for 2 seconds
10293 el.highlight("0000ff", { attr: 'color', duration: 2 });
10294
10295 // common config options shown with default values
10296 el.highlight("ffff9c", {
10297     attr: "background-color", //can be any valid CSS property (attribute) that supports a color value
10298     endColor: (current color) or "ffffff",
10299     easing: 'easeIn',
10300     duration: 1
10301 });
10302 </code></pre>
10303      * @param {String} color (optional) The highlight color. Should be a 6 char hex color without the leading # (defaults to yellow: 'ffff9c')
10304      * @param {Object} options (optional) Object literal with any of the Fx config options
10305      * @return {Roo.Element} The Element
10306      */ 
10307     highlight : function(color, o){
10308         var el = this.getFxEl();
10309         o = o || {};
10310
10311         el.queueFx(o, function(){
10312             color = color || "ffff9c";
10313             attr = o.attr || "backgroundColor";
10314
10315             this.clearOpacity();
10316             this.show();
10317
10318             var origColor = this.getColor(attr);
10319             var restoreColor = this.dom.style[attr];
10320             endColor = (o.endColor || origColor) || "ffffff";
10321
10322             var after = function(){
10323                 el.dom.style[attr] = restoreColor;
10324                 el.afterFx(o);
10325             };
10326
10327             var a = {};
10328             a[attr] = {from: color, to: endColor};
10329             arguments.callee.anim = this.fxanim(a,
10330                 o,
10331                 'color',
10332                 1,
10333                 'easeIn', after);
10334         });
10335         return this;
10336     },
10337
10338    /**
10339     * Shows a ripple of exploding, attenuating borders to draw attention to an Element.
10340     * Usage:
10341 <pre><code>
10342 // default: a single light blue ripple
10343 el.frame();
10344
10345 // custom: 3 red ripples lasting 3 seconds total
10346 el.frame("ff0000", 3, { duration: 3 });
10347
10348 // common config options shown with default values
10349 el.frame("C3DAF9", 1, {
10350     duration: 1 //duration of entire animation (not each individual ripple)
10351     // Note: Easing is not configurable and will be ignored if included
10352 });
10353 </code></pre>
10354     * @param {String} color (optional) The color of the border.  Should be a 6 char hex color without the leading # (defaults to light blue: 'C3DAF9').
10355     * @param {Number} count (optional) The number of ripples to display (defaults to 1)
10356     * @param {Object} options (optional) Object literal with any of the Fx config options
10357     * @return {Roo.Element} The Element
10358     */
10359     frame : function(color, count, o){
10360         var el = this.getFxEl();
10361         o = o || {};
10362
10363         el.queueFx(o, function(){
10364             color = color || "#C3DAF9";
10365             if(color.length == 6){
10366                 color = "#" + color;
10367             }
10368             count = count || 1;
10369             duration = o.duration || 1;
10370             this.show();
10371
10372             var b = this.getBox();
10373             var animFn = function(){
10374                 var proxy = this.createProxy({
10375
10376                      style:{
10377                         visbility:"hidden",
10378                         position:"absolute",
10379                         "z-index":"35000", // yee haw
10380                         border:"0px solid " + color
10381                      }
10382                   });
10383                 var scale = Roo.isBorderBox ? 2 : 1;
10384                 proxy.animate({
10385                     top:{from:b.y, to:b.y - 20},
10386                     left:{from:b.x, to:b.x - 20},
10387                     borderWidth:{from:0, to:10},
10388                     opacity:{from:1, to:0},
10389                     height:{from:b.height, to:(b.height + (20*scale))},
10390                     width:{from:b.width, to:(b.width + (20*scale))}
10391                 }, duration, function(){
10392                     proxy.remove();
10393                 });
10394                 if(--count > 0){
10395                      animFn.defer((duration/2)*1000, this);
10396                 }else{
10397                     el.afterFx(o);
10398                 }
10399             };
10400             animFn.call(this);
10401         });
10402         return this;
10403     },
10404
10405    /**
10406     * Creates a pause before any subsequent queued effects begin.  If there are
10407     * no effects queued after the pause it will have no effect.
10408     * Usage:
10409 <pre><code>
10410 el.pause(1);
10411 </code></pre>
10412     * @param {Number} seconds The length of time to pause (in seconds)
10413     * @return {Roo.Element} The Element
10414     */
10415     pause : function(seconds){
10416         var el = this.getFxEl();
10417         var o = {};
10418
10419         el.queueFx(o, function(){
10420             setTimeout(function(){
10421                 el.afterFx(o);
10422             }, seconds * 1000);
10423         });
10424         return this;
10425     },
10426
10427    /**
10428     * Fade an element in (from transparent to opaque).  The ending opacity can be specified
10429     * using the "endOpacity" config option.
10430     * Usage:
10431 <pre><code>
10432 // default: fade in from opacity 0 to 100%
10433 el.fadeIn();
10434
10435 // custom: fade in from opacity 0 to 75% over 2 seconds
10436 el.fadeIn({ endOpacity: .75, duration: 2});
10437
10438 // common config options shown with default values
10439 el.fadeIn({
10440     endOpacity: 1, //can be any value between 0 and 1 (e.g. .5)
10441     easing: 'easeOut',
10442     duration: .5
10443 });
10444 </code></pre>
10445     * @param {Object} options (optional) Object literal with any of the Fx config options
10446     * @return {Roo.Element} The Element
10447     */
10448     fadeIn : function(o){
10449         var el = this.getFxEl();
10450         o = o || {};
10451         el.queueFx(o, function(){
10452             this.setOpacity(0);
10453             this.fixDisplay();
10454             this.dom.style.visibility = 'visible';
10455             var to = o.endOpacity || 1;
10456             arguments.callee.anim = this.fxanim({opacity:{to:to}},
10457                 o, null, .5, "easeOut", function(){
10458                 if(to == 1){
10459                     this.clearOpacity();
10460                 }
10461                 el.afterFx(o);
10462             });
10463         });
10464         return this;
10465     },
10466
10467    /**
10468     * Fade an element out (from opaque to transparent).  The ending opacity can be specified
10469     * using the "endOpacity" config option.
10470     * Usage:
10471 <pre><code>
10472 // default: fade out from the element's current opacity to 0
10473 el.fadeOut();
10474
10475 // custom: fade out from the element's current opacity to 25% over 2 seconds
10476 el.fadeOut({ endOpacity: .25, duration: 2});
10477
10478 // common config options shown with default values
10479 el.fadeOut({
10480     endOpacity: 0, //can be any value between 0 and 1 (e.g. .5)
10481     easing: 'easeOut',
10482     duration: .5
10483     remove: false,
10484     useDisplay: false
10485 });
10486 </code></pre>
10487     * @param {Object} options (optional) Object literal with any of the Fx config options
10488     * @return {Roo.Element} The Element
10489     */
10490     fadeOut : function(o){
10491         var el = this.getFxEl();
10492         o = o || {};
10493         el.queueFx(o, function(){
10494             arguments.callee.anim = this.fxanim({opacity:{to:o.endOpacity || 0}},
10495                 o, null, .5, "easeOut", function(){
10496                 if(this.visibilityMode == Roo.Element.DISPLAY || o.useDisplay){
10497                      this.dom.style.display = "none";
10498                 }else{
10499                      this.dom.style.visibility = "hidden";
10500                 }
10501                 this.clearOpacity();
10502                 el.afterFx(o);
10503             });
10504         });
10505         return this;
10506     },
10507
10508    /**
10509     * Animates the transition of an element's dimensions from a starting height/width
10510     * to an ending height/width.
10511     * Usage:
10512 <pre><code>
10513 // change height and width to 100x100 pixels
10514 el.scale(100, 100);
10515
10516 // common config options shown with default values.  The height and width will default to
10517 // the element's existing values if passed as null.
10518 el.scale(
10519     [element's width],
10520     [element's height], {
10521     easing: 'easeOut',
10522     duration: .35
10523 });
10524 </code></pre>
10525     * @param {Number} width  The new width (pass undefined to keep the original width)
10526     * @param {Number} height  The new height (pass undefined to keep the original height)
10527     * @param {Object} options (optional) Object literal with any of the Fx config options
10528     * @return {Roo.Element} The Element
10529     */
10530     scale : function(w, h, o){
10531         this.shift(Roo.apply({}, o, {
10532             width: w,
10533             height: h
10534         }));
10535         return this;
10536     },
10537
10538    /**
10539     * Animates the transition of any combination of an element's dimensions, xy position and/or opacity.
10540     * Any of these properties not specified in the config object will not be changed.  This effect 
10541     * requires that at least one new dimension, position or opacity setting must be passed in on
10542     * the config object in order for the function to have any effect.
10543     * Usage:
10544 <pre><code>
10545 // slide the element horizontally to x position 200 while changing the height and opacity
10546 el.shift({ x: 200, height: 50, opacity: .8 });
10547
10548 // common config options shown with default values.
10549 el.shift({
10550     width: [element's width],
10551     height: [element's height],
10552     x: [element's x position],
10553     y: [element's y position],
10554     opacity: [element's opacity],
10555     easing: 'easeOut',
10556     duration: .35
10557 });
10558 </code></pre>
10559     * @param {Object} options  Object literal with any of the Fx config options
10560     * @return {Roo.Element} The Element
10561     */
10562     shift : function(o){
10563         var el = this.getFxEl();
10564         o = o || {};
10565         el.queueFx(o, function(){
10566             var a = {}, w = o.width, h = o.height, x = o.x, y = o.y,  op = o.opacity;
10567             if(w !== undefined){
10568                 a.width = {to: this.adjustWidth(w)};
10569             }
10570             if(h !== undefined){
10571                 a.height = {to: this.adjustHeight(h)};
10572             }
10573             if(x !== undefined || y !== undefined){
10574                 a.points = {to: [
10575                     x !== undefined ? x : this.getX(),
10576                     y !== undefined ? y : this.getY()
10577                 ]};
10578             }
10579             if(op !== undefined){
10580                 a.opacity = {to: op};
10581             }
10582             if(o.xy !== undefined){
10583                 a.points = {to: o.xy};
10584             }
10585             arguments.callee.anim = this.fxanim(a,
10586                 o, 'motion', .35, "easeOut", function(){
10587                 el.afterFx(o);
10588             });
10589         });
10590         return this;
10591     },
10592
10593         /**
10594          * Slides the element while fading it out of view.  An anchor point can be optionally passed to set the 
10595          * ending point of the effect.
10596          * Usage:
10597          *<pre><code>
10598 // default: slide the element downward while fading out
10599 el.ghost();
10600
10601 // custom: slide the element out to the right with a 2-second duration
10602 el.ghost('r', { duration: 2 });
10603
10604 // common config options shown with default values
10605 el.ghost('b', {
10606     easing: 'easeOut',
10607     duration: .5
10608     remove: false,
10609     useDisplay: false
10610 });
10611 </code></pre>
10612          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to bottom: 'b')
10613          * @param {Object} options (optional) Object literal with any of the Fx config options
10614          * @return {Roo.Element} The Element
10615          */
10616     ghost : function(anchor, o){
10617         var el = this.getFxEl();
10618         o = o || {};
10619
10620         el.queueFx(o, function(){
10621             anchor = anchor || "b";
10622
10623             // restore values after effect
10624             var r = this.getFxRestore();
10625             var w = this.getWidth(),
10626                 h = this.getHeight();
10627
10628             var st = this.dom.style;
10629
10630             var after = function(){
10631                 if(o.useDisplay){
10632                     el.setDisplayed(false);
10633                 }else{
10634                     el.hide();
10635                 }
10636
10637                 el.clearOpacity();
10638                 el.setPositioning(r.pos);
10639                 st.width = r.width;
10640                 st.height = r.height;
10641
10642                 el.afterFx(o);
10643             };
10644
10645             var a = {opacity: {to: 0}, points: {}}, pt = a.points;
10646             switch(anchor.toLowerCase()){
10647                 case "t":
10648                     pt.by = [0, -h];
10649                 break;
10650                 case "l":
10651                     pt.by = [-w, 0];
10652                 break;
10653                 case "r":
10654                     pt.by = [w, 0];
10655                 break;
10656                 case "b":
10657                     pt.by = [0, h];
10658                 break;
10659                 case "tl":
10660                     pt.by = [-w, -h];
10661                 break;
10662                 case "bl":
10663                     pt.by = [-w, h];
10664                 break;
10665                 case "br":
10666                     pt.by = [w, h];
10667                 break;
10668                 case "tr":
10669                     pt.by = [w, -h];
10670                 break;
10671             }
10672
10673             arguments.callee.anim = this.fxanim(a,
10674                 o,
10675                 'motion',
10676                 .5,
10677                 "easeOut", after);
10678         });
10679         return this;
10680     },
10681
10682         /**
10683          * Ensures that all effects queued after syncFx is called on the element are
10684          * run concurrently.  This is the opposite of {@link #sequenceFx}.
10685          * @return {Roo.Element} The Element
10686          */
10687     syncFx : function(){
10688         this.fxDefaults = Roo.apply(this.fxDefaults || {}, {
10689             block : false,
10690             concurrent : true,
10691             stopFx : false
10692         });
10693         return this;
10694     },
10695
10696         /**
10697          * Ensures that all effects queued after sequenceFx is called on the element are
10698          * run in sequence.  This is the opposite of {@link #syncFx}.
10699          * @return {Roo.Element} The Element
10700          */
10701     sequenceFx : function(){
10702         this.fxDefaults = Roo.apply(this.fxDefaults || {}, {
10703             block : false,
10704             concurrent : false,
10705             stopFx : false
10706         });
10707         return this;
10708     },
10709
10710         /* @private */
10711     nextFx : function(){
10712         var ef = this.fxQueue[0];
10713         if(ef){
10714             ef.call(this);
10715         }
10716     },
10717
10718         /**
10719          * Returns true if the element has any effects actively running or queued, else returns false.
10720          * @return {Boolean} True if element has active effects, else false
10721          */
10722     hasActiveFx : function(){
10723         return this.fxQueue && this.fxQueue[0];
10724     },
10725
10726         /**
10727          * Stops any running effects and clears the element's internal effects queue if it contains
10728          * any additional effects that haven't started yet.
10729          * @return {Roo.Element} The Element
10730          */
10731     stopFx : function(){
10732         if(this.hasActiveFx()){
10733             var cur = this.fxQueue[0];
10734             if(cur && cur.anim && cur.anim.isAnimated()){
10735                 this.fxQueue = [cur]; // clear out others
10736                 cur.anim.stop(true);
10737             }
10738         }
10739         return this;
10740     },
10741
10742         /* @private */
10743     beforeFx : function(o){
10744         if(this.hasActiveFx() && !o.concurrent){
10745            if(o.stopFx){
10746                this.stopFx();
10747                return true;
10748            }
10749            return false;
10750         }
10751         return true;
10752     },
10753
10754         /**
10755          * Returns true if the element is currently blocking so that no other effect can be queued
10756          * until this effect is finished, else returns false if blocking is not set.  This is commonly
10757          * used to ensure that an effect initiated by a user action runs to completion prior to the
10758          * same effect being restarted (e.g., firing only one effect even if the user clicks several times).
10759          * @return {Boolean} True if blocking, else false
10760          */
10761     hasFxBlock : function(){
10762         var q = this.fxQueue;
10763         return q && q[0] && q[0].block;
10764     },
10765
10766         /* @private */
10767     queueFx : function(o, fn){
10768         if(!this.fxQueue){
10769             this.fxQueue = [];
10770         }
10771         if(!this.hasFxBlock()){
10772             Roo.applyIf(o, this.fxDefaults);
10773             if(!o.concurrent){
10774                 var run = this.beforeFx(o);
10775                 fn.block = o.block;
10776                 this.fxQueue.push(fn);
10777                 if(run){
10778                     this.nextFx();
10779                 }
10780             }else{
10781                 fn.call(this);
10782             }
10783         }
10784         return this;
10785     },
10786
10787         /* @private */
10788     fxWrap : function(pos, o, vis){
10789         var wrap;
10790         if(!o.wrap || !(wrap = Roo.get(o.wrap))){
10791             var wrapXY;
10792             if(o.fixPosition){
10793                 wrapXY = this.getXY();
10794             }
10795             var div = document.createElement("div");
10796             div.style.visibility = vis;
10797             wrap = Roo.get(this.dom.parentNode.insertBefore(div, this.dom));
10798             wrap.setPositioning(pos);
10799             if(wrap.getStyle("position") == "static"){
10800                 wrap.position("relative");
10801             }
10802             this.clearPositioning('auto');
10803             wrap.clip();
10804             wrap.dom.appendChild(this.dom);
10805             if(wrapXY){
10806                 wrap.setXY(wrapXY);
10807             }
10808         }
10809         return wrap;
10810     },
10811
10812         /* @private */
10813     fxUnwrap : function(wrap, pos, o){
10814         this.clearPositioning();
10815         this.setPositioning(pos);
10816         if(!o.wrap){
10817             wrap.dom.parentNode.insertBefore(this.dom, wrap.dom);
10818             wrap.remove();
10819         }
10820     },
10821
10822         /* @private */
10823     getFxRestore : function(){
10824         var st = this.dom.style;
10825         return {pos: this.getPositioning(), width: st.width, height : st.height};
10826     },
10827
10828         /* @private */
10829     afterFx : function(o){
10830         if(o.afterStyle){
10831             this.applyStyles(o.afterStyle);
10832         }
10833         if(o.afterCls){
10834             this.addClass(o.afterCls);
10835         }
10836         if(o.remove === true){
10837             this.remove();
10838         }
10839         Roo.callback(o.callback, o.scope, [this]);
10840         if(!o.concurrent){
10841             this.fxQueue.shift();
10842             this.nextFx();
10843         }
10844     },
10845
10846         /* @private */
10847     getFxEl : function(){ // support for composite element fx
10848         return Roo.get(this.dom);
10849     },
10850
10851         /* @private */
10852     fxanim : function(args, opt, animType, defaultDur, defaultEase, cb){
10853         animType = animType || 'run';
10854         opt = opt || {};
10855         var anim = Roo.lib.Anim[animType](
10856             this.dom, args,
10857             (opt.duration || defaultDur) || .35,
10858             (opt.easing || defaultEase) || 'easeOut',
10859             function(){
10860                 Roo.callback(cb, this);
10861             },
10862             this
10863         );
10864         opt.anim = anim;
10865         return anim;
10866     }
10867 };
10868
10869 // backwords compat
10870 Roo.Fx.resize = Roo.Fx.scale;
10871
10872 //When included, Roo.Fx is automatically applied to Element so that all basic
10873 //effects are available directly via the Element API
10874 Roo.apply(Roo.Element.prototype, Roo.Fx);/*
10875  * Based on:
10876  * Ext JS Library 1.1.1
10877  * Copyright(c) 2006-2007, Ext JS, LLC.
10878  *
10879  * Originally Released Under LGPL - original licence link has changed is not relivant.
10880  *
10881  * Fork - LGPL
10882  * <script type="text/javascript">
10883  */
10884
10885
10886 /**
10887  * @class Roo.CompositeElement
10888  * Standard composite class. Creates a Roo.Element for every element in the collection.
10889  * <br><br>
10890  * <b>NOTE: Although they are not listed, this class supports all of the set/update methods of Roo.Element. All Roo.Element
10891  * actions will be performed on all the elements in this collection.</b>
10892  * <br><br>
10893  * All methods return <i>this</i> and can be chained.
10894  <pre><code>
10895  var els = Roo.select("#some-el div.some-class", true);
10896  // or select directly from an existing element
10897  var el = Roo.get('some-el');
10898  el.select('div.some-class', true);
10899
10900  els.setWidth(100); // all elements become 100 width
10901  els.hide(true); // all elements fade out and hide
10902  // or
10903  els.setWidth(100).hide(true);
10904  </code></pre>
10905  */
10906 Roo.CompositeElement = function(els){
10907     this.elements = [];
10908     this.addElements(els);
10909 };
10910 Roo.CompositeElement.prototype = {
10911     isComposite: true,
10912     addElements : function(els){
10913         if(!els) return this;
10914         if(typeof els == "string"){
10915             els = Roo.Element.selectorFunction(els);
10916         }
10917         var yels = this.elements;
10918         var index = yels.length-1;
10919         for(var i = 0, len = els.length; i < len; i++) {
10920                 yels[++index] = Roo.get(els[i]);
10921         }
10922         return this;
10923     },
10924
10925     /**
10926     * Clears this composite and adds the elements returned by the passed selector.
10927     * @param {String/Array} els A string CSS selector, an array of elements or an element
10928     * @return {CompositeElement} this
10929     */
10930     fill : function(els){
10931         this.elements = [];
10932         this.add(els);
10933         return this;
10934     },
10935
10936     /**
10937     * Filters this composite to only elements that match the passed selector.
10938     * @param {String} selector A string CSS selector
10939     * @return {CompositeElement} this
10940     */
10941     filter : function(selector){
10942         var els = [];
10943         this.each(function(el){
10944             if(el.is(selector)){
10945                 els[els.length] = el.dom;
10946             }
10947         });
10948         this.fill(els);
10949         return this;
10950     },
10951
10952     invoke : function(fn, args){
10953         var els = this.elements;
10954         for(var i = 0, len = els.length; i < len; i++) {
10955                 Roo.Element.prototype[fn].apply(els[i], args);
10956         }
10957         return this;
10958     },
10959     /**
10960     * Adds elements to this composite.
10961     * @param {String/Array} els A string CSS selector, an array of elements or an element
10962     * @return {CompositeElement} this
10963     */
10964     add : function(els){
10965         if(typeof els == "string"){
10966             this.addElements(Roo.Element.selectorFunction(els));
10967         }else if(els.length !== undefined){
10968             this.addElements(els);
10969         }else{
10970             this.addElements([els]);
10971         }
10972         return this;
10973     },
10974     /**
10975     * Calls the passed function passing (el, this, index) for each element in this composite.
10976     * @param {Function} fn The function to call
10977     * @param {Object} scope (optional) The <i>this</i> object (defaults to the element)
10978     * @return {CompositeElement} this
10979     */
10980     each : function(fn, scope){
10981         var els = this.elements;
10982         for(var i = 0, len = els.length; i < len; i++){
10983             if(fn.call(scope || els[i], els[i], this, i) === false) {
10984                 break;
10985             }
10986         }
10987         return this;
10988     },
10989
10990     /**
10991      * Returns the Element object at the specified index
10992      * @param {Number} index
10993      * @return {Roo.Element}
10994      */
10995     item : function(index){
10996         return this.elements[index] || null;
10997     },
10998
10999     /**
11000      * Returns the first Element
11001      * @return {Roo.Element}
11002      */
11003     first : function(){
11004         return this.item(0);
11005     },
11006
11007     /**
11008      * Returns the last Element
11009      * @return {Roo.Element}
11010      */
11011     last : function(){
11012         return this.item(this.elements.length-1);
11013     },
11014
11015     /**
11016      * Returns the number of elements in this composite
11017      * @return Number
11018      */
11019     getCount : function(){
11020         return this.elements.length;
11021     },
11022
11023     /**
11024      * Returns true if this composite contains the passed element
11025      * @return Boolean
11026      */
11027     contains : function(el){
11028         return this.indexOf(el) !== -1;
11029     },
11030
11031     /**
11032      * Returns true if this composite contains the passed element
11033      * @return Boolean
11034      */
11035     indexOf : function(el){
11036         return this.elements.indexOf(Roo.get(el));
11037     },
11038
11039
11040     /**
11041     * Removes the specified element(s).
11042     * @param {Mixed} el The id of an element, the Element itself, the index of the element in this composite
11043     * or an array of any of those.
11044     * @param {Boolean} removeDom (optional) True to also remove the element from the document
11045     * @return {CompositeElement} this
11046     */
11047     removeElement : function(el, removeDom){
11048         if(el instanceof Array){
11049             for(var i = 0, len = el.length; i < len; i++){
11050                 this.removeElement(el[i]);
11051             }
11052             return this;
11053         }
11054         var index = typeof el == 'number' ? el : this.indexOf(el);
11055         if(index !== -1){
11056             if(removeDom){
11057                 var d = this.elements[index];
11058                 if(d.dom){
11059                     d.remove();
11060                 }else{
11061                     d.parentNode.removeChild(d);
11062                 }
11063             }
11064             this.elements.splice(index, 1);
11065         }
11066         return this;
11067     },
11068
11069     /**
11070     * Replaces the specified element with the passed element.
11071     * @param {String/HTMLElement/Element/Number} el The id of an element, the Element itself, the index of the element in this composite
11072     * to replace.
11073     * @param {String/HTMLElement/Element} replacement The id of an element or the Element itself.
11074     * @param {Boolean} domReplace (Optional) True to remove and replace the element in the document too.
11075     * @return {CompositeElement} this
11076     */
11077     replaceElement : function(el, replacement, domReplace){
11078         var index = typeof el == 'number' ? el : this.indexOf(el);
11079         if(index !== -1){
11080             if(domReplace){
11081                 this.elements[index].replaceWith(replacement);
11082             }else{
11083                 this.elements.splice(index, 1, Roo.get(replacement))
11084             }
11085         }
11086         return this;
11087     },
11088
11089     /**
11090      * Removes all elements.
11091      */
11092     clear : function(){
11093         this.elements = [];
11094     }
11095 };
11096 (function(){
11097     Roo.CompositeElement.createCall = function(proto, fnName){
11098         if(!proto[fnName]){
11099             proto[fnName] = function(){
11100                 return this.invoke(fnName, arguments);
11101             };
11102         }
11103     };
11104     for(var fnName in Roo.Element.prototype){
11105         if(typeof Roo.Element.prototype[fnName] == "function"){
11106             Roo.CompositeElement.createCall(Roo.CompositeElement.prototype, fnName);
11107         }
11108     };
11109 })();
11110 /*
11111  * Based on:
11112  * Ext JS Library 1.1.1
11113  * Copyright(c) 2006-2007, Ext JS, LLC.
11114  *
11115  * Originally Released Under LGPL - original licence link has changed is not relivant.
11116  *
11117  * Fork - LGPL
11118  * <script type="text/javascript">
11119  */
11120
11121 /**
11122  * @class Roo.CompositeElementLite
11123  * @extends Roo.CompositeElement
11124  * Flyweight composite class. Reuses the same Roo.Element for element operations.
11125  <pre><code>
11126  var els = Roo.select("#some-el div.some-class");
11127  // or select directly from an existing element
11128  var el = Roo.get('some-el');
11129  el.select('div.some-class');
11130
11131  els.setWidth(100); // all elements become 100 width
11132  els.hide(true); // all elements fade out and hide
11133  // or
11134  els.setWidth(100).hide(true);
11135  </code></pre><br><br>
11136  * <b>NOTE: Although they are not listed, this class supports all of the set/update methods of Roo.Element. All Roo.Element
11137  * actions will be performed on all the elements in this collection.</b>
11138  */
11139 Roo.CompositeElementLite = function(els){
11140     Roo.CompositeElementLite.superclass.constructor.call(this, els);
11141     this.el = new Roo.Element.Flyweight();
11142 };
11143 Roo.extend(Roo.CompositeElementLite, Roo.CompositeElement, {
11144     addElements : function(els){
11145         if(els){
11146             if(els instanceof Array){
11147                 this.elements = this.elements.concat(els);
11148             }else{
11149                 var yels = this.elements;
11150                 var index = yels.length-1;
11151                 for(var i = 0, len = els.length; i < len; i++) {
11152                     yels[++index] = els[i];
11153                 }
11154             }
11155         }
11156         return this;
11157     },
11158     invoke : function(fn, args){
11159         var els = this.elements;
11160         var el = this.el;
11161         for(var i = 0, len = els.length; i < len; i++) {
11162             el.dom = els[i];
11163                 Roo.Element.prototype[fn].apply(el, args);
11164         }
11165         return this;
11166     },
11167     /**
11168      * Returns a flyweight Element of the dom element object at the specified index
11169      * @param {Number} index
11170      * @return {Roo.Element}
11171      */
11172     item : function(index){
11173         if(!this.elements[index]){
11174             return null;
11175         }
11176         this.el.dom = this.elements[index];
11177         return this.el;
11178     },
11179
11180     // fixes scope with flyweight
11181     addListener : function(eventName, handler, scope, opt){
11182         var els = this.elements;
11183         for(var i = 0, len = els.length; i < len; i++) {
11184             Roo.EventManager.on(els[i], eventName, handler, scope || els[i], opt);
11185         }
11186         return this;
11187     },
11188
11189     /**
11190     * Calls the passed function passing (el, this, index) for each element in this composite. <b>The element
11191     * passed is the flyweight (shared) Roo.Element instance, so if you require a
11192     * a reference to the dom node, use el.dom.</b>
11193     * @param {Function} fn The function to call
11194     * @param {Object} scope (optional) The <i>this</i> object (defaults to the element)
11195     * @return {CompositeElement} this
11196     */
11197     each : function(fn, scope){
11198         var els = this.elements;
11199         var el = this.el;
11200         for(var i = 0, len = els.length; i < len; i++){
11201             el.dom = els[i];
11202                 if(fn.call(scope || el, el, this, i) === false){
11203                 break;
11204             }
11205         }
11206         return this;
11207     },
11208
11209     indexOf : function(el){
11210         return this.elements.indexOf(Roo.getDom(el));
11211     },
11212
11213     replaceElement : function(el, replacement, domReplace){
11214         var index = typeof el == 'number' ? el : this.indexOf(el);
11215         if(index !== -1){
11216             replacement = Roo.getDom(replacement);
11217             if(domReplace){
11218                 var d = this.elements[index];
11219                 d.parentNode.insertBefore(replacement, d);
11220                 d.parentNode.removeChild(d);
11221             }
11222             this.elements.splice(index, 1, replacement);
11223         }
11224         return this;
11225     }
11226 });
11227 Roo.CompositeElementLite.prototype.on = Roo.CompositeElementLite.prototype.addListener;
11228
11229 /*
11230  * Based on:
11231  * Ext JS Library 1.1.1
11232  * Copyright(c) 2006-2007, Ext JS, LLC.
11233  *
11234  * Originally Released Under LGPL - original licence link has changed is not relivant.
11235  *
11236  * Fork - LGPL
11237  * <script type="text/javascript">
11238  */
11239
11240  
11241
11242 /**
11243  * @class Roo.data.Connection
11244  * @extends Roo.util.Observable
11245  * The class encapsulates a connection to the page's originating domain, allowing requests to be made
11246  * either to a configured URL, or to a URL specified at request time.<br><br>
11247  * <p>
11248  * Requests made by this class are asynchronous, and will return immediately. No data from
11249  * the server will be available to the statement immediately following the {@link #request} call.
11250  * To process returned data, use a callback in the request options object, or an event listener.</p><br>
11251  * <p>
11252  * Note: If you are doing a file upload, you will not get a normal response object sent back to
11253  * your callback or event handler.  Since the upload is handled via in IFRAME, there is no XMLHttpRequest.
11254  * The response object is created using the innerHTML of the IFRAME's document as the responseText
11255  * property and, if present, the IFRAME's XML document as the responseXML property.</p><br>
11256  * This means that a valid XML or HTML document must be returned. If JSON data is required, it is suggested
11257  * that it be placed either inside a &lt;textarea> in an HTML document and retrieved from the responseText
11258  * using a regex, or inside a CDATA section in an XML document and retrieved from the responseXML using
11259  * standard DOM methods.
11260  * @constructor
11261  * @param {Object} config a configuration object.
11262  */
11263 Roo.data.Connection = function(config){
11264     Roo.apply(this, config);
11265     this.addEvents({
11266         /**
11267          * @event beforerequest
11268          * Fires before a network request is made to retrieve a data object.
11269          * @param {Connection} conn This Connection object.
11270          * @param {Object} options The options config object passed to the {@link #request} method.
11271          */
11272         "beforerequest" : true,
11273         /**
11274          * @event requestcomplete
11275          * Fires if the request was successfully completed.
11276          * @param {Connection} conn This Connection object.
11277          * @param {Object} response The XHR object containing the response data.
11278          * See {@link http://www.w3.org/TR/XMLHttpRequest/} for details.
11279          * @param {Object} options The options config object passed to the {@link #request} method.
11280          */
11281         "requestcomplete" : true,
11282         /**
11283          * @event requestexception
11284          * Fires if an error HTTP status was returned from the server.
11285          * See {@link http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html} for details of HTTP status codes.
11286          * @param {Connection} conn This Connection object.
11287          * @param {Object} response The XHR object containing the response data.
11288          * See {@link http://www.w3.org/TR/XMLHttpRequest/} for details.
11289          * @param {Object} options The options config object passed to the {@link #request} method.
11290          */
11291         "requestexception" : true
11292     });
11293     Roo.data.Connection.superclass.constructor.call(this);
11294 };
11295
11296 Roo.extend(Roo.data.Connection, Roo.util.Observable, {
11297     /**
11298      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
11299      */
11300     /**
11301      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
11302      * extra parameters to each request made by this object. (defaults to undefined)
11303      */
11304     /**
11305      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
11306      *  to each request made by this object. (defaults to undefined)
11307      */
11308     /**
11309      * @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)
11310      */
11311     /**
11312      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
11313      */
11314     timeout : 30000,
11315     /**
11316      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
11317      * @type Boolean
11318      */
11319     autoAbort:false,
11320
11321     /**
11322      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
11323      * @type Boolean
11324      */
11325     disableCaching: true,
11326
11327     /**
11328      * Sends an HTTP request to a remote server.
11329      * @param {Object} options An object which may contain the following properties:<ul>
11330      * <li><b>url</b> {String} (Optional) The URL to which to send the request. Defaults to configured URL</li>
11331      * <li><b>params</b> {Object/String/Function} (Optional) An object containing properties which are used as parameters to the
11332      * request, a url encoded string or a function to call to get either.</li>
11333      * <li><b>method</b> {String} (Optional) The HTTP method to use for the request. Defaults to the configured method, or
11334      * if no method was configured, "GET" if no parameters are being sent, and "POST" if parameters are being sent.</li>
11335      * <li><b>callback</b> {Function} (Optional) The function to be called upon receipt of the HTTP response.
11336      * The callback is called regardless of success or failure and is passed the following parameters:<ul>
11337      * <li>options {Object} The parameter to the request call.</li>
11338      * <li>success {Boolean} True if the request succeeded.</li>
11339      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
11340      * </ul></li>
11341      * <li><b>success</b> {Function} (Optional) The function to be called upon success of the request.
11342      * The callback is passed the following parameters:<ul>
11343      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
11344      * <li>options {Object} The parameter to the request call.</li>
11345      * </ul></li>
11346      * <li><b>failure</b> {Function} (Optional) The function to be called upon failure of the request.
11347      * The callback is passed the following parameters:<ul>
11348      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
11349      * <li>options {Object} The parameter to the request call.</li>
11350      * </ul></li>
11351      * <li><b>scope</b> {Object} (Optional) The scope in which to execute the callbacks: The "this" object
11352      * for the callback function. Defaults to the browser window.</li>
11353      * <li><b>form</b> {Object/String} (Optional) A form object or id to pull parameters from.</li>
11354      * <li><b>isUpload</b> {Boolean} (Optional) True if the form object is a file upload (will usually be automatically detected).</li>
11355      * <li><b>headers</b> {Object} (Optional) Request headers to set for the request.</li>
11356      * <li><b>xmlData</b> {Object} (Optional) XML document to use for the post. Note: This will be used instead of
11357      * params for the post data. Any params will be appended to the URL.</li>
11358      * <li><b>disableCaching</b> {Boolean} (Optional) True to add a unique cache-buster param to GET requests.</li>
11359      * </ul>
11360      * @return {Number} transactionId
11361      */
11362     request : function(o){
11363         if(this.fireEvent("beforerequest", this, o) !== false){
11364             var p = o.params;
11365
11366             if(typeof p == "function"){
11367                 p = p.call(o.scope||window, o);
11368             }
11369             if(typeof p == "object"){
11370                 p = Roo.urlEncode(o.params);
11371             }
11372             if(this.extraParams){
11373                 var extras = Roo.urlEncode(this.extraParams);
11374                 p = p ? (p + '&' + extras) : extras;
11375             }
11376
11377             var url = o.url || this.url;
11378             if(typeof url == 'function'){
11379                 url = url.call(o.scope||window, o);
11380             }
11381
11382             if(o.form){
11383                 var form = Roo.getDom(o.form);
11384                 url = url || form.action;
11385
11386                 var enctype = form.getAttribute("enctype");
11387                 if(o.isUpload || (enctype && enctype.toLowerCase() == 'multipart/form-data')){
11388                     return this.doFormUpload(o, p, url);
11389                 }
11390                 var f = Roo.lib.Ajax.serializeForm(form);
11391                 p = p ? (p + '&' + f) : f;
11392             }
11393
11394             var hs = o.headers;
11395             if(this.defaultHeaders){
11396                 hs = Roo.apply(hs || {}, this.defaultHeaders);
11397                 if(!o.headers){
11398                     o.headers = hs;
11399                 }
11400             }
11401
11402             var cb = {
11403                 success: this.handleResponse,
11404                 failure: this.handleFailure,
11405                 scope: this,
11406                 argument: {options: o},
11407                 timeout : o.timeout || this.timeout
11408             };
11409
11410             var method = o.method||this.method||(p ? "POST" : "GET");
11411
11412             if(method == 'GET' && (this.disableCaching && o.disableCaching !== false) || o.disableCaching === true){
11413                 url += (url.indexOf('?') != -1 ? '&' : '?') + '_dc=' + (new Date().getTime());
11414             }
11415
11416             if(typeof o.autoAbort == 'boolean'){ // options gets top priority
11417                 if(o.autoAbort){
11418                     this.abort();
11419                 }
11420             }else if(this.autoAbort !== false){
11421                 this.abort();
11422             }
11423
11424             if((method == 'GET' && p) || o.xmlData){
11425                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
11426                 p = '';
11427             }
11428             this.transId = Roo.lib.Ajax.request(method, url, cb, p, o);
11429             return this.transId;
11430         }else{
11431             Roo.callback(o.callback, o.scope, [o, null, null]);
11432             return null;
11433         }
11434     },
11435
11436     /**
11437      * Determine whether this object has a request outstanding.
11438      * @param {Number} transactionId (Optional) defaults to the last transaction
11439      * @return {Boolean} True if there is an outstanding request.
11440      */
11441     isLoading : function(transId){
11442         if(transId){
11443             return Roo.lib.Ajax.isCallInProgress(transId);
11444         }else{
11445             return this.transId ? true : false;
11446         }
11447     },
11448
11449     /**
11450      * Aborts any outstanding request.
11451      * @param {Number} transactionId (Optional) defaults to the last transaction
11452      */
11453     abort : function(transId){
11454         if(transId || this.isLoading()){
11455             Roo.lib.Ajax.abort(transId || this.transId);
11456         }
11457     },
11458
11459     // private
11460     handleResponse : function(response){
11461         this.transId = false;
11462         var options = response.argument.options;
11463         response.argument = options ? options.argument : null;
11464         this.fireEvent("requestcomplete", this, response, options);
11465         Roo.callback(options.success, options.scope, [response, options]);
11466         Roo.callback(options.callback, options.scope, [options, true, response]);
11467     },
11468
11469     // private
11470     handleFailure : function(response, e){
11471         this.transId = false;
11472         var options = response.argument.options;
11473         response.argument = options ? options.argument : null;
11474         this.fireEvent("requestexception", this, response, options, e);
11475         Roo.callback(options.failure, options.scope, [response, options]);
11476         Roo.callback(options.callback, options.scope, [options, false, response]);
11477     },
11478
11479     // private
11480     doFormUpload : function(o, ps, url){
11481         var id = Roo.id();
11482         var frame = document.createElement('iframe');
11483         frame.id = id;
11484         frame.name = id;
11485         frame.className = 'x-hidden';
11486         if(Roo.isIE){
11487             frame.src = Roo.SSL_SECURE_URL;
11488         }
11489         document.body.appendChild(frame);
11490
11491         if(Roo.isIE){
11492            document.frames[id].name = id;
11493         }
11494
11495         var form = Roo.getDom(o.form);
11496         form.target = id;
11497         form.method = 'POST';
11498         form.enctype = form.encoding = 'multipart/form-data';
11499         if(url){
11500             form.action = url;
11501         }
11502
11503         var hiddens, hd;
11504         if(ps){ // add dynamic params
11505             hiddens = [];
11506             ps = Roo.urlDecode(ps, false);
11507             for(var k in ps){
11508                 if(ps.hasOwnProperty(k)){
11509                     hd = document.createElement('input');
11510                     hd.type = 'hidden';
11511                     hd.name = k;
11512                     hd.value = ps[k];
11513                     form.appendChild(hd);
11514                     hiddens.push(hd);
11515                 }
11516             }
11517         }
11518
11519         function cb(){
11520             var r = {  // bogus response object
11521                 responseText : '',
11522                 responseXML : null
11523             };
11524
11525             r.argument = o ? o.argument : null;
11526
11527             try { //
11528                 var doc;
11529                 if(Roo.isIE){
11530                     doc = frame.contentWindow.document;
11531                 }else {
11532                     doc = (frame.contentDocument || window.frames[id].document);
11533                 }
11534                 if(doc && doc.body){
11535                     r.responseText = doc.body.innerHTML;
11536                 }
11537                 if(doc && doc.XMLDocument){
11538                     r.responseXML = doc.XMLDocument;
11539                 }else {
11540                     r.responseXML = doc;
11541                 }
11542             }
11543             catch(e) {
11544                 // ignore
11545             }
11546
11547             Roo.EventManager.removeListener(frame, 'load', cb, this);
11548
11549             this.fireEvent("requestcomplete", this, r, o);
11550             Roo.callback(o.success, o.scope, [r, o]);
11551             Roo.callback(o.callback, o.scope, [o, true, r]);
11552
11553             setTimeout(function(){document.body.removeChild(frame);}, 100);
11554         }
11555
11556         Roo.EventManager.on(frame, 'load', cb, this);
11557         form.submit();
11558
11559         if(hiddens){ // remove dynamic params
11560             for(var i = 0, len = hiddens.length; i < len; i++){
11561                 form.removeChild(hiddens[i]);
11562             }
11563         }
11564     }
11565 });
11566 /*
11567  * Based on:
11568  * Ext JS Library 1.1.1
11569  * Copyright(c) 2006-2007, Ext JS, LLC.
11570  *
11571  * Originally Released Under LGPL - original licence link has changed is not relivant.
11572  *
11573  * Fork - LGPL
11574  * <script type="text/javascript">
11575  */
11576  
11577 /**
11578  * Global Ajax request class.
11579  * 
11580  * @class Roo.Ajax
11581  * @extends Roo.data.Connection
11582  * @static
11583  * 
11584  * @cfg {String} url  The default URL to be used for requests to the server. (defaults to undefined)
11585  * @cfg {Object} extraParams  An object containing properties which are used as extra parameters to each request made by this object. (defaults to undefined)
11586  * @cfg {Object} defaultHeaders  An object containing request headers which are added to each request made by this object. (defaults to undefined)
11587  * @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)
11588  * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
11589  * @cfg {Boolean} autoAbort (Optional) Whether a new request should abort any pending requests. (defaults to false)
11590  * @cfg {Boolean} disableCaching (Optional)   True to add a unique cache-buster param to GET requests. (defaults to true)
11591  */
11592 Roo.Ajax = new Roo.data.Connection({
11593     // fix up the docs
11594     /**
11595      * @scope Roo.Ajax
11596      * @type {Boolear} 
11597      */
11598     autoAbort : false,
11599
11600     /**
11601      * Serialize the passed form into a url encoded string
11602      * @scope Roo.Ajax
11603      * @param {String/HTMLElement} form
11604      * @return {String}
11605      */
11606     serializeForm : function(form){
11607         return Roo.lib.Ajax.serializeForm(form);
11608     }
11609 });/*
11610  * Based on:
11611  * Ext JS Library 1.1.1
11612  * Copyright(c) 2006-2007, Ext JS, LLC.
11613  *
11614  * Originally Released Under LGPL - original licence link has changed is not relivant.
11615  *
11616  * Fork - LGPL
11617  * <script type="text/javascript">
11618  */
11619
11620  
11621 /**
11622  * @class Roo.UpdateManager
11623  * @extends Roo.util.Observable
11624  * Provides AJAX-style update for Element object.<br><br>
11625  * Usage:<br>
11626  * <pre><code>
11627  * // Get it from a Roo.Element object
11628  * var el = Roo.get("foo");
11629  * var mgr = el.getUpdateManager();
11630  * mgr.update("http://myserver.com/index.php", "param1=1&amp;param2=2");
11631  * ...
11632  * mgr.formUpdate("myFormId", "http://myserver.com/index.php");
11633  * <br>
11634  * // or directly (returns the same UpdateManager instance)
11635  * var mgr = new Roo.UpdateManager("myElementId");
11636  * mgr.startAutoRefresh(60, "http://myserver.com/index.php");
11637  * mgr.on("update", myFcnNeedsToKnow);
11638  * <br>
11639    // short handed call directly from the element object
11640    Roo.get("foo").load({
11641         url: "bar.php",
11642         scripts:true,
11643         params: "for=bar",
11644         text: "Loading Foo..."
11645    });
11646  * </code></pre>
11647  * @constructor
11648  * Create new UpdateManager directly.
11649  * @param {String/HTMLElement/Roo.Element} el The element to update
11650  * @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).
11651  */
11652 Roo.UpdateManager = function(el, forceNew){
11653     el = Roo.get(el);
11654     if(!forceNew && el.updateManager){
11655         return el.updateManager;
11656     }
11657     /**
11658      * The Element object
11659      * @type Roo.Element
11660      */
11661     this.el = el;
11662     /**
11663      * Cached url to use for refreshes. Overwritten every time update() is called unless "discardUrl" param is set to true.
11664      * @type String
11665      */
11666     this.defaultUrl = null;
11667
11668     this.addEvents({
11669         /**
11670          * @event beforeupdate
11671          * Fired before an update is made, return false from your handler and the update is cancelled.
11672          * @param {Roo.Element} el
11673          * @param {String/Object/Function} url
11674          * @param {String/Object} params
11675          */
11676         "beforeupdate": true,
11677         /**
11678          * @event update
11679          * Fired after successful update is made.
11680          * @param {Roo.Element} el
11681          * @param {Object} oResponseObject The response Object
11682          */
11683         "update": true,
11684         /**
11685          * @event failure
11686          * Fired on update failure.
11687          * @param {Roo.Element} el
11688          * @param {Object} oResponseObject The response Object
11689          */
11690         "failure": true
11691     });
11692     var d = Roo.UpdateManager.defaults;
11693     /**
11694      * Blank page URL to use with SSL file uploads (Defaults to Roo.UpdateManager.defaults.sslBlankUrl or "about:blank").
11695      * @type String
11696      */
11697     this.sslBlankUrl = d.sslBlankUrl;
11698     /**
11699      * Whether to append unique parameter on get request to disable caching (Defaults to Roo.UpdateManager.defaults.disableCaching or false).
11700      * @type Boolean
11701      */
11702     this.disableCaching = d.disableCaching;
11703     /**
11704      * Text for loading indicator (Defaults to Roo.UpdateManager.defaults.indicatorText or '&lt;div class="loading-indicator"&gt;Loading...&lt;/div&gt;').
11705      * @type String
11706      */
11707     this.indicatorText = d.indicatorText;
11708     /**
11709      * Whether to show indicatorText when loading (Defaults to Roo.UpdateManager.defaults.showLoadIndicator or true).
11710      * @type String
11711      */
11712     this.showLoadIndicator = d.showLoadIndicator;
11713     /**
11714      * Timeout for requests or form posts in seconds (Defaults to Roo.UpdateManager.defaults.timeout or 30 seconds).
11715      * @type Number
11716      */
11717     this.timeout = d.timeout;
11718
11719     /**
11720      * True to process scripts in the output (Defaults to Roo.UpdateManager.defaults.loadScripts (false)).
11721      * @type Boolean
11722      */
11723     this.loadScripts = d.loadScripts;
11724
11725     /**
11726      * Transaction object of current executing transaction
11727      */
11728     this.transaction = null;
11729
11730     /**
11731      * @private
11732      */
11733     this.autoRefreshProcId = null;
11734     /**
11735      * Delegate for refresh() prebound to "this", use myUpdater.refreshDelegate.createCallback(arg1, arg2) to bind arguments
11736      * @type Function
11737      */
11738     this.refreshDelegate = this.refresh.createDelegate(this);
11739     /**
11740      * Delegate for update() prebound to "this", use myUpdater.updateDelegate.createCallback(arg1, arg2) to bind arguments
11741      * @type Function
11742      */
11743     this.updateDelegate = this.update.createDelegate(this);
11744     /**
11745      * Delegate for formUpdate() prebound to "this", use myUpdater.formUpdateDelegate.createCallback(arg1, arg2) to bind arguments
11746      * @type Function
11747      */
11748     this.formUpdateDelegate = this.formUpdate.createDelegate(this);
11749     /**
11750      * @private
11751      */
11752     this.successDelegate = this.processSuccess.createDelegate(this);
11753     /**
11754      * @private
11755      */
11756     this.failureDelegate = this.processFailure.createDelegate(this);
11757
11758     if(!this.renderer){
11759      /**
11760       * The renderer for this UpdateManager. Defaults to {@link Roo.UpdateManager.BasicRenderer}.
11761       */
11762     this.renderer = new Roo.UpdateManager.BasicRenderer();
11763     }
11764     
11765     Roo.UpdateManager.superclass.constructor.call(this);
11766 };
11767
11768 Roo.extend(Roo.UpdateManager, Roo.util.Observable, {
11769     /**
11770      * Get the Element this UpdateManager is bound to
11771      * @return {Roo.Element} The element
11772      */
11773     getEl : function(){
11774         return this.el;
11775     },
11776     /**
11777      * Performs an async request, updating this element with the response. If params are specified it uses POST, otherwise it uses GET.
11778      * @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:
11779 <pre><code>
11780 um.update({<br/>
11781     url: "your-url.php",<br/>
11782     params: {param1: "foo", param2: "bar"}, // or a URL encoded string<br/>
11783     callback: yourFunction,<br/>
11784     scope: yourObject, //(optional scope)  <br/>
11785     discardUrl: false, <br/>
11786     nocache: false,<br/>
11787     text: "Loading...",<br/>
11788     timeout: 30,<br/>
11789     scripts: false<br/>
11790 });
11791 </code></pre>
11792      * The only required property is url. The optional properties nocache, text and scripts
11793      * are shorthand for disableCaching, indicatorText and loadScripts and are used to set their associated property on this UpdateManager instance.
11794      * @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}
11795      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess, oResponse)
11796      * @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.
11797      */
11798     update : function(url, params, callback, discardUrl){
11799         if(this.fireEvent("beforeupdate", this.el, url, params) !== false){
11800             var method = this.method,
11801                 cfg;
11802             if(typeof url == "object"){ // must be config object
11803                 cfg = url;
11804                 url = cfg.url;
11805                 params = params || cfg.params;
11806                 callback = callback || cfg.callback;
11807                 discardUrl = discardUrl || cfg.discardUrl;
11808                 if(callback && cfg.scope){
11809                     callback = callback.createDelegate(cfg.scope);
11810                 }
11811                 if(typeof cfg.method != "undefined"){method = cfg.method;};
11812                 if(typeof cfg.nocache != "undefined"){this.disableCaching = cfg.nocache;};
11813                 if(typeof cfg.text != "undefined"){this.indicatorText = '<div class="loading-indicator">'+cfg.text+"</div>";};
11814                 if(typeof cfg.scripts != "undefined"){this.loadScripts = cfg.scripts;};
11815                 if(typeof cfg.timeout != "undefined"){this.timeout = cfg.timeout;};
11816             }
11817             this.showLoading();
11818             if(!discardUrl){
11819                 this.defaultUrl = url;
11820             }
11821             if(typeof url == "function"){
11822                 url = url.call(this);
11823             }
11824
11825             method = method || (params ? "POST" : "GET");
11826             if(method == "GET"){
11827                 url = this.prepareUrl(url);
11828             }
11829
11830             var o = Roo.apply(cfg ||{}, {
11831                 url : url,
11832                 params: params,
11833                 success: this.successDelegate,
11834                 failure: this.failureDelegate,
11835                 callback: undefined,
11836                 timeout: (this.timeout*1000),
11837                 argument: {"url": url, "form": null, "callback": callback, "params": params}
11838             });
11839             Roo.log("updated manager called with timeout of " + o.timeout);
11840             this.transaction = Roo.Ajax.request(o);
11841         }
11842     },
11843
11844     /**
11845      * 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.
11846      * Uses this.sslBlankUrl for SSL file uploads to prevent IE security warning.
11847      * @param {String/HTMLElement} form The form Id or form element
11848      * @param {String} url (optional) The url to pass the form to. If omitted the action attribute on the form will be used.
11849      * @param {Boolean} reset (optional) Whether to try to reset the form after the update
11850      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess, oResponse)
11851      */
11852     formUpdate : function(form, url, reset, callback){
11853         if(this.fireEvent("beforeupdate", this.el, form, url) !== false){
11854             if(typeof url == "function"){
11855                 url = url.call(this);
11856             }
11857             form = Roo.getDom(form);
11858             this.transaction = Roo.Ajax.request({
11859                 form: form,
11860                 url:url,
11861                 success: this.successDelegate,
11862                 failure: this.failureDelegate,
11863                 timeout: (this.timeout*1000),
11864                 argument: {"url": url, "form": form, "callback": callback, "reset": reset}
11865             });
11866             this.showLoading.defer(1, this);
11867         }
11868     },
11869
11870     /**
11871      * Refresh the element with the last used url or defaultUrl. If there is no url, it returns immediately
11872      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
11873      */
11874     refresh : function(callback){
11875         if(this.defaultUrl == null){
11876             return;
11877         }
11878         this.update(this.defaultUrl, null, callback, true);
11879     },
11880
11881     /**
11882      * Set this element to auto refresh.
11883      * @param {Number} interval How often to update (in seconds).
11884      * @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)
11885      * @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}
11886      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
11887      * @param {Boolean} refreshNow (optional) Whether to execute the refresh now, or wait the interval
11888      */
11889     startAutoRefresh : function(interval, url, params, callback, refreshNow){
11890         if(refreshNow){
11891             this.update(url || this.defaultUrl, params, callback, true);
11892         }
11893         if(this.autoRefreshProcId){
11894             clearInterval(this.autoRefreshProcId);
11895         }
11896         this.autoRefreshProcId = setInterval(this.update.createDelegate(this, [url || this.defaultUrl, params, callback, true]), interval*1000);
11897     },
11898
11899     /**
11900      * Stop auto refresh on this element.
11901      */
11902      stopAutoRefresh : function(){
11903         if(this.autoRefreshProcId){
11904             clearInterval(this.autoRefreshProcId);
11905             delete this.autoRefreshProcId;
11906         }
11907     },
11908
11909     isAutoRefreshing : function(){
11910        return this.autoRefreshProcId ? true : false;
11911     },
11912     /**
11913      * Called to update the element to "Loading" state. Override to perform custom action.
11914      */
11915     showLoading : function(){
11916         if(this.showLoadIndicator){
11917             this.el.update(this.indicatorText);
11918         }
11919     },
11920
11921     /**
11922      * Adds unique parameter to query string if disableCaching = true
11923      * @private
11924      */
11925     prepareUrl : function(url){
11926         if(this.disableCaching){
11927             var append = "_dc=" + (new Date().getTime());
11928             if(url.indexOf("?") !== -1){
11929                 url += "&" + append;
11930             }else{
11931                 url += "?" + append;
11932             }
11933         }
11934         return url;
11935     },
11936
11937     /**
11938      * @private
11939      */
11940     processSuccess : function(response){
11941         this.transaction = null;
11942         if(response.argument.form && response.argument.reset){
11943             try{ // put in try/catch since some older FF releases had problems with this
11944                 response.argument.form.reset();
11945             }catch(e){}
11946         }
11947         if(this.loadScripts){
11948             this.renderer.render(this.el, response, this,
11949                 this.updateComplete.createDelegate(this, [response]));
11950         }else{
11951             this.renderer.render(this.el, response, this);
11952             this.updateComplete(response);
11953         }
11954     },
11955
11956     updateComplete : function(response){
11957         this.fireEvent("update", this.el, response);
11958         if(typeof response.argument.callback == "function"){
11959             response.argument.callback(this.el, true, response);
11960         }
11961     },
11962
11963     /**
11964      * @private
11965      */
11966     processFailure : function(response){
11967         this.transaction = null;
11968         this.fireEvent("failure", this.el, response);
11969         if(typeof response.argument.callback == "function"){
11970             response.argument.callback(this.el, false, response);
11971         }
11972     },
11973
11974     /**
11975      * Set the content renderer for this UpdateManager. See {@link Roo.UpdateManager.BasicRenderer#render} for more details.
11976      * @param {Object} renderer The object implementing the render() method
11977      */
11978     setRenderer : function(renderer){
11979         this.renderer = renderer;
11980     },
11981
11982     getRenderer : function(){
11983        return this.renderer;
11984     },
11985
11986     /**
11987      * Set the defaultUrl used for updates
11988      * @param {String/Function} defaultUrl The url or a function to call to get the url
11989      */
11990     setDefaultUrl : function(defaultUrl){
11991         this.defaultUrl = defaultUrl;
11992     },
11993
11994     /**
11995      * Aborts the executing transaction
11996      */
11997     abort : function(){
11998         if(this.transaction){
11999             Roo.Ajax.abort(this.transaction);
12000         }
12001     },
12002
12003     /**
12004      * Returns true if an update is in progress
12005      * @return {Boolean}
12006      */
12007     isUpdating : function(){
12008         if(this.transaction){
12009             return Roo.Ajax.isLoading(this.transaction);
12010         }
12011         return false;
12012     }
12013 });
12014
12015 /**
12016  * @class Roo.UpdateManager.defaults
12017  * @static (not really - but it helps the doc tool)
12018  * The defaults collection enables customizing the default properties of UpdateManager
12019  */
12020    Roo.UpdateManager.defaults = {
12021        /**
12022          * Timeout for requests or form posts in seconds (Defaults 30 seconds).
12023          * @type Number
12024          */
12025          timeout : 30,
12026
12027          /**
12028          * True to process scripts by default (Defaults to false).
12029          * @type Boolean
12030          */
12031         loadScripts : false,
12032
12033         /**
12034         * Blank page URL to use with SSL file uploads (Defaults to "javascript:false").
12035         * @type String
12036         */
12037         sslBlankUrl : (Roo.SSL_SECURE_URL || "javascript:false"),
12038         /**
12039          * Whether to append unique parameter on get request to disable caching (Defaults to false).
12040          * @type Boolean
12041          */
12042         disableCaching : false,
12043         /**
12044          * Whether to show indicatorText when loading (Defaults to true).
12045          * @type Boolean
12046          */
12047         showLoadIndicator : true,
12048         /**
12049          * Text for loading indicator (Defaults to '&lt;div class="loading-indicator"&gt;Loading...&lt;/div&gt;').
12050          * @type String
12051          */
12052         indicatorText : '<div class="loading-indicator">Loading...</div>'
12053    };
12054
12055 /**
12056  * Static convenience method. This method is deprecated in favor of el.load({url:'foo.php', ...}).
12057  *Usage:
12058  * <pre><code>Roo.UpdateManager.updateElement("my-div", "stuff.php");</code></pre>
12059  * @param {String/HTMLElement/Roo.Element} el The element to update
12060  * @param {String} url The url
12061  * @param {String/Object} params (optional) Url encoded param string or an object of name/value pairs
12062  * @param {Object} options (optional) A config object with any of the UpdateManager properties you want to set - for example: {disableCaching:true, indicatorText: "Loading data..."}
12063  * @static
12064  * @deprecated
12065  * @member Roo.UpdateManager
12066  */
12067 Roo.UpdateManager.updateElement = function(el, url, params, options){
12068     var um = Roo.get(el, true).getUpdateManager();
12069     Roo.apply(um, options);
12070     um.update(url, params, options ? options.callback : null);
12071 };
12072 // alias for backwards compat
12073 Roo.UpdateManager.update = Roo.UpdateManager.updateElement;
12074 /**
12075  * @class Roo.UpdateManager.BasicRenderer
12076  * Default Content renderer. Updates the elements innerHTML with the responseText.
12077  */
12078 Roo.UpdateManager.BasicRenderer = function(){};
12079
12080 Roo.UpdateManager.BasicRenderer.prototype = {
12081     /**
12082      * This is called when the transaction is completed and it's time to update the element - The BasicRenderer
12083      * updates the elements innerHTML with the responseText - To perform a custom render (i.e. XML or JSON processing),
12084      * create an object with a "render(el, response)" method and pass it to setRenderer on the UpdateManager.
12085      * @param {Roo.Element} el The element being rendered
12086      * @param {Object} response The YUI Connect response object
12087      * @param {UpdateManager} updateManager The calling update manager
12088      * @param {Function} callback A callback that will need to be called if loadScripts is true on the UpdateManager
12089      */
12090      render : function(el, response, updateManager, callback){
12091         el.update(response.responseText, updateManager.loadScripts, callback);
12092     }
12093 };
12094 /*
12095  * Based on:
12096  * Roo JS
12097  * (c)) Alan Knowles
12098  * Licence : LGPL
12099  */
12100
12101
12102 /**
12103  * @class Roo.DomTemplate
12104  * @extends Roo.Template
12105  * An effort at a dom based template engine..
12106  *
12107  * Similar to XTemplate, except it uses dom parsing to create the template..
12108  *
12109  * Supported features:
12110  *
12111  *  Tags:
12112
12113 <pre><code>
12114       {a_variable} - output encoded.
12115       {a_variable.format:("Y-m-d")} - call a method on the variable
12116       {a_variable:raw} - unencoded output
12117       {a_variable:toFixed(1,2)} - Roo.util.Format."toFixed"
12118       {a_variable:this.method_on_template(...)} - call a method on the template object.
12119  
12120 </code></pre>
12121  *  The tpl tag:
12122 <pre><code>
12123         &lt;div roo-for="a_variable or condition.."&gt;&lt;/div&gt;
12124         &lt;div roo-if="a_variable or condition"&gt;&lt;/div&gt;
12125         &lt;div roo-exec="some javascript"&gt;&lt;/div&gt;
12126         &lt;div roo-name="named_template"&gt;&lt;/div&gt; 
12127   
12128 </code></pre>
12129  *      
12130  */
12131 Roo.DomTemplate = function()
12132 {
12133      Roo.DomTemplate.superclass.constructor.apply(this, arguments);
12134      if (this.html) {
12135         this.compile();
12136      }
12137 };
12138
12139
12140 Roo.extend(Roo.DomTemplate, Roo.Template, {
12141     /**
12142      * id counter for sub templates.
12143      */
12144     id : 0,
12145     /**
12146      * flag to indicate if dom parser is inside a pre,
12147      * it will strip whitespace if not.
12148      */
12149     inPre : false,
12150     
12151     /**
12152      * The various sub templates
12153      */
12154     tpls : false,
12155     
12156     
12157     
12158     /**
12159      *
12160      * basic tag replacing syntax
12161      * WORD:WORD()
12162      *
12163      * // you can fake an object call by doing this
12164      *  x.t:(test,tesT) 
12165      * 
12166      */
12167     re : /(\{|\%7B)([\w-\.]+)(?:\:([\w\.]*)(?:\(([^)]*?)?\))?)?(\}|\%7D)/g,
12168     //re : /\{([\w-\.]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
12169     
12170     iterChild : function (node, method) {
12171         
12172         var oldPre = this.inPre;
12173         if (node.tagName == 'PRE') {
12174             this.inPre = true;
12175         }
12176         for( var i = 0; i < node.childNodes.length; i++) {
12177             method.call(this, node.childNodes[i]);
12178         }
12179         this.inPre = oldPre;
12180     },
12181     
12182     
12183     
12184     /**
12185      * compile the template
12186      *
12187      * This is not recursive, so I'm not sure how nested templates are really going to be handled..
12188      *
12189      */
12190     compile: function()
12191     {
12192         var s = this.html;
12193         
12194         // covert the html into DOM...
12195         var doc = false;
12196         var div =false;
12197         try {
12198             doc = document.implementation.createHTMLDocument("");
12199             doc.documentElement.innerHTML =   this.html  ;
12200             div = doc.documentElement;
12201         } catch (e) {
12202             // old IE... - nasty -- it causes all sorts of issues.. with
12203             // images getting pulled from server..
12204             div = document.createElement('div');
12205             div.innerHTML = this.html;
12206         }
12207         //doc.documentElement.innerHTML = htmlBody
12208          
12209         
12210         
12211         this.tpls = [];
12212         var _t = this;
12213         this.iterChild(div, function(n) {_t.compileNode(n, true); });
12214         
12215         var tpls = this.tpls;
12216         
12217         // create a top level template from the snippet..
12218         
12219         //Roo.log(div.innerHTML);
12220         
12221         var tpl = {
12222             uid : 'master',
12223             id : this.id++,
12224             attr : false,
12225             value : false,
12226             body : div.innerHTML,
12227             
12228             forCall : false,
12229             execCall : false,
12230             dom : div,
12231             isTop : true
12232             
12233         };
12234         tpls.unshift(tpl);
12235         
12236         
12237         // compile them...
12238         this.tpls = [];
12239         Roo.each(tpls, function(tp){
12240             this.compileTpl(tp);
12241             this.tpls[tp.id] = tp;
12242         }, this);
12243         
12244         this.master = tpls[0];
12245         return this;
12246         
12247         
12248     },
12249     
12250     compileNode : function(node, istop) {
12251         // test for
12252         //Roo.log(node);
12253         
12254         
12255         // skip anything not a tag..
12256         if (node.nodeType != 1) {
12257             if (node.nodeType == 3 && !this.inPre) {
12258                 // reduce white space..
12259                 node.nodeValue = node.nodeValue.replace(/\s+/g, ' '); 
12260                 
12261             }
12262             return;
12263         }
12264         
12265         var tpl = {
12266             uid : false,
12267             id : false,
12268             attr : false,
12269             value : false,
12270             body : '',
12271             
12272             forCall : false,
12273             execCall : false,
12274             dom : false,
12275             isTop : istop
12276             
12277             
12278         };
12279         
12280         
12281         switch(true) {
12282             case (node.hasAttribute('roo-for')): tpl.attr = 'for'; break;
12283             case (node.hasAttribute('roo-if')): tpl.attr = 'if'; break;
12284             case (node.hasAttribute('roo-name')): tpl.attr = 'name'; break;
12285             case (node.hasAttribute('roo-exec')): tpl.attr = 'exec'; break;
12286             // no default..
12287         }
12288         
12289         
12290         if (!tpl.attr) {
12291             // just itterate children..
12292             this.iterChild(node,this.compileNode);
12293             return;
12294         }
12295         tpl.uid = this.id++;
12296         tpl.value = node.getAttribute('roo-' +  tpl.attr);
12297         node.removeAttribute('roo-'+ tpl.attr);
12298         if (tpl.attr != 'name') {
12299             var placeholder = document.createTextNode('{domtpl' + tpl.uid + '}');
12300             node.parentNode.replaceChild(placeholder,  node);
12301         } else {
12302             
12303             var placeholder =  document.createElement('span');
12304             placeholder.className = 'roo-tpl-' + tpl.value;
12305             node.parentNode.replaceChild(placeholder,  node);
12306         }
12307         
12308         // parent now sees '{domtplXXXX}
12309         this.iterChild(node,this.compileNode);
12310         
12311         // we should now have node body...
12312         var div = document.createElement('div');
12313         div.appendChild(node);
12314         tpl.dom = node;
12315         // this has the unfortunate side effect of converting tagged attributes
12316         // eg. href="{...}" into %7C...%7D
12317         // this has been fixed by searching for those combo's although it's a bit hacky..
12318         
12319         
12320         tpl.body = div.innerHTML;
12321         
12322         
12323          
12324         tpl.id = tpl.uid;
12325         switch(tpl.attr) {
12326             case 'for' :
12327                 switch (tpl.value) {
12328                     case '.':  tpl.forCall = new Function('values', 'parent', 'with(values){ return values; }'); break;
12329                     case '..': tpl.forCall= new Function('values', 'parent', 'with(values){ return parent; }'); break;
12330                     default:   tpl.forCall= new Function('values', 'parent', 'with(values){ return '+tpl.value+'; }');
12331                 }
12332                 break;
12333             
12334             case 'exec':
12335                 tpl.execCall = new Function('values', 'parent', 'with(values){ '+(Roo.util.Format.htmlDecode(tpl.value))+'; }');
12336                 break;
12337             
12338             case 'if':     
12339                 tpl.ifCall = new Function('values', 'parent', 'with(values){ return '+(Roo.util.Format.htmlDecode(tpl.value))+'; }');
12340                 break;
12341             
12342             case 'name':
12343                 tpl.id  = tpl.value; // replace non characters???
12344                 break;
12345             
12346         }
12347         
12348         
12349         this.tpls.push(tpl);
12350         
12351         
12352         
12353     },
12354     
12355     
12356     
12357     
12358     /**
12359      * Compile a segment of the template into a 'sub-template'
12360      *
12361      * 
12362      * 
12363      *
12364      */
12365     compileTpl : function(tpl)
12366     {
12367         var fm = Roo.util.Format;
12368         var useF = this.disableFormats !== true;
12369         
12370         var sep = Roo.isGecko ? "+\n" : ",\n";
12371         
12372         var undef = function(str) {
12373             Roo.debug && Roo.log("Property not found :"  + str);
12374             return '';
12375         };
12376           
12377         //Roo.log(tpl.body);
12378         
12379         
12380         
12381         var fn = function(m, lbrace, name, format, args)
12382         {
12383             //Roo.log("ARGS");
12384             //Roo.log(arguments);
12385             args = args ? args.replace(/\\'/g,"'") : args;
12386             //["{TEST:(a,b,c)}", "TEST", "", "a,b,c", 0, "{TEST:(a,b,c)}"]
12387             if (typeof(format) == 'undefined') {
12388                 format =  'htmlEncode'; 
12389             }
12390             if (format == 'raw' ) {
12391                 format = false;
12392             }
12393             
12394             if(name.substr(0, 6) == 'domtpl'){
12395                 return "'"+ sep +'this.applySubTemplate('+name.substr(6)+', values, parent)'+sep+"'";
12396             }
12397             
12398             // build an array of options to determine if value is undefined..
12399             
12400             // basically get 'xxxx.yyyy' then do
12401             // (typeof(xxxx) == 'undefined' || typeof(xxx.yyyy) == 'undefined') ?
12402             //    (function () { Roo.log("Property not found"); return ''; })() :
12403             //    ......
12404             
12405             var udef_ar = [];
12406             var lookfor = '';
12407             Roo.each(name.split('.'), function(st) {
12408                 lookfor += (lookfor.length ? '.': '') + st;
12409                 udef_ar.push(  "(typeof(" + lookfor + ") == 'undefined')"  );
12410             });
12411             
12412             var udef_st = '((' + udef_ar.join(" || ") +") ? undef('" + name + "') : "; // .. needs )
12413             
12414             
12415             if(format && useF){
12416                 
12417                 args = args ? ',' + args : "";
12418                  
12419                 if(format.substr(0, 5) != "this."){
12420                     format = "fm." + format + '(';
12421                 }else{
12422                     format = 'this.call("'+ format.substr(5) + '", ';
12423                     args = ", values";
12424                 }
12425                 
12426                 return "'"+ sep +   udef_st   +    format + name + args + "))"+sep+"'";
12427             }
12428              
12429             if (args && args.length) {
12430                 // called with xxyx.yuu:(test,test)
12431                 // change to ()
12432                 return "'"+ sep + udef_st  + name + '(' +  args + "))"+sep+"'";
12433             }
12434             // raw.. - :raw modifier..
12435             return "'"+ sep + udef_st  + name + ")"+sep+"'";
12436             
12437         };
12438         var body;
12439         // branched to use + in gecko and [].join() in others
12440         if(Roo.isGecko){
12441             body = "tpl.compiled = function(values, parent){  with(values) { return '" +
12442                    tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
12443                     "';};};";
12444         }else{
12445             body = ["tpl.compiled = function(values, parent){  with (values) { return ['"];
12446             body.push(tpl.body.replace(/(\r\n|\n)/g,
12447                             '\\n').replace(/'/g, "\\'").replace(this.re, fn));
12448             body.push("'].join('');};};");
12449             body = body.join('');
12450         }
12451         
12452         Roo.debug && Roo.log(body.replace(/\\n/,'\n'));
12453        
12454         /** eval:var:tpl eval:var:fm eval:var:useF eval:var:undef  */
12455         eval(body);
12456         
12457         return this;
12458     },
12459      
12460     /**
12461      * same as applyTemplate, except it's done to one of the subTemplates
12462      * when using named templates, you can do:
12463      *
12464      * var str = pl.applySubTemplate('your-name', values);
12465      *
12466      * 
12467      * @param {Number} id of the template
12468      * @param {Object} values to apply to template
12469      * @param {Object} parent (normaly the instance of this object)
12470      */
12471     applySubTemplate : function(id, values, parent)
12472     {
12473         
12474         
12475         var t = this.tpls[id];
12476         
12477         
12478         try { 
12479             if(t.ifCall && !t.ifCall.call(this, values, parent)){
12480                 Roo.debug && Roo.log('if call on ' + t.value + ' return false');
12481                 return '';
12482             }
12483         } catch(e) {
12484             Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on roo-if="' + t.value + '" - ' + e.toString());
12485             Roo.log(values);
12486           
12487             return '';
12488         }
12489         try { 
12490             
12491             if(t.execCall && t.execCall.call(this, values, parent)){
12492                 return '';
12493             }
12494         } catch(e) {
12495             Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on roo-for="' + t.value + '" - ' + e.toString());
12496             Roo.log(values);
12497             return '';
12498         }
12499         
12500         try {
12501             var vs = t.forCall ? t.forCall.call(this, values, parent) : values;
12502             parent = t.target ? values : parent;
12503             if(t.forCall && vs instanceof Array){
12504                 var buf = [];
12505                 for(var i = 0, len = vs.length; i < len; i++){
12506                     try {
12507                         buf[buf.length] = t.compiled.call(this, vs[i], parent);
12508                     } catch (e) {
12509                         Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on body="' + t.value + '" - ' + e.toString());
12510                         Roo.log(e.body);
12511                         //Roo.log(t.compiled);
12512                         Roo.log(vs[i]);
12513                     }   
12514                 }
12515                 return buf.join('');
12516             }
12517         } catch (e) {
12518             Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on roo-for="' + t.value + '" - ' + e.toString());
12519             Roo.log(values);
12520             return '';
12521         }
12522         try {
12523             return t.compiled.call(this, vs, parent);
12524         } catch (e) {
12525             Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on body="' + t.value + '" - ' + e.toString());
12526             Roo.log(e.body);
12527             //Roo.log(t.compiled);
12528             Roo.log(values);
12529             return '';
12530         }
12531     },
12532
12533    
12534
12535     applyTemplate : function(values){
12536         return this.master.compiled.call(this, values, {});
12537         //var s = this.subs;
12538     },
12539
12540     apply : function(){
12541         return this.applyTemplate.apply(this, arguments);
12542     }
12543
12544  });
12545
12546 Roo.DomTemplate.from = function(el){
12547     el = Roo.getDom(el);
12548     return new Roo.Domtemplate(el.value || el.innerHTML);
12549 };/*
12550  * Based on:
12551  * Ext JS Library 1.1.1
12552  * Copyright(c) 2006-2007, Ext JS, LLC.
12553  *
12554  * Originally Released Under LGPL - original licence link has changed is not relivant.
12555  *
12556  * Fork - LGPL
12557  * <script type="text/javascript">
12558  */
12559
12560 /**
12561  * @class Roo.util.DelayedTask
12562  * Provides a convenient method of performing setTimeout where a new
12563  * timeout cancels the old timeout. An example would be performing validation on a keypress.
12564  * You can use this class to buffer
12565  * the keypress events for a certain number of milliseconds, and perform only if they stop
12566  * for that amount of time.
12567  * @constructor The parameters to this constructor serve as defaults and are not required.
12568  * @param {Function} fn (optional) The default function to timeout
12569  * @param {Object} scope (optional) The default scope of that timeout
12570  * @param {Array} args (optional) The default Array of arguments
12571  */
12572 Roo.util.DelayedTask = function(fn, scope, args){
12573     var id = null, d, t;
12574
12575     var call = function(){
12576         var now = new Date().getTime();
12577         if(now - t >= d){
12578             clearInterval(id);
12579             id = null;
12580             fn.apply(scope, args || []);
12581         }
12582     };
12583     /**
12584      * Cancels any pending timeout and queues a new one
12585      * @param {Number} delay The milliseconds to delay
12586      * @param {Function} newFn (optional) Overrides function passed to constructor
12587      * @param {Object} newScope (optional) Overrides scope passed to constructor
12588      * @param {Array} newArgs (optional) Overrides args passed to constructor
12589      */
12590     this.delay = function(delay, newFn, newScope, newArgs){
12591         if(id && delay != d){
12592             this.cancel();
12593         }
12594         d = delay;
12595         t = new Date().getTime();
12596         fn = newFn || fn;
12597         scope = newScope || scope;
12598         args = newArgs || args;
12599         if(!id){
12600             id = setInterval(call, d);
12601         }
12602     };
12603
12604     /**
12605      * Cancel the last queued timeout
12606      */
12607     this.cancel = function(){
12608         if(id){
12609             clearInterval(id);
12610             id = null;
12611         }
12612     };
12613 };/*
12614  * Based on:
12615  * Ext JS Library 1.1.1
12616  * Copyright(c) 2006-2007, Ext JS, LLC.
12617  *
12618  * Originally Released Under LGPL - original licence link has changed is not relivant.
12619  *
12620  * Fork - LGPL
12621  * <script type="text/javascript">
12622  */
12623  
12624  
12625 Roo.util.TaskRunner = function(interval){
12626     interval = interval || 10;
12627     var tasks = [], removeQueue = [];
12628     var id = 0;
12629     var running = false;
12630
12631     var stopThread = function(){
12632         running = false;
12633         clearInterval(id);
12634         id = 0;
12635     };
12636
12637     var startThread = function(){
12638         if(!running){
12639             running = true;
12640             id = setInterval(runTasks, interval);
12641         }
12642     };
12643
12644     var removeTask = function(task){
12645         removeQueue.push(task);
12646         if(task.onStop){
12647             task.onStop();
12648         }
12649     };
12650
12651     var runTasks = function(){
12652         if(removeQueue.length > 0){
12653             for(var i = 0, len = removeQueue.length; i < len; i++){
12654                 tasks.remove(removeQueue[i]);
12655             }
12656             removeQueue = [];
12657             if(tasks.length < 1){
12658                 stopThread();
12659                 return;
12660             }
12661         }
12662         var now = new Date().getTime();
12663         for(var i = 0, len = tasks.length; i < len; ++i){
12664             var t = tasks[i];
12665             var itime = now - t.taskRunTime;
12666             if(t.interval <= itime){
12667                 var rt = t.run.apply(t.scope || t, t.args || [++t.taskRunCount]);
12668                 t.taskRunTime = now;
12669                 if(rt === false || t.taskRunCount === t.repeat){
12670                     removeTask(t);
12671                     return;
12672                 }
12673             }
12674             if(t.duration && t.duration <= (now - t.taskStartTime)){
12675                 removeTask(t);
12676             }
12677         }
12678     };
12679
12680     /**
12681      * Queues a new task.
12682      * @param {Object} task
12683      */
12684     this.start = function(task){
12685         tasks.push(task);
12686         task.taskStartTime = new Date().getTime();
12687         task.taskRunTime = 0;
12688         task.taskRunCount = 0;
12689         startThread();
12690         return task;
12691     };
12692
12693     this.stop = function(task){
12694         removeTask(task);
12695         return task;
12696     };
12697
12698     this.stopAll = function(){
12699         stopThread();
12700         for(var i = 0, len = tasks.length; i < len; i++){
12701             if(tasks[i].onStop){
12702                 tasks[i].onStop();
12703             }
12704         }
12705         tasks = [];
12706         removeQueue = [];
12707     };
12708 };
12709
12710 Roo.TaskMgr = new Roo.util.TaskRunner();/*
12711  * Based on:
12712  * Ext JS Library 1.1.1
12713  * Copyright(c) 2006-2007, Ext JS, LLC.
12714  *
12715  * Originally Released Under LGPL - original licence link has changed is not relivant.
12716  *
12717  * Fork - LGPL
12718  * <script type="text/javascript">
12719  */
12720
12721  
12722 /**
12723  * @class Roo.util.MixedCollection
12724  * @extends Roo.util.Observable
12725  * A Collection class that maintains both numeric indexes and keys and exposes events.
12726  * @constructor
12727  * @param {Boolean} allowFunctions True if the addAll function should add function references to the
12728  * collection (defaults to false)
12729  * @param {Function} keyFn A function that can accept an item of the type(s) stored in this MixedCollection
12730  * and return the key value for that item.  This is used when available to look up the key on items that
12731  * were passed without an explicit key parameter to a MixedCollection method.  Passing this parameter is
12732  * equivalent to providing an implementation for the {@link #getKey} method.
12733  */
12734 Roo.util.MixedCollection = function(allowFunctions, keyFn){
12735     this.items = [];
12736     this.map = {};
12737     this.keys = [];
12738     this.length = 0;
12739     this.addEvents({
12740         /**
12741          * @event clear
12742          * Fires when the collection is cleared.
12743          */
12744         "clear" : true,
12745         /**
12746          * @event add
12747          * Fires when an item is added to the collection.
12748          * @param {Number} index The index at which the item was added.
12749          * @param {Object} o The item added.
12750          * @param {String} key The key associated with the added item.
12751          */
12752         "add" : true,
12753         /**
12754          * @event replace
12755          * Fires when an item is replaced in the collection.
12756          * @param {String} key he key associated with the new added.
12757          * @param {Object} old The item being replaced.
12758          * @param {Object} new The new item.
12759          */
12760         "replace" : true,
12761         /**
12762          * @event remove
12763          * Fires when an item is removed from the collection.
12764          * @param {Object} o The item being removed.
12765          * @param {String} key (optional) The key associated with the removed item.
12766          */
12767         "remove" : true,
12768         "sort" : true
12769     });
12770     this.allowFunctions = allowFunctions === true;
12771     if(keyFn){
12772         this.getKey = keyFn;
12773     }
12774     Roo.util.MixedCollection.superclass.constructor.call(this);
12775 };
12776
12777 Roo.extend(Roo.util.MixedCollection, Roo.util.Observable, {
12778     allowFunctions : false,
12779     
12780 /**
12781  * Adds an item to the collection.
12782  * @param {String} key The key to associate with the item
12783  * @param {Object} o The item to add.
12784  * @return {Object} The item added.
12785  */
12786     add : function(key, o){
12787         if(arguments.length == 1){
12788             o = arguments[0];
12789             key = this.getKey(o);
12790         }
12791         if(typeof key == "undefined" || key === null){
12792             this.length++;
12793             this.items.push(o);
12794             this.keys.push(null);
12795         }else{
12796             var old = this.map[key];
12797             if(old){
12798                 return this.replace(key, o);
12799             }
12800             this.length++;
12801             this.items.push(o);
12802             this.map[key] = o;
12803             this.keys.push(key);
12804         }
12805         this.fireEvent("add", this.length-1, o, key);
12806         return o;
12807     },
12808        
12809 /**
12810   * MixedCollection has a generic way to fetch keys if you implement getKey.
12811 <pre><code>
12812 // normal way
12813 var mc = new Roo.util.MixedCollection();
12814 mc.add(someEl.dom.id, someEl);
12815 mc.add(otherEl.dom.id, otherEl);
12816 //and so on
12817
12818 // using getKey
12819 var mc = new Roo.util.MixedCollection();
12820 mc.getKey = function(el){
12821    return el.dom.id;
12822 };
12823 mc.add(someEl);
12824 mc.add(otherEl);
12825
12826 // or via the constructor
12827 var mc = new Roo.util.MixedCollection(false, function(el){
12828    return el.dom.id;
12829 });
12830 mc.add(someEl);
12831 mc.add(otherEl);
12832 </code></pre>
12833  * @param o {Object} The item for which to find the key.
12834  * @return {Object} The key for the passed item.
12835  */
12836     getKey : function(o){
12837          return o.id; 
12838     },
12839    
12840 /**
12841  * Replaces an item in the collection.
12842  * @param {String} key The key associated with the item to replace, or the item to replace.
12843  * @param o {Object} o (optional) If the first parameter passed was a key, the item to associate with that key.
12844  * @return {Object}  The new item.
12845  */
12846     replace : function(key, o){
12847         if(arguments.length == 1){
12848             o = arguments[0];
12849             key = this.getKey(o);
12850         }
12851         var old = this.item(key);
12852         if(typeof key == "undefined" || key === null || typeof old == "undefined"){
12853              return this.add(key, o);
12854         }
12855         var index = this.indexOfKey(key);
12856         this.items[index] = o;
12857         this.map[key] = o;
12858         this.fireEvent("replace", key, old, o);
12859         return o;
12860     },
12861    
12862 /**
12863  * Adds all elements of an Array or an Object to the collection.
12864  * @param {Object/Array} objs An Object containing properties which will be added to the collection, or
12865  * an Array of values, each of which are added to the collection.
12866  */
12867     addAll : function(objs){
12868         if(arguments.length > 1 || objs instanceof Array){
12869             var args = arguments.length > 1 ? arguments : objs;
12870             for(var i = 0, len = args.length; i < len; i++){
12871                 this.add(args[i]);
12872             }
12873         }else{
12874             for(var key in objs){
12875                 if(this.allowFunctions || typeof objs[key] != "function"){
12876                     this.add(key, objs[key]);
12877                 }
12878             }
12879         }
12880     },
12881    
12882 /**
12883  * Executes the specified function once for every item in the collection, passing each
12884  * item as the first and only parameter. returning false from the function will stop the iteration.
12885  * @param {Function} fn The function to execute for each item.
12886  * @param {Object} scope (optional) The scope in which to execute the function.
12887  */
12888     each : function(fn, scope){
12889         var items = [].concat(this.items); // each safe for removal
12890         for(var i = 0, len = items.length; i < len; i++){
12891             if(fn.call(scope || items[i], items[i], i, len) === false){
12892                 break;
12893             }
12894         }
12895     },
12896    
12897 /**
12898  * Executes the specified function once for every key in the collection, passing each
12899  * key, and its associated item as the first two parameters.
12900  * @param {Function} fn The function to execute for each item.
12901  * @param {Object} scope (optional) The scope in which to execute the function.
12902  */
12903     eachKey : function(fn, scope){
12904         for(var i = 0, len = this.keys.length; i < len; i++){
12905             fn.call(scope || window, this.keys[i], this.items[i], i, len);
12906         }
12907     },
12908    
12909 /**
12910  * Returns the first item in the collection which elicits a true return value from the
12911  * passed selection function.
12912  * @param {Function} fn The selection function to execute for each item.
12913  * @param {Object} scope (optional) The scope in which to execute the function.
12914  * @return {Object} The first item in the collection which returned true from the selection function.
12915  */
12916     find : function(fn, scope){
12917         for(var i = 0, len = this.items.length; i < len; i++){
12918             if(fn.call(scope || window, this.items[i], this.keys[i])){
12919                 return this.items[i];
12920             }
12921         }
12922         return null;
12923     },
12924    
12925 /**
12926  * Inserts an item at the specified index in the collection.
12927  * @param {Number} index The index to insert the item at.
12928  * @param {String} key The key to associate with the new item, or the item itself.
12929  * @param {Object} o  (optional) If the second parameter was a key, the new item.
12930  * @return {Object} The item inserted.
12931  */
12932     insert : function(index, key, o){
12933         if(arguments.length == 2){
12934             o = arguments[1];
12935             key = this.getKey(o);
12936         }
12937         if(index >= this.length){
12938             return this.add(key, o);
12939         }
12940         this.length++;
12941         this.items.splice(index, 0, o);
12942         if(typeof key != "undefined" && key != null){
12943             this.map[key] = o;
12944         }
12945         this.keys.splice(index, 0, key);
12946         this.fireEvent("add", index, o, key);
12947         return o;
12948     },
12949    
12950 /**
12951  * Removed an item from the collection.
12952  * @param {Object} o The item to remove.
12953  * @return {Object} The item removed.
12954  */
12955     remove : function(o){
12956         return this.removeAt(this.indexOf(o));
12957     },
12958    
12959 /**
12960  * Remove an item from a specified index in the collection.
12961  * @param {Number} index The index within the collection of the item to remove.
12962  */
12963     removeAt : function(index){
12964         if(index < this.length && index >= 0){
12965             this.length--;
12966             var o = this.items[index];
12967             this.items.splice(index, 1);
12968             var key = this.keys[index];
12969             if(typeof key != "undefined"){
12970                 delete this.map[key];
12971             }
12972             this.keys.splice(index, 1);
12973             this.fireEvent("remove", o, key);
12974         }
12975     },
12976    
12977 /**
12978  * Removed an item associated with the passed key fom the collection.
12979  * @param {String} key The key of the item to remove.
12980  */
12981     removeKey : function(key){
12982         return this.removeAt(this.indexOfKey(key));
12983     },
12984    
12985 /**
12986  * Returns the number of items in the collection.
12987  * @return {Number} the number of items in the collection.
12988  */
12989     getCount : function(){
12990         return this.length; 
12991     },
12992    
12993 /**
12994  * Returns index within the collection of the passed Object.
12995  * @param {Object} o The item to find the index of.
12996  * @return {Number} index of the item.
12997  */
12998     indexOf : function(o){
12999         if(!this.items.indexOf){
13000             for(var i = 0, len = this.items.length; i < len; i++){
13001                 if(this.items[i] == o) return i;
13002             }
13003             return -1;
13004         }else{
13005             return this.items.indexOf(o);
13006         }
13007     },
13008    
13009 /**
13010  * Returns index within the collection of the passed key.
13011  * @param {String} key The key to find the index of.
13012  * @return {Number} index of the key.
13013  */
13014     indexOfKey : function(key){
13015         if(!this.keys.indexOf){
13016             for(var i = 0, len = this.keys.length; i < len; i++){
13017                 if(this.keys[i] == key) return i;
13018             }
13019             return -1;
13020         }else{
13021             return this.keys.indexOf(key);
13022         }
13023     },
13024    
13025 /**
13026  * Returns the item associated with the passed key OR index. Key has priority over index.
13027  * @param {String/Number} key The key or index of the item.
13028  * @return {Object} The item associated with the passed key.
13029  */
13030     item : function(key){
13031         var item = typeof this.map[key] != "undefined" ? this.map[key] : this.items[key];
13032         return typeof item != 'function' || this.allowFunctions ? item : null; // for prototype!
13033     },
13034     
13035 /**
13036  * Returns the item at the specified index.
13037  * @param {Number} index The index of the item.
13038  * @return {Object}
13039  */
13040     itemAt : function(index){
13041         return this.items[index];
13042     },
13043     
13044 /**
13045  * Returns the item associated with the passed key.
13046  * @param {String/Number} key The key of the item.
13047  * @return {Object} The item associated with the passed key.
13048  */
13049     key : function(key){
13050         return this.map[key];
13051     },
13052    
13053 /**
13054  * Returns true if the collection contains the passed Object as an item.
13055  * @param {Object} o  The Object to look for in the collection.
13056  * @return {Boolean} True if the collection contains the Object as an item.
13057  */
13058     contains : function(o){
13059         return this.indexOf(o) != -1;
13060     },
13061    
13062 /**
13063  * Returns true if the collection contains the passed Object as a key.
13064  * @param {String} key The key to look for in the collection.
13065  * @return {Boolean} True if the collection contains the Object as a key.
13066  */
13067     containsKey : function(key){
13068         return typeof this.map[key] != "undefined";
13069     },
13070    
13071 /**
13072  * Removes all items from the collection.
13073  */
13074     clear : function(){
13075         this.length = 0;
13076         this.items = [];
13077         this.keys = [];
13078         this.map = {};
13079         this.fireEvent("clear");
13080     },
13081    
13082 /**
13083  * Returns the first item in the collection.
13084  * @return {Object} the first item in the collection..
13085  */
13086     first : function(){
13087         return this.items[0]; 
13088     },
13089    
13090 /**
13091  * Returns the last item in the collection.
13092  * @return {Object} the last item in the collection..
13093  */
13094     last : function(){
13095         return this.items[this.length-1];   
13096     },
13097     
13098     _sort : function(property, dir, fn){
13099         var dsc = String(dir).toUpperCase() == "DESC" ? -1 : 1;
13100         fn = fn || function(a, b){
13101             return a-b;
13102         };
13103         var c = [], k = this.keys, items = this.items;
13104         for(var i = 0, len = items.length; i < len; i++){
13105             c[c.length] = {key: k[i], value: items[i], index: i};
13106         }
13107         c.sort(function(a, b){
13108             var v = fn(a[property], b[property]) * dsc;
13109             if(v == 0){
13110                 v = (a.index < b.index ? -1 : 1);
13111             }
13112             return v;
13113         });
13114         for(var i = 0, len = c.length; i < len; i++){
13115             items[i] = c[i].value;
13116             k[i] = c[i].key;
13117         }
13118         this.fireEvent("sort", this);
13119     },
13120     
13121     /**
13122      * Sorts this collection with the passed comparison function
13123      * @param {String} direction (optional) "ASC" or "DESC"
13124      * @param {Function} fn (optional) comparison function
13125      */
13126     sort : function(dir, fn){
13127         this._sort("value", dir, fn);
13128     },
13129     
13130     /**
13131      * Sorts this collection by keys
13132      * @param {String} direction (optional) "ASC" or "DESC"
13133      * @param {Function} fn (optional) a comparison function (defaults to case insensitive string)
13134      */
13135     keySort : function(dir, fn){
13136         this._sort("key", dir, fn || function(a, b){
13137             return String(a).toUpperCase()-String(b).toUpperCase();
13138         });
13139     },
13140     
13141     /**
13142      * Returns a range of items in this collection
13143      * @param {Number} startIndex (optional) defaults to 0
13144      * @param {Number} endIndex (optional) default to the last item
13145      * @return {Array} An array of items
13146      */
13147     getRange : function(start, end){
13148         var items = this.items;
13149         if(items.length < 1){
13150             return [];
13151         }
13152         start = start || 0;
13153         end = Math.min(typeof end == "undefined" ? this.length-1 : end, this.length-1);
13154         var r = [];
13155         if(start <= end){
13156             for(var i = start; i <= end; i++) {
13157                     r[r.length] = items[i];
13158             }
13159         }else{
13160             for(var i = start; i >= end; i--) {
13161                     r[r.length] = items[i];
13162             }
13163         }
13164         return r;
13165     },
13166         
13167     /**
13168      * Filter the <i>objects</i> in this collection by a specific property. 
13169      * Returns a new collection that has been filtered.
13170      * @param {String} property A property on your objects
13171      * @param {String/RegExp} value Either string that the property values 
13172      * should start with or a RegExp to test against the property
13173      * @return {MixedCollection} The new filtered collection
13174      */
13175     filter : function(property, value){
13176         if(!value.exec){ // not a regex
13177             value = String(value);
13178             if(value.length == 0){
13179                 return this.clone();
13180             }
13181             value = new RegExp("^" + Roo.escapeRe(value), "i");
13182         }
13183         return this.filterBy(function(o){
13184             return o && value.test(o[property]);
13185         });
13186         },
13187     
13188     /**
13189      * Filter by a function. * Returns a new collection that has been filtered.
13190      * The passed function will be called with each 
13191      * object in the collection. If the function returns true, the value is included 
13192      * otherwise it is filtered.
13193      * @param {Function} fn The function to be called, it will receive the args o (the object), k (the key)
13194      * @param {Object} scope (optional) The scope of the function (defaults to this) 
13195      * @return {MixedCollection} The new filtered collection
13196      */
13197     filterBy : function(fn, scope){
13198         var r = new Roo.util.MixedCollection();
13199         r.getKey = this.getKey;
13200         var k = this.keys, it = this.items;
13201         for(var i = 0, len = it.length; i < len; i++){
13202             if(fn.call(scope||this, it[i], k[i])){
13203                                 r.add(k[i], it[i]);
13204                         }
13205         }
13206         return r;
13207     },
13208     
13209     /**
13210      * Creates a duplicate of this collection
13211      * @return {MixedCollection}
13212      */
13213     clone : function(){
13214         var r = new Roo.util.MixedCollection();
13215         var k = this.keys, it = this.items;
13216         for(var i = 0, len = it.length; i < len; i++){
13217             r.add(k[i], it[i]);
13218         }
13219         r.getKey = this.getKey;
13220         return r;
13221     }
13222 });
13223 /**
13224  * Returns the item associated with the passed key or index.
13225  * @method
13226  * @param {String/Number} key The key or index of the item.
13227  * @return {Object} The item associated with the passed key.
13228  */
13229 Roo.util.MixedCollection.prototype.get = Roo.util.MixedCollection.prototype.item;/*
13230  * Based on:
13231  * Ext JS Library 1.1.1
13232  * Copyright(c) 2006-2007, Ext JS, LLC.
13233  *
13234  * Originally Released Under LGPL - original licence link has changed is not relivant.
13235  *
13236  * Fork - LGPL
13237  * <script type="text/javascript">
13238  */
13239 /**
13240  * @class Roo.util.JSON
13241  * Modified version of Douglas Crockford"s json.js that doesn"t
13242  * mess with the Object prototype 
13243  * http://www.json.org/js.html
13244  * @singleton
13245  */
13246 Roo.util.JSON = new (function(){
13247     var useHasOwn = {}.hasOwnProperty ? true : false;
13248     
13249     // crashes Safari in some instances
13250     //var validRE = /^("(\\.|[^"\\\n\r])*?"|[,:{}\[\]0-9.\-+Eaeflnr-u \n\r\t])+?$/;
13251     
13252     var pad = function(n) {
13253         return n < 10 ? "0" + n : n;
13254     };
13255     
13256     var m = {
13257         "\b": '\\b',
13258         "\t": '\\t',
13259         "\n": '\\n',
13260         "\f": '\\f',
13261         "\r": '\\r',
13262         '"' : '\\"',
13263         "\\": '\\\\'
13264     };
13265
13266     var encodeString = function(s){
13267         if (/["\\\x00-\x1f]/.test(s)) {
13268             return '"' + s.replace(/([\x00-\x1f\\"])/g, function(a, b) {
13269                 var c = m[b];
13270                 if(c){
13271                     return c;
13272                 }
13273                 c = b.charCodeAt();
13274                 return "\\u00" +
13275                     Math.floor(c / 16).toString(16) +
13276                     (c % 16).toString(16);
13277             }) + '"';
13278         }
13279         return '"' + s + '"';
13280     };
13281     
13282     var encodeArray = function(o){
13283         var a = ["["], b, i, l = o.length, v;
13284             for (i = 0; i < l; i += 1) {
13285                 v = o[i];
13286                 switch (typeof v) {
13287                     case "undefined":
13288                     case "function":
13289                     case "unknown":
13290                         break;
13291                     default:
13292                         if (b) {
13293                             a.push(',');
13294                         }
13295                         a.push(v === null ? "null" : Roo.util.JSON.encode(v));
13296                         b = true;
13297                 }
13298             }
13299             a.push("]");
13300             return a.join("");
13301     };
13302     
13303     var encodeDate = function(o){
13304         return '"' + o.getFullYear() + "-" +
13305                 pad(o.getMonth() + 1) + "-" +
13306                 pad(o.getDate()) + "T" +
13307                 pad(o.getHours()) + ":" +
13308                 pad(o.getMinutes()) + ":" +
13309                 pad(o.getSeconds()) + '"';
13310     };
13311     
13312     /**
13313      * Encodes an Object, Array or other value
13314      * @param {Mixed} o The variable to encode
13315      * @return {String} The JSON string
13316      */
13317     this.encode = function(o)
13318     {
13319         // should this be extended to fully wrap stringify..
13320         
13321         if(typeof o == "undefined" || o === null){
13322             return "null";
13323         }else if(o instanceof Array){
13324             return encodeArray(o);
13325         }else if(o instanceof Date){
13326             return encodeDate(o);
13327         }else if(typeof o == "string"){
13328             return encodeString(o);
13329         }else if(typeof o == "number"){
13330             return isFinite(o) ? String(o) : "null";
13331         }else if(typeof o == "boolean"){
13332             return String(o);
13333         }else {
13334             var a = ["{"], b, i, v;
13335             for (i in o) {
13336                 if(!useHasOwn || o.hasOwnProperty(i)) {
13337                     v = o[i];
13338                     switch (typeof v) {
13339                     case "undefined":
13340                     case "function":
13341                     case "unknown":
13342                         break;
13343                     default:
13344                         if(b){
13345                             a.push(',');
13346                         }
13347                         a.push(this.encode(i), ":",
13348                                 v === null ? "null" : this.encode(v));
13349                         b = true;
13350                     }
13351                 }
13352             }
13353             a.push("}");
13354             return a.join("");
13355         }
13356     };
13357     
13358     /**
13359      * Decodes (parses) a JSON string to an object. If the JSON is invalid, this function throws a SyntaxError.
13360      * @param {String} json The JSON string
13361      * @return {Object} The resulting object
13362      */
13363     this.decode = function(json){
13364         
13365         return  /** eval:var:json */ eval("(" + json + ')');
13366     };
13367 })();
13368 /** 
13369  * Shorthand for {@link Roo.util.JSON#encode}
13370  * @member Roo encode 
13371  * @method */
13372 Roo.encode = typeof(JSON) != 'undefined' && JSON.stringify ? JSON.stringify : Roo.util.JSON.encode;
13373 /** 
13374  * Shorthand for {@link Roo.util.JSON#decode}
13375  * @member Roo decode 
13376  * @method */
13377 Roo.decode = typeof(JSON) != 'undefined' && JSON.parse ? JSON.parse : Roo.util.JSON.decode;
13378 /*
13379  * Based on:
13380  * Ext JS Library 1.1.1
13381  * Copyright(c) 2006-2007, Ext JS, LLC.
13382  *
13383  * Originally Released Under LGPL - original licence link has changed is not relivant.
13384  *
13385  * Fork - LGPL
13386  * <script type="text/javascript">
13387  */
13388  
13389 /**
13390  * @class Roo.util.Format
13391  * Reusable data formatting functions
13392  * @singleton
13393  */
13394 Roo.util.Format = function(){
13395     var trimRe = /^\s+|\s+$/g;
13396     return {
13397         /**
13398          * Truncate a string and add an ellipsis ('...') to the end if it exceeds the specified length
13399          * @param {String} value The string to truncate
13400          * @param {Number} length The maximum length to allow before truncating
13401          * @return {String} The converted text
13402          */
13403         ellipsis : function(value, len){
13404             if(value && value.length > len){
13405                 return value.substr(0, len-3)+"...";
13406             }
13407             return value;
13408         },
13409
13410         /**
13411          * Checks a reference and converts it to empty string if it is undefined
13412          * @param {Mixed} value Reference to check
13413          * @return {Mixed} Empty string if converted, otherwise the original value
13414          */
13415         undef : function(value){
13416             return typeof value != "undefined" ? value : "";
13417         },
13418
13419         /**
13420          * Convert certain characters (&, <, >, and ') to their HTML character equivalents for literal display in web pages.
13421          * @param {String} value The string to encode
13422          * @return {String} The encoded text
13423          */
13424         htmlEncode : function(value){
13425             return !value ? value : String(value).replace(/&/g, "&amp;").replace(/>/g, "&gt;").replace(/</g, "&lt;").replace(/"/g, "&quot;");
13426         },
13427
13428         /**
13429          * Convert certain characters (&, <, >, and ') from their HTML character equivalents.
13430          * @param {String} value The string to decode
13431          * @return {String} The decoded text
13432          */
13433         htmlDecode : function(value){
13434             return !value ? value : String(value).replace(/&amp;/g, "&").replace(/&gt;/g, ">").replace(/&lt;/g, "<").replace(/&quot;/g, '"');
13435         },
13436
13437         /**
13438          * Trims any whitespace from either side of a string
13439          * @param {String} value The text to trim
13440          * @return {String} The trimmed text
13441          */
13442         trim : function(value){
13443             return String(value).replace(trimRe, "");
13444         },
13445
13446         /**
13447          * Returns a substring from within an original string
13448          * @param {String} value The original text
13449          * @param {Number} start The start index of the substring
13450          * @param {Number} length The length of the substring
13451          * @return {String} The substring
13452          */
13453         substr : function(value, start, length){
13454             return String(value).substr(start, length);
13455         },
13456
13457         /**
13458          * Converts a string to all lower case letters
13459          * @param {String} value The text to convert
13460          * @return {String} The converted text
13461          */
13462         lowercase : function(value){
13463             return String(value).toLowerCase();
13464         },
13465
13466         /**
13467          * Converts a string to all upper case letters
13468          * @param {String} value The text to convert
13469          * @return {String} The converted text
13470          */
13471         uppercase : function(value){
13472             return String(value).toUpperCase();
13473         },
13474
13475         /**
13476          * Converts the first character only of a string to upper case
13477          * @param {String} value The text to convert
13478          * @return {String} The converted text
13479          */
13480         capitalize : function(value){
13481             return !value ? value : value.charAt(0).toUpperCase() + value.substr(1).toLowerCase();
13482         },
13483
13484         // private
13485         call : function(value, fn){
13486             if(arguments.length > 2){
13487                 var args = Array.prototype.slice.call(arguments, 2);
13488                 args.unshift(value);
13489                  
13490                 return /** eval:var:value */  eval(fn).apply(window, args);
13491             }else{
13492                 /** eval:var:value */
13493                 return /** eval:var:value */ eval(fn).call(window, value);
13494             }
13495         },
13496
13497        
13498         /**
13499          * safer version of Math.toFixed..??/
13500          * @param {Number/String} value The numeric value to format
13501          * @param {Number/String} value Decimal places 
13502          * @return {String} The formatted currency string
13503          */
13504         toFixed : function(v, n)
13505         {
13506             // why not use to fixed - precision is buggered???
13507             if (!n) {
13508                 return Math.round(v-0);
13509             }
13510             var fact = Math.pow(10,n+1);
13511             v = (Math.round((v-0)*fact))/fact;
13512             var z = (''+fact).substring(2);
13513             if (v == Math.floor(v)) {
13514                 return Math.floor(v) + '.' + z;
13515             }
13516             
13517             // now just padd decimals..
13518             var ps = String(v).split('.');
13519             var fd = (ps[1] + z);
13520             var r = fd.substring(0,n); 
13521             var rm = fd.substring(n); 
13522             if (rm < 5) {
13523                 return ps[0] + '.' + r;
13524             }
13525             r*=1; // turn it into a number;
13526             r++;
13527             if (String(r).length != n) {
13528                 ps[0]*=1;
13529                 ps[0]++;
13530                 r = String(r).substring(1); // chop the end off.
13531             }
13532             
13533             return ps[0] + '.' + r;
13534              
13535         },
13536         
13537         /**
13538          * Format a number as US currency
13539          * @param {Number/String} value The numeric value to format
13540          * @return {String} The formatted currency string
13541          */
13542         usMoney : function(v){
13543             return '$' + Roo.util.Format.number(v);
13544         },
13545         
13546         /**
13547          * Format a number
13548          * eventually this should probably emulate php's number_format
13549          * @param {Number/String} value The numeric value to format
13550          * @param {Number} decimals number of decimal places
13551          * @return {String} The formatted currency string
13552          */
13553         number : function(v,decimals)
13554         {
13555             // multiply and round.
13556             decimals = typeof(decimals) == 'undefined' ? 2 : decimals;
13557             var mul = Math.pow(10, decimals);
13558             var zero = String(mul).substring(1);
13559             v = (Math.round((v-0)*mul))/mul;
13560             
13561             // if it's '0' number.. then
13562             
13563             //v = (v == Math.floor(v)) ? v + "." + zero : ((v*10 == Math.floor(v*10)) ? v + "0" : v);
13564             v = String(v);
13565             var ps = v.split('.');
13566             var whole = ps[0];
13567             
13568             
13569             var r = /(\d+)(\d{3})/;
13570             // add comma's
13571             while (r.test(whole)) {
13572                 whole = whole.replace(r, '$1' + ',' + '$2');
13573             }
13574             
13575             
13576             var sub = ps[1] ?
13577                     // has decimals..
13578                     (decimals ?  ('.'+ ps[1] + zero.substring(ps[1].length)) : '') :
13579                     // does not have decimals
13580                     (decimals ? ('.' + zero) : '');
13581             
13582             
13583             return whole + sub ;
13584         },
13585         
13586         /**
13587          * Parse a value into a formatted date using the specified format pattern.
13588          * @param {Mixed} value The value to format
13589          * @param {String} format (optional) Any valid date format string (defaults to 'm/d/Y')
13590          * @return {String} The formatted date string
13591          */
13592         date : function(v, format){
13593             if(!v){
13594                 return "";
13595             }
13596             if(!(v instanceof Date)){
13597                 v = new Date(Date.parse(v));
13598             }
13599             return v.dateFormat(format || "m/d/Y");
13600         },
13601
13602         /**
13603          * Returns a date rendering function that can be reused to apply a date format multiple times efficiently
13604          * @param {String} format Any valid date format string
13605          * @return {Function} The date formatting function
13606          */
13607         dateRenderer : function(format){
13608             return function(v){
13609                 return Roo.util.Format.date(v, format);  
13610             };
13611         },
13612
13613         // private
13614         stripTagsRE : /<\/?[^>]+>/gi,
13615         
13616         /**
13617          * Strips all HTML tags
13618          * @param {Mixed} value The text from which to strip tags
13619          * @return {String} The stripped text
13620          */
13621         stripTags : function(v){
13622             return !v ? v : String(v).replace(this.stripTagsRE, "");
13623         }
13624     };
13625 }();/*
13626  * Based on:
13627  * Ext JS Library 1.1.1
13628  * Copyright(c) 2006-2007, Ext JS, LLC.
13629  *
13630  * Originally Released Under LGPL - original licence link has changed is not relivant.
13631  *
13632  * Fork - LGPL
13633  * <script type="text/javascript">
13634  */
13635
13636
13637  
13638
13639 /**
13640  * @class Roo.MasterTemplate
13641  * @extends Roo.Template
13642  * Provides a template that can have child templates. The syntax is:
13643 <pre><code>
13644 var t = new Roo.MasterTemplate(
13645         '&lt;select name="{name}"&gt;',
13646                 '&lt;tpl name="options"&gt;&lt;option value="{value:trim}"&gt;{text:ellipsis(10)}&lt;/option&gt;&lt;/tpl&gt;',
13647         '&lt;/select&gt;'
13648 );
13649 t.add('options', {value: 'foo', text: 'bar'});
13650 // or you can add multiple child elements in one shot
13651 t.addAll('options', [
13652     {value: 'foo', text: 'bar'},
13653     {value: 'foo2', text: 'bar2'},
13654     {value: 'foo3', text: 'bar3'}
13655 ]);
13656 // then append, applying the master template values
13657 t.append('my-form', {name: 'my-select'});
13658 </code></pre>
13659 * A name attribute for the child template is not required if you have only one child
13660 * template or you want to refer to them by index.
13661  */
13662 Roo.MasterTemplate = function(){
13663     Roo.MasterTemplate.superclass.constructor.apply(this, arguments);
13664     this.originalHtml = this.html;
13665     var st = {};
13666     var m, re = this.subTemplateRe;
13667     re.lastIndex = 0;
13668     var subIndex = 0;
13669     while(m = re.exec(this.html)){
13670         var name = m[1], content = m[2];
13671         st[subIndex] = {
13672             name: name,
13673             index: subIndex,
13674             buffer: [],
13675             tpl : new Roo.Template(content)
13676         };
13677         if(name){
13678             st[name] = st[subIndex];
13679         }
13680         st[subIndex].tpl.compile();
13681         st[subIndex].tpl.call = this.call.createDelegate(this);
13682         subIndex++;
13683     }
13684     this.subCount = subIndex;
13685     this.subs = st;
13686 };
13687 Roo.extend(Roo.MasterTemplate, Roo.Template, {
13688     /**
13689     * The regular expression used to match sub templates
13690     * @type RegExp
13691     * @property
13692     */
13693     subTemplateRe : /<tpl(?:\sname="([\w-]+)")?>((?:.|\n)*?)<\/tpl>/gi,
13694
13695     /**
13696      * Applies the passed values to a child template.
13697      * @param {String/Number} name (optional) The name or index of the child template
13698      * @param {Array/Object} values The values to be applied to the template
13699      * @return {MasterTemplate} this
13700      */
13701      add : function(name, values){
13702         if(arguments.length == 1){
13703             values = arguments[0];
13704             name = 0;
13705         }
13706         var s = this.subs[name];
13707         s.buffer[s.buffer.length] = s.tpl.apply(values);
13708         return this;
13709     },
13710
13711     /**
13712      * Applies all the passed values to a child template.
13713      * @param {String/Number} name (optional) The name or index of the child template
13714      * @param {Array} values The values to be applied to the template, this should be an array of objects.
13715      * @param {Boolean} reset (optional) True to reset the template first
13716      * @return {MasterTemplate} this
13717      */
13718     fill : function(name, values, reset){
13719         var a = arguments;
13720         if(a.length == 1 || (a.length == 2 && typeof a[1] == "boolean")){
13721             values = a[0];
13722             name = 0;
13723             reset = a[1];
13724         }
13725         if(reset){
13726             this.reset();
13727         }
13728         for(var i = 0, len = values.length; i < len; i++){
13729             this.add(name, values[i]);
13730         }
13731         return this;
13732     },
13733
13734     /**
13735      * Resets the template for reuse
13736      * @return {MasterTemplate} this
13737      */
13738      reset : function(){
13739         var s = this.subs;
13740         for(var i = 0; i < this.subCount; i++){
13741             s[i].buffer = [];
13742         }
13743         return this;
13744     },
13745
13746     applyTemplate : function(values){
13747         var s = this.subs;
13748         var replaceIndex = -1;
13749         this.html = this.originalHtml.replace(this.subTemplateRe, function(m, name){
13750             return s[++replaceIndex].buffer.join("");
13751         });
13752         return Roo.MasterTemplate.superclass.applyTemplate.call(this, values);
13753     },
13754
13755     apply : function(){
13756         return this.applyTemplate.apply(this, arguments);
13757     },
13758
13759     compile : function(){return this;}
13760 });
13761
13762 /**
13763  * Alias for fill().
13764  * @method
13765  */
13766 Roo.MasterTemplate.prototype.addAll = Roo.MasterTemplate.prototype.fill;
13767  /**
13768  * Creates a template from the passed element's value (display:none textarea, preferred) or innerHTML. e.g.
13769  * var tpl = Roo.MasterTemplate.from('element-id');
13770  * @param {String/HTMLElement} el
13771  * @param {Object} config
13772  * @static
13773  */
13774 Roo.MasterTemplate.from = function(el, config){
13775     el = Roo.getDom(el);
13776     return new Roo.MasterTemplate(el.value || el.innerHTML, config || '');
13777 };/*
13778  * Based on:
13779  * Ext JS Library 1.1.1
13780  * Copyright(c) 2006-2007, Ext JS, LLC.
13781  *
13782  * Originally Released Under LGPL - original licence link has changed is not relivant.
13783  *
13784  * Fork - LGPL
13785  * <script type="text/javascript">
13786  */
13787
13788  
13789 /**
13790  * @class Roo.util.CSS
13791  * Utility class for manipulating CSS rules
13792  * @singleton
13793  */
13794 Roo.util.CSS = function(){
13795         var rules = null;
13796         var doc = document;
13797
13798     var camelRe = /(-[a-z])/gi;
13799     var camelFn = function(m, a){ return a.charAt(1).toUpperCase(); };
13800
13801    return {
13802    /**
13803     * Very simple dynamic creation of stylesheets from a text blob of rules.  The text will wrapped in a style
13804     * tag and appended to the HEAD of the document.
13805     * @param {String|Object} cssText The text containing the css rules
13806     * @param {String} id An id to add to the stylesheet for later removal
13807     * @return {StyleSheet}
13808     */
13809     createStyleSheet : function(cssText, id){
13810         var ss;
13811         var head = doc.getElementsByTagName("head")[0];
13812         var nrules = doc.createElement("style");
13813         nrules.setAttribute("type", "text/css");
13814         if(id){
13815             nrules.setAttribute("id", id);
13816         }
13817         if (typeof(cssText) != 'string') {
13818             // support object maps..
13819             // not sure if this a good idea.. 
13820             // perhaps it should be merged with the general css handling
13821             // and handle js style props.
13822             var cssTextNew = [];
13823             for(var n in cssText) {
13824                 var citems = [];
13825                 for(var k in cssText[n]) {
13826                     citems.push( k + ' : ' +cssText[n][k] + ';' );
13827                 }
13828                 cssTextNew.push( n + ' { ' + citems.join(' ') + '} ');
13829                 
13830             }
13831             cssText = cssTextNew.join("\n");
13832             
13833         }
13834        
13835        
13836        if(Roo.isIE){
13837            head.appendChild(nrules);
13838            ss = nrules.styleSheet;
13839            ss.cssText = cssText;
13840        }else{
13841            try{
13842                 nrules.appendChild(doc.createTextNode(cssText));
13843            }catch(e){
13844                nrules.cssText = cssText; 
13845            }
13846            head.appendChild(nrules);
13847            ss = nrules.styleSheet ? nrules.styleSheet : (nrules.sheet || doc.styleSheets[doc.styleSheets.length-1]);
13848        }
13849        this.cacheStyleSheet(ss);
13850        return ss;
13851    },
13852
13853    /**
13854     * Removes a style or link tag by id
13855     * @param {String} id The id of the tag
13856     */
13857    removeStyleSheet : function(id){
13858        var existing = doc.getElementById(id);
13859        if(existing){
13860            existing.parentNode.removeChild(existing);
13861        }
13862    },
13863
13864    /**
13865     * Dynamically swaps an existing stylesheet reference for a new one
13866     * @param {String} id The id of an existing link tag to remove
13867     * @param {String} url The href of the new stylesheet to include
13868     */
13869    swapStyleSheet : function(id, url){
13870        this.removeStyleSheet(id);
13871        var ss = doc.createElement("link");
13872        ss.setAttribute("rel", "stylesheet");
13873        ss.setAttribute("type", "text/css");
13874        ss.setAttribute("id", id);
13875        ss.setAttribute("href", url);
13876        doc.getElementsByTagName("head")[0].appendChild(ss);
13877    },
13878    
13879    /**
13880     * Refresh the rule cache if you have dynamically added stylesheets
13881     * @return {Object} An object (hash) of rules indexed by selector
13882     */
13883    refreshCache : function(){
13884        return this.getRules(true);
13885    },
13886
13887    // private
13888    cacheStyleSheet : function(stylesheet){
13889        if(!rules){
13890            rules = {};
13891        }
13892        try{// try catch for cross domain access issue
13893            var ssRules = stylesheet.cssRules || stylesheet.rules;
13894            for(var j = ssRules.length-1; j >= 0; --j){
13895                rules[ssRules[j].selectorText] = ssRules[j];
13896            }
13897        }catch(e){}
13898    },
13899    
13900    /**
13901     * Gets all css rules for the document
13902     * @param {Boolean} refreshCache true to refresh the internal cache
13903     * @return {Object} An object (hash) of rules indexed by selector
13904     */
13905    getRules : function(refreshCache){
13906                 if(rules == null || refreshCache){
13907                         rules = {};
13908                         var ds = doc.styleSheets;
13909                         for(var i =0, len = ds.length; i < len; i++){
13910                             try{
13911                         this.cacheStyleSheet(ds[i]);
13912                     }catch(e){} 
13913                 }
13914                 }
13915                 return rules;
13916         },
13917         
13918         /**
13919     * Gets an an individual CSS rule by selector(s)
13920     * @param {String/Array} selector The CSS selector or an array of selectors to try. The first selector that is found is returned.
13921     * @param {Boolean} refreshCache true to refresh the internal cache if you have recently updated any rules or added styles dynamically
13922     * @return {CSSRule} The CSS rule or null if one is not found
13923     */
13924    getRule : function(selector, refreshCache){
13925                 var rs = this.getRules(refreshCache);
13926                 if(!(selector instanceof Array)){
13927                     return rs[selector];
13928                 }
13929                 for(var i = 0; i < selector.length; i++){
13930                         if(rs[selector[i]]){
13931                                 return rs[selector[i]];
13932                         }
13933                 }
13934                 return null;
13935         },
13936         
13937         
13938         /**
13939     * Updates a rule property
13940     * @param {String/Array} selector If it's an array it tries each selector until it finds one. Stops immediately once one is found.
13941     * @param {String} property The css property
13942     * @param {String} value The new value for the property
13943     * @return {Boolean} true If a rule was found and updated
13944     */
13945    updateRule : function(selector, property, value){
13946                 if(!(selector instanceof Array)){
13947                         var rule = this.getRule(selector);
13948                         if(rule){
13949                                 rule.style[property.replace(camelRe, camelFn)] = value;
13950                                 return true;
13951                         }
13952                 }else{
13953                         for(var i = 0; i < selector.length; i++){
13954                                 if(this.updateRule(selector[i], property, value)){
13955                                         return true;
13956                                 }
13957                         }
13958                 }
13959                 return false;
13960         }
13961    };   
13962 }();/*
13963  * Based on:
13964  * Ext JS Library 1.1.1
13965  * Copyright(c) 2006-2007, Ext JS, LLC.
13966  *
13967  * Originally Released Under LGPL - original licence link has changed is not relivant.
13968  *
13969  * Fork - LGPL
13970  * <script type="text/javascript">
13971  */
13972
13973  
13974
13975 /**
13976  * @class Roo.util.ClickRepeater
13977  * @extends Roo.util.Observable
13978  * 
13979  * A wrapper class which can be applied to any element. Fires a "click" event while the
13980  * mouse is pressed. The interval between firings may be specified in the config but
13981  * defaults to 10 milliseconds.
13982  * 
13983  * Optionally, a CSS class may be applied to the element during the time it is pressed.
13984  * 
13985  * @cfg {String/HTMLElement/Element} el The element to act as a button.
13986  * @cfg {Number} delay The initial delay before the repeating event begins firing.
13987  * Similar to an autorepeat key delay.
13988  * @cfg {Number} interval The interval between firings of the "click" event. Default 10 ms.
13989  * @cfg {String} pressClass A CSS class name to be applied to the element while pressed.
13990  * @cfg {Boolean} accelerate True if autorepeating should start slowly and accelerate.
13991  *           "interval" and "delay" are ignored. "immediate" is honored.
13992  * @cfg {Boolean} preventDefault True to prevent the default click event
13993  * @cfg {Boolean} stopDefault True to stop the default click event
13994  * 
13995  * @history
13996  *     2007-02-02 jvs Original code contributed by Nige "Animal" White
13997  *     2007-02-02 jvs Renamed to ClickRepeater
13998  *   2007-02-03 jvs Modifications for FF Mac and Safari 
13999  *
14000  *  @constructor
14001  * @param {String/HTMLElement/Element} el The element to listen on
14002  * @param {Object} config
14003  **/
14004 Roo.util.ClickRepeater = function(el, config)
14005 {
14006     this.el = Roo.get(el);
14007     this.el.unselectable();
14008
14009     Roo.apply(this, config);
14010
14011     this.addEvents({
14012     /**
14013      * @event mousedown
14014      * Fires when the mouse button is depressed.
14015      * @param {Roo.util.ClickRepeater} this
14016      */
14017         "mousedown" : true,
14018     /**
14019      * @event click
14020      * Fires on a specified interval during the time the element is pressed.
14021      * @param {Roo.util.ClickRepeater} this
14022      */
14023         "click" : true,
14024     /**
14025      * @event mouseup
14026      * Fires when the mouse key is released.
14027      * @param {Roo.util.ClickRepeater} this
14028      */
14029         "mouseup" : true
14030     });
14031
14032     this.el.on("mousedown", this.handleMouseDown, this);
14033     if(this.preventDefault || this.stopDefault){
14034         this.el.on("click", function(e){
14035             if(this.preventDefault){
14036                 e.preventDefault();
14037             }
14038             if(this.stopDefault){
14039                 e.stopEvent();
14040             }
14041         }, this);
14042     }
14043
14044     // allow inline handler
14045     if(this.handler){
14046         this.on("click", this.handler,  this.scope || this);
14047     }
14048
14049     Roo.util.ClickRepeater.superclass.constructor.call(this);
14050 };
14051
14052 Roo.extend(Roo.util.ClickRepeater, Roo.util.Observable, {
14053     interval : 20,
14054     delay: 250,
14055     preventDefault : true,
14056     stopDefault : false,
14057     timer : 0,
14058
14059     // private
14060     handleMouseDown : function(){
14061         clearTimeout(this.timer);
14062         this.el.blur();
14063         if(this.pressClass){
14064             this.el.addClass(this.pressClass);
14065         }
14066         this.mousedownTime = new Date();
14067
14068         Roo.get(document).on("mouseup", this.handleMouseUp, this);
14069         this.el.on("mouseout", this.handleMouseOut, this);
14070
14071         this.fireEvent("mousedown", this);
14072         this.fireEvent("click", this);
14073         
14074         this.timer = this.click.defer(this.delay || this.interval, this);
14075     },
14076
14077     // private
14078     click : function(){
14079         this.fireEvent("click", this);
14080         this.timer = this.click.defer(this.getInterval(), this);
14081     },
14082
14083     // private
14084     getInterval: function(){
14085         if(!this.accelerate){
14086             return this.interval;
14087         }
14088         var pressTime = this.mousedownTime.getElapsed();
14089         if(pressTime < 500){
14090             return 400;
14091         }else if(pressTime < 1700){
14092             return 320;
14093         }else if(pressTime < 2600){
14094             return 250;
14095         }else if(pressTime < 3500){
14096             return 180;
14097         }else if(pressTime < 4400){
14098             return 140;
14099         }else if(pressTime < 5300){
14100             return 80;
14101         }else if(pressTime < 6200){
14102             return 50;
14103         }else{
14104             return 10;
14105         }
14106     },
14107
14108     // private
14109     handleMouseOut : function(){
14110         clearTimeout(this.timer);
14111         if(this.pressClass){
14112             this.el.removeClass(this.pressClass);
14113         }
14114         this.el.on("mouseover", this.handleMouseReturn, this);
14115     },
14116
14117     // private
14118     handleMouseReturn : function(){
14119         this.el.un("mouseover", this.handleMouseReturn);
14120         if(this.pressClass){
14121             this.el.addClass(this.pressClass);
14122         }
14123         this.click();
14124     },
14125
14126     // private
14127     handleMouseUp : function(){
14128         clearTimeout(this.timer);
14129         this.el.un("mouseover", this.handleMouseReturn);
14130         this.el.un("mouseout", this.handleMouseOut);
14131         Roo.get(document).un("mouseup", this.handleMouseUp);
14132         this.el.removeClass(this.pressClass);
14133         this.fireEvent("mouseup", this);
14134     }
14135 });/*
14136  * Based on:
14137  * Ext JS Library 1.1.1
14138  * Copyright(c) 2006-2007, Ext JS, LLC.
14139  *
14140  * Originally Released Under LGPL - original licence link has changed is not relivant.
14141  *
14142  * Fork - LGPL
14143  * <script type="text/javascript">
14144  */
14145
14146  
14147 /**
14148  * @class Roo.KeyNav
14149  * <p>Provides a convenient wrapper for normalized keyboard navigation.  KeyNav allows you to bind
14150  * navigation keys to function calls that will get called when the keys are pressed, providing an easy
14151  * way to implement custom navigation schemes for any UI component.</p>
14152  * <p>The following are all of the possible keys that can be implemented: enter, left, right, up, down, tab, esc,
14153  * pageUp, pageDown, del, home, end.  Usage:</p>
14154  <pre><code>
14155 var nav = new Roo.KeyNav("my-element", {
14156     "left" : function(e){
14157         this.moveLeft(e.ctrlKey);
14158     },
14159     "right" : function(e){
14160         this.moveRight(e.ctrlKey);
14161     },
14162     "enter" : function(e){
14163         this.save();
14164     },
14165     scope : this
14166 });
14167 </code></pre>
14168  * @constructor
14169  * @param {String/HTMLElement/Roo.Element} el The element to bind to
14170  * @param {Object} config The config
14171  */
14172 Roo.KeyNav = function(el, config){
14173     this.el = Roo.get(el);
14174     Roo.apply(this, config);
14175     if(!this.disabled){
14176         this.disabled = true;
14177         this.enable();
14178     }
14179 };
14180
14181 Roo.KeyNav.prototype = {
14182     /**
14183      * @cfg {Boolean} disabled
14184      * True to disable this KeyNav instance (defaults to false)
14185      */
14186     disabled : false,
14187     /**
14188      * @cfg {String} defaultEventAction
14189      * The method to call on the {@link Roo.EventObject} after this KeyNav intercepts a key.  Valid values are
14190      * {@link Roo.EventObject#stopEvent}, {@link Roo.EventObject#preventDefault} and
14191      * {@link Roo.EventObject#stopPropagation} (defaults to 'stopEvent')
14192      */
14193     defaultEventAction: "stopEvent",
14194     /**
14195      * @cfg {Boolean} forceKeyDown
14196      * Handle the keydown event instead of keypress (defaults to false).  KeyNav automatically does this for IE since
14197      * IE does not propagate special keys on keypress, but setting this to true will force other browsers to also
14198      * handle keydown instead of keypress.
14199      */
14200     forceKeyDown : false,
14201
14202     // private
14203     prepareEvent : function(e){
14204         var k = e.getKey();
14205         var h = this.keyToHandler[k];
14206         //if(h && this[h]){
14207         //    e.stopPropagation();
14208         //}
14209         if(Roo.isSafari && h && k >= 37 && k <= 40){
14210             e.stopEvent();
14211         }
14212     },
14213
14214     // private
14215     relay : function(e){
14216         var k = e.getKey();
14217         var h = this.keyToHandler[k];
14218         if(h && this[h]){
14219             if(this.doRelay(e, this[h], h) !== true){
14220                 e[this.defaultEventAction]();
14221             }
14222         }
14223     },
14224
14225     // private
14226     doRelay : function(e, h, hname){
14227         return h.call(this.scope || this, e);
14228     },
14229
14230     // possible handlers
14231     enter : false,
14232     left : false,
14233     right : false,
14234     up : false,
14235     down : false,
14236     tab : false,
14237     esc : false,
14238     pageUp : false,
14239     pageDown : false,
14240     del : false,
14241     home : false,
14242     end : false,
14243
14244     // quick lookup hash
14245     keyToHandler : {
14246         37 : "left",
14247         39 : "right",
14248         38 : "up",
14249         40 : "down",
14250         33 : "pageUp",
14251         34 : "pageDown",
14252         46 : "del",
14253         36 : "home",
14254         35 : "end",
14255         13 : "enter",
14256         27 : "esc",
14257         9  : "tab"
14258     },
14259
14260         /**
14261          * Enable this KeyNav
14262          */
14263         enable: function(){
14264                 if(this.disabled){
14265             // ie won't do special keys on keypress, no one else will repeat keys with keydown
14266             // the EventObject will normalize Safari automatically
14267             if(this.forceKeyDown || Roo.isIE || Roo.isAir){
14268                 this.el.on("keydown", this.relay,  this);
14269             }else{
14270                 this.el.on("keydown", this.prepareEvent,  this);
14271                 this.el.on("keypress", this.relay,  this);
14272             }
14273                     this.disabled = false;
14274                 }
14275         },
14276
14277         /**
14278          * Disable this KeyNav
14279          */
14280         disable: function(){
14281                 if(!this.disabled){
14282                     if(this.forceKeyDown || Roo.isIE || Roo.isAir){
14283                 this.el.un("keydown", this.relay);
14284             }else{
14285                 this.el.un("keydown", this.prepareEvent);
14286                 this.el.un("keypress", this.relay);
14287             }
14288                     this.disabled = true;
14289                 }
14290         }
14291 };/*
14292  * Based on:
14293  * Ext JS Library 1.1.1
14294  * Copyright(c) 2006-2007, Ext JS, LLC.
14295  *
14296  * Originally Released Under LGPL - original licence link has changed is not relivant.
14297  *
14298  * Fork - LGPL
14299  * <script type="text/javascript">
14300  */
14301
14302  
14303 /**
14304  * @class Roo.KeyMap
14305  * Handles mapping keys to actions for an element. One key map can be used for multiple actions.
14306  * The constructor accepts the same config object as defined by {@link #addBinding}.
14307  * If you bind a callback function to a KeyMap, anytime the KeyMap handles an expected key
14308  * combination it will call the function with this signature (if the match is a multi-key
14309  * combination the callback will still be called only once): (String key, Roo.EventObject e)
14310  * A KeyMap can also handle a string representation of keys.<br />
14311  * Usage:
14312  <pre><code>
14313 // map one key by key code
14314 var map = new Roo.KeyMap("my-element", {
14315     key: 13, // or Roo.EventObject.ENTER
14316     fn: myHandler,
14317     scope: myObject
14318 });
14319
14320 // map multiple keys to one action by string
14321 var map = new Roo.KeyMap("my-element", {
14322     key: "a\r\n\t",
14323     fn: myHandler,
14324     scope: myObject
14325 });
14326
14327 // map multiple keys to multiple actions by strings and array of codes
14328 var map = new Roo.KeyMap("my-element", [
14329     {
14330         key: [10,13],
14331         fn: function(){ alert("Return was pressed"); }
14332     }, {
14333         key: "abc",
14334         fn: function(){ alert('a, b or c was pressed'); }
14335     }, {
14336         key: "\t",
14337         ctrl:true,
14338         shift:true,
14339         fn: function(){ alert('Control + shift + tab was pressed.'); }
14340     }
14341 ]);
14342 </code></pre>
14343  * <b>Note: A KeyMap starts enabled</b>
14344  * @constructor
14345  * @param {String/HTMLElement/Roo.Element} el The element to bind to
14346  * @param {Object} config The config (see {@link #addBinding})
14347  * @param {String} eventName (optional) The event to bind to (defaults to "keydown")
14348  */
14349 Roo.KeyMap = function(el, config, eventName){
14350     this.el  = Roo.get(el);
14351     this.eventName = eventName || "keydown";
14352     this.bindings = [];
14353     if(config){
14354         this.addBinding(config);
14355     }
14356     this.enable();
14357 };
14358
14359 Roo.KeyMap.prototype = {
14360     /**
14361      * True to stop the event from bubbling and prevent the default browser action if the
14362      * key was handled by the KeyMap (defaults to false)
14363      * @type Boolean
14364      */
14365     stopEvent : false,
14366
14367     /**
14368      * Add a new binding to this KeyMap. The following config object properties are supported:
14369      * <pre>
14370 Property    Type             Description
14371 ----------  ---------------  ----------------------------------------------------------------------
14372 key         String/Array     A single keycode or an array of keycodes to handle
14373 shift       Boolean          True to handle key only when shift is pressed (defaults to false)
14374 ctrl        Boolean          True to handle key only when ctrl is pressed (defaults to false)
14375 alt         Boolean          True to handle key only when alt is pressed (defaults to false)
14376 fn          Function         The function to call when KeyMap finds the expected key combination
14377 scope       Object           The scope of the callback function
14378 </pre>
14379      *
14380      * Usage:
14381      * <pre><code>
14382 // Create a KeyMap
14383 var map = new Roo.KeyMap(document, {
14384     key: Roo.EventObject.ENTER,
14385     fn: handleKey,
14386     scope: this
14387 });
14388
14389 //Add a new binding to the existing KeyMap later
14390 map.addBinding({
14391     key: 'abc',
14392     shift: true,
14393     fn: handleKey,
14394     scope: this
14395 });
14396 </code></pre>
14397      * @param {Object/Array} config A single KeyMap config or an array of configs
14398      */
14399         addBinding : function(config){
14400         if(config instanceof Array){
14401             for(var i = 0, len = config.length; i < len; i++){
14402                 this.addBinding(config[i]);
14403             }
14404             return;
14405         }
14406         var keyCode = config.key,
14407             shift = config.shift, 
14408             ctrl = config.ctrl, 
14409             alt = config.alt,
14410             fn = config.fn,
14411             scope = config.scope;
14412         if(typeof keyCode == "string"){
14413             var ks = [];
14414             var keyString = keyCode.toUpperCase();
14415             for(var j = 0, len = keyString.length; j < len; j++){
14416                 ks.push(keyString.charCodeAt(j));
14417             }
14418             keyCode = ks;
14419         }
14420         var keyArray = keyCode instanceof Array;
14421         var handler = function(e){
14422             if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) &&  (!alt || e.altKey)){
14423                 var k = e.getKey();
14424                 if(keyArray){
14425                     for(var i = 0, len = keyCode.length; i < len; i++){
14426                         if(keyCode[i] == k){
14427                           if(this.stopEvent){
14428                               e.stopEvent();
14429                           }
14430                           fn.call(scope || window, k, e);
14431                           return;
14432                         }
14433                     }
14434                 }else{
14435                     if(k == keyCode){
14436                         if(this.stopEvent){
14437                            e.stopEvent();
14438                         }
14439                         fn.call(scope || window, k, e);
14440                     }
14441                 }
14442             }
14443         };
14444         this.bindings.push(handler);  
14445         },
14446
14447     /**
14448      * Shorthand for adding a single key listener
14449      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the
14450      * following options:
14451      * {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
14452      * @param {Function} fn The function to call
14453      * @param {Object} scope (optional) The scope of the function
14454      */
14455     on : function(key, fn, scope){
14456         var keyCode, shift, ctrl, alt;
14457         if(typeof key == "object" && !(key instanceof Array)){
14458             keyCode = key.key;
14459             shift = key.shift;
14460             ctrl = key.ctrl;
14461             alt = key.alt;
14462         }else{
14463             keyCode = key;
14464         }
14465         this.addBinding({
14466             key: keyCode,
14467             shift: shift,
14468             ctrl: ctrl,
14469             alt: alt,
14470             fn: fn,
14471             scope: scope
14472         })
14473     },
14474
14475     // private
14476     handleKeyDown : function(e){
14477             if(this.enabled){ //just in case
14478             var b = this.bindings;
14479             for(var i = 0, len = b.length; i < len; i++){
14480                 b[i].call(this, e);
14481             }
14482             }
14483         },
14484         
14485         /**
14486          * Returns true if this KeyMap is enabled
14487          * @return {Boolean} 
14488          */
14489         isEnabled : function(){
14490             return this.enabled;  
14491         },
14492         
14493         /**
14494          * Enables this KeyMap
14495          */
14496         enable: function(){
14497                 if(!this.enabled){
14498                     this.el.on(this.eventName, this.handleKeyDown, this);
14499                     this.enabled = true;
14500                 }
14501         },
14502
14503         /**
14504          * Disable this KeyMap
14505          */
14506         disable: function(){
14507                 if(this.enabled){
14508                     this.el.removeListener(this.eventName, this.handleKeyDown, this);
14509                     this.enabled = false;
14510                 }
14511         }
14512 };/*
14513  * Based on:
14514  * Ext JS Library 1.1.1
14515  * Copyright(c) 2006-2007, Ext JS, LLC.
14516  *
14517  * Originally Released Under LGPL - original licence link has changed is not relivant.
14518  *
14519  * Fork - LGPL
14520  * <script type="text/javascript">
14521  */
14522
14523  
14524 /**
14525  * @class Roo.util.TextMetrics
14526  * Provides precise pixel measurements for blocks of text so that you can determine exactly how high and
14527  * wide, in pixels, a given block of text will be.
14528  * @singleton
14529  */
14530 Roo.util.TextMetrics = function(){
14531     var shared;
14532     return {
14533         /**
14534          * Measures the size of the specified text
14535          * @param {String/HTMLElement} el The element, dom node or id from which to copy existing CSS styles
14536          * that can affect the size of the rendered text
14537          * @param {String} text The text to measure
14538          * @param {Number} fixedWidth (optional) If the text will be multiline, you have to set a fixed width
14539          * in order to accurately measure the text height
14540          * @return {Object} An object containing the text's size {width: (width), height: (height)}
14541          */
14542         measure : function(el, text, fixedWidth){
14543             if(!shared){
14544                 shared = Roo.util.TextMetrics.Instance(el, fixedWidth);
14545             }
14546             shared.bind(el);
14547             shared.setFixedWidth(fixedWidth || 'auto');
14548             return shared.getSize(text);
14549         },
14550
14551         /**
14552          * Return a unique TextMetrics instance that can be bound directly to an element and reused.  This reduces
14553          * the overhead of multiple calls to initialize the style properties on each measurement.
14554          * @param {String/HTMLElement} el The element, dom node or id that the instance will be bound to
14555          * @param {Number} fixedWidth (optional) If the text will be multiline, you have to set a fixed width
14556          * in order to accurately measure the text height
14557          * @return {Roo.util.TextMetrics.Instance} instance The new instance
14558          */
14559         createInstance : function(el, fixedWidth){
14560             return Roo.util.TextMetrics.Instance(el, fixedWidth);
14561         }
14562     };
14563 }();
14564
14565  
14566
14567 Roo.util.TextMetrics.Instance = function(bindTo, fixedWidth){
14568     var ml = new Roo.Element(document.createElement('div'));
14569     document.body.appendChild(ml.dom);
14570     ml.position('absolute');
14571     ml.setLeftTop(-1000, -1000);
14572     ml.hide();
14573
14574     if(fixedWidth){
14575         ml.setWidth(fixedWidth);
14576     }
14577      
14578     var instance = {
14579         /**
14580          * Returns the size of the specified text based on the internal element's style and width properties
14581          * @memberOf Roo.util.TextMetrics.Instance#
14582          * @param {String} text The text to measure
14583          * @return {Object} An object containing the text's size {width: (width), height: (height)}
14584          */
14585         getSize : function(text){
14586             ml.update(text);
14587             var s = ml.getSize();
14588             ml.update('');
14589             return s;
14590         },
14591
14592         /**
14593          * Binds this TextMetrics instance to an element from which to copy existing CSS styles
14594          * that can affect the size of the rendered text
14595          * @memberOf Roo.util.TextMetrics.Instance#
14596          * @param {String/HTMLElement} el The element, dom node or id
14597          */
14598         bind : function(el){
14599             ml.setStyle(
14600                 Roo.fly(el).getStyles('font-size','font-style', 'font-weight', 'font-family','line-height')
14601             );
14602         },
14603
14604         /**
14605          * Sets a fixed width on the internal measurement element.  If the text will be multiline, you have
14606          * to set a fixed width in order to accurately measure the text height.
14607          * @memberOf Roo.util.TextMetrics.Instance#
14608          * @param {Number} width The width to set on the element
14609          */
14610         setFixedWidth : function(width){
14611             ml.setWidth(width);
14612         },
14613
14614         /**
14615          * Returns the measured width of the specified text
14616          * @memberOf Roo.util.TextMetrics.Instance#
14617          * @param {String} text The text to measure
14618          * @return {Number} width The width in pixels
14619          */
14620         getWidth : function(text){
14621             ml.dom.style.width = 'auto';
14622             return this.getSize(text).width;
14623         },
14624
14625         /**
14626          * Returns the measured height of the specified text.  For multiline text, be sure to call
14627          * {@link #setFixedWidth} if necessary.
14628          * @memberOf Roo.util.TextMetrics.Instance#
14629          * @param {String} text The text to measure
14630          * @return {Number} height The height in pixels
14631          */
14632         getHeight : function(text){
14633             return this.getSize(text).height;
14634         }
14635     };
14636
14637     instance.bind(bindTo);
14638
14639     return instance;
14640 };
14641
14642 // backwards compat
14643 Roo.Element.measureText = Roo.util.TextMetrics.measure;/*
14644  * Based on:
14645  * Ext JS Library 1.1.1
14646  * Copyright(c) 2006-2007, Ext JS, LLC.
14647  *
14648  * Originally Released Under LGPL - original licence link has changed is not relivant.
14649  *
14650  * Fork - LGPL
14651  * <script type="text/javascript">
14652  */
14653
14654 /**
14655  * @class Roo.state.Provider
14656  * Abstract base class for state provider implementations. This class provides methods
14657  * for encoding and decoding <b>typed</b> variables including dates and defines the 
14658  * Provider interface.
14659  */
14660 Roo.state.Provider = function(){
14661     /**
14662      * @event statechange
14663      * Fires when a state change occurs.
14664      * @param {Provider} this This state provider
14665      * @param {String} key The state key which was changed
14666      * @param {String} value The encoded value for the state
14667      */
14668     this.addEvents({
14669         "statechange": true
14670     });
14671     this.state = {};
14672     Roo.state.Provider.superclass.constructor.call(this);
14673 };
14674 Roo.extend(Roo.state.Provider, Roo.util.Observable, {
14675     /**
14676      * Returns the current value for a key
14677      * @param {String} name The key name
14678      * @param {Mixed} defaultValue A default value to return if the key's value is not found
14679      * @return {Mixed} The state data
14680      */
14681     get : function(name, defaultValue){
14682         return typeof this.state[name] == "undefined" ?
14683             defaultValue : this.state[name];
14684     },
14685     
14686     /**
14687      * Clears a value from the state
14688      * @param {String} name The key name
14689      */
14690     clear : function(name){
14691         delete this.state[name];
14692         this.fireEvent("statechange", this, name, null);
14693     },
14694     
14695     /**
14696      * Sets the value for a key
14697      * @param {String} name The key name
14698      * @param {Mixed} value The value to set
14699      */
14700     set : function(name, value){
14701         this.state[name] = value;
14702         this.fireEvent("statechange", this, name, value);
14703     },
14704     
14705     /**
14706      * Decodes a string previously encoded with {@link #encodeValue}.
14707      * @param {String} value The value to decode
14708      * @return {Mixed} The decoded value
14709      */
14710     decodeValue : function(cookie){
14711         var re = /^(a|n|d|b|s|o)\:(.*)$/;
14712         var matches = re.exec(unescape(cookie));
14713         if(!matches || !matches[1]) return; // non state cookie
14714         var type = matches[1];
14715         var v = matches[2];
14716         switch(type){
14717             case "n":
14718                 return parseFloat(v);
14719             case "d":
14720                 return new Date(Date.parse(v));
14721             case "b":
14722                 return (v == "1");
14723             case "a":
14724                 var all = [];
14725                 var values = v.split("^");
14726                 for(var i = 0, len = values.length; i < len; i++){
14727                     all.push(this.decodeValue(values[i]));
14728                 }
14729                 return all;
14730            case "o":
14731                 var all = {};
14732                 var values = v.split("^");
14733                 for(var i = 0, len = values.length; i < len; i++){
14734                     var kv = values[i].split("=");
14735                     all[kv[0]] = this.decodeValue(kv[1]);
14736                 }
14737                 return all;
14738            default:
14739                 return v;
14740         }
14741     },
14742     
14743     /**
14744      * Encodes a value including type information.  Decode with {@link #decodeValue}.
14745      * @param {Mixed} value The value to encode
14746      * @return {String} The encoded value
14747      */
14748     encodeValue : function(v){
14749         var enc;
14750         if(typeof v == "number"){
14751             enc = "n:" + v;
14752         }else if(typeof v == "boolean"){
14753             enc = "b:" + (v ? "1" : "0");
14754         }else if(v instanceof Date){
14755             enc = "d:" + v.toGMTString();
14756         }else if(v instanceof Array){
14757             var flat = "";
14758             for(var i = 0, len = v.length; i < len; i++){
14759                 flat += this.encodeValue(v[i]);
14760                 if(i != len-1) flat += "^";
14761             }
14762             enc = "a:" + flat;
14763         }else if(typeof v == "object"){
14764             var flat = "";
14765             for(var key in v){
14766                 if(typeof v[key] != "function"){
14767                     flat += key + "=" + this.encodeValue(v[key]) + "^";
14768                 }
14769             }
14770             enc = "o:" + flat.substring(0, flat.length-1);
14771         }else{
14772             enc = "s:" + v;
14773         }
14774         return escape(enc);        
14775     }
14776 });
14777
14778 /*
14779  * Based on:
14780  * Ext JS Library 1.1.1
14781  * Copyright(c) 2006-2007, Ext JS, LLC.
14782  *
14783  * Originally Released Under LGPL - original licence link has changed is not relivant.
14784  *
14785  * Fork - LGPL
14786  * <script type="text/javascript">
14787  */
14788 /**
14789  * @class Roo.state.Manager
14790  * This is the global state manager. By default all components that are "state aware" check this class
14791  * for state information if you don't pass them a custom state provider. In order for this class
14792  * to be useful, it must be initialized with a provider when your application initializes.
14793  <pre><code>
14794 // in your initialization function
14795 init : function(){
14796    Roo.state.Manager.setProvider(new Roo.state.CookieProvider());
14797    ...
14798    // supposed you have a {@link Roo.BorderLayout}
14799    var layout = new Roo.BorderLayout(...);
14800    layout.restoreState();
14801    // or a {Roo.BasicDialog}
14802    var dialog = new Roo.BasicDialog(...);
14803    dialog.restoreState();
14804  </code></pre>
14805  * @singleton
14806  */
14807 Roo.state.Manager = function(){
14808     var provider = new Roo.state.Provider();
14809     
14810     return {
14811         /**
14812          * Configures the default state provider for your application
14813          * @param {Provider} stateProvider The state provider to set
14814          */
14815         setProvider : function(stateProvider){
14816             provider = stateProvider;
14817         },
14818         
14819         /**
14820          * Returns the current value for a key
14821          * @param {String} name The key name
14822          * @param {Mixed} defaultValue The default value to return if the key lookup does not match
14823          * @return {Mixed} The state data
14824          */
14825         get : function(key, defaultValue){
14826             return provider.get(key, defaultValue);
14827         },
14828         
14829         /**
14830          * Sets the value for a key
14831          * @param {String} name The key name
14832          * @param {Mixed} value The state data
14833          */
14834          set : function(key, value){
14835             provider.set(key, value);
14836         },
14837         
14838         /**
14839          * Clears a value from the state
14840          * @param {String} name The key name
14841          */
14842         clear : function(key){
14843             provider.clear(key);
14844         },
14845         
14846         /**
14847          * Gets the currently configured state provider
14848          * @return {Provider} The state provider
14849          */
14850         getProvider : function(){
14851             return provider;
14852         }
14853     };
14854 }();
14855 /*
14856  * Based on:
14857  * Ext JS Library 1.1.1
14858  * Copyright(c) 2006-2007, Ext JS, LLC.
14859  *
14860  * Originally Released Under LGPL - original licence link has changed is not relivant.
14861  *
14862  * Fork - LGPL
14863  * <script type="text/javascript">
14864  */
14865 /**
14866  * @class Roo.state.CookieProvider
14867  * @extends Roo.state.Provider
14868  * The default Provider implementation which saves state via cookies.
14869  * <br />Usage:
14870  <pre><code>
14871    var cp = new Roo.state.CookieProvider({
14872        path: "/cgi-bin/",
14873        expires: new Date(new Date().getTime()+(1000*60*60*24*30)); //30 days
14874        domain: "roojs.com"
14875    })
14876    Roo.state.Manager.setProvider(cp);
14877  </code></pre>
14878  * @cfg {String} path The path for which the cookie is active (defaults to root '/' which makes it active for all pages in the site)
14879  * @cfg {Date} expires The cookie expiration date (defaults to 7 days from now)
14880  * @cfg {String} domain The domain to save the cookie for.  Note that you cannot specify a different domain than
14881  * your page is on, but you can specify a sub-domain, or simply the domain itself like 'roojs.com' to include
14882  * all sub-domains if you need to access cookies across different sub-domains (defaults to null which uses the same
14883  * domain the page is running on including the 'www' like 'www.roojs.com')
14884  * @cfg {Boolean} secure True if the site is using SSL (defaults to false)
14885  * @constructor
14886  * Create a new CookieProvider
14887  * @param {Object} config The configuration object
14888  */
14889 Roo.state.CookieProvider = function(config){
14890     Roo.state.CookieProvider.superclass.constructor.call(this);
14891     this.path = "/";
14892     this.expires = new Date(new Date().getTime()+(1000*60*60*24*7)); //7 days
14893     this.domain = null;
14894     this.secure = false;
14895     Roo.apply(this, config);
14896     this.state = this.readCookies();
14897 };
14898
14899 Roo.extend(Roo.state.CookieProvider, Roo.state.Provider, {
14900     // private
14901     set : function(name, value){
14902         if(typeof value == "undefined" || value === null){
14903             this.clear(name);
14904             return;
14905         }
14906         this.setCookie(name, value);
14907         Roo.state.CookieProvider.superclass.set.call(this, name, value);
14908     },
14909
14910     // private
14911     clear : function(name){
14912         this.clearCookie(name);
14913         Roo.state.CookieProvider.superclass.clear.call(this, name);
14914     },
14915
14916     // private
14917     readCookies : function(){
14918         var cookies = {};
14919         var c = document.cookie + ";";
14920         var re = /\s?(.*?)=(.*?);/g;
14921         var matches;
14922         while((matches = re.exec(c)) != null){
14923             var name = matches[1];
14924             var value = matches[2];
14925             if(name && name.substring(0,3) == "ys-"){
14926                 cookies[name.substr(3)] = this.decodeValue(value);
14927             }
14928         }
14929         return cookies;
14930     },
14931
14932     // private
14933     setCookie : function(name, value){
14934         document.cookie = "ys-"+ name + "=" + this.encodeValue(value) +
14935            ((this.expires == null) ? "" : ("; expires=" + this.expires.toGMTString())) +
14936            ((this.path == null) ? "" : ("; path=" + this.path)) +
14937            ((this.domain == null) ? "" : ("; domain=" + this.domain)) +
14938            ((this.secure == true) ? "; secure" : "");
14939     },
14940
14941     // private
14942     clearCookie : function(name){
14943         document.cookie = "ys-" + name + "=null; expires=Thu, 01-Jan-70 00:00:01 GMT" +
14944            ((this.path == null) ? "" : ("; path=" + this.path)) +
14945            ((this.domain == null) ? "" : ("; domain=" + this.domain)) +
14946            ((this.secure == true) ? "; secure" : "");
14947     }
14948 });/*
14949  * Based on:
14950  * Ext JS Library 1.1.1
14951  * Copyright(c) 2006-2007, Ext JS, LLC.
14952  *
14953  * Originally Released Under LGPL - original licence link has changed is not relivant.
14954  *
14955  * Fork - LGPL
14956  * <script type="text/javascript">
14957  */
14958
14959
14960
14961 /*
14962  * These classes are derivatives of the similarly named classes in the YUI Library.
14963  * The original license:
14964  * Copyright (c) 2006, Yahoo! Inc. All rights reserved.
14965  * Code licensed under the BSD License:
14966  * http://developer.yahoo.net/yui/license.txt
14967  */
14968
14969 (function() {
14970
14971 var Event=Roo.EventManager;
14972 var Dom=Roo.lib.Dom;
14973
14974 /**
14975  * @class Roo.dd.DragDrop
14976  * @extends Roo.util.Observable
14977  * Defines the interface and base operation of items that that can be
14978  * dragged or can be drop targets.  It was designed to be extended, overriding
14979  * the event handlers for startDrag, onDrag, onDragOver and onDragOut.
14980  * Up to three html elements can be associated with a DragDrop instance:
14981  * <ul>
14982  * <li>linked element: the element that is passed into the constructor.
14983  * This is the element which defines the boundaries for interaction with
14984  * other DragDrop objects.</li>
14985  * <li>handle element(s): The drag operation only occurs if the element that
14986  * was clicked matches a handle element.  By default this is the linked
14987  * element, but there are times that you will want only a portion of the
14988  * linked element to initiate the drag operation, and the setHandleElId()
14989  * method provides a way to define this.</li>
14990  * <li>drag element: this represents the element that would be moved along
14991  * with the cursor during a drag operation.  By default, this is the linked
14992  * element itself as in {@link Roo.dd.DD}.  setDragElId() lets you define
14993  * a separate element that would be moved, as in {@link Roo.dd.DDProxy}.
14994  * </li>
14995  * </ul>
14996  * This class should not be instantiated until the onload event to ensure that
14997  * the associated elements are available.
14998  * The following would define a DragDrop obj that would interact with any
14999  * other DragDrop obj in the "group1" group:
15000  * <pre>
15001  *  dd = new Roo.dd.DragDrop("div1", "group1");
15002  * </pre>
15003  * Since none of the event handlers have been implemented, nothing would
15004  * actually happen if you were to run the code above.  Normally you would
15005  * override this class or one of the default implementations, but you can
15006  * also override the methods you want on an instance of the class...
15007  * <pre>
15008  *  dd.onDragDrop = function(e, id) {
15009  *  &nbsp;&nbsp;alert("dd was dropped on " + id);
15010  *  }
15011  * </pre>
15012  * @constructor
15013  * @param {String} id of the element that is linked to this instance
15014  * @param {String} sGroup the group of related DragDrop objects
15015  * @param {object} config an object containing configurable attributes
15016  *                Valid properties for DragDrop:
15017  *                    padding, isTarget, maintainOffset, primaryButtonOnly
15018  */
15019 Roo.dd.DragDrop = function(id, sGroup, config) {
15020     if (id) {
15021         this.init(id, sGroup, config);
15022     }
15023     
15024 };
15025
15026 Roo.extend(Roo.dd.DragDrop, Roo.util.Observable , {
15027
15028     /**
15029      * The id of the element associated with this object.  This is what we
15030      * refer to as the "linked element" because the size and position of
15031      * this element is used to determine when the drag and drop objects have
15032      * interacted.
15033      * @property id
15034      * @type String
15035      */
15036     id: null,
15037
15038     /**
15039      * Configuration attributes passed into the constructor
15040      * @property config
15041      * @type object
15042      */
15043     config: null,
15044
15045     /**
15046      * The id of the element that will be dragged.  By default this is same
15047      * as the linked element , but could be changed to another element. Ex:
15048      * Roo.dd.DDProxy
15049      * @property dragElId
15050      * @type String
15051      * @private
15052      */
15053     dragElId: null,
15054
15055     /**
15056      * the id of the element that initiates the drag operation.  By default
15057      * this is the linked element, but could be changed to be a child of this
15058      * element.  This lets us do things like only starting the drag when the
15059      * header element within the linked html element is clicked.
15060      * @property handleElId
15061      * @type String
15062      * @private
15063      */
15064     handleElId: null,
15065
15066     /**
15067      * An associative array of HTML tags that will be ignored if clicked.
15068      * @property invalidHandleTypes
15069      * @type {string: string}
15070      */
15071     invalidHandleTypes: null,
15072
15073     /**
15074      * An associative array of ids for elements that will be ignored if clicked
15075      * @property invalidHandleIds
15076      * @type {string: string}
15077      */
15078     invalidHandleIds: null,
15079
15080     /**
15081      * An indexted array of css class names for elements that will be ignored
15082      * if clicked.
15083      * @property invalidHandleClasses
15084      * @type string[]
15085      */
15086     invalidHandleClasses: null,
15087
15088     /**
15089      * The linked element's absolute X position at the time the drag was
15090      * started
15091      * @property startPageX
15092      * @type int
15093      * @private
15094      */
15095     startPageX: 0,
15096
15097     /**
15098      * The linked element's absolute X position at the time the drag was
15099      * started
15100      * @property startPageY
15101      * @type int
15102      * @private
15103      */
15104     startPageY: 0,
15105
15106     /**
15107      * The group defines a logical collection of DragDrop objects that are
15108      * related.  Instances only get events when interacting with other
15109      * DragDrop object in the same group.  This lets us define multiple
15110      * groups using a single DragDrop subclass if we want.
15111      * @property groups
15112      * @type {string: string}
15113      */
15114     groups: null,
15115
15116     /**
15117      * Individual drag/drop instances can be locked.  This will prevent
15118      * onmousedown start drag.
15119      * @property locked
15120      * @type boolean
15121      * @private
15122      */
15123     locked: false,
15124
15125     /**
15126      * Lock this instance
15127      * @method lock
15128      */
15129     lock: function() { this.locked = true; },
15130
15131     /**
15132      * Unlock this instace
15133      * @method unlock
15134      */
15135     unlock: function() { this.locked = false; },
15136
15137     /**
15138      * By default, all insances can be a drop target.  This can be disabled by
15139      * setting isTarget to false.
15140      * @method isTarget
15141      * @type boolean
15142      */
15143     isTarget: true,
15144
15145     /**
15146      * The padding configured for this drag and drop object for calculating
15147      * the drop zone intersection with this object.
15148      * @method padding
15149      * @type int[]
15150      */
15151     padding: null,
15152
15153     /**
15154      * Cached reference to the linked element
15155      * @property _domRef
15156      * @private
15157      */
15158     _domRef: null,
15159
15160     /**
15161      * Internal typeof flag
15162      * @property __ygDragDrop
15163      * @private
15164      */
15165     __ygDragDrop: true,
15166
15167     /**
15168      * Set to true when horizontal contraints are applied
15169      * @property constrainX
15170      * @type boolean
15171      * @private
15172      */
15173     constrainX: false,
15174
15175     /**
15176      * Set to true when vertical contraints are applied
15177      * @property constrainY
15178      * @type boolean
15179      * @private
15180      */
15181     constrainY: false,
15182
15183     /**
15184      * The left constraint
15185      * @property minX
15186      * @type int
15187      * @private
15188      */
15189     minX: 0,
15190
15191     /**
15192      * The right constraint
15193      * @property maxX
15194      * @type int
15195      * @private
15196      */
15197     maxX: 0,
15198
15199     /**
15200      * The up constraint
15201      * @property minY
15202      * @type int
15203      * @type int
15204      * @private
15205      */
15206     minY: 0,
15207
15208     /**
15209      * The down constraint
15210      * @property maxY
15211      * @type int
15212      * @private
15213      */
15214     maxY: 0,
15215
15216     /**
15217      * Maintain offsets when we resetconstraints.  Set to true when you want
15218      * the position of the element relative to its parent to stay the same
15219      * when the page changes
15220      *
15221      * @property maintainOffset
15222      * @type boolean
15223      */
15224     maintainOffset: false,
15225
15226     /**
15227      * Array of pixel locations the element will snap to if we specified a
15228      * horizontal graduation/interval.  This array is generated automatically
15229      * when you define a tick interval.
15230      * @property xTicks
15231      * @type int[]
15232      */
15233     xTicks: null,
15234
15235     /**
15236      * Array of pixel locations the element will snap to if we specified a
15237      * vertical graduation/interval.  This array is generated automatically
15238      * when you define a tick interval.
15239      * @property yTicks
15240      * @type int[]
15241      */
15242     yTicks: null,
15243
15244     /**
15245      * By default the drag and drop instance will only respond to the primary
15246      * button click (left button for a right-handed mouse).  Set to true to
15247      * allow drag and drop to start with any mouse click that is propogated
15248      * by the browser
15249      * @property primaryButtonOnly
15250      * @type boolean
15251      */
15252     primaryButtonOnly: true,
15253
15254     /**
15255      * The availabe property is false until the linked dom element is accessible.
15256      * @property available
15257      * @type boolean
15258      */
15259     available: false,
15260
15261     /**
15262      * By default, drags can only be initiated if the mousedown occurs in the
15263      * region the linked element is.  This is done in part to work around a
15264      * bug in some browsers that mis-report the mousedown if the previous
15265      * mouseup happened outside of the window.  This property is set to true
15266      * if outer handles are defined.
15267      *
15268      * @property hasOuterHandles
15269      * @type boolean
15270      * @default false
15271      */
15272     hasOuterHandles: false,
15273
15274     /**
15275      * Code that executes immediately before the startDrag event
15276      * @method b4StartDrag
15277      * @private
15278      */
15279     b4StartDrag: function(x, y) { },
15280
15281     /**
15282      * Abstract method called after a drag/drop object is clicked
15283      * and the drag or mousedown time thresholds have beeen met.
15284      * @method startDrag
15285      * @param {int} X click location
15286      * @param {int} Y click location
15287      */
15288     startDrag: function(x, y) { /* override this */ },
15289
15290     /**
15291      * Code that executes immediately before the onDrag event
15292      * @method b4Drag
15293      * @private
15294      */
15295     b4Drag: function(e) { },
15296
15297     /**
15298      * Abstract method called during the onMouseMove event while dragging an
15299      * object.
15300      * @method onDrag
15301      * @param {Event} e the mousemove event
15302      */
15303     onDrag: function(e) { /* override this */ },
15304
15305     /**
15306      * Abstract method called when this element fist begins hovering over
15307      * another DragDrop obj
15308      * @method onDragEnter
15309      * @param {Event} e the mousemove event
15310      * @param {String|DragDrop[]} id In POINT mode, the element
15311      * id this is hovering over.  In INTERSECT mode, an array of one or more
15312      * dragdrop items being hovered over.
15313      */
15314     onDragEnter: function(e, id) { /* override this */ },
15315
15316     /**
15317      * Code that executes immediately before the onDragOver event
15318      * @method b4DragOver
15319      * @private
15320      */
15321     b4DragOver: function(e) { },
15322
15323     /**
15324      * Abstract method called when this element is hovering over another
15325      * DragDrop obj
15326      * @method onDragOver
15327      * @param {Event} e the mousemove event
15328      * @param {String|DragDrop[]} id In POINT mode, the element
15329      * id this is hovering over.  In INTERSECT mode, an array of dd items
15330      * being hovered over.
15331      */
15332     onDragOver: function(e, id) { /* override this */ },
15333
15334     /**
15335      * Code that executes immediately before the onDragOut event
15336      * @method b4DragOut
15337      * @private
15338      */
15339     b4DragOut: function(e) { },
15340
15341     /**
15342      * Abstract method called when we are no longer hovering over an element
15343      * @method onDragOut
15344      * @param {Event} e the mousemove event
15345      * @param {String|DragDrop[]} id In POINT mode, the element
15346      * id this was hovering over.  In INTERSECT mode, an array of dd items
15347      * that the mouse is no longer over.
15348      */
15349     onDragOut: function(e, id) { /* override this */ },
15350
15351     /**
15352      * Code that executes immediately before the onDragDrop event
15353      * @method b4DragDrop
15354      * @private
15355      */
15356     b4DragDrop: function(e) { },
15357
15358     /**
15359      * Abstract method called when this item is dropped on another DragDrop
15360      * obj
15361      * @method onDragDrop
15362      * @param {Event} e the mouseup event
15363      * @param {String|DragDrop[]} id In POINT mode, the element
15364      * id this was dropped on.  In INTERSECT mode, an array of dd items this
15365      * was dropped on.
15366      */
15367     onDragDrop: function(e, id) { /* override this */ },
15368
15369     /**
15370      * Abstract method called when this item is dropped on an area with no
15371      * drop target
15372      * @method onInvalidDrop
15373      * @param {Event} e the mouseup event
15374      */
15375     onInvalidDrop: function(e) { /* override this */ },
15376
15377     /**
15378      * Code that executes immediately before the endDrag event
15379      * @method b4EndDrag
15380      * @private
15381      */
15382     b4EndDrag: function(e) { },
15383
15384     /**
15385      * Fired when we are done dragging the object
15386      * @method endDrag
15387      * @param {Event} e the mouseup event
15388      */
15389     endDrag: function(e) { /* override this */ },
15390
15391     /**
15392      * Code executed immediately before the onMouseDown event
15393      * @method b4MouseDown
15394      * @param {Event} e the mousedown event
15395      * @private
15396      */
15397     b4MouseDown: function(e) {  },
15398
15399     /**
15400      * Event handler that fires when a drag/drop obj gets a mousedown
15401      * @method onMouseDown
15402      * @param {Event} e the mousedown event
15403      */
15404     onMouseDown: function(e) { /* override this */ },
15405
15406     /**
15407      * Event handler that fires when a drag/drop obj gets a mouseup
15408      * @method onMouseUp
15409      * @param {Event} e the mouseup event
15410      */
15411     onMouseUp: function(e) { /* override this */ },
15412
15413     /**
15414      * Override the onAvailable method to do what is needed after the initial
15415      * position was determined.
15416      * @method onAvailable
15417      */
15418     onAvailable: function () {
15419     },
15420
15421     /*
15422      * Provides default constraint padding to "constrainTo" elements (defaults to {left: 0, right:0, top:0, bottom:0}).
15423      * @type Object
15424      */
15425     defaultPadding : {left:0, right:0, top:0, bottom:0},
15426
15427     /*
15428      * Initializes the drag drop object's constraints to restrict movement to a certain element.
15429  *
15430  * Usage:
15431  <pre><code>
15432  var dd = new Roo.dd.DDProxy("dragDiv1", "proxytest",
15433                 { dragElId: "existingProxyDiv" });
15434  dd.startDrag = function(){
15435      this.constrainTo("parent-id");
15436  };
15437  </code></pre>
15438  * Or you can initalize it using the {@link Roo.Element} object:
15439  <pre><code>
15440  Roo.get("dragDiv1").initDDProxy("proxytest", {dragElId: "existingProxyDiv"}, {
15441      startDrag : function(){
15442          this.constrainTo("parent-id");
15443      }
15444  });
15445  </code></pre>
15446      * @param {String/HTMLElement/Element} constrainTo The element to constrain to.
15447      * @param {Object/Number} pad (optional) Pad provides a way to specify "padding" of the constraints,
15448      * and can be either a number for symmetrical padding (4 would be equal to {left:4, right:4, top:4, bottom:4}) or
15449      * an object containing the sides to pad. For example: {right:10, bottom:10}
15450      * @param {Boolean} inContent (optional) Constrain the draggable in the content box of the element (inside padding and borders)
15451      */
15452     constrainTo : function(constrainTo, pad, inContent){
15453         if(typeof pad == "number"){
15454             pad = {left: pad, right:pad, top:pad, bottom:pad};
15455         }
15456         pad = pad || this.defaultPadding;
15457         var b = Roo.get(this.getEl()).getBox();
15458         var ce = Roo.get(constrainTo);
15459         var s = ce.getScroll();
15460         var c, cd = ce.dom;
15461         if(cd == document.body){
15462             c = { x: s.left, y: s.top, width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
15463         }else{
15464             xy = ce.getXY();
15465             c = {x : xy[0]+s.left, y: xy[1]+s.top, width: cd.clientWidth, height: cd.clientHeight};
15466         }
15467
15468
15469         var topSpace = b.y - c.y;
15470         var leftSpace = b.x - c.x;
15471
15472         this.resetConstraints();
15473         this.setXConstraint(leftSpace - (pad.left||0), // left
15474                 c.width - leftSpace - b.width - (pad.right||0) //right
15475         );
15476         this.setYConstraint(topSpace - (pad.top||0), //top
15477                 c.height - topSpace - b.height - (pad.bottom||0) //bottom
15478         );
15479     },
15480
15481     /**
15482      * Returns a reference to the linked element
15483      * @method getEl
15484      * @return {HTMLElement} the html element
15485      */
15486     getEl: function() {
15487         if (!this._domRef) {
15488             this._domRef = Roo.getDom(this.id);
15489         }
15490
15491         return this._domRef;
15492     },
15493
15494     /**
15495      * Returns a reference to the actual element to drag.  By default this is
15496      * the same as the html element, but it can be assigned to another
15497      * element. An example of this can be found in Roo.dd.DDProxy
15498      * @method getDragEl
15499      * @return {HTMLElement} the html element
15500      */
15501     getDragEl: function() {
15502         return Roo.getDom(this.dragElId);
15503     },
15504
15505     /**
15506      * Sets up the DragDrop object.  Must be called in the constructor of any
15507      * Roo.dd.DragDrop subclass
15508      * @method init
15509      * @param id the id of the linked element
15510      * @param {String} sGroup the group of related items
15511      * @param {object} config configuration attributes
15512      */
15513     init: function(id, sGroup, config) {
15514         this.initTarget(id, sGroup, config);
15515         Event.on(this.id, "mousedown", this.handleMouseDown, this);
15516         // Event.on(this.id, "selectstart", Event.preventDefault);
15517     },
15518
15519     /**
15520      * Initializes Targeting functionality only... the object does not
15521      * get a mousedown handler.
15522      * @method initTarget
15523      * @param id the id of the linked element
15524      * @param {String} sGroup the group of related items
15525      * @param {object} config configuration attributes
15526      */
15527     initTarget: function(id, sGroup, config) {
15528
15529         // configuration attributes
15530         this.config = config || {};
15531
15532         // create a local reference to the drag and drop manager
15533         this.DDM = Roo.dd.DDM;
15534         // initialize the groups array
15535         this.groups = {};
15536
15537         // assume that we have an element reference instead of an id if the
15538         // parameter is not a string
15539         if (typeof id !== "string") {
15540             id = Roo.id(id);
15541         }
15542
15543         // set the id
15544         this.id = id;
15545
15546         // add to an interaction group
15547         this.addToGroup((sGroup) ? sGroup : "default");
15548
15549         // We don't want to register this as the handle with the manager
15550         // so we just set the id rather than calling the setter.
15551         this.handleElId = id;
15552
15553         // the linked element is the element that gets dragged by default
15554         this.setDragElId(id);
15555
15556         // by default, clicked anchors will not start drag operations.
15557         this.invalidHandleTypes = { A: "A" };
15558         this.invalidHandleIds = {};
15559         this.invalidHandleClasses = [];
15560
15561         this.applyConfig();
15562
15563         this.handleOnAvailable();
15564     },
15565
15566     /**
15567      * Applies the configuration parameters that were passed into the constructor.
15568      * This is supposed to happen at each level through the inheritance chain.  So
15569      * a DDProxy implentation will execute apply config on DDProxy, DD, and
15570      * DragDrop in order to get all of the parameters that are available in
15571      * each object.
15572      * @method applyConfig
15573      */
15574     applyConfig: function() {
15575
15576         // configurable properties:
15577         //    padding, isTarget, maintainOffset, primaryButtonOnly
15578         this.padding           = this.config.padding || [0, 0, 0, 0];
15579         this.isTarget          = (this.config.isTarget !== false);
15580         this.maintainOffset    = (this.config.maintainOffset);
15581         this.primaryButtonOnly = (this.config.primaryButtonOnly !== false);
15582
15583     },
15584
15585     /**
15586      * Executed when the linked element is available
15587      * @method handleOnAvailable
15588      * @private
15589      */
15590     handleOnAvailable: function() {
15591         this.available = true;
15592         this.resetConstraints();
15593         this.onAvailable();
15594     },
15595
15596      /**
15597      * Configures the padding for the target zone in px.  Effectively expands
15598      * (or reduces) the virtual object size for targeting calculations.
15599      * Supports css-style shorthand; if only one parameter is passed, all sides
15600      * will have that padding, and if only two are passed, the top and bottom
15601      * will have the first param, the left and right the second.
15602      * @method setPadding
15603      * @param {int} iTop    Top pad
15604      * @param {int} iRight  Right pad
15605      * @param {int} iBot    Bot pad
15606      * @param {int} iLeft   Left pad
15607      */
15608     setPadding: function(iTop, iRight, iBot, iLeft) {
15609         // this.padding = [iLeft, iRight, iTop, iBot];
15610         if (!iRight && 0 !== iRight) {
15611             this.padding = [iTop, iTop, iTop, iTop];
15612         } else if (!iBot && 0 !== iBot) {
15613             this.padding = [iTop, iRight, iTop, iRight];
15614         } else {
15615             this.padding = [iTop, iRight, iBot, iLeft];
15616         }
15617     },
15618
15619     /**
15620      * Stores the initial placement of the linked element.
15621      * @method setInitialPosition
15622      * @param {int} diffX   the X offset, default 0
15623      * @param {int} diffY   the Y offset, default 0
15624      */
15625     setInitPosition: function(diffX, diffY) {
15626         var el = this.getEl();
15627
15628         if (!this.DDM.verifyEl(el)) {
15629             return;
15630         }
15631
15632         var dx = diffX || 0;
15633         var dy = diffY || 0;
15634
15635         var p = Dom.getXY( el );
15636
15637         this.initPageX = p[0] - dx;
15638         this.initPageY = p[1] - dy;
15639
15640         this.lastPageX = p[0];
15641         this.lastPageY = p[1];
15642
15643
15644         this.setStartPosition(p);
15645     },
15646
15647     /**
15648      * Sets the start position of the element.  This is set when the obj
15649      * is initialized, the reset when a drag is started.
15650      * @method setStartPosition
15651      * @param pos current position (from previous lookup)
15652      * @private
15653      */
15654     setStartPosition: function(pos) {
15655         var p = pos || Dom.getXY( this.getEl() );
15656         this.deltaSetXY = null;
15657
15658         this.startPageX = p[0];
15659         this.startPageY = p[1];
15660     },
15661
15662     /**
15663      * Add this instance to a group of related drag/drop objects.  All
15664      * instances belong to at least one group, and can belong to as many
15665      * groups as needed.
15666      * @method addToGroup
15667      * @param sGroup {string} the name of the group
15668      */
15669     addToGroup: function(sGroup) {
15670         this.groups[sGroup] = true;
15671         this.DDM.regDragDrop(this, sGroup);
15672     },
15673
15674     /**
15675      * Remove's this instance from the supplied interaction group
15676      * @method removeFromGroup
15677      * @param {string}  sGroup  The group to drop
15678      */
15679     removeFromGroup: function(sGroup) {
15680         if (this.groups[sGroup]) {
15681             delete this.groups[sGroup];
15682         }
15683
15684         this.DDM.removeDDFromGroup(this, sGroup);
15685     },
15686
15687     /**
15688      * Allows you to specify that an element other than the linked element
15689      * will be moved with the cursor during a drag
15690      * @method setDragElId
15691      * @param id {string} the id of the element that will be used to initiate the drag
15692      */
15693     setDragElId: function(id) {
15694         this.dragElId = id;
15695     },
15696
15697     /**
15698      * Allows you to specify a child of the linked element that should be
15699      * used to initiate the drag operation.  An example of this would be if
15700      * you have a content div with text and links.  Clicking anywhere in the
15701      * content area would normally start the drag operation.  Use this method
15702      * to specify that an element inside of the content div is the element
15703      * that starts the drag operation.
15704      * @method setHandleElId
15705      * @param id {string} the id of the element that will be used to
15706      * initiate the drag.
15707      */
15708     setHandleElId: function(id) {
15709         if (typeof id !== "string") {
15710             id = Roo.id(id);
15711         }
15712         this.handleElId = id;
15713         this.DDM.regHandle(this.id, id);
15714     },
15715
15716     /**
15717      * Allows you to set an element outside of the linked element as a drag
15718      * handle
15719      * @method setOuterHandleElId
15720      * @param id the id of the element that will be used to initiate the drag
15721      */
15722     setOuterHandleElId: function(id) {
15723         if (typeof id !== "string") {
15724             id = Roo.id(id);
15725         }
15726         Event.on(id, "mousedown",
15727                 this.handleMouseDown, this);
15728         this.setHandleElId(id);
15729
15730         this.hasOuterHandles = true;
15731     },
15732
15733     /**
15734      * Remove all drag and drop hooks for this element
15735      * @method unreg
15736      */
15737     unreg: function() {
15738         Event.un(this.id, "mousedown",
15739                 this.handleMouseDown);
15740         this._domRef = null;
15741         this.DDM._remove(this);
15742     },
15743
15744     destroy : function(){
15745         this.unreg();
15746     },
15747
15748     /**
15749      * Returns true if this instance is locked, or the drag drop mgr is locked
15750      * (meaning that all drag/drop is disabled on the page.)
15751      * @method isLocked
15752      * @return {boolean} true if this obj or all drag/drop is locked, else
15753      * false
15754      */
15755     isLocked: function() {
15756         return (this.DDM.isLocked() || this.locked);
15757     },
15758
15759     /**
15760      * Fired when this object is clicked
15761      * @method handleMouseDown
15762      * @param {Event} e
15763      * @param {Roo.dd.DragDrop} oDD the clicked dd object (this dd obj)
15764      * @private
15765      */
15766     handleMouseDown: function(e, oDD){
15767         if (this.primaryButtonOnly && e.button != 0) {
15768             return;
15769         }
15770
15771         if (this.isLocked()) {
15772             return;
15773         }
15774
15775         this.DDM.refreshCache(this.groups);
15776
15777         var pt = new Roo.lib.Point(Roo.lib.Event.getPageX(e), Roo.lib.Event.getPageY(e));
15778         if (!this.hasOuterHandles && !this.DDM.isOverTarget(pt, this) )  {
15779         } else {
15780             if (this.clickValidator(e)) {
15781
15782                 // set the initial element position
15783                 this.setStartPosition();
15784
15785
15786                 this.b4MouseDown(e);
15787                 this.onMouseDown(e);
15788
15789                 this.DDM.handleMouseDown(e, this);
15790
15791                 this.DDM.stopEvent(e);
15792             } else {
15793
15794
15795             }
15796         }
15797     },
15798
15799     clickValidator: function(e) {
15800         var target = e.getTarget();
15801         return ( this.isValidHandleChild(target) &&
15802                     (this.id == this.handleElId ||
15803                         this.DDM.handleWasClicked(target, this.id)) );
15804     },
15805
15806     /**
15807      * Allows you to specify a tag name that should not start a drag operation
15808      * when clicked.  This is designed to facilitate embedding links within a
15809      * drag handle that do something other than start the drag.
15810      * @method addInvalidHandleType
15811      * @param {string} tagName the type of element to exclude
15812      */
15813     addInvalidHandleType: function(tagName) {
15814         var type = tagName.toUpperCase();
15815         this.invalidHandleTypes[type] = type;
15816     },
15817
15818     /**
15819      * Lets you to specify an element id for a child of a drag handle
15820      * that should not initiate a drag
15821      * @method addInvalidHandleId
15822      * @param {string} id the element id of the element you wish to ignore
15823      */
15824     addInvalidHandleId: function(id) {
15825         if (typeof id !== "string") {
15826             id = Roo.id(id);
15827         }
15828         this.invalidHandleIds[id] = id;
15829     },
15830
15831     /**
15832      * Lets you specify a css class of elements that will not initiate a drag
15833      * @method addInvalidHandleClass
15834      * @param {string} cssClass the class of the elements you wish to ignore
15835      */
15836     addInvalidHandleClass: function(cssClass) {
15837         this.invalidHandleClasses.push(cssClass);
15838     },
15839
15840     /**
15841      * Unsets an excluded tag name set by addInvalidHandleType
15842      * @method removeInvalidHandleType
15843      * @param {string} tagName the type of element to unexclude
15844      */
15845     removeInvalidHandleType: function(tagName) {
15846         var type = tagName.toUpperCase();
15847         // this.invalidHandleTypes[type] = null;
15848         delete this.invalidHandleTypes[type];
15849     },
15850
15851     /**
15852      * Unsets an invalid handle id
15853      * @method removeInvalidHandleId
15854      * @param {string} id the id of the element to re-enable
15855      */
15856     removeInvalidHandleId: function(id) {
15857         if (typeof id !== "string") {
15858             id = Roo.id(id);
15859         }
15860         delete this.invalidHandleIds[id];
15861     },
15862
15863     /**
15864      * Unsets an invalid css class
15865      * @method removeInvalidHandleClass
15866      * @param {string} cssClass the class of the element(s) you wish to
15867      * re-enable
15868      */
15869     removeInvalidHandleClass: function(cssClass) {
15870         for (var i=0, len=this.invalidHandleClasses.length; i<len; ++i) {
15871             if (this.invalidHandleClasses[i] == cssClass) {
15872                 delete this.invalidHandleClasses[i];
15873             }
15874         }
15875     },
15876
15877     /**
15878      * Checks the tag exclusion list to see if this click should be ignored
15879      * @method isValidHandleChild
15880      * @param {HTMLElement} node the HTMLElement to evaluate
15881      * @return {boolean} true if this is a valid tag type, false if not
15882      */
15883     isValidHandleChild: function(node) {
15884
15885         var valid = true;
15886         // var n = (node.nodeName == "#text") ? node.parentNode : node;
15887         var nodeName;
15888         try {
15889             nodeName = node.nodeName.toUpperCase();
15890         } catch(e) {
15891             nodeName = node.nodeName;
15892         }
15893         valid = valid && !this.invalidHandleTypes[nodeName];
15894         valid = valid && !this.invalidHandleIds[node.id];
15895
15896         for (var i=0, len=this.invalidHandleClasses.length; valid && i<len; ++i) {
15897             valid = !Dom.hasClass(node, this.invalidHandleClasses[i]);
15898         }
15899
15900
15901         return valid;
15902
15903     },
15904
15905     /**
15906      * Create the array of horizontal tick marks if an interval was specified
15907      * in setXConstraint().
15908      * @method setXTicks
15909      * @private
15910      */
15911     setXTicks: function(iStartX, iTickSize) {
15912         this.xTicks = [];
15913         this.xTickSize = iTickSize;
15914
15915         var tickMap = {};
15916
15917         for (var i = this.initPageX; i >= this.minX; i = i - iTickSize) {
15918             if (!tickMap[i]) {
15919                 this.xTicks[this.xTicks.length] = i;
15920                 tickMap[i] = true;
15921             }
15922         }
15923
15924         for (i = this.initPageX; i <= this.maxX; i = i + iTickSize) {
15925             if (!tickMap[i]) {
15926                 this.xTicks[this.xTicks.length] = i;
15927                 tickMap[i] = true;
15928             }
15929         }
15930
15931         this.xTicks.sort(this.DDM.numericSort) ;
15932     },
15933
15934     /**
15935      * Create the array of vertical tick marks if an interval was specified in
15936      * setYConstraint().
15937      * @method setYTicks
15938      * @private
15939      */
15940     setYTicks: function(iStartY, iTickSize) {
15941         this.yTicks = [];
15942         this.yTickSize = iTickSize;
15943
15944         var tickMap = {};
15945
15946         for (var i = this.initPageY; i >= this.minY; i = i - iTickSize) {
15947             if (!tickMap[i]) {
15948                 this.yTicks[this.yTicks.length] = i;
15949                 tickMap[i] = true;
15950             }
15951         }
15952
15953         for (i = this.initPageY; i <= this.maxY; i = i + iTickSize) {
15954             if (!tickMap[i]) {
15955                 this.yTicks[this.yTicks.length] = i;
15956                 tickMap[i] = true;
15957             }
15958         }
15959
15960         this.yTicks.sort(this.DDM.numericSort) ;
15961     },
15962
15963     /**
15964      * By default, the element can be dragged any place on the screen.  Use
15965      * this method to limit the horizontal travel of the element.  Pass in
15966      * 0,0 for the parameters if you want to lock the drag to the y axis.
15967      * @method setXConstraint
15968      * @param {int} iLeft the number of pixels the element can move to the left
15969      * @param {int} iRight the number of pixels the element can move to the
15970      * right
15971      * @param {int} iTickSize optional parameter for specifying that the
15972      * element
15973      * should move iTickSize pixels at a time.
15974      */
15975     setXConstraint: function(iLeft, iRight, iTickSize) {
15976         this.leftConstraint = iLeft;
15977         this.rightConstraint = iRight;
15978
15979         this.minX = this.initPageX - iLeft;
15980         this.maxX = this.initPageX + iRight;
15981         if (iTickSize) { this.setXTicks(this.initPageX, iTickSize); }
15982
15983         this.constrainX = true;
15984     },
15985
15986     /**
15987      * Clears any constraints applied to this instance.  Also clears ticks
15988      * since they can't exist independent of a constraint at this time.
15989      * @method clearConstraints
15990      */
15991     clearConstraints: function() {
15992         this.constrainX = false;
15993         this.constrainY = false;
15994         this.clearTicks();
15995     },
15996
15997     /**
15998      * Clears any tick interval defined for this instance
15999      * @method clearTicks
16000      */
16001     clearTicks: function() {
16002         this.xTicks = null;
16003         this.yTicks = null;
16004         this.xTickSize = 0;
16005         this.yTickSize = 0;
16006     },
16007
16008     /**
16009      * By default, the element can be dragged any place on the screen.  Set
16010      * this to limit the vertical travel of the element.  Pass in 0,0 for the
16011      * parameters if you want to lock the drag to the x axis.
16012      * @method setYConstraint
16013      * @param {int} iUp the number of pixels the element can move up
16014      * @param {int} iDown the number of pixels the element can move down
16015      * @param {int} iTickSize optional parameter for specifying that the
16016      * element should move iTickSize pixels at a time.
16017      */
16018     setYConstraint: function(iUp, iDown, iTickSize) {
16019         this.topConstraint = iUp;
16020         this.bottomConstraint = iDown;
16021
16022         this.minY = this.initPageY - iUp;
16023         this.maxY = this.initPageY + iDown;
16024         if (iTickSize) { this.setYTicks(this.initPageY, iTickSize); }
16025
16026         this.constrainY = true;
16027
16028     },
16029
16030     /**
16031      * resetConstraints must be called if you manually reposition a dd element.
16032      * @method resetConstraints
16033      * @param {boolean} maintainOffset
16034      */
16035     resetConstraints: function() {
16036
16037
16038         // Maintain offsets if necessary
16039         if (this.initPageX || this.initPageX === 0) {
16040             // figure out how much this thing has moved
16041             var dx = (this.maintainOffset) ? this.lastPageX - this.initPageX : 0;
16042             var dy = (this.maintainOffset) ? this.lastPageY - this.initPageY : 0;
16043
16044             this.setInitPosition(dx, dy);
16045
16046         // This is the first time we have detected the element's position
16047         } else {
16048             this.setInitPosition();
16049         }
16050
16051         if (this.constrainX) {
16052             this.setXConstraint( this.leftConstraint,
16053                                  this.rightConstraint,
16054                                  this.xTickSize        );
16055         }
16056
16057         if (this.constrainY) {
16058             this.setYConstraint( this.topConstraint,
16059                                  this.bottomConstraint,
16060                                  this.yTickSize         );
16061         }
16062     },
16063
16064     /**
16065      * Normally the drag element is moved pixel by pixel, but we can specify
16066      * that it move a number of pixels at a time.  This method resolves the
16067      * location when we have it set up like this.
16068      * @method getTick
16069      * @param {int} val where we want to place the object
16070      * @param {int[]} tickArray sorted array of valid points
16071      * @return {int} the closest tick
16072      * @private
16073      */
16074     getTick: function(val, tickArray) {
16075
16076         if (!tickArray) {
16077             // If tick interval is not defined, it is effectively 1 pixel,
16078             // so we return the value passed to us.
16079             return val;
16080         } else if (tickArray[0] >= val) {
16081             // The value is lower than the first tick, so we return the first
16082             // tick.
16083             return tickArray[0];
16084         } else {
16085             for (var i=0, len=tickArray.length; i<len; ++i) {
16086                 var next = i + 1;
16087                 if (tickArray[next] && tickArray[next] >= val) {
16088                     var diff1 = val - tickArray[i];
16089                     var diff2 = tickArray[next] - val;
16090                     return (diff2 > diff1) ? tickArray[i] : tickArray[next];
16091                 }
16092             }
16093
16094             // The value is larger than the last tick, so we return the last
16095             // tick.
16096             return tickArray[tickArray.length - 1];
16097         }
16098     },
16099
16100     /**
16101      * toString method
16102      * @method toString
16103      * @return {string} string representation of the dd obj
16104      */
16105     toString: function() {
16106         return ("DragDrop " + this.id);
16107     }
16108
16109 });
16110
16111 })();
16112 /*
16113  * Based on:
16114  * Ext JS Library 1.1.1
16115  * Copyright(c) 2006-2007, Ext JS, LLC.
16116  *
16117  * Originally Released Under LGPL - original licence link has changed is not relivant.
16118  *
16119  * Fork - LGPL
16120  * <script type="text/javascript">
16121  */
16122
16123
16124 /**
16125  * The drag and drop utility provides a framework for building drag and drop
16126  * applications.  In addition to enabling drag and drop for specific elements,
16127  * the drag and drop elements are tracked by the manager class, and the
16128  * interactions between the various elements are tracked during the drag and
16129  * the implementing code is notified about these important moments.
16130  */
16131
16132 // Only load the library once.  Rewriting the manager class would orphan
16133 // existing drag and drop instances.
16134 if (!Roo.dd.DragDropMgr) {
16135
16136 /**
16137  * @class Roo.dd.DragDropMgr
16138  * DragDropMgr is a singleton that tracks the element interaction for
16139  * all DragDrop items in the window.  Generally, you will not call
16140  * this class directly, but it does have helper methods that could
16141  * be useful in your DragDrop implementations.
16142  * @singleton
16143  */
16144 Roo.dd.DragDropMgr = function() {
16145
16146     var Event = Roo.EventManager;
16147
16148     return {
16149
16150         /**
16151          * Two dimensional Array of registered DragDrop objects.  The first
16152          * dimension is the DragDrop item group, the second the DragDrop
16153          * object.
16154          * @property ids
16155          * @type {string: string}
16156          * @private
16157          * @static
16158          */
16159         ids: {},
16160
16161         /**
16162          * Array of element ids defined as drag handles.  Used to determine
16163          * if the element that generated the mousedown event is actually the
16164          * handle and not the html element itself.
16165          * @property handleIds
16166          * @type {string: string}
16167          * @private
16168          * @static
16169          */
16170         handleIds: {},
16171
16172         /**
16173          * the DragDrop object that is currently being dragged
16174          * @property dragCurrent
16175          * @type DragDrop
16176          * @private
16177          * @static
16178          **/
16179         dragCurrent: null,
16180
16181         /**
16182          * the DragDrop object(s) that are being hovered over
16183          * @property dragOvers
16184          * @type Array
16185          * @private
16186          * @static
16187          */
16188         dragOvers: {},
16189
16190         /**
16191          * the X distance between the cursor and the object being dragged
16192          * @property deltaX
16193          * @type int
16194          * @private
16195          * @static
16196          */
16197         deltaX: 0,
16198
16199         /**
16200          * the Y distance between the cursor and the object being dragged
16201          * @property deltaY
16202          * @type int
16203          * @private
16204          * @static
16205          */
16206         deltaY: 0,
16207
16208         /**
16209          * Flag to determine if we should prevent the default behavior of the
16210          * events we define. By default this is true, but this can be set to
16211          * false if you need the default behavior (not recommended)
16212          * @property preventDefault
16213          * @type boolean
16214          * @static
16215          */
16216         preventDefault: true,
16217
16218         /**
16219          * Flag to determine if we should stop the propagation of the events
16220          * we generate. This is true by default but you may want to set it to
16221          * false if the html element contains other features that require the
16222          * mouse click.
16223          * @property stopPropagation
16224          * @type boolean
16225          * @static
16226          */
16227         stopPropagation: true,
16228
16229         /**
16230          * Internal flag that is set to true when drag and drop has been
16231          * intialized
16232          * @property initialized
16233          * @private
16234          * @static
16235          */
16236         initalized: false,
16237
16238         /**
16239          * All drag and drop can be disabled.
16240          * @property locked
16241          * @private
16242          * @static
16243          */
16244         locked: false,
16245
16246         /**
16247          * Called the first time an element is registered.
16248          * @method init
16249          * @private
16250          * @static
16251          */
16252         init: function() {
16253             this.initialized = true;
16254         },
16255
16256         /**
16257          * In point mode, drag and drop interaction is defined by the
16258          * location of the cursor during the drag/drop
16259          * @property POINT
16260          * @type int
16261          * @static
16262          */
16263         POINT: 0,
16264
16265         /**
16266          * In intersect mode, drag and drop interactio nis defined by the
16267          * overlap of two or more drag and drop objects.
16268          * @property INTERSECT
16269          * @type int
16270          * @static
16271          */
16272         INTERSECT: 1,
16273
16274         /**
16275          * The current drag and drop mode.  Default: POINT
16276          * @property mode
16277          * @type int
16278          * @static
16279          */
16280         mode: 0,
16281
16282         /**
16283          * Runs method on all drag and drop objects
16284          * @method _execOnAll
16285          * @private
16286          * @static
16287          */
16288         _execOnAll: function(sMethod, args) {
16289             for (var i in this.ids) {
16290                 for (var j in this.ids[i]) {
16291                     var oDD = this.ids[i][j];
16292                     if (! this.isTypeOfDD(oDD)) {
16293                         continue;
16294                     }
16295                     oDD[sMethod].apply(oDD, args);
16296                 }
16297             }
16298         },
16299
16300         /**
16301          * Drag and drop initialization.  Sets up the global event handlers
16302          * @method _onLoad
16303          * @private
16304          * @static
16305          */
16306         _onLoad: function() {
16307
16308             this.init();
16309
16310
16311             Event.on(document, "mouseup",   this.handleMouseUp, this, true);
16312             Event.on(document, "mousemove", this.handleMouseMove, this, true);
16313             Event.on(window,   "unload",    this._onUnload, this, true);
16314             Event.on(window,   "resize",    this._onResize, this, true);
16315             // Event.on(window,   "mouseout",    this._test);
16316
16317         },
16318
16319         /**
16320          * Reset constraints on all drag and drop objs
16321          * @method _onResize
16322          * @private
16323          * @static
16324          */
16325         _onResize: function(e) {
16326             this._execOnAll("resetConstraints", []);
16327         },
16328
16329         /**
16330          * Lock all drag and drop functionality
16331          * @method lock
16332          * @static
16333          */
16334         lock: function() { this.locked = true; },
16335
16336         /**
16337          * Unlock all drag and drop functionality
16338          * @method unlock
16339          * @static
16340          */
16341         unlock: function() { this.locked = false; },
16342
16343         /**
16344          * Is drag and drop locked?
16345          * @method isLocked
16346          * @return {boolean} True if drag and drop is locked, false otherwise.
16347          * @static
16348          */
16349         isLocked: function() { return this.locked; },
16350
16351         /**
16352          * Location cache that is set for all drag drop objects when a drag is
16353          * initiated, cleared when the drag is finished.
16354          * @property locationCache
16355          * @private
16356          * @static
16357          */
16358         locationCache: {},
16359
16360         /**
16361          * Set useCache to false if you want to force object the lookup of each
16362          * drag and drop linked element constantly during a drag.
16363          * @property useCache
16364          * @type boolean
16365          * @static
16366          */
16367         useCache: true,
16368
16369         /**
16370          * The number of pixels that the mouse needs to move after the
16371          * mousedown before the drag is initiated.  Default=3;
16372          * @property clickPixelThresh
16373          * @type int
16374          * @static
16375          */
16376         clickPixelThresh: 3,
16377
16378         /**
16379          * The number of milliseconds after the mousedown event to initiate the
16380          * drag if we don't get a mouseup event. Default=1000
16381          * @property clickTimeThresh
16382          * @type int
16383          * @static
16384          */
16385         clickTimeThresh: 350,
16386
16387         /**
16388          * Flag that indicates that either the drag pixel threshold or the
16389          * mousdown time threshold has been met
16390          * @property dragThreshMet
16391          * @type boolean
16392          * @private
16393          * @static
16394          */
16395         dragThreshMet: false,
16396
16397         /**
16398          * Timeout used for the click time threshold
16399          * @property clickTimeout
16400          * @type Object
16401          * @private
16402          * @static
16403          */
16404         clickTimeout: null,
16405
16406         /**
16407          * The X position of the mousedown event stored for later use when a
16408          * drag threshold is met.
16409          * @property startX
16410          * @type int
16411          * @private
16412          * @static
16413          */
16414         startX: 0,
16415
16416         /**
16417          * The Y position of the mousedown event stored for later use when a
16418          * drag threshold is met.
16419          * @property startY
16420          * @type int
16421          * @private
16422          * @static
16423          */
16424         startY: 0,
16425
16426         /**
16427          * Each DragDrop instance must be registered with the DragDropMgr.
16428          * This is executed in DragDrop.init()
16429          * @method regDragDrop
16430          * @param {DragDrop} oDD the DragDrop object to register
16431          * @param {String} sGroup the name of the group this element belongs to
16432          * @static
16433          */
16434         regDragDrop: function(oDD, sGroup) {
16435             if (!this.initialized) { this.init(); }
16436
16437             if (!this.ids[sGroup]) {
16438                 this.ids[sGroup] = {};
16439             }
16440             this.ids[sGroup][oDD.id] = oDD;
16441         },
16442
16443         /**
16444          * Removes the supplied dd instance from the supplied group. Executed
16445          * by DragDrop.removeFromGroup, so don't call this function directly.
16446          * @method removeDDFromGroup
16447          * @private
16448          * @static
16449          */
16450         removeDDFromGroup: function(oDD, sGroup) {
16451             if (!this.ids[sGroup]) {
16452                 this.ids[sGroup] = {};
16453             }
16454
16455             var obj = this.ids[sGroup];
16456             if (obj && obj[oDD.id]) {
16457                 delete obj[oDD.id];
16458             }
16459         },
16460
16461         /**
16462          * Unregisters a drag and drop item.  This is executed in
16463          * DragDrop.unreg, use that method instead of calling this directly.
16464          * @method _remove
16465          * @private
16466          * @static
16467          */
16468         _remove: function(oDD) {
16469             for (var g in oDD.groups) {
16470                 if (g && this.ids[g][oDD.id]) {
16471                     delete this.ids[g][oDD.id];
16472                 }
16473             }
16474             delete this.handleIds[oDD.id];
16475         },
16476
16477         /**
16478          * Each DragDrop handle element must be registered.  This is done
16479          * automatically when executing DragDrop.setHandleElId()
16480          * @method regHandle
16481          * @param {String} sDDId the DragDrop id this element is a handle for
16482          * @param {String} sHandleId the id of the element that is the drag
16483          * handle
16484          * @static
16485          */
16486         regHandle: function(sDDId, sHandleId) {
16487             if (!this.handleIds[sDDId]) {
16488                 this.handleIds[sDDId] = {};
16489             }
16490             this.handleIds[sDDId][sHandleId] = sHandleId;
16491         },
16492
16493         /**
16494          * Utility function to determine if a given element has been
16495          * registered as a drag drop item.
16496          * @method isDragDrop
16497          * @param {String} id the element id to check
16498          * @return {boolean} true if this element is a DragDrop item,
16499          * false otherwise
16500          * @static
16501          */
16502         isDragDrop: function(id) {
16503             return ( this.getDDById(id) ) ? true : false;
16504         },
16505
16506         /**
16507          * Returns the drag and drop instances that are in all groups the
16508          * passed in instance belongs to.
16509          * @method getRelated
16510          * @param {DragDrop} p_oDD the obj to get related data for
16511          * @param {boolean} bTargetsOnly if true, only return targetable objs
16512          * @return {DragDrop[]} the related instances
16513          * @static
16514          */
16515         getRelated: function(p_oDD, bTargetsOnly) {
16516             var oDDs = [];
16517             for (var i in p_oDD.groups) {
16518                 for (j in this.ids[i]) {
16519                     var dd = this.ids[i][j];
16520                     if (! this.isTypeOfDD(dd)) {
16521                         continue;
16522                     }
16523                     if (!bTargetsOnly || dd.isTarget) {
16524                         oDDs[oDDs.length] = dd;
16525                     }
16526                 }
16527             }
16528
16529             return oDDs;
16530         },
16531
16532         /**
16533          * Returns true if the specified dd target is a legal target for
16534          * the specifice drag obj
16535          * @method isLegalTarget
16536          * @param {DragDrop} the drag obj
16537          * @param {DragDrop} the target
16538          * @return {boolean} true if the target is a legal target for the
16539          * dd obj
16540          * @static
16541          */
16542         isLegalTarget: function (oDD, oTargetDD) {
16543             var targets = this.getRelated(oDD, true);
16544             for (var i=0, len=targets.length;i<len;++i) {
16545                 if (targets[i].id == oTargetDD.id) {
16546                     return true;
16547                 }
16548             }
16549
16550             return false;
16551         },
16552
16553         /**
16554          * My goal is to be able to transparently determine if an object is
16555          * typeof DragDrop, and the exact subclass of DragDrop.  typeof
16556          * returns "object", oDD.constructor.toString() always returns
16557          * "DragDrop" and not the name of the subclass.  So for now it just
16558          * evaluates a well-known variable in DragDrop.
16559          * @method isTypeOfDD
16560          * @param {Object} the object to evaluate
16561          * @return {boolean} true if typeof oDD = DragDrop
16562          * @static
16563          */
16564         isTypeOfDD: function (oDD) {
16565             return (oDD && oDD.__ygDragDrop);
16566         },
16567
16568         /**
16569          * Utility function to determine if a given element has been
16570          * registered as a drag drop handle for the given Drag Drop object.
16571          * @method isHandle
16572          * @param {String} id the element id to check
16573          * @return {boolean} true if this element is a DragDrop handle, false
16574          * otherwise
16575          * @static
16576          */
16577         isHandle: function(sDDId, sHandleId) {
16578             return ( this.handleIds[sDDId] &&
16579                             this.handleIds[sDDId][sHandleId] );
16580         },
16581
16582         /**
16583          * Returns the DragDrop instance for a given id
16584          * @method getDDById
16585          * @param {String} id the id of the DragDrop object
16586          * @return {DragDrop} the drag drop object, null if it is not found
16587          * @static
16588          */
16589         getDDById: function(id) {
16590             for (var i in this.ids) {
16591                 if (this.ids[i][id]) {
16592                     return this.ids[i][id];
16593                 }
16594             }
16595             return null;
16596         },
16597
16598         /**
16599          * Fired after a registered DragDrop object gets the mousedown event.
16600          * Sets up the events required to track the object being dragged
16601          * @method handleMouseDown
16602          * @param {Event} e the event
16603          * @param oDD the DragDrop object being dragged
16604          * @private
16605          * @static
16606          */
16607         handleMouseDown: function(e, oDD) {
16608             if(Roo.QuickTips){
16609                 Roo.QuickTips.disable();
16610             }
16611             this.currentTarget = e.getTarget();
16612
16613             this.dragCurrent = oDD;
16614
16615             var el = oDD.getEl();
16616
16617             // track start position
16618             this.startX = e.getPageX();
16619             this.startY = e.getPageY();
16620
16621             this.deltaX = this.startX - el.offsetLeft;
16622             this.deltaY = this.startY - el.offsetTop;
16623
16624             this.dragThreshMet = false;
16625
16626             this.clickTimeout = setTimeout(
16627                     function() {
16628                         var DDM = Roo.dd.DDM;
16629                         DDM.startDrag(DDM.startX, DDM.startY);
16630                     },
16631                     this.clickTimeThresh );
16632         },
16633
16634         /**
16635          * Fired when either the drag pixel threshol or the mousedown hold
16636          * time threshold has been met.
16637          * @method startDrag
16638          * @param x {int} the X position of the original mousedown
16639          * @param y {int} the Y position of the original mousedown
16640          * @static
16641          */
16642         startDrag: function(x, y) {
16643             clearTimeout(this.clickTimeout);
16644             if (this.dragCurrent) {
16645                 this.dragCurrent.b4StartDrag(x, y);
16646                 this.dragCurrent.startDrag(x, y);
16647             }
16648             this.dragThreshMet = true;
16649         },
16650
16651         /**
16652          * Internal function to handle the mouseup event.  Will be invoked
16653          * from the context of the document.
16654          * @method handleMouseUp
16655          * @param {Event} e the event
16656          * @private
16657          * @static
16658          */
16659         handleMouseUp: function(e) {
16660
16661             if(Roo.QuickTips){
16662                 Roo.QuickTips.enable();
16663             }
16664             if (! this.dragCurrent) {
16665                 return;
16666             }
16667
16668             clearTimeout(this.clickTimeout);
16669
16670             if (this.dragThreshMet) {
16671                 this.fireEvents(e, true);
16672             } else {
16673             }
16674
16675             this.stopDrag(e);
16676
16677             this.stopEvent(e);
16678         },
16679
16680         /**
16681          * Utility to stop event propagation and event default, if these
16682          * features are turned on.
16683          * @method stopEvent
16684          * @param {Event} e the event as returned by this.getEvent()
16685          * @static
16686          */
16687         stopEvent: function(e){
16688             if(this.stopPropagation) {
16689                 e.stopPropagation();
16690             }
16691
16692             if (this.preventDefault) {
16693                 e.preventDefault();
16694             }
16695         },
16696
16697         /**
16698          * Internal function to clean up event handlers after the drag
16699          * operation is complete
16700          * @method stopDrag
16701          * @param {Event} e the event
16702          * @private
16703          * @static
16704          */
16705         stopDrag: function(e) {
16706             // Fire the drag end event for the item that was dragged
16707             if (this.dragCurrent) {
16708                 if (this.dragThreshMet) {
16709                     this.dragCurrent.b4EndDrag(e);
16710                     this.dragCurrent.endDrag(e);
16711                 }
16712
16713                 this.dragCurrent.onMouseUp(e);
16714             }
16715
16716             this.dragCurrent = null;
16717             this.dragOvers = {};
16718         },
16719
16720         /**
16721          * Internal function to handle the mousemove event.  Will be invoked
16722          * from the context of the html element.
16723          *
16724          * @TODO figure out what we can do about mouse events lost when the
16725          * user drags objects beyond the window boundary.  Currently we can
16726          * detect this in internet explorer by verifying that the mouse is
16727          * down during the mousemove event.  Firefox doesn't give us the
16728          * button state on the mousemove event.
16729          * @method handleMouseMove
16730          * @param {Event} e the event
16731          * @private
16732          * @static
16733          */
16734         handleMouseMove: function(e) {
16735             if (! this.dragCurrent) {
16736                 return true;
16737             }
16738
16739             // var button = e.which || e.button;
16740
16741             // check for IE mouseup outside of page boundary
16742             if (Roo.isIE && (e.button !== 0 && e.button !== 1 && e.button !== 2)) {
16743                 this.stopEvent(e);
16744                 return this.handleMouseUp(e);
16745             }
16746
16747             if (!this.dragThreshMet) {
16748                 var diffX = Math.abs(this.startX - e.getPageX());
16749                 var diffY = Math.abs(this.startY - e.getPageY());
16750                 if (diffX > this.clickPixelThresh ||
16751                             diffY > this.clickPixelThresh) {
16752                     this.startDrag(this.startX, this.startY);
16753                 }
16754             }
16755
16756             if (this.dragThreshMet) {
16757                 this.dragCurrent.b4Drag(e);
16758                 this.dragCurrent.onDrag(e);
16759                 if(!this.dragCurrent.moveOnly){
16760                     this.fireEvents(e, false);
16761                 }
16762             }
16763
16764             this.stopEvent(e);
16765
16766             return true;
16767         },
16768
16769         /**
16770          * Iterates over all of the DragDrop elements to find ones we are
16771          * hovering over or dropping on
16772          * @method fireEvents
16773          * @param {Event} e the event
16774          * @param {boolean} isDrop is this a drop op or a mouseover op?
16775          * @private
16776          * @static
16777          */
16778         fireEvents: function(e, isDrop) {
16779             var dc = this.dragCurrent;
16780
16781             // If the user did the mouse up outside of the window, we could
16782             // get here even though we have ended the drag.
16783             if (!dc || dc.isLocked()) {
16784                 return;
16785             }
16786
16787             var pt = e.getPoint();
16788
16789             // cache the previous dragOver array
16790             var oldOvers = [];
16791
16792             var outEvts   = [];
16793             var overEvts  = [];
16794             var dropEvts  = [];
16795             var enterEvts = [];
16796
16797             // Check to see if the object(s) we were hovering over is no longer
16798             // being hovered over so we can fire the onDragOut event
16799             for (var i in this.dragOvers) {
16800
16801                 var ddo = this.dragOvers[i];
16802
16803                 if (! this.isTypeOfDD(ddo)) {
16804                     continue;
16805                 }
16806
16807                 if (! this.isOverTarget(pt, ddo, this.mode)) {
16808                     outEvts.push( ddo );
16809                 }
16810
16811                 oldOvers[i] = true;
16812                 delete this.dragOvers[i];
16813             }
16814
16815             for (var sGroup in dc.groups) {
16816
16817                 if ("string" != typeof sGroup) {
16818                     continue;
16819                 }
16820
16821                 for (i in this.ids[sGroup]) {
16822                     var oDD = this.ids[sGroup][i];
16823                     if (! this.isTypeOfDD(oDD)) {
16824                         continue;
16825                     }
16826
16827                     if (oDD.isTarget && !oDD.isLocked() && oDD != dc) {
16828                         if (this.isOverTarget(pt, oDD, this.mode)) {
16829                             // look for drop interactions
16830                             if (isDrop) {
16831                                 dropEvts.push( oDD );
16832                             // look for drag enter and drag over interactions
16833                             } else {
16834
16835                                 // initial drag over: dragEnter fires
16836                                 if (!oldOvers[oDD.id]) {
16837                                     enterEvts.push( oDD );
16838                                 // subsequent drag overs: dragOver fires
16839                                 } else {
16840                                     overEvts.push( oDD );
16841                                 }
16842
16843                                 this.dragOvers[oDD.id] = oDD;
16844                             }
16845                         }
16846                     }
16847                 }
16848             }
16849
16850             if (this.mode) {
16851                 if (outEvts.length) {
16852                     dc.b4DragOut(e, outEvts);
16853                     dc.onDragOut(e, outEvts);
16854                 }
16855
16856                 if (enterEvts.length) {
16857                     dc.onDragEnter(e, enterEvts);
16858                 }
16859
16860                 if (overEvts.length) {
16861                     dc.b4DragOver(e, overEvts);
16862                     dc.onDragOver(e, overEvts);
16863                 }
16864
16865                 if (dropEvts.length) {
16866                     dc.b4DragDrop(e, dropEvts);
16867                     dc.onDragDrop(e, dropEvts);
16868                 }
16869
16870             } else {
16871                 // fire dragout events
16872                 var len = 0;
16873                 for (i=0, len=outEvts.length; i<len; ++i) {
16874                     dc.b4DragOut(e, outEvts[i].id);
16875                     dc.onDragOut(e, outEvts[i].id);
16876                 }
16877
16878                 // fire enter events
16879                 for (i=0,len=enterEvts.length; i<len; ++i) {
16880                     // dc.b4DragEnter(e, oDD.id);
16881                     dc.onDragEnter(e, enterEvts[i].id);
16882                 }
16883
16884                 // fire over events
16885                 for (i=0,len=overEvts.length; i<len; ++i) {
16886                     dc.b4DragOver(e, overEvts[i].id);
16887                     dc.onDragOver(e, overEvts[i].id);
16888                 }
16889
16890                 // fire drop events
16891                 for (i=0, len=dropEvts.length; i<len; ++i) {
16892                     dc.b4DragDrop(e, dropEvts[i].id);
16893                     dc.onDragDrop(e, dropEvts[i].id);
16894                 }
16895
16896             }
16897
16898             // notify about a drop that did not find a target
16899             if (isDrop && !dropEvts.length) {
16900                 dc.onInvalidDrop(e);
16901             }
16902
16903         },
16904
16905         /**
16906          * Helper function for getting the best match from the list of drag
16907          * and drop objects returned by the drag and drop events when we are
16908          * in INTERSECT mode.  It returns either the first object that the
16909          * cursor is over, or the object that has the greatest overlap with
16910          * the dragged element.
16911          * @method getBestMatch
16912          * @param  {DragDrop[]} dds The array of drag and drop objects
16913          * targeted
16914          * @return {DragDrop}       The best single match
16915          * @static
16916          */
16917         getBestMatch: function(dds) {
16918             var winner = null;
16919             // Return null if the input is not what we expect
16920             //if (!dds || !dds.length || dds.length == 0) {
16921                // winner = null;
16922             // If there is only one item, it wins
16923             //} else if (dds.length == 1) {
16924
16925             var len = dds.length;
16926
16927             if (len == 1) {
16928                 winner = dds[0];
16929             } else {
16930                 // Loop through the targeted items
16931                 for (var i=0; i<len; ++i) {
16932                     var dd = dds[i];
16933                     // If the cursor is over the object, it wins.  If the
16934                     // cursor is over multiple matches, the first one we come
16935                     // to wins.
16936                     if (dd.cursorIsOver) {
16937                         winner = dd;
16938                         break;
16939                     // Otherwise the object with the most overlap wins
16940                     } else {
16941                         if (!winner ||
16942                             winner.overlap.getArea() < dd.overlap.getArea()) {
16943                             winner = dd;
16944                         }
16945                     }
16946                 }
16947             }
16948
16949             return winner;
16950         },
16951
16952         /**
16953          * Refreshes the cache of the top-left and bottom-right points of the
16954          * drag and drop objects in the specified group(s).  This is in the
16955          * format that is stored in the drag and drop instance, so typical
16956          * usage is:
16957          * <code>
16958          * Roo.dd.DragDropMgr.refreshCache(ddinstance.groups);
16959          * </code>
16960          * Alternatively:
16961          * <code>
16962          * Roo.dd.DragDropMgr.refreshCache({group1:true, group2:true});
16963          * </code>
16964          * @TODO this really should be an indexed array.  Alternatively this
16965          * method could accept both.
16966          * @method refreshCache
16967          * @param {Object} groups an associative array of groups to refresh
16968          * @static
16969          */
16970         refreshCache: function(groups) {
16971             for (var sGroup in groups) {
16972                 if ("string" != typeof sGroup) {
16973                     continue;
16974                 }
16975                 for (var i in this.ids[sGroup]) {
16976                     var oDD = this.ids[sGroup][i];
16977
16978                     if (this.isTypeOfDD(oDD)) {
16979                     // if (this.isTypeOfDD(oDD) && oDD.isTarget) {
16980                         var loc = this.getLocation(oDD);
16981                         if (loc) {
16982                             this.locationCache[oDD.id] = loc;
16983                         } else {
16984                             delete this.locationCache[oDD.id];
16985                             // this will unregister the drag and drop object if
16986                             // the element is not in a usable state
16987                             // oDD.unreg();
16988                         }
16989                     }
16990                 }
16991             }
16992         },
16993
16994         /**
16995          * This checks to make sure an element exists and is in the DOM.  The
16996          * main purpose is to handle cases where innerHTML is used to remove
16997          * drag and drop objects from the DOM.  IE provides an 'unspecified
16998          * error' when trying to access the offsetParent of such an element
16999          * @method verifyEl
17000          * @param {HTMLElement} el the element to check
17001          * @return {boolean} true if the element looks usable
17002          * @static
17003          */
17004         verifyEl: function(el) {
17005             if (el) {
17006                 var parent;
17007                 if(Roo.isIE){
17008                     try{
17009                         parent = el.offsetParent;
17010                     }catch(e){}
17011                 }else{
17012                     parent = el.offsetParent;
17013                 }
17014                 if (parent) {
17015                     return true;
17016                 }
17017             }
17018
17019             return false;
17020         },
17021
17022         /**
17023          * Returns a Region object containing the drag and drop element's position
17024          * and size, including the padding configured for it
17025          * @method getLocation
17026          * @param {DragDrop} oDD the drag and drop object to get the
17027          *                       location for
17028          * @return {Roo.lib.Region} a Region object representing the total area
17029          *                             the element occupies, including any padding
17030          *                             the instance is configured for.
17031          * @static
17032          */
17033         getLocation: function(oDD) {
17034             if (! this.isTypeOfDD(oDD)) {
17035                 return null;
17036             }
17037
17038             var el = oDD.getEl(), pos, x1, x2, y1, y2, t, r, b, l;
17039
17040             try {
17041                 pos= Roo.lib.Dom.getXY(el);
17042             } catch (e) { }
17043
17044             if (!pos) {
17045                 return null;
17046             }
17047
17048             x1 = pos[0];
17049             x2 = x1 + el.offsetWidth;
17050             y1 = pos[1];
17051             y2 = y1 + el.offsetHeight;
17052
17053             t = y1 - oDD.padding[0];
17054             r = x2 + oDD.padding[1];
17055             b = y2 + oDD.padding[2];
17056             l = x1 - oDD.padding[3];
17057
17058             return new Roo.lib.Region( t, r, b, l );
17059         },
17060
17061         /**
17062          * Checks the cursor location to see if it over the target
17063          * @method isOverTarget
17064          * @param {Roo.lib.Point} pt The point to evaluate
17065          * @param {DragDrop} oTarget the DragDrop object we are inspecting
17066          * @return {boolean} true if the mouse is over the target
17067          * @private
17068          * @static
17069          */
17070         isOverTarget: function(pt, oTarget, intersect) {
17071             // use cache if available
17072             var loc = this.locationCache[oTarget.id];
17073             if (!loc || !this.useCache) {
17074                 loc = this.getLocation(oTarget);
17075                 this.locationCache[oTarget.id] = loc;
17076
17077             }
17078
17079             if (!loc) {
17080                 return false;
17081             }
17082
17083             oTarget.cursorIsOver = loc.contains( pt );
17084
17085             // DragDrop is using this as a sanity check for the initial mousedown
17086             // in this case we are done.  In POINT mode, if the drag obj has no
17087             // contraints, we are also done. Otherwise we need to evaluate the
17088             // location of the target as related to the actual location of the
17089             // dragged element.
17090             var dc = this.dragCurrent;
17091             if (!dc || !dc.getTargetCoord ||
17092                     (!intersect && !dc.constrainX && !dc.constrainY)) {
17093                 return oTarget.cursorIsOver;
17094             }
17095
17096             oTarget.overlap = null;
17097
17098             // Get the current location of the drag element, this is the
17099             // location of the mouse event less the delta that represents
17100             // where the original mousedown happened on the element.  We
17101             // need to consider constraints and ticks as well.
17102             var pos = dc.getTargetCoord(pt.x, pt.y);
17103
17104             var el = dc.getDragEl();
17105             var curRegion = new Roo.lib.Region( pos.y,
17106                                                    pos.x + el.offsetWidth,
17107                                                    pos.y + el.offsetHeight,
17108                                                    pos.x );
17109
17110             var overlap = curRegion.intersect(loc);
17111
17112             if (overlap) {
17113                 oTarget.overlap = overlap;
17114                 return (intersect) ? true : oTarget.cursorIsOver;
17115             } else {
17116                 return false;
17117             }
17118         },
17119
17120         /**
17121          * unload event handler
17122          * @method _onUnload
17123          * @private
17124          * @static
17125          */
17126         _onUnload: function(e, me) {
17127             Roo.dd.DragDropMgr.unregAll();
17128         },
17129
17130         /**
17131          * Cleans up the drag and drop events and objects.
17132          * @method unregAll
17133          * @private
17134          * @static
17135          */
17136         unregAll: function() {
17137
17138             if (this.dragCurrent) {
17139                 this.stopDrag();
17140                 this.dragCurrent = null;
17141             }
17142
17143             this._execOnAll("unreg", []);
17144
17145             for (i in this.elementCache) {
17146                 delete this.elementCache[i];
17147             }
17148
17149             this.elementCache = {};
17150             this.ids = {};
17151         },
17152
17153         /**
17154          * A cache of DOM elements
17155          * @property elementCache
17156          * @private
17157          * @static
17158          */
17159         elementCache: {},
17160
17161         /**
17162          * Get the wrapper for the DOM element specified
17163          * @method getElWrapper
17164          * @param {String} id the id of the element to get
17165          * @return {Roo.dd.DDM.ElementWrapper} the wrapped element
17166          * @private
17167          * @deprecated This wrapper isn't that useful
17168          * @static
17169          */
17170         getElWrapper: function(id) {
17171             var oWrapper = this.elementCache[id];
17172             if (!oWrapper || !oWrapper.el) {
17173                 oWrapper = this.elementCache[id] =
17174                     new this.ElementWrapper(Roo.getDom(id));
17175             }
17176             return oWrapper;
17177         },
17178
17179         /**
17180          * Returns the actual DOM element
17181          * @method getElement
17182          * @param {String} id the id of the elment to get
17183          * @return {Object} The element
17184          * @deprecated use Roo.getDom instead
17185          * @static
17186          */
17187         getElement: function(id) {
17188             return Roo.getDom(id);
17189         },
17190
17191         /**
17192          * Returns the style property for the DOM element (i.e.,
17193          * document.getElById(id).style)
17194          * @method getCss
17195          * @param {String} id the id of the elment to get
17196          * @return {Object} The style property of the element
17197          * @deprecated use Roo.getDom instead
17198          * @static
17199          */
17200         getCss: function(id) {
17201             var el = Roo.getDom(id);
17202             return (el) ? el.style : null;
17203         },
17204
17205         /**
17206          * Inner class for cached elements
17207          * @class DragDropMgr.ElementWrapper
17208          * @for DragDropMgr
17209          * @private
17210          * @deprecated
17211          */
17212         ElementWrapper: function(el) {
17213                 /**
17214                  * The element
17215                  * @property el
17216                  */
17217                 this.el = el || null;
17218                 /**
17219                  * The element id
17220                  * @property id
17221                  */
17222                 this.id = this.el && el.id;
17223                 /**
17224                  * A reference to the style property
17225                  * @property css
17226                  */
17227                 this.css = this.el && el.style;
17228             },
17229
17230         /**
17231          * Returns the X position of an html element
17232          * @method getPosX
17233          * @param el the element for which to get the position
17234          * @return {int} the X coordinate
17235          * @for DragDropMgr
17236          * @deprecated use Roo.lib.Dom.getX instead
17237          * @static
17238          */
17239         getPosX: function(el) {
17240             return Roo.lib.Dom.getX(el);
17241         },
17242
17243         /**
17244          * Returns the Y position of an html element
17245          * @method getPosY
17246          * @param el the element for which to get the position
17247          * @return {int} the Y coordinate
17248          * @deprecated use Roo.lib.Dom.getY instead
17249          * @static
17250          */
17251         getPosY: function(el) {
17252             return Roo.lib.Dom.getY(el);
17253         },
17254
17255         /**
17256          * Swap two nodes.  In IE, we use the native method, for others we
17257          * emulate the IE behavior
17258          * @method swapNode
17259          * @param n1 the first node to swap
17260          * @param n2 the other node to swap
17261          * @static
17262          */
17263         swapNode: function(n1, n2) {
17264             if (n1.swapNode) {
17265                 n1.swapNode(n2);
17266             } else {
17267                 var p = n2.parentNode;
17268                 var s = n2.nextSibling;
17269
17270                 if (s == n1) {
17271                     p.insertBefore(n1, n2);
17272                 } else if (n2 == n1.nextSibling) {
17273                     p.insertBefore(n2, n1);
17274                 } else {
17275                     n1.parentNode.replaceChild(n2, n1);
17276                     p.insertBefore(n1, s);
17277                 }
17278             }
17279         },
17280
17281         /**
17282          * Returns the current scroll position
17283          * @method getScroll
17284          * @private
17285          * @static
17286          */
17287         getScroll: function () {
17288             var t, l, dde=document.documentElement, db=document.body;
17289             if (dde && (dde.scrollTop || dde.scrollLeft)) {
17290                 t = dde.scrollTop;
17291                 l = dde.scrollLeft;
17292             } else if (db) {
17293                 t = db.scrollTop;
17294                 l = db.scrollLeft;
17295             } else {
17296
17297             }
17298             return { top: t, left: l };
17299         },
17300
17301         /**
17302          * Returns the specified element style property
17303          * @method getStyle
17304          * @param {HTMLElement} el          the element
17305          * @param {string}      styleProp   the style property
17306          * @return {string} The value of the style property
17307          * @deprecated use Roo.lib.Dom.getStyle
17308          * @static
17309          */
17310         getStyle: function(el, styleProp) {
17311             return Roo.fly(el).getStyle(styleProp);
17312         },
17313
17314         /**
17315          * Gets the scrollTop
17316          * @method getScrollTop
17317          * @return {int} the document's scrollTop
17318          * @static
17319          */
17320         getScrollTop: function () { return this.getScroll().top; },
17321
17322         /**
17323          * Gets the scrollLeft
17324          * @method getScrollLeft
17325          * @return {int} the document's scrollTop
17326          * @static
17327          */
17328         getScrollLeft: function () { return this.getScroll().left; },
17329
17330         /**
17331          * Sets the x/y position of an element to the location of the
17332          * target element.
17333          * @method moveToEl
17334          * @param {HTMLElement} moveEl      The element to move
17335          * @param {HTMLElement} targetEl    The position reference element
17336          * @static
17337          */
17338         moveToEl: function (moveEl, targetEl) {
17339             var aCoord = Roo.lib.Dom.getXY(targetEl);
17340             Roo.lib.Dom.setXY(moveEl, aCoord);
17341         },
17342
17343         /**
17344          * Numeric array sort function
17345          * @method numericSort
17346          * @static
17347          */
17348         numericSort: function(a, b) { return (a - b); },
17349
17350         /**
17351          * Internal counter
17352          * @property _timeoutCount
17353          * @private
17354          * @static
17355          */
17356         _timeoutCount: 0,
17357
17358         /**
17359          * Trying to make the load order less important.  Without this we get
17360          * an error if this file is loaded before the Event Utility.
17361          * @method _addListeners
17362          * @private
17363          * @static
17364          */
17365         _addListeners: function() {
17366             var DDM = Roo.dd.DDM;
17367             if ( Roo.lib.Event && document ) {
17368                 DDM._onLoad();
17369             } else {
17370                 if (DDM._timeoutCount > 2000) {
17371                 } else {
17372                     setTimeout(DDM._addListeners, 10);
17373                     if (document && document.body) {
17374                         DDM._timeoutCount += 1;
17375                     }
17376                 }
17377             }
17378         },
17379
17380         /**
17381          * Recursively searches the immediate parent and all child nodes for
17382          * the handle element in order to determine wheter or not it was
17383          * clicked.
17384          * @method handleWasClicked
17385          * @param node the html element to inspect
17386          * @static
17387          */
17388         handleWasClicked: function(node, id) {
17389             if (this.isHandle(id, node.id)) {
17390                 return true;
17391             } else {
17392                 // check to see if this is a text node child of the one we want
17393                 var p = node.parentNode;
17394
17395                 while (p) {
17396                     if (this.isHandle(id, p.id)) {
17397                         return true;
17398                     } else {
17399                         p = p.parentNode;
17400                     }
17401                 }
17402             }
17403
17404             return false;
17405         }
17406
17407     };
17408
17409 }();
17410
17411 // shorter alias, save a few bytes
17412 Roo.dd.DDM = Roo.dd.DragDropMgr;
17413 Roo.dd.DDM._addListeners();
17414
17415 }/*
17416  * Based on:
17417  * Ext JS Library 1.1.1
17418  * Copyright(c) 2006-2007, Ext JS, LLC.
17419  *
17420  * Originally Released Under LGPL - original licence link has changed is not relivant.
17421  *
17422  * Fork - LGPL
17423  * <script type="text/javascript">
17424  */
17425
17426 /**
17427  * @class Roo.dd.DD
17428  * A DragDrop implementation where the linked element follows the
17429  * mouse cursor during a drag.
17430  * @extends Roo.dd.DragDrop
17431  * @constructor
17432  * @param {String} id the id of the linked element
17433  * @param {String} sGroup the group of related DragDrop items
17434  * @param {object} config an object containing configurable attributes
17435  *                Valid properties for DD:
17436  *                    scroll
17437  */
17438 Roo.dd.DD = function(id, sGroup, config) {
17439     if (id) {
17440         this.init(id, sGroup, config);
17441     }
17442 };
17443
17444 Roo.extend(Roo.dd.DD, Roo.dd.DragDrop, {
17445
17446     /**
17447      * When set to true, the utility automatically tries to scroll the browser
17448      * window wehn a drag and drop element is dragged near the viewport boundary.
17449      * Defaults to true.
17450      * @property scroll
17451      * @type boolean
17452      */
17453     scroll: true,
17454
17455     /**
17456      * Sets the pointer offset to the distance between the linked element's top
17457      * left corner and the location the element was clicked
17458      * @method autoOffset
17459      * @param {int} iPageX the X coordinate of the click
17460      * @param {int} iPageY the Y coordinate of the click
17461      */
17462     autoOffset: function(iPageX, iPageY) {
17463         var x = iPageX - this.startPageX;
17464         var y = iPageY - this.startPageY;
17465         this.setDelta(x, y);
17466     },
17467
17468     /**
17469      * Sets the pointer offset.  You can call this directly to force the
17470      * offset to be in a particular location (e.g., pass in 0,0 to set it
17471      * to the center of the object)
17472      * @method setDelta
17473      * @param {int} iDeltaX the distance from the left
17474      * @param {int} iDeltaY the distance from the top
17475      */
17476     setDelta: function(iDeltaX, iDeltaY) {
17477         this.deltaX = iDeltaX;
17478         this.deltaY = iDeltaY;
17479     },
17480
17481     /**
17482      * Sets the drag element to the location of the mousedown or click event,
17483      * maintaining the cursor location relative to the location on the element
17484      * that was clicked.  Override this if you want to place the element in a
17485      * location other than where the cursor is.
17486      * @method setDragElPos
17487      * @param {int} iPageX the X coordinate of the mousedown or drag event
17488      * @param {int} iPageY the Y coordinate of the mousedown or drag event
17489      */
17490     setDragElPos: function(iPageX, iPageY) {
17491         // the first time we do this, we are going to check to make sure
17492         // the element has css positioning
17493
17494         var el = this.getDragEl();
17495         this.alignElWithMouse(el, iPageX, iPageY);
17496     },
17497
17498     /**
17499      * Sets the element to the location of the mousedown or click event,
17500      * maintaining the cursor location relative to the location on the element
17501      * that was clicked.  Override this if you want to place the element in a
17502      * location other than where the cursor is.
17503      * @method alignElWithMouse
17504      * @param {HTMLElement} el the element to move
17505      * @param {int} iPageX the X coordinate of the mousedown or drag event
17506      * @param {int} iPageY the Y coordinate of the mousedown or drag event
17507      */
17508     alignElWithMouse: function(el, iPageX, iPageY) {
17509         var oCoord = this.getTargetCoord(iPageX, iPageY);
17510         var fly = el.dom ? el : Roo.fly(el);
17511         if (!this.deltaSetXY) {
17512             var aCoord = [oCoord.x, oCoord.y];
17513             fly.setXY(aCoord);
17514             var newLeft = fly.getLeft(true);
17515             var newTop  = fly.getTop(true);
17516             this.deltaSetXY = [ newLeft - oCoord.x, newTop - oCoord.y ];
17517         } else {
17518             fly.setLeftTop(oCoord.x + this.deltaSetXY[0], oCoord.y + this.deltaSetXY[1]);
17519         }
17520
17521         this.cachePosition(oCoord.x, oCoord.y);
17522         this.autoScroll(oCoord.x, oCoord.y, el.offsetHeight, el.offsetWidth);
17523         return oCoord;
17524     },
17525
17526     /**
17527      * Saves the most recent position so that we can reset the constraints and
17528      * tick marks on-demand.  We need to know this so that we can calculate the
17529      * number of pixels the element is offset from its original position.
17530      * @method cachePosition
17531      * @param iPageX the current x position (optional, this just makes it so we
17532      * don't have to look it up again)
17533      * @param iPageY the current y position (optional, this just makes it so we
17534      * don't have to look it up again)
17535      */
17536     cachePosition: function(iPageX, iPageY) {
17537         if (iPageX) {
17538             this.lastPageX = iPageX;
17539             this.lastPageY = iPageY;
17540         } else {
17541             var aCoord = Roo.lib.Dom.getXY(this.getEl());
17542             this.lastPageX = aCoord[0];
17543             this.lastPageY = aCoord[1];
17544         }
17545     },
17546
17547     /**
17548      * Auto-scroll the window if the dragged object has been moved beyond the
17549      * visible window boundary.
17550      * @method autoScroll
17551      * @param {int} x the drag element's x position
17552      * @param {int} y the drag element's y position
17553      * @param {int} h the height of the drag element
17554      * @param {int} w the width of the drag element
17555      * @private
17556      */
17557     autoScroll: function(x, y, h, w) {
17558
17559         if (this.scroll) {
17560             // The client height
17561             var clientH = Roo.lib.Dom.getViewWidth();
17562
17563             // The client width
17564             var clientW = Roo.lib.Dom.getViewHeight();
17565
17566             // The amt scrolled down
17567             var st = this.DDM.getScrollTop();
17568
17569             // The amt scrolled right
17570             var sl = this.DDM.getScrollLeft();
17571
17572             // Location of the bottom of the element
17573             var bot = h + y;
17574
17575             // Location of the right of the element
17576             var right = w + x;
17577
17578             // The distance from the cursor to the bottom of the visible area,
17579             // adjusted so that we don't scroll if the cursor is beyond the
17580             // element drag constraints
17581             var toBot = (clientH + st - y - this.deltaY);
17582
17583             // The distance from the cursor to the right of the visible area
17584             var toRight = (clientW + sl - x - this.deltaX);
17585
17586
17587             // How close to the edge the cursor must be before we scroll
17588             // var thresh = (document.all) ? 100 : 40;
17589             var thresh = 40;
17590
17591             // How many pixels to scroll per autoscroll op.  This helps to reduce
17592             // clunky scrolling. IE is more sensitive about this ... it needs this
17593             // value to be higher.
17594             var scrAmt = (document.all) ? 80 : 30;
17595
17596             // Scroll down if we are near the bottom of the visible page and the
17597             // obj extends below the crease
17598             if ( bot > clientH && toBot < thresh ) {
17599                 window.scrollTo(sl, st + scrAmt);
17600             }
17601
17602             // Scroll up if the window is scrolled down and the top of the object
17603             // goes above the top border
17604             if ( y < st && st > 0 && y - st < thresh ) {
17605                 window.scrollTo(sl, st - scrAmt);
17606             }
17607
17608             // Scroll right if the obj is beyond the right border and the cursor is
17609             // near the border.
17610             if ( right > clientW && toRight < thresh ) {
17611                 window.scrollTo(sl + scrAmt, st);
17612             }
17613
17614             // Scroll left if the window has been scrolled to the right and the obj
17615             // extends past the left border
17616             if ( x < sl && sl > 0 && x - sl < thresh ) {
17617                 window.scrollTo(sl - scrAmt, st);
17618             }
17619         }
17620     },
17621
17622     /**
17623      * Finds the location the element should be placed if we want to move
17624      * it to where the mouse location less the click offset would place us.
17625      * @method getTargetCoord
17626      * @param {int} iPageX the X coordinate of the click
17627      * @param {int} iPageY the Y coordinate of the click
17628      * @return an object that contains the coordinates (Object.x and Object.y)
17629      * @private
17630      */
17631     getTargetCoord: function(iPageX, iPageY) {
17632
17633
17634         var x = iPageX - this.deltaX;
17635         var y = iPageY - this.deltaY;
17636
17637         if (this.constrainX) {
17638             if (x < this.minX) { x = this.minX; }
17639             if (x > this.maxX) { x = this.maxX; }
17640         }
17641
17642         if (this.constrainY) {
17643             if (y < this.minY) { y = this.minY; }
17644             if (y > this.maxY) { y = this.maxY; }
17645         }
17646
17647         x = this.getTick(x, this.xTicks);
17648         y = this.getTick(y, this.yTicks);
17649
17650
17651         return {x:x, y:y};
17652     },
17653
17654     /*
17655      * Sets up config options specific to this class. Overrides
17656      * Roo.dd.DragDrop, but all versions of this method through the
17657      * inheritance chain are called
17658      */
17659     applyConfig: function() {
17660         Roo.dd.DD.superclass.applyConfig.call(this);
17661         this.scroll = (this.config.scroll !== false);
17662     },
17663
17664     /*
17665      * Event that fires prior to the onMouseDown event.  Overrides
17666      * Roo.dd.DragDrop.
17667      */
17668     b4MouseDown: function(e) {
17669         // this.resetConstraints();
17670         this.autoOffset(e.getPageX(),
17671                             e.getPageY());
17672     },
17673
17674     /*
17675      * Event that fires prior to the onDrag event.  Overrides
17676      * Roo.dd.DragDrop.
17677      */
17678     b4Drag: function(e) {
17679         this.setDragElPos(e.getPageX(),
17680                             e.getPageY());
17681     },
17682
17683     toString: function() {
17684         return ("DD " + this.id);
17685     }
17686
17687     //////////////////////////////////////////////////////////////////////////
17688     // Debugging ygDragDrop events that can be overridden
17689     //////////////////////////////////////////////////////////////////////////
17690     /*
17691     startDrag: function(x, y) {
17692     },
17693
17694     onDrag: function(e) {
17695     },
17696
17697     onDragEnter: function(e, id) {
17698     },
17699
17700     onDragOver: function(e, id) {
17701     },
17702
17703     onDragOut: function(e, id) {
17704     },
17705
17706     onDragDrop: function(e, id) {
17707     },
17708
17709     endDrag: function(e) {
17710     }
17711
17712     */
17713
17714 });/*
17715  * Based on:
17716  * Ext JS Library 1.1.1
17717  * Copyright(c) 2006-2007, Ext JS, LLC.
17718  *
17719  * Originally Released Under LGPL - original licence link has changed is not relivant.
17720  *
17721  * Fork - LGPL
17722  * <script type="text/javascript">
17723  */
17724
17725 /**
17726  * @class Roo.dd.DDProxy
17727  * A DragDrop implementation that inserts an empty, bordered div into
17728  * the document that follows the cursor during drag operations.  At the time of
17729  * the click, the frame div is resized to the dimensions of the linked html
17730  * element, and moved to the exact location of the linked element.
17731  *
17732  * References to the "frame" element refer to the single proxy element that
17733  * was created to be dragged in place of all DDProxy elements on the
17734  * page.
17735  *
17736  * @extends Roo.dd.DD
17737  * @constructor
17738  * @param {String} id the id of the linked html element
17739  * @param {String} sGroup the group of related DragDrop objects
17740  * @param {object} config an object containing configurable attributes
17741  *                Valid properties for DDProxy in addition to those in DragDrop:
17742  *                   resizeFrame, centerFrame, dragElId
17743  */
17744 Roo.dd.DDProxy = function(id, sGroup, config) {
17745     if (id) {
17746         this.init(id, sGroup, config);
17747         this.initFrame();
17748     }
17749 };
17750
17751 /**
17752  * The default drag frame div id
17753  * @property Roo.dd.DDProxy.dragElId
17754  * @type String
17755  * @static
17756  */
17757 Roo.dd.DDProxy.dragElId = "ygddfdiv";
17758
17759 Roo.extend(Roo.dd.DDProxy, Roo.dd.DD, {
17760
17761     /**
17762      * By default we resize the drag frame to be the same size as the element
17763      * we want to drag (this is to get the frame effect).  We can turn it off
17764      * if we want a different behavior.
17765      * @property resizeFrame
17766      * @type boolean
17767      */
17768     resizeFrame: true,
17769
17770     /**
17771      * By default the frame is positioned exactly where the drag element is, so
17772      * we use the cursor offset provided by Roo.dd.DD.  Another option that works only if
17773      * you do not have constraints on the obj is to have the drag frame centered
17774      * around the cursor.  Set centerFrame to true for this effect.
17775      * @property centerFrame
17776      * @type boolean
17777      */
17778     centerFrame: false,
17779
17780     /**
17781      * Creates the proxy element if it does not yet exist
17782      * @method createFrame
17783      */
17784     createFrame: function() {
17785         var self = this;
17786         var body = document.body;
17787
17788         if (!body || !body.firstChild) {
17789             setTimeout( function() { self.createFrame(); }, 50 );
17790             return;
17791         }
17792
17793         var div = this.getDragEl();
17794
17795         if (!div) {
17796             div    = document.createElement("div");
17797             div.id = this.dragElId;
17798             var s  = div.style;
17799
17800             s.position   = "absolute";
17801             s.visibility = "hidden";
17802             s.cursor     = "move";
17803             s.border     = "2px solid #aaa";
17804             s.zIndex     = 999;
17805
17806             // appendChild can blow up IE if invoked prior to the window load event
17807             // while rendering a table.  It is possible there are other scenarios
17808             // that would cause this to happen as well.
17809             body.insertBefore(div, body.firstChild);
17810         }
17811     },
17812
17813     /**
17814      * Initialization for the drag frame element.  Must be called in the
17815      * constructor of all subclasses
17816      * @method initFrame
17817      */
17818     initFrame: function() {
17819         this.createFrame();
17820     },
17821
17822     applyConfig: function() {
17823         Roo.dd.DDProxy.superclass.applyConfig.call(this);
17824
17825         this.resizeFrame = (this.config.resizeFrame !== false);
17826         this.centerFrame = (this.config.centerFrame);
17827         this.setDragElId(this.config.dragElId || Roo.dd.DDProxy.dragElId);
17828     },
17829
17830     /**
17831      * Resizes the drag frame to the dimensions of the clicked object, positions
17832      * it over the object, and finally displays it
17833      * @method showFrame
17834      * @param {int} iPageX X click position
17835      * @param {int} iPageY Y click position
17836      * @private
17837      */
17838     showFrame: function(iPageX, iPageY) {
17839         var el = this.getEl();
17840         var dragEl = this.getDragEl();
17841         var s = dragEl.style;
17842
17843         this._resizeProxy();
17844
17845         if (this.centerFrame) {
17846             this.setDelta( Math.round(parseInt(s.width,  10)/2),
17847                            Math.round(parseInt(s.height, 10)/2) );
17848         }
17849
17850         this.setDragElPos(iPageX, iPageY);
17851
17852         Roo.fly(dragEl).show();
17853     },
17854
17855     /**
17856      * The proxy is automatically resized to the dimensions of the linked
17857      * element when a drag is initiated, unless resizeFrame is set to false
17858      * @method _resizeProxy
17859      * @private
17860      */
17861     _resizeProxy: function() {
17862         if (this.resizeFrame) {
17863             var el = this.getEl();
17864             Roo.fly(this.getDragEl()).setSize(el.offsetWidth, el.offsetHeight);
17865         }
17866     },
17867
17868     // overrides Roo.dd.DragDrop
17869     b4MouseDown: function(e) {
17870         var x = e.getPageX();
17871         var y = e.getPageY();
17872         this.autoOffset(x, y);
17873         this.setDragElPos(x, y);
17874     },
17875
17876     // overrides Roo.dd.DragDrop
17877     b4StartDrag: function(x, y) {
17878         // show the drag frame
17879         this.showFrame(x, y);
17880     },
17881
17882     // overrides Roo.dd.DragDrop
17883     b4EndDrag: function(e) {
17884         Roo.fly(this.getDragEl()).hide();
17885     },
17886
17887     // overrides Roo.dd.DragDrop
17888     // By default we try to move the element to the last location of the frame.
17889     // This is so that the default behavior mirrors that of Roo.dd.DD.
17890     endDrag: function(e) {
17891
17892         var lel = this.getEl();
17893         var del = this.getDragEl();
17894
17895         // Show the drag frame briefly so we can get its position
17896         del.style.visibility = "";
17897
17898         this.beforeMove();
17899         // Hide the linked element before the move to get around a Safari
17900         // rendering bug.
17901         lel.style.visibility = "hidden";
17902         Roo.dd.DDM.moveToEl(lel, del);
17903         del.style.visibility = "hidden";
17904         lel.style.visibility = "";
17905
17906         this.afterDrag();
17907     },
17908
17909     beforeMove : function(){
17910
17911     },
17912
17913     afterDrag : function(){
17914
17915     },
17916
17917     toString: function() {
17918         return ("DDProxy " + this.id);
17919     }
17920
17921 });
17922 /*
17923  * Based on:
17924  * Ext JS Library 1.1.1
17925  * Copyright(c) 2006-2007, Ext JS, LLC.
17926  *
17927  * Originally Released Under LGPL - original licence link has changed is not relivant.
17928  *
17929  * Fork - LGPL
17930  * <script type="text/javascript">
17931  */
17932
17933  /**
17934  * @class Roo.dd.DDTarget
17935  * A DragDrop implementation that does not move, but can be a drop
17936  * target.  You would get the same result by simply omitting implementation
17937  * for the event callbacks, but this way we reduce the processing cost of the
17938  * event listener and the callbacks.
17939  * @extends Roo.dd.DragDrop
17940  * @constructor
17941  * @param {String} id the id of the element that is a drop target
17942  * @param {String} sGroup the group of related DragDrop objects
17943  * @param {object} config an object containing configurable attributes
17944  *                 Valid properties for DDTarget in addition to those in
17945  *                 DragDrop:
17946  *                    none
17947  */
17948 Roo.dd.DDTarget = function(id, sGroup, config) {
17949     if (id) {
17950         this.initTarget(id, sGroup, config);
17951     }
17952     if (config.listeners || config.events) { 
17953        Roo.dd.DragDrop.superclass.constructor.call(this,  { 
17954             listeners : config.listeners || {}, 
17955             events : config.events || {} 
17956         });    
17957     }
17958 };
17959
17960 // Roo.dd.DDTarget.prototype = new Roo.dd.DragDrop();
17961 Roo.extend(Roo.dd.DDTarget, Roo.dd.DragDrop, {
17962     toString: function() {
17963         return ("DDTarget " + this.id);
17964     }
17965 });
17966 /*
17967  * Based on:
17968  * Ext JS Library 1.1.1
17969  * Copyright(c) 2006-2007, Ext JS, LLC.
17970  *
17971  * Originally Released Under LGPL - original licence link has changed is not relivant.
17972  *
17973  * Fork - LGPL
17974  * <script type="text/javascript">
17975  */
17976  
17977
17978 /**
17979  * @class Roo.dd.ScrollManager
17980  * Provides automatic scrolling of overflow regions in the page during drag operations.<br><br>
17981  * <b>Note: This class uses "Point Mode" and is untested in "Intersect Mode".</b>
17982  * @singleton
17983  */
17984 Roo.dd.ScrollManager = function(){
17985     var ddm = Roo.dd.DragDropMgr;
17986     var els = {};
17987     var dragEl = null;
17988     var proc = {};
17989     
17990     
17991     
17992     var onStop = function(e){
17993         dragEl = null;
17994         clearProc();
17995     };
17996     
17997     var triggerRefresh = function(){
17998         if(ddm.dragCurrent){
17999              ddm.refreshCache(ddm.dragCurrent.groups);
18000         }
18001     };
18002     
18003     var doScroll = function(){
18004         if(ddm.dragCurrent){
18005             var dds = Roo.dd.ScrollManager;
18006             if(!dds.animate){
18007                 if(proc.el.scroll(proc.dir, dds.increment)){
18008                     triggerRefresh();
18009                 }
18010             }else{
18011                 proc.el.scroll(proc.dir, dds.increment, true, dds.animDuration, triggerRefresh);
18012             }
18013         }
18014     };
18015     
18016     var clearProc = function(){
18017         if(proc.id){
18018             clearInterval(proc.id);
18019         }
18020         proc.id = 0;
18021         proc.el = null;
18022         proc.dir = "";
18023     };
18024     
18025     var startProc = function(el, dir){
18026          Roo.log('scroll startproc');
18027         clearProc();
18028         proc.el = el;
18029         proc.dir = dir;
18030         proc.id = setInterval(doScroll, Roo.dd.ScrollManager.frequency);
18031     };
18032     
18033     var onFire = function(e, isDrop){
18034        
18035         if(isDrop || !ddm.dragCurrent){ return; }
18036         var dds = Roo.dd.ScrollManager;
18037         if(!dragEl || dragEl != ddm.dragCurrent){
18038             dragEl = ddm.dragCurrent;
18039             // refresh regions on drag start
18040             dds.refreshCache();
18041         }
18042         
18043         var xy = Roo.lib.Event.getXY(e);
18044         var pt = new Roo.lib.Point(xy[0], xy[1]);
18045         for(var id in els){
18046             var el = els[id], r = el._region;
18047             if(r && r.contains(pt) && el.isScrollable()){
18048                 if(r.bottom - pt.y <= dds.thresh){
18049                     if(proc.el != el){
18050                         startProc(el, "down");
18051                     }
18052                     return;
18053                 }else if(r.right - pt.x <= dds.thresh){
18054                     if(proc.el != el){
18055                         startProc(el, "left");
18056                     }
18057                     return;
18058                 }else if(pt.y - r.top <= dds.thresh){
18059                     if(proc.el != el){
18060                         startProc(el, "up");
18061                     }
18062                     return;
18063                 }else if(pt.x - r.left <= dds.thresh){
18064                     if(proc.el != el){
18065                         startProc(el, "right");
18066                     }
18067                     return;
18068                 }
18069             }
18070         }
18071         clearProc();
18072     };
18073     
18074     ddm.fireEvents = ddm.fireEvents.createSequence(onFire, ddm);
18075     ddm.stopDrag = ddm.stopDrag.createSequence(onStop, ddm);
18076     
18077     return {
18078         /**
18079          * Registers new overflow element(s) to auto scroll
18080          * @param {String/HTMLElement/Element/Array} el The id of or the element to be scrolled or an array of either
18081          */
18082         register : function(el){
18083             if(el instanceof Array){
18084                 for(var i = 0, len = el.length; i < len; i++) {
18085                         this.register(el[i]);
18086                 }
18087             }else{
18088                 el = Roo.get(el);
18089                 els[el.id] = el;
18090             }
18091             Roo.dd.ScrollManager.els = els;
18092         },
18093         
18094         /**
18095          * Unregisters overflow element(s) so they are no longer scrolled
18096          * @param {String/HTMLElement/Element/Array} el The id of or the element to be removed or an array of either
18097          */
18098         unregister : function(el){
18099             if(el instanceof Array){
18100                 for(var i = 0, len = el.length; i < len; i++) {
18101                         this.unregister(el[i]);
18102                 }
18103             }else{
18104                 el = Roo.get(el);
18105                 delete els[el.id];
18106             }
18107         },
18108         
18109         /**
18110          * The number of pixels from the edge of a container the pointer needs to be to 
18111          * trigger scrolling (defaults to 25)
18112          * @type Number
18113          */
18114         thresh : 25,
18115         
18116         /**
18117          * The number of pixels to scroll in each scroll increment (defaults to 50)
18118          * @type Number
18119          */
18120         increment : 100,
18121         
18122         /**
18123          * The frequency of scrolls in milliseconds (defaults to 500)
18124          * @type Number
18125          */
18126         frequency : 500,
18127         
18128         /**
18129          * True to animate the scroll (defaults to true)
18130          * @type Boolean
18131          */
18132         animate: true,
18133         
18134         /**
18135          * The animation duration in seconds - 
18136          * MUST BE less than Roo.dd.ScrollManager.frequency! (defaults to .4)
18137          * @type Number
18138          */
18139         animDuration: .4,
18140         
18141         /**
18142          * Manually trigger a cache refresh.
18143          */
18144         refreshCache : function(){
18145             for(var id in els){
18146                 if(typeof els[id] == 'object'){ // for people extending the object prototype
18147                     els[id]._region = els[id].getRegion();
18148                 }
18149             }
18150         }
18151     };
18152 }();/*
18153  * Based on:
18154  * Ext JS Library 1.1.1
18155  * Copyright(c) 2006-2007, Ext JS, LLC.
18156  *
18157  * Originally Released Under LGPL - original licence link has changed is not relivant.
18158  *
18159  * Fork - LGPL
18160  * <script type="text/javascript">
18161  */
18162  
18163
18164 /**
18165  * @class Roo.dd.Registry
18166  * Provides easy access to all drag drop components that are registered on a page.  Items can be retrieved either
18167  * directly by DOM node id, or by passing in the drag drop event that occurred and looking up the event target.
18168  * @singleton
18169  */
18170 Roo.dd.Registry = function(){
18171     var elements = {}; 
18172     var handles = {}; 
18173     var autoIdSeed = 0;
18174
18175     var getId = function(el, autogen){
18176         if(typeof el == "string"){
18177             return el;
18178         }
18179         var id = el.id;
18180         if(!id && autogen !== false){
18181             id = "roodd-" + (++autoIdSeed);
18182             el.id = id;
18183         }
18184         return id;
18185     };
18186     
18187     return {
18188     /**
18189      * Register a drag drop element
18190      * @param {String|HTMLElement} element The id or DOM node to register
18191      * @param {Object} data (optional) A custom data object that will be passed between the elements that are involved
18192      * in drag drop operations.  You can populate this object with any arbitrary properties that your own code
18193      * knows how to interpret, plus there are some specific properties known to the Registry that should be
18194      * populated in the data object (if applicable):
18195      * <pre>
18196 Value      Description<br />
18197 ---------  ------------------------------------------<br />
18198 handles    Array of DOM nodes that trigger dragging<br />
18199            for the element being registered<br />
18200 isHandle   True if the element passed in triggers<br />
18201            dragging itself, else false
18202 </pre>
18203      */
18204         register : function(el, data){
18205             data = data || {};
18206             if(typeof el == "string"){
18207                 el = document.getElementById(el);
18208             }
18209             data.ddel = el;
18210             elements[getId(el)] = data;
18211             if(data.isHandle !== false){
18212                 handles[data.ddel.id] = data;
18213             }
18214             if(data.handles){
18215                 var hs = data.handles;
18216                 for(var i = 0, len = hs.length; i < len; i++){
18217                         handles[getId(hs[i])] = data;
18218                 }
18219             }
18220         },
18221
18222     /**
18223      * Unregister a drag drop element
18224      * @param {String|HTMLElement}  element The id or DOM node to unregister
18225      */
18226         unregister : function(el){
18227             var id = getId(el, false);
18228             var data = elements[id];
18229             if(data){
18230                 delete elements[id];
18231                 if(data.handles){
18232                     var hs = data.handles;
18233                     for(var i = 0, len = hs.length; i < len; i++){
18234                         delete handles[getId(hs[i], false)];
18235                     }
18236                 }
18237             }
18238         },
18239
18240     /**
18241      * Returns the handle registered for a DOM Node by id
18242      * @param {String|HTMLElement} id The DOM node or id to look up
18243      * @return {Object} handle The custom handle data
18244      */
18245         getHandle : function(id){
18246             if(typeof id != "string"){ // must be element?
18247                 id = id.id;
18248             }
18249             return handles[id];
18250         },
18251
18252     /**
18253      * Returns the handle that is registered for the DOM node that is the target of the event
18254      * @param {Event} e The event
18255      * @return {Object} handle The custom handle data
18256      */
18257         getHandleFromEvent : function(e){
18258             var t = Roo.lib.Event.getTarget(e);
18259             return t ? handles[t.id] : null;
18260         },
18261
18262     /**
18263      * Returns a custom data object that is registered for a DOM node by id
18264      * @param {String|HTMLElement} id The DOM node or id to look up
18265      * @return {Object} data The custom data
18266      */
18267         getTarget : function(id){
18268             if(typeof id != "string"){ // must be element?
18269                 id = id.id;
18270             }
18271             return elements[id];
18272         },
18273
18274     /**
18275      * Returns a custom data object that is registered for the DOM node that is the target of the event
18276      * @param {Event} e The event
18277      * @return {Object} data The custom data
18278      */
18279         getTargetFromEvent : function(e){
18280             var t = Roo.lib.Event.getTarget(e);
18281             return t ? elements[t.id] || handles[t.id] : null;
18282         }
18283     };
18284 }();/*
18285  * Based on:
18286  * Ext JS Library 1.1.1
18287  * Copyright(c) 2006-2007, Ext JS, LLC.
18288  *
18289  * Originally Released Under LGPL - original licence link has changed is not relivant.
18290  *
18291  * Fork - LGPL
18292  * <script type="text/javascript">
18293  */
18294  
18295
18296 /**
18297  * @class Roo.dd.StatusProxy
18298  * A specialized drag proxy that supports a drop status icon, {@link Roo.Layer} styles and auto-repair.  This is the
18299  * default drag proxy used by all Roo.dd components.
18300  * @constructor
18301  * @param {Object} config
18302  */
18303 Roo.dd.StatusProxy = function(config){
18304     Roo.apply(this, config);
18305     this.id = this.id || Roo.id();
18306     this.el = new Roo.Layer({
18307         dh: {
18308             id: this.id, tag: "div", cls: "x-dd-drag-proxy "+this.dropNotAllowed, children: [
18309                 {tag: "div", cls: "x-dd-drop-icon"},
18310                 {tag: "div", cls: "x-dd-drag-ghost"}
18311             ]
18312         }, 
18313         shadow: !config || config.shadow !== false
18314     });
18315     this.ghost = Roo.get(this.el.dom.childNodes[1]);
18316     this.dropStatus = this.dropNotAllowed;
18317 };
18318
18319 Roo.dd.StatusProxy.prototype = {
18320     /**
18321      * @cfg {String} dropAllowed
18322      * The CSS class to apply to the status element when drop is allowed (defaults to "x-dd-drop-ok").
18323      */
18324     dropAllowed : "x-dd-drop-ok",
18325     /**
18326      * @cfg {String} dropNotAllowed
18327      * The CSS class to apply to the status element when drop is not allowed (defaults to "x-dd-drop-nodrop").
18328      */
18329     dropNotAllowed : "x-dd-drop-nodrop",
18330
18331     /**
18332      * Updates the proxy's visual element to indicate the status of whether or not drop is allowed
18333      * over the current target element.
18334      * @param {String} cssClass The css class for the new drop status indicator image
18335      */
18336     setStatus : function(cssClass){
18337         cssClass = cssClass || this.dropNotAllowed;
18338         if(this.dropStatus != cssClass){
18339             this.el.replaceClass(this.dropStatus, cssClass);
18340             this.dropStatus = cssClass;
18341         }
18342     },
18343
18344     /**
18345      * Resets the status indicator to the default dropNotAllowed value
18346      * @param {Boolean} clearGhost True to also remove all content from the ghost, false to preserve it
18347      */
18348     reset : function(clearGhost){
18349         this.el.dom.className = "x-dd-drag-proxy " + this.dropNotAllowed;
18350         this.dropStatus = this.dropNotAllowed;
18351         if(clearGhost){
18352             this.ghost.update("");
18353         }
18354     },
18355
18356     /**
18357      * Updates the contents of the ghost element
18358      * @param {String} html The html that will replace the current innerHTML of the ghost element
18359      */
18360     update : function(html){
18361         if(typeof html == "string"){
18362             this.ghost.update(html);
18363         }else{
18364             this.ghost.update("");
18365             html.style.margin = "0";
18366             this.ghost.dom.appendChild(html);
18367         }
18368         // ensure float = none set?? cant remember why though.
18369         var el = this.ghost.dom.firstChild;
18370                 if(el){
18371                         Roo.fly(el).setStyle('float', 'none');
18372                 }
18373     },
18374     
18375     /**
18376      * Returns the underlying proxy {@link Roo.Layer}
18377      * @return {Roo.Layer} el
18378     */
18379     getEl : function(){
18380         return this.el;
18381     },
18382
18383     /**
18384      * Returns the ghost element
18385      * @return {Roo.Element} el
18386      */
18387     getGhost : function(){
18388         return this.ghost;
18389     },
18390
18391     /**
18392      * Hides the proxy
18393      * @param {Boolean} clear True to reset the status and clear the ghost contents, false to preserve them
18394      */
18395     hide : function(clear){
18396         this.el.hide();
18397         if(clear){
18398             this.reset(true);
18399         }
18400     },
18401
18402     /**
18403      * Stops the repair animation if it's currently running
18404      */
18405     stop : function(){
18406         if(this.anim && this.anim.isAnimated && this.anim.isAnimated()){
18407             this.anim.stop();
18408         }
18409     },
18410
18411     /**
18412      * Displays this proxy
18413      */
18414     show : function(){
18415         this.el.show();
18416     },
18417
18418     /**
18419      * Force the Layer to sync its shadow and shim positions to the element
18420      */
18421     sync : function(){
18422         this.el.sync();
18423     },
18424
18425     /**
18426      * Causes the proxy to return to its position of origin via an animation.  Should be called after an
18427      * invalid drop operation by the item being dragged.
18428      * @param {Array} xy The XY position of the element ([x, y])
18429      * @param {Function} callback The function to call after the repair is complete
18430      * @param {Object} scope The scope in which to execute the callback
18431      */
18432     repair : function(xy, callback, scope){
18433         this.callback = callback;
18434         this.scope = scope;
18435         if(xy && this.animRepair !== false){
18436             this.el.addClass("x-dd-drag-repair");
18437             this.el.hideUnders(true);
18438             this.anim = this.el.shift({
18439                 duration: this.repairDuration || .5,
18440                 easing: 'easeOut',
18441                 xy: xy,
18442                 stopFx: true,
18443                 callback: this.afterRepair,
18444                 scope: this
18445             });
18446         }else{
18447             this.afterRepair();
18448         }
18449     },
18450
18451     // private
18452     afterRepair : function(){
18453         this.hide(true);
18454         if(typeof this.callback == "function"){
18455             this.callback.call(this.scope || this);
18456         }
18457         this.callback = null;
18458         this.scope = null;
18459     }
18460 };/*
18461  * Based on:
18462  * Ext JS Library 1.1.1
18463  * Copyright(c) 2006-2007, Ext JS, LLC.
18464  *
18465  * Originally Released Under LGPL - original licence link has changed is not relivant.
18466  *
18467  * Fork - LGPL
18468  * <script type="text/javascript">
18469  */
18470
18471 /**
18472  * @class Roo.dd.DragSource
18473  * @extends Roo.dd.DDProxy
18474  * A simple class that provides the basic implementation needed to make any element draggable.
18475  * @constructor
18476  * @param {String/HTMLElement/Element} el The container element
18477  * @param {Object} config
18478  */
18479 Roo.dd.DragSource = function(el, config){
18480     this.el = Roo.get(el);
18481     this.dragData = {};
18482     
18483     Roo.apply(this, config);
18484     
18485     if(!this.proxy){
18486         this.proxy = new Roo.dd.StatusProxy();
18487     }
18488
18489     Roo.dd.DragSource.superclass.constructor.call(this, this.el.dom, this.ddGroup || this.group,
18490           {dragElId : this.proxy.id, resizeFrame: false, isTarget: false, scroll: this.scroll === true});
18491     
18492     this.dragging = false;
18493 };
18494
18495 Roo.extend(Roo.dd.DragSource, Roo.dd.DDProxy, {
18496     /**
18497      * @cfg {String} dropAllowed
18498      * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
18499      */
18500     dropAllowed : "x-dd-drop-ok",
18501     /**
18502      * @cfg {String} dropNotAllowed
18503      * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
18504      */
18505     dropNotAllowed : "x-dd-drop-nodrop",
18506
18507     /**
18508      * Returns the data object associated with this drag source
18509      * @return {Object} data An object containing arbitrary data
18510      */
18511     getDragData : function(e){
18512         return this.dragData;
18513     },
18514
18515     // private
18516     onDragEnter : function(e, id){
18517         var target = Roo.dd.DragDropMgr.getDDById(id);
18518         this.cachedTarget = target;
18519         if(this.beforeDragEnter(target, e, id) !== false){
18520             if(target.isNotifyTarget){
18521                 var status = target.notifyEnter(this, e, this.dragData);
18522                 this.proxy.setStatus(status);
18523             }else{
18524                 this.proxy.setStatus(this.dropAllowed);
18525             }
18526             
18527             if(this.afterDragEnter){
18528                 /**
18529                  * An empty function by default, but provided so that you can perform a custom action
18530                  * when the dragged item enters the drop target by providing an implementation.
18531                  * @param {Roo.dd.DragDrop} target The drop target
18532                  * @param {Event} e The event object
18533                  * @param {String} id The id of the dragged element
18534                  * @method afterDragEnter
18535                  */
18536                 this.afterDragEnter(target, e, id);
18537             }
18538         }
18539     },
18540
18541     /**
18542      * An empty function by default, but provided so that you can perform a custom action
18543      * before the dragged item enters the drop target and optionally cancel the onDragEnter.
18544      * @param {Roo.dd.DragDrop} target The drop target
18545      * @param {Event} e The event object
18546      * @param {String} id The id of the dragged element
18547      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
18548      */
18549     beforeDragEnter : function(target, e, id){
18550         return true;
18551     },
18552
18553     // private
18554     alignElWithMouse: function() {
18555         Roo.dd.DragSource.superclass.alignElWithMouse.apply(this, arguments);
18556         this.proxy.sync();
18557     },
18558
18559     // private
18560     onDragOver : function(e, id){
18561         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
18562         if(this.beforeDragOver(target, e, id) !== false){
18563             if(target.isNotifyTarget){
18564                 var status = target.notifyOver(this, e, this.dragData);
18565                 this.proxy.setStatus(status);
18566             }
18567
18568             if(this.afterDragOver){
18569                 /**
18570                  * An empty function by default, but provided so that you can perform a custom action
18571                  * while the dragged item is over the drop target by providing an implementation.
18572                  * @param {Roo.dd.DragDrop} target The drop target
18573                  * @param {Event} e The event object
18574                  * @param {String} id The id of the dragged element
18575                  * @method afterDragOver
18576                  */
18577                 this.afterDragOver(target, e, id);
18578             }
18579         }
18580     },
18581
18582     /**
18583      * An empty function by default, but provided so that you can perform a custom action
18584      * while the dragged item is over the drop target and optionally cancel the onDragOver.
18585      * @param {Roo.dd.DragDrop} target The drop target
18586      * @param {Event} e The event object
18587      * @param {String} id The id of the dragged element
18588      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
18589      */
18590     beforeDragOver : function(target, e, id){
18591         return true;
18592     },
18593
18594     // private
18595     onDragOut : function(e, id){
18596         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
18597         if(this.beforeDragOut(target, e, id) !== false){
18598             if(target.isNotifyTarget){
18599                 target.notifyOut(this, e, this.dragData);
18600             }
18601             this.proxy.reset();
18602             if(this.afterDragOut){
18603                 /**
18604                  * An empty function by default, but provided so that you can perform a custom action
18605                  * after the dragged item is dragged out of the target without dropping.
18606                  * @param {Roo.dd.DragDrop} target The drop target
18607                  * @param {Event} e The event object
18608                  * @param {String} id The id of the dragged element
18609                  * @method afterDragOut
18610                  */
18611                 this.afterDragOut(target, e, id);
18612             }
18613         }
18614         this.cachedTarget = null;
18615     },
18616
18617     /**
18618      * An empty function by default, but provided so that you can perform a custom action before the dragged
18619      * item is dragged out of the target without dropping, and optionally cancel the onDragOut.
18620      * @param {Roo.dd.DragDrop} target The drop target
18621      * @param {Event} e The event object
18622      * @param {String} id The id of the dragged element
18623      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
18624      */
18625     beforeDragOut : function(target, e, id){
18626         return true;
18627     },
18628     
18629     // private
18630     onDragDrop : function(e, id){
18631         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
18632         if(this.beforeDragDrop(target, e, id) !== false){
18633             if(target.isNotifyTarget){
18634                 if(target.notifyDrop(this, e, this.dragData)){ // valid drop?
18635                     this.onValidDrop(target, e, id);
18636                 }else{
18637                     this.onInvalidDrop(target, e, id);
18638                 }
18639             }else{
18640                 this.onValidDrop(target, e, id);
18641             }
18642             
18643             if(this.afterDragDrop){
18644                 /**
18645                  * An empty function by default, but provided so that you can perform a custom action
18646                  * after a valid drag drop has occurred by providing an implementation.
18647                  * @param {Roo.dd.DragDrop} target The drop target
18648                  * @param {Event} e The event object
18649                  * @param {String} id The id of the dropped element
18650                  * @method afterDragDrop
18651                  */
18652                 this.afterDragDrop(target, e, id);
18653             }
18654         }
18655         delete this.cachedTarget;
18656     },
18657
18658     /**
18659      * An empty function by default, but provided so that you can perform a custom action before the dragged
18660      * item is dropped onto the target and optionally cancel the onDragDrop.
18661      * @param {Roo.dd.DragDrop} target The drop target
18662      * @param {Event} e The event object
18663      * @param {String} id The id of the dragged element
18664      * @return {Boolean} isValid True if the drag drop event is valid, else false to cancel
18665      */
18666     beforeDragDrop : function(target, e, id){
18667         return true;
18668     },
18669
18670     // private
18671     onValidDrop : function(target, e, id){
18672         this.hideProxy();
18673         if(this.afterValidDrop){
18674             /**
18675              * An empty function by default, but provided so that you can perform a custom action
18676              * after a valid drop has occurred by providing an implementation.
18677              * @param {Object} target The target DD 
18678              * @param {Event} e The event object
18679              * @param {String} id The id of the dropped element
18680              * @method afterInvalidDrop
18681              */
18682             this.afterValidDrop(target, e, id);
18683         }
18684     },
18685
18686     // private
18687     getRepairXY : function(e, data){
18688         return this.el.getXY();  
18689     },
18690
18691     // private
18692     onInvalidDrop : function(target, e, id){
18693         this.beforeInvalidDrop(target, e, id);
18694         if(this.cachedTarget){
18695             if(this.cachedTarget.isNotifyTarget){
18696                 this.cachedTarget.notifyOut(this, e, this.dragData);
18697             }
18698             this.cacheTarget = null;
18699         }
18700         this.proxy.repair(this.getRepairXY(e, this.dragData), this.afterRepair, this);
18701
18702         if(this.afterInvalidDrop){
18703             /**
18704              * An empty function by default, but provided so that you can perform a custom action
18705              * after an invalid drop has occurred by providing an implementation.
18706              * @param {Event} e The event object
18707              * @param {String} id The id of the dropped element
18708              * @method afterInvalidDrop
18709              */
18710             this.afterInvalidDrop(e, id);
18711         }
18712     },
18713
18714     // private
18715     afterRepair : function(){
18716         if(Roo.enableFx){
18717             this.el.highlight(this.hlColor || "c3daf9");
18718         }
18719         this.dragging = false;
18720     },
18721
18722     /**
18723      * An empty function by default, but provided so that you can perform a custom action after an invalid
18724      * drop has occurred.
18725      * @param {Roo.dd.DragDrop} target The drop target
18726      * @param {Event} e The event object
18727      * @param {String} id The id of the dragged element
18728      * @return {Boolean} isValid True if the invalid drop should proceed, else false to cancel
18729      */
18730     beforeInvalidDrop : function(target, e, id){
18731         return true;
18732     },
18733
18734     // private
18735     handleMouseDown : function(e){
18736         if(this.dragging) {
18737             return;
18738         }
18739         var data = this.getDragData(e);
18740         if(data && this.onBeforeDrag(data, e) !== false){
18741             this.dragData = data;
18742             this.proxy.stop();
18743             Roo.dd.DragSource.superclass.handleMouseDown.apply(this, arguments);
18744         } 
18745     },
18746
18747     /**
18748      * An empty function by default, but provided so that you can perform a custom action before the initial
18749      * drag event begins and optionally cancel it.
18750      * @param {Object} data An object containing arbitrary data to be shared with drop targets
18751      * @param {Event} e The event object
18752      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
18753      */
18754     onBeforeDrag : function(data, e){
18755         return true;
18756     },
18757
18758     /**
18759      * An empty function by default, but provided so that you can perform a custom action once the initial
18760      * drag event has begun.  The drag cannot be canceled from this function.
18761      * @param {Number} x The x position of the click on the dragged object
18762      * @param {Number} y The y position of the click on the dragged object
18763      */
18764     onStartDrag : Roo.emptyFn,
18765
18766     // private - YUI override
18767     startDrag : function(x, y){
18768         this.proxy.reset();
18769         this.dragging = true;
18770         this.proxy.update("");
18771         this.onInitDrag(x, y);
18772         this.proxy.show();
18773     },
18774
18775     // private
18776     onInitDrag : function(x, y){
18777         var clone = this.el.dom.cloneNode(true);
18778         clone.id = Roo.id(); // prevent duplicate ids
18779         this.proxy.update(clone);
18780         this.onStartDrag(x, y);
18781         return true;
18782     },
18783
18784     /**
18785      * Returns the drag source's underlying {@link Roo.dd.StatusProxy}
18786      * @return {Roo.dd.StatusProxy} proxy The StatusProxy
18787      */
18788     getProxy : function(){
18789         return this.proxy;  
18790     },
18791
18792     /**
18793      * Hides the drag source's {@link Roo.dd.StatusProxy}
18794      */
18795     hideProxy : function(){
18796         this.proxy.hide();  
18797         this.proxy.reset(true);
18798         this.dragging = false;
18799     },
18800
18801     // private
18802     triggerCacheRefresh : function(){
18803         Roo.dd.DDM.refreshCache(this.groups);
18804     },
18805
18806     // private - override to prevent hiding
18807     b4EndDrag: function(e) {
18808     },
18809
18810     // private - override to prevent moving
18811     endDrag : function(e){
18812         this.onEndDrag(this.dragData, e);
18813     },
18814
18815     // private
18816     onEndDrag : function(data, e){
18817     },
18818     
18819     // private - pin to cursor
18820     autoOffset : function(x, y) {
18821         this.setDelta(-12, -20);
18822     }    
18823 });/*
18824  * Based on:
18825  * Ext JS Library 1.1.1
18826  * Copyright(c) 2006-2007, Ext JS, LLC.
18827  *
18828  * Originally Released Under LGPL - original licence link has changed is not relivant.
18829  *
18830  * Fork - LGPL
18831  * <script type="text/javascript">
18832  */
18833
18834
18835 /**
18836  * @class Roo.dd.DropTarget
18837  * @extends Roo.dd.DDTarget
18838  * A simple class that provides the basic implementation needed to make any element a drop target that can have
18839  * draggable items dropped onto it.  The drop has no effect until an implementation of notifyDrop is provided.
18840  * @constructor
18841  * @param {String/HTMLElement/Element} el The container element
18842  * @param {Object} config
18843  */
18844 Roo.dd.DropTarget = function(el, config){
18845     this.el = Roo.get(el);
18846     
18847     var listeners = false; ;
18848     if (config && config.listeners) {
18849         listeners= config.listeners;
18850         delete config.listeners;
18851     }
18852     Roo.apply(this, config);
18853     
18854     if(this.containerScroll){
18855         Roo.dd.ScrollManager.register(this.el);
18856     }
18857     this.addEvents( {
18858          /**
18859          * @scope Roo.dd.DropTarget
18860          */
18861          
18862          /**
18863          * @event enter
18864          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source is now over the
18865          * target.  This default implementation adds the CSS class specified by overClass (if any) to the drop element
18866          * and returns the dropAllowed config value.  This method should be overridden if drop validation is required.
18867          * 
18868          * IMPORTANT : it should set this.overClass and this.dropAllowed
18869          * 
18870          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
18871          * @param {Event} e The event
18872          * @param {Object} data An object containing arbitrary data supplied by the drag source
18873          */
18874         "enter" : true,
18875         
18876          /**
18877          * @event over
18878          * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the target.
18879          * This method will be called on every mouse movement while the drag source is over the drop target.
18880          * This default implementation simply returns the dropAllowed config value.
18881          * 
18882          * IMPORTANT : it should set this.dropAllowed
18883          * 
18884          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
18885          * @param {Event} e The event
18886          * @param {Object} data An object containing arbitrary data supplied by the drag source
18887          
18888          */
18889         "over" : true,
18890         /**
18891          * @event out
18892          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source has been dragged
18893          * out of the target without dropping.  This default implementation simply removes the CSS class specified by
18894          * overClass (if any) from the drop element.
18895          * 
18896          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
18897          * @param {Event} e The event
18898          * @param {Object} data An object containing arbitrary data supplied by the drag source
18899          */
18900          "out" : true,
18901          
18902         /**
18903          * @event drop
18904          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the dragged item has
18905          * been dropped on it.  This method has no default implementation and returns false, so you must provide an
18906          * implementation that does something to process the drop event and returns true so that the drag source's
18907          * repair action does not run.
18908          * 
18909          * IMPORTANT : it should set this.success
18910          * 
18911          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
18912          * @param {Event} e The event
18913          * @param {Object} data An object containing arbitrary data supplied by the drag source
18914         */
18915          "drop" : true
18916     });
18917             
18918      
18919     Roo.dd.DropTarget.superclass.constructor.call(  this, 
18920         this.el.dom, 
18921         this.ddGroup || this.group,
18922         {
18923             isTarget: true,
18924             listeners : listeners || {} 
18925            
18926         
18927         }
18928     );
18929
18930 };
18931
18932 Roo.extend(Roo.dd.DropTarget, Roo.dd.DDTarget, {
18933     /**
18934      * @cfg {String} overClass
18935      * The CSS class applied to the drop target element while the drag source is over it (defaults to "").
18936      */
18937      /**
18938      * @cfg {String} ddGroup
18939      * The drag drop group to handle drop events for
18940      */
18941      
18942     /**
18943      * @cfg {String} dropAllowed
18944      * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
18945      */
18946     dropAllowed : "x-dd-drop-ok",
18947     /**
18948      * @cfg {String} dropNotAllowed
18949      * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
18950      */
18951     dropNotAllowed : "x-dd-drop-nodrop",
18952     /**
18953      * @cfg {boolean} success
18954      * set this after drop listener.. 
18955      */
18956     success : false,
18957     /**
18958      * @cfg {boolean|String} valid true/false or string (ok-add/ok-sub/ok/nodrop)
18959      * if the drop point is valid for over/enter..
18960      */
18961     valid : false,
18962     // private
18963     isTarget : true,
18964
18965     // private
18966     isNotifyTarget : true,
18967     
18968     /**
18969      * @hide
18970      */
18971     notifyEnter : function(dd, e, data)
18972     {
18973         this.valid = true;
18974         this.fireEvent('enter', dd, e, data);
18975         if(this.overClass){
18976             this.el.addClass(this.overClass);
18977         }
18978         return typeof(this.valid) == 'string' ? 'x-dd-drop-' + this.valid : (
18979             this.valid ? this.dropAllowed : this.dropNotAllowed
18980         );
18981     },
18982
18983     /**
18984      * @hide
18985      */
18986     notifyOver : function(dd, e, data)
18987     {
18988         this.valid = true;
18989         this.fireEvent('over', dd, e, data);
18990         return typeof(this.valid) == 'string' ? 'x-dd-drop-' + this.valid : (
18991             this.valid ? this.dropAllowed : this.dropNotAllowed
18992         );
18993     },
18994
18995     /**
18996      * @hide
18997      */
18998     notifyOut : function(dd, e, data)
18999     {
19000         this.fireEvent('out', dd, e, data);
19001         if(this.overClass){
19002             this.el.removeClass(this.overClass);
19003         }
19004     },
19005
19006     /**
19007      * @hide
19008      */
19009     notifyDrop : function(dd, e, data)
19010     {
19011         this.success = false;
19012         this.fireEvent('drop', dd, e, data);
19013         return this.success;
19014     }
19015 });/*
19016  * Based on:
19017  * Ext JS Library 1.1.1
19018  * Copyright(c) 2006-2007, Ext JS, LLC.
19019  *
19020  * Originally Released Under LGPL - original licence link has changed is not relivant.
19021  *
19022  * Fork - LGPL
19023  * <script type="text/javascript">
19024  */
19025
19026
19027 /**
19028  * @class Roo.dd.DragZone
19029  * @extends Roo.dd.DragSource
19030  * This class provides a container DD instance that proxies for multiple child node sources.<br />
19031  * By default, this class requires that draggable child nodes are registered with {@link Roo.dd.Registry}.
19032  * @constructor
19033  * @param {String/HTMLElement/Element} el The container element
19034  * @param {Object} config
19035  */
19036 Roo.dd.DragZone = function(el, config){
19037     Roo.dd.DragZone.superclass.constructor.call(this, el, config);
19038     if(this.containerScroll){
19039         Roo.dd.ScrollManager.register(this.el);
19040     }
19041 };
19042
19043 Roo.extend(Roo.dd.DragZone, Roo.dd.DragSource, {
19044     /**
19045      * @cfg {Boolean} containerScroll True to register this container with the Scrollmanager
19046      * for auto scrolling during drag operations.
19047      */
19048     /**
19049      * @cfg {String} hlColor The color to use when visually highlighting the drag source in the afterRepair
19050      * method after a failed drop (defaults to "c3daf9" - light blue)
19051      */
19052
19053     /**
19054      * Called when a mousedown occurs in this container. Looks in {@link Roo.dd.Registry}
19055      * for a valid target to drag based on the mouse down. Override this method
19056      * to provide your own lookup logic (e.g. finding a child by class name). Make sure your returned
19057      * object has a "ddel" attribute (with an HTML Element) for other functions to work.
19058      * @param {EventObject} e The mouse down event
19059      * @return {Object} The dragData
19060      */
19061     getDragData : function(e){
19062         return Roo.dd.Registry.getHandleFromEvent(e);
19063     },
19064     
19065     /**
19066      * Called once drag threshold has been reached to initialize the proxy element. By default, it clones the
19067      * this.dragData.ddel
19068      * @param {Number} x The x position of the click on the dragged object
19069      * @param {Number} y The y position of the click on the dragged object
19070      * @return {Boolean} true to continue the drag, false to cancel
19071      */
19072     onInitDrag : function(x, y){
19073         this.proxy.update(this.dragData.ddel.cloneNode(true));
19074         this.onStartDrag(x, y);
19075         return true;
19076     },
19077     
19078     /**
19079      * Called after a repair of an invalid drop. By default, highlights this.dragData.ddel 
19080      */
19081     afterRepair : function(){
19082         if(Roo.enableFx){
19083             Roo.Element.fly(this.dragData.ddel).highlight(this.hlColor || "c3daf9");
19084         }
19085         this.dragging = false;
19086     },
19087
19088     /**
19089      * Called before a repair of an invalid drop to get the XY to animate to. By default returns
19090      * the XY of this.dragData.ddel
19091      * @param {EventObject} e The mouse up event
19092      * @return {Array} The xy location (e.g. [100, 200])
19093      */
19094     getRepairXY : function(e){
19095         return Roo.Element.fly(this.dragData.ddel).getXY();  
19096     }
19097 });/*
19098  * Based on:
19099  * Ext JS Library 1.1.1
19100  * Copyright(c) 2006-2007, Ext JS, LLC.
19101  *
19102  * Originally Released Under LGPL - original licence link has changed is not relivant.
19103  *
19104  * Fork - LGPL
19105  * <script type="text/javascript">
19106  */
19107 /**
19108  * @class Roo.dd.DropZone
19109  * @extends Roo.dd.DropTarget
19110  * This class provides a container DD instance that proxies for multiple child node targets.<br />
19111  * By default, this class requires that child nodes accepting drop are registered with {@link Roo.dd.Registry}.
19112  * @constructor
19113  * @param {String/HTMLElement/Element} el The container element
19114  * @param {Object} config
19115  */
19116 Roo.dd.DropZone = function(el, config){
19117     Roo.dd.DropZone.superclass.constructor.call(this, el, config);
19118 };
19119
19120 Roo.extend(Roo.dd.DropZone, Roo.dd.DropTarget, {
19121     /**
19122      * Returns a custom data object associated with the DOM node that is the target of the event.  By default
19123      * this looks up the event target in the {@link Roo.dd.Registry}, although you can override this method to
19124      * provide your own custom lookup.
19125      * @param {Event} e The event
19126      * @return {Object} data The custom data
19127      */
19128     getTargetFromEvent : function(e){
19129         return Roo.dd.Registry.getTargetFromEvent(e);
19130     },
19131
19132     /**
19133      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has entered a drop node
19134      * that it has registered.  This method has no default implementation and should be overridden to provide
19135      * node-specific processing if necessary.
19136      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from 
19137      * {@link #getTargetFromEvent} for this node)
19138      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
19139      * @param {Event} e The event
19140      * @param {Object} data An object containing arbitrary data supplied by the drag source
19141      */
19142     onNodeEnter : function(n, dd, e, data){
19143         
19144     },
19145
19146     /**
19147      * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is over a drop node
19148      * that it has registered.  The default implementation returns this.dropNotAllowed, so it should be
19149      * overridden to provide the proper feedback.
19150      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
19151      * {@link #getTargetFromEvent} for this node)
19152      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
19153      * @param {Event} e The event
19154      * @param {Object} data An object containing arbitrary data supplied by the drag source
19155      * @return {String} status The CSS class that communicates the drop status back to the source so that the
19156      * underlying {@link Roo.dd.StatusProxy} can be updated
19157      */
19158     onNodeOver : function(n, dd, e, data){
19159         return this.dropAllowed;
19160     },
19161
19162     /**
19163      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dragged out of
19164      * the drop node without dropping.  This method has no default implementation and should be overridden to provide
19165      * node-specific processing if necessary.
19166      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
19167      * {@link #getTargetFromEvent} for this node)
19168      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
19169      * @param {Event} e The event
19170      * @param {Object} data An object containing arbitrary data supplied by the drag source
19171      */
19172     onNodeOut : function(n, dd, e, data){
19173         
19174     },
19175
19176     /**
19177      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped onto
19178      * the drop node.  The default implementation returns false, so it should be overridden to provide the
19179      * appropriate processing of the drop event and return true so that the drag source's repair action does not run.
19180      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
19181      * {@link #getTargetFromEvent} for this node)
19182      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
19183      * @param {Event} e The event
19184      * @param {Object} data An object containing arbitrary data supplied by the drag source
19185      * @return {Boolean} True if the drop was valid, else false
19186      */
19187     onNodeDrop : function(n, dd, e, data){
19188         return false;
19189     },
19190
19191     /**
19192      * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is being dragged over it,
19193      * but not over any of its registered drop nodes.  The default implementation returns this.dropNotAllowed, so
19194      * it should be overridden to provide the proper feedback if necessary.
19195      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
19196      * @param {Event} e The event
19197      * @param {Object} data An object containing arbitrary data supplied by the drag source
19198      * @return {String} status The CSS class that communicates the drop status back to the source so that the
19199      * underlying {@link Roo.dd.StatusProxy} can be updated
19200      */
19201     onContainerOver : function(dd, e, data){
19202         return this.dropNotAllowed;
19203     },
19204
19205     /**
19206      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped on it,
19207      * but not on any of its registered drop nodes.  The default implementation returns false, so it should be
19208      * overridden to provide the appropriate processing of the drop event if you need the drop zone itself to
19209      * be able to accept drops.  It should return true when valid so that the drag source's repair action does not run.
19210      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
19211      * @param {Event} e The event
19212      * @param {Object} data An object containing arbitrary data supplied by the drag source
19213      * @return {Boolean} True if the drop was valid, else false
19214      */
19215     onContainerDrop : function(dd, e, data){
19216         return false;
19217     },
19218
19219     /**
19220      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source is now over
19221      * the zone.  The default implementation returns this.dropNotAllowed and expects that only registered drop
19222      * nodes can process drag drop operations, so if you need the drop zone itself to be able to process drops
19223      * you should override this method and provide a custom implementation.
19224      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
19225      * @param {Event} e The event
19226      * @param {Object} data An object containing arbitrary data supplied by the drag source
19227      * @return {String} status The CSS class that communicates the drop status back to the source so that the
19228      * underlying {@link Roo.dd.StatusProxy} can be updated
19229      */
19230     notifyEnter : function(dd, e, data){
19231         return this.dropNotAllowed;
19232     },
19233
19234     /**
19235      * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the drop zone.
19236      * This method will be called on every mouse movement while the drag source is over the drop zone.
19237      * It will call {@link #onNodeOver} while the drag source is over a registered node, and will also automatically
19238      * delegate to the appropriate node-specific methods as necessary when the drag source enters and exits
19239      * registered nodes ({@link #onNodeEnter}, {@link #onNodeOut}). If the drag source is not currently over a
19240      * registered node, it will call {@link #onContainerOver}.
19241      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
19242      * @param {Event} e The event
19243      * @param {Object} data An object containing arbitrary data supplied by the drag source
19244      * @return {String} status The CSS class that communicates the drop status back to the source so that the
19245      * underlying {@link Roo.dd.StatusProxy} can be updated
19246      */
19247     notifyOver : function(dd, e, data){
19248         var n = this.getTargetFromEvent(e);
19249         if(!n){ // not over valid drop target
19250             if(this.lastOverNode){
19251                 this.onNodeOut(this.lastOverNode, dd, e, data);
19252                 this.lastOverNode = null;
19253             }
19254             return this.onContainerOver(dd, e, data);
19255         }
19256         if(this.lastOverNode != n){
19257             if(this.lastOverNode){
19258                 this.onNodeOut(this.lastOverNode, dd, e, data);
19259             }
19260             this.onNodeEnter(n, dd, e, data);
19261             this.lastOverNode = n;
19262         }
19263         return this.onNodeOver(n, dd, e, data);
19264     },
19265
19266     /**
19267      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source has been dragged
19268      * out of the zone without dropping.  If the drag source is currently over a registered node, the notification
19269      * will be delegated to {@link #onNodeOut} for node-specific handling, otherwise it will be ignored.
19270      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
19271      * @param {Event} e The event
19272      * @param {Object} data An object containing arbitrary data supplied by the drag zone
19273      */
19274     notifyOut : function(dd, e, data){
19275         if(this.lastOverNode){
19276             this.onNodeOut(this.lastOverNode, dd, e, data);
19277             this.lastOverNode = null;
19278         }
19279     },
19280
19281     /**
19282      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the dragged item has
19283      * been dropped on it.  The drag zone will look up the target node based on the event passed in, and if there
19284      * is a node registered for that event, it will delegate to {@link #onNodeDrop} for node-specific handling,
19285      * otherwise it will call {@link #onContainerDrop}.
19286      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
19287      * @param {Event} e The event
19288      * @param {Object} data An object containing arbitrary data supplied by the drag source
19289      * @return {Boolean} True if the drop was valid, else false
19290      */
19291     notifyDrop : function(dd, e, data){
19292         if(this.lastOverNode){
19293             this.onNodeOut(this.lastOverNode, dd, e, data);
19294             this.lastOverNode = null;
19295         }
19296         var n = this.getTargetFromEvent(e);
19297         return n ?
19298             this.onNodeDrop(n, dd, e, data) :
19299             this.onContainerDrop(dd, e, data);
19300     },
19301
19302     // private
19303     triggerCacheRefresh : function(){
19304         Roo.dd.DDM.refreshCache(this.groups);
19305     }  
19306 });/*
19307  * Based on:
19308  * Ext JS Library 1.1.1
19309  * Copyright(c) 2006-2007, Ext JS, LLC.
19310  *
19311  * Originally Released Under LGPL - original licence link has changed is not relivant.
19312  *
19313  * Fork - LGPL
19314  * <script type="text/javascript">
19315  */
19316
19317
19318 /**
19319  * @class Roo.data.SortTypes
19320  * @singleton
19321  * Defines the default sorting (casting?) comparison functions used when sorting data.
19322  */
19323 Roo.data.SortTypes = {
19324     /**
19325      * Default sort that does nothing
19326      * @param {Mixed} s The value being converted
19327      * @return {Mixed} The comparison value
19328      */
19329     none : function(s){
19330         return s;
19331     },
19332     
19333     /**
19334      * The regular expression used to strip tags
19335      * @type {RegExp}
19336      * @property
19337      */
19338     stripTagsRE : /<\/?[^>]+>/gi,
19339     
19340     /**
19341      * Strips all HTML tags to sort on text only
19342      * @param {Mixed} s The value being converted
19343      * @return {String} The comparison value
19344      */
19345     asText : function(s){
19346         return String(s).replace(this.stripTagsRE, "");
19347     },
19348     
19349     /**
19350      * Strips all HTML tags to sort on text only - Case insensitive
19351      * @param {Mixed} s The value being converted
19352      * @return {String} The comparison value
19353      */
19354     asUCText : function(s){
19355         return String(s).toUpperCase().replace(this.stripTagsRE, "");
19356     },
19357     
19358     /**
19359      * Case insensitive string
19360      * @param {Mixed} s The value being converted
19361      * @return {String} The comparison value
19362      */
19363     asUCString : function(s) {
19364         return String(s).toUpperCase();
19365     },
19366     
19367     /**
19368      * Date sorting
19369      * @param {Mixed} s The value being converted
19370      * @return {Number} The comparison value
19371      */
19372     asDate : function(s) {
19373         if(!s){
19374             return 0;
19375         }
19376         if(s instanceof Date){
19377             return s.getTime();
19378         }
19379         return Date.parse(String(s));
19380     },
19381     
19382     /**
19383      * Float sorting
19384      * @param {Mixed} s The value being converted
19385      * @return {Float} The comparison value
19386      */
19387     asFloat : function(s) {
19388         var val = parseFloat(String(s).replace(/,/g, ""));
19389         if(isNaN(val)) val = 0;
19390         return val;
19391     },
19392     
19393     /**
19394      * Integer sorting
19395      * @param {Mixed} s The value being converted
19396      * @return {Number} The comparison value
19397      */
19398     asInt : function(s) {
19399         var val = parseInt(String(s).replace(/,/g, ""));
19400         if(isNaN(val)) val = 0;
19401         return val;
19402     }
19403 };/*
19404  * Based on:
19405  * Ext JS Library 1.1.1
19406  * Copyright(c) 2006-2007, Ext JS, LLC.
19407  *
19408  * Originally Released Under LGPL - original licence link has changed is not relivant.
19409  *
19410  * Fork - LGPL
19411  * <script type="text/javascript">
19412  */
19413
19414 /**
19415 * @class Roo.data.Record
19416  * Instances of this class encapsulate both record <em>definition</em> information, and record
19417  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
19418  * to access Records cached in an {@link Roo.data.Store} object.<br>
19419  * <p>
19420  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
19421  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
19422  * objects.<br>
19423  * <p>
19424  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
19425  * @constructor
19426  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
19427  * {@link #create}. The parameters are the same.
19428  * @param {Array} data An associative Array of data values keyed by the field name.
19429  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
19430  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
19431  * not specified an integer id is generated.
19432  */
19433 Roo.data.Record = function(data, id){
19434     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
19435     this.data = data;
19436 };
19437
19438 /**
19439  * Generate a constructor for a specific record layout.
19440  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
19441  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
19442  * Each field definition object may contain the following properties: <ul>
19443  * <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,
19444  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
19445  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
19446  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
19447  * is being used, then this is a string containing the javascript expression to reference the data relative to 
19448  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
19449  * to the data item relative to the record element. If the mapping expression is the same as the field name,
19450  * this may be omitted.</p></li>
19451  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
19452  * <ul><li>auto (Default, implies no conversion)</li>
19453  * <li>string</li>
19454  * <li>int</li>
19455  * <li>float</li>
19456  * <li>boolean</li>
19457  * <li>date</li></ul></p></li>
19458  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
19459  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
19460  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
19461  * by the Reader into an object that will be stored in the Record. It is passed the
19462  * following parameters:<ul>
19463  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
19464  * </ul></p></li>
19465  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
19466  * </ul>
19467  * <br>usage:<br><pre><code>
19468 var TopicRecord = Roo.data.Record.create(
19469     {name: 'title', mapping: 'topic_title'},
19470     {name: 'author', mapping: 'username'},
19471     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
19472     {name: 'lastPost', mapping: 'post_time', type: 'date'},
19473     {name: 'lastPoster', mapping: 'user2'},
19474     {name: 'excerpt', mapping: 'post_text'}
19475 );
19476
19477 var myNewRecord = new TopicRecord({
19478     title: 'Do my job please',
19479     author: 'noobie',
19480     totalPosts: 1,
19481     lastPost: new Date(),
19482     lastPoster: 'Animal',
19483     excerpt: 'No way dude!'
19484 });
19485 myStore.add(myNewRecord);
19486 </code></pre>
19487  * @method create
19488  * @static
19489  */
19490 Roo.data.Record.create = function(o){
19491     var f = function(){
19492         f.superclass.constructor.apply(this, arguments);
19493     };
19494     Roo.extend(f, Roo.data.Record);
19495     var p = f.prototype;
19496     p.fields = new Roo.util.MixedCollection(false, function(field){
19497         return field.name;
19498     });
19499     for(var i = 0, len = o.length; i < len; i++){
19500         p.fields.add(new Roo.data.Field(o[i]));
19501     }
19502     f.getField = function(name){
19503         return p.fields.get(name);  
19504     };
19505     return f;
19506 };
19507
19508 Roo.data.Record.AUTO_ID = 1000;
19509 Roo.data.Record.EDIT = 'edit';
19510 Roo.data.Record.REJECT = 'reject';
19511 Roo.data.Record.COMMIT = 'commit';
19512
19513 Roo.data.Record.prototype = {
19514     /**
19515      * Readonly flag - true if this record has been modified.
19516      * @type Boolean
19517      */
19518     dirty : false,
19519     editing : false,
19520     error: null,
19521     modified: null,
19522
19523     // private
19524     join : function(store){
19525         this.store = store;
19526     },
19527
19528     /**
19529      * Set the named field to the specified value.
19530      * @param {String} name The name of the field to set.
19531      * @param {Object} value The value to set the field to.
19532      */
19533     set : function(name, value){
19534         if(this.data[name] == value){
19535             return;
19536         }
19537         this.dirty = true;
19538         if(!this.modified){
19539             this.modified = {};
19540         }
19541         if(typeof this.modified[name] == 'undefined'){
19542             this.modified[name] = this.data[name];
19543         }
19544         this.data[name] = value;
19545         if(!this.editing && this.store){
19546             this.store.afterEdit(this);
19547         }       
19548     },
19549
19550     /**
19551      * Get the value of the named field.
19552      * @param {String} name The name of the field to get the value of.
19553      * @return {Object} The value of the field.
19554      */
19555     get : function(name){
19556         return this.data[name]; 
19557     },
19558
19559     // private
19560     beginEdit : function(){
19561         this.editing = true;
19562         this.modified = {}; 
19563     },
19564
19565     // private
19566     cancelEdit : function(){
19567         this.editing = false;
19568         delete this.modified;
19569     },
19570
19571     // private
19572     endEdit : function(){
19573         this.editing = false;
19574         if(this.dirty && this.store){
19575             this.store.afterEdit(this);
19576         }
19577     },
19578
19579     /**
19580      * Usually called by the {@link Roo.data.Store} which owns the Record.
19581      * Rejects all changes made to the Record since either creation, or the last commit operation.
19582      * Modified fields are reverted to their original values.
19583      * <p>
19584      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
19585      * of reject operations.
19586      */
19587     reject : function(){
19588         var m = this.modified;
19589         for(var n in m){
19590             if(typeof m[n] != "function"){
19591                 this.data[n] = m[n];
19592             }
19593         }
19594         this.dirty = false;
19595         delete this.modified;
19596         this.editing = false;
19597         if(this.store){
19598             this.store.afterReject(this);
19599         }
19600     },
19601
19602     /**
19603      * Usually called by the {@link Roo.data.Store} which owns the Record.
19604      * Commits all changes made to the Record since either creation, or the last commit operation.
19605      * <p>
19606      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
19607      * of commit operations.
19608      */
19609     commit : function(){
19610         this.dirty = false;
19611         delete this.modified;
19612         this.editing = false;
19613         if(this.store){
19614             this.store.afterCommit(this);
19615         }
19616     },
19617
19618     // private
19619     hasError : function(){
19620         return this.error != null;
19621     },
19622
19623     // private
19624     clearError : function(){
19625         this.error = null;
19626     },
19627
19628     /**
19629      * Creates a copy of this record.
19630      * @param {String} id (optional) A new record id if you don't want to use this record's id
19631      * @return {Record}
19632      */
19633     copy : function(newId) {
19634         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
19635     }
19636 };/*
19637  * Based on:
19638  * Ext JS Library 1.1.1
19639  * Copyright(c) 2006-2007, Ext JS, LLC.
19640  *
19641  * Originally Released Under LGPL - original licence link has changed is not relivant.
19642  *
19643  * Fork - LGPL
19644  * <script type="text/javascript">
19645  */
19646
19647
19648
19649 /**
19650  * @class Roo.data.Store
19651  * @extends Roo.util.Observable
19652  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
19653  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
19654  * <p>
19655  * 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
19656  * has no knowledge of the format of the data returned by the Proxy.<br>
19657  * <p>
19658  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
19659  * instances from the data object. These records are cached and made available through accessor functions.
19660  * @constructor
19661  * Creates a new Store.
19662  * @param {Object} config A config object containing the objects needed for the Store to access data,
19663  * and read the data into Records.
19664  */
19665 Roo.data.Store = function(config){
19666     this.data = new Roo.util.MixedCollection(false);
19667     this.data.getKey = function(o){
19668         return o.id;
19669     };
19670     this.baseParams = {};
19671     // private
19672     this.paramNames = {
19673         "start" : "start",
19674         "limit" : "limit",
19675         "sort" : "sort",
19676         "dir" : "dir",
19677         "multisort" : "_multisort"
19678     };
19679
19680     if(config && config.data){
19681         this.inlineData = config.data;
19682         delete config.data;
19683     }
19684
19685     Roo.apply(this, config);
19686     
19687     if(this.reader){ // reader passed
19688         this.reader = Roo.factory(this.reader, Roo.data);
19689         this.reader.xmodule = this.xmodule || false;
19690         if(!this.recordType){
19691             this.recordType = this.reader.recordType;
19692         }
19693         if(this.reader.onMetaChange){
19694             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
19695         }
19696     }
19697
19698     if(this.recordType){
19699         this.fields = this.recordType.prototype.fields;
19700     }
19701     this.modified = [];
19702
19703     this.addEvents({
19704         /**
19705          * @event datachanged
19706          * Fires when the data cache has changed, and a widget which is using this Store
19707          * as a Record cache should refresh its view.
19708          * @param {Store} this
19709          */
19710         datachanged : true,
19711         /**
19712          * @event metachange
19713          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
19714          * @param {Store} this
19715          * @param {Object} meta The JSON metadata
19716          */
19717         metachange : true,
19718         /**
19719          * @event add
19720          * Fires when Records have been added to the Store
19721          * @param {Store} this
19722          * @param {Roo.data.Record[]} records The array of Records added
19723          * @param {Number} index The index at which the record(s) were added
19724          */
19725         add : true,
19726         /**
19727          * @event remove
19728          * Fires when a Record has been removed from the Store
19729          * @param {Store} this
19730          * @param {Roo.data.Record} record The Record that was removed
19731          * @param {Number} index The index at which the record was removed
19732          */
19733         remove : true,
19734         /**
19735          * @event update
19736          * Fires when a Record has been updated
19737          * @param {Store} this
19738          * @param {Roo.data.Record} record The Record that was updated
19739          * @param {String} operation The update operation being performed.  Value may be one of:
19740          * <pre><code>
19741  Roo.data.Record.EDIT
19742  Roo.data.Record.REJECT
19743  Roo.data.Record.COMMIT
19744          * </code></pre>
19745          */
19746         update : true,
19747         /**
19748          * @event clear
19749          * Fires when the data cache has been cleared.
19750          * @param {Store} this
19751          */
19752         clear : true,
19753         /**
19754          * @event beforeload
19755          * Fires before a request is made for a new data object.  If the beforeload handler returns false
19756          * the load action will be canceled.
19757          * @param {Store} this
19758          * @param {Object} options The loading options that were specified (see {@link #load} for details)
19759          */
19760         beforeload : true,
19761         /**
19762          * @event beforeloadadd
19763          * Fires after a new set of Records has been loaded.
19764          * @param {Store} this
19765          * @param {Roo.data.Record[]} records The Records that were loaded
19766          * @param {Object} options The loading options that were specified (see {@link #load} for details)
19767          */
19768         beforeloadadd : true,
19769         /**
19770          * @event load
19771          * Fires after a new set of Records has been loaded, before they are added to the store.
19772          * @param {Store} this
19773          * @param {Roo.data.Record[]} records The Records that were loaded
19774          * @param {Object} options The loading options that were specified (see {@link #load} for details)
19775          * @params {Object} return from reader
19776          */
19777         load : true,
19778         /**
19779          * @event loadexception
19780          * Fires if an exception occurs in the Proxy during loading.
19781          * Called with the signature of the Proxy's "loadexception" event.
19782          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
19783          * 
19784          * @param {Proxy} 
19785          * @param {Object} return from JsonData.reader() - success, totalRecords, records
19786          * @param {Object} load options 
19787          * @param {Object} jsonData from your request (normally this contains the Exception)
19788          */
19789         loadexception : true
19790     });
19791     
19792     if(this.proxy){
19793         this.proxy = Roo.factory(this.proxy, Roo.data);
19794         this.proxy.xmodule = this.xmodule || false;
19795         this.relayEvents(this.proxy,  ["loadexception"]);
19796     }
19797     this.sortToggle = {};
19798     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
19799
19800     Roo.data.Store.superclass.constructor.call(this);
19801
19802     if(this.inlineData){
19803         this.loadData(this.inlineData);
19804         delete this.inlineData;
19805     }
19806 };
19807
19808 Roo.extend(Roo.data.Store, Roo.util.Observable, {
19809      /**
19810     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
19811     * without a remote query - used by combo/forms at present.
19812     */
19813     
19814     /**
19815     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
19816     */
19817     /**
19818     * @cfg {Array} data Inline data to be loaded when the store is initialized.
19819     */
19820     /**
19821     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
19822     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
19823     */
19824     /**
19825     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
19826     * on any HTTP request
19827     */
19828     /**
19829     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
19830     */
19831     /**
19832     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
19833     */
19834     multiSort: false,
19835     /**
19836     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
19837     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
19838     */
19839     remoteSort : false,
19840
19841     /**
19842     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
19843      * loaded or when a record is removed. (defaults to false).
19844     */
19845     pruneModifiedRecords : false,
19846
19847     // private
19848     lastOptions : null,
19849
19850     /**
19851      * Add Records to the Store and fires the add event.
19852      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
19853      */
19854     add : function(records){
19855         records = [].concat(records);
19856         for(var i = 0, len = records.length; i < len; i++){
19857             records[i].join(this);
19858         }
19859         var index = this.data.length;
19860         this.data.addAll(records);
19861         this.fireEvent("add", this, records, index);
19862     },
19863
19864     /**
19865      * Remove a Record from the Store and fires the remove event.
19866      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
19867      */
19868     remove : function(record){
19869         var index = this.data.indexOf(record);
19870         this.data.removeAt(index);
19871         if(this.pruneModifiedRecords){
19872             this.modified.remove(record);
19873         }
19874         this.fireEvent("remove", this, record, index);
19875     },
19876
19877     /**
19878      * Remove all Records from the Store and fires the clear event.
19879      */
19880     removeAll : function(){
19881         this.data.clear();
19882         if(this.pruneModifiedRecords){
19883             this.modified = [];
19884         }
19885         this.fireEvent("clear", this);
19886     },
19887
19888     /**
19889      * Inserts Records to the Store at the given index and fires the add event.
19890      * @param {Number} index The start index at which to insert the passed Records.
19891      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
19892      */
19893     insert : function(index, records){
19894         records = [].concat(records);
19895         for(var i = 0, len = records.length; i < len; i++){
19896             this.data.insert(index, records[i]);
19897             records[i].join(this);
19898         }
19899         this.fireEvent("add", this, records, index);
19900     },
19901
19902     /**
19903      * Get the index within the cache of the passed Record.
19904      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
19905      * @return {Number} The index of the passed Record. Returns -1 if not found.
19906      */
19907     indexOf : function(record){
19908         return this.data.indexOf(record);
19909     },
19910
19911     /**
19912      * Get the index within the cache of the Record with the passed id.
19913      * @param {String} id The id of the Record to find.
19914      * @return {Number} The index of the Record. Returns -1 if not found.
19915      */
19916     indexOfId : function(id){
19917         return this.data.indexOfKey(id);
19918     },
19919
19920     /**
19921      * Get the Record with the specified id.
19922      * @param {String} id The id of the Record to find.
19923      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
19924      */
19925     getById : function(id){
19926         return this.data.key(id);
19927     },
19928
19929     /**
19930      * Get the Record at the specified index.
19931      * @param {Number} index The index of the Record to find.
19932      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
19933      */
19934     getAt : function(index){
19935         return this.data.itemAt(index);
19936     },
19937
19938     /**
19939      * Returns a range of Records between specified indices.
19940      * @param {Number} startIndex (optional) The starting index (defaults to 0)
19941      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
19942      * @return {Roo.data.Record[]} An array of Records
19943      */
19944     getRange : function(start, end){
19945         return this.data.getRange(start, end);
19946     },
19947
19948     // private
19949     storeOptions : function(o){
19950         o = Roo.apply({}, o);
19951         delete o.callback;
19952         delete o.scope;
19953         this.lastOptions = o;
19954     },
19955
19956     /**
19957      * Loads the Record cache from the configured Proxy using the configured Reader.
19958      * <p>
19959      * If using remote paging, then the first load call must specify the <em>start</em>
19960      * and <em>limit</em> properties in the options.params property to establish the initial
19961      * position within the dataset, and the number of Records to cache on each read from the Proxy.
19962      * <p>
19963      * <strong>It is important to note that for remote data sources, loading is asynchronous,
19964      * and this call will return before the new data has been loaded. Perform any post-processing
19965      * in a callback function, or in a "load" event handler.</strong>
19966      * <p>
19967      * @param {Object} options An object containing properties which control loading options:<ul>
19968      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
19969      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
19970      * passed the following arguments:<ul>
19971      * <li>r : Roo.data.Record[]</li>
19972      * <li>options: Options object from the load call</li>
19973      * <li>success: Boolean success indicator</li></ul></li>
19974      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
19975      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
19976      * </ul>
19977      */
19978     load : function(options){
19979         options = options || {};
19980         if(this.fireEvent("beforeload", this, options) !== false){
19981             this.storeOptions(options);
19982             var p = Roo.apply(options.params || {}, this.baseParams);
19983             // if meta was not loaded from remote source.. try requesting it.
19984             if (!this.reader.metaFromRemote) {
19985                 p._requestMeta = 1;
19986             }
19987             if(this.sortInfo && this.remoteSort){
19988                 var pn = this.paramNames;
19989                 p[pn["sort"]] = this.sortInfo.field;
19990                 p[pn["dir"]] = this.sortInfo.direction;
19991             }
19992             if (this.multiSort) {
19993                 var pn = this.paramNames;
19994                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
19995             }
19996             
19997             this.proxy.load(p, this.reader, this.loadRecords, this, options);
19998         }
19999     },
20000
20001     /**
20002      * Reloads the Record cache from the configured Proxy using the configured Reader and
20003      * the options from the last load operation performed.
20004      * @param {Object} options (optional) An object containing properties which may override the options
20005      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
20006      * the most recently used options are reused).
20007      */
20008     reload : function(options){
20009         this.load(Roo.applyIf(options||{}, this.lastOptions));
20010     },
20011
20012     // private
20013     // Called as a callback by the Reader during a load operation.
20014     loadRecords : function(o, options, success){
20015         if(!o || success === false){
20016             if(success !== false){
20017                 this.fireEvent("load", this, [], options, o);
20018             }
20019             if(options.callback){
20020                 options.callback.call(options.scope || this, [], options, false);
20021             }
20022             return;
20023         }
20024         // if data returned failure - throw an exception.
20025         if (o.success === false) {
20026             // show a message if no listener is registered.
20027             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
20028                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
20029             }
20030             // loadmask wil be hooked into this..
20031             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
20032             return;
20033         }
20034         var r = o.records, t = o.totalRecords || r.length;
20035         
20036         this.fireEvent("beforeloadadd", this, r, options, o);
20037         
20038         if(!options || options.add !== true){
20039             if(this.pruneModifiedRecords){
20040                 this.modified = [];
20041             }
20042             for(var i = 0, len = r.length; i < len; i++){
20043                 r[i].join(this);
20044             }
20045             if(this.snapshot){
20046                 this.data = this.snapshot;
20047                 delete this.snapshot;
20048             }
20049             this.data.clear();
20050             this.data.addAll(r);
20051             this.totalLength = t;
20052             this.applySort();
20053             this.fireEvent("datachanged", this);
20054         }else{
20055             this.totalLength = Math.max(t, this.data.length+r.length);
20056             this.add(r);
20057         }
20058         this.fireEvent("load", this, r, options, o);
20059         if(options.callback){
20060             options.callback.call(options.scope || this, r, options, true);
20061         }
20062     },
20063
20064
20065     /**
20066      * Loads data from a passed data block. A Reader which understands the format of the data
20067      * must have been configured in the constructor.
20068      * @param {Object} data The data block from which to read the Records.  The format of the data expected
20069      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
20070      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
20071      */
20072     loadData : function(o, append){
20073         var r = this.reader.readRecords(o);
20074         this.loadRecords(r, {add: append}, true);
20075     },
20076
20077     /**
20078      * Gets the number of cached records.
20079      * <p>
20080      * <em>If using paging, this may not be the total size of the dataset. If the data object
20081      * used by the Reader contains the dataset size, then the getTotalCount() function returns
20082      * the data set size</em>
20083      */
20084     getCount : function(){
20085         return this.data.length || 0;
20086     },
20087
20088     /**
20089      * Gets the total number of records in the dataset as returned by the server.
20090      * <p>
20091      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
20092      * the dataset size</em>
20093      */
20094     getTotalCount : function(){
20095         return this.totalLength || 0;
20096     },
20097
20098     /**
20099      * Returns the sort state of the Store as an object with two properties:
20100      * <pre><code>
20101  field {String} The name of the field by which the Records are sorted
20102  direction {String} The sort order, "ASC" or "DESC"
20103      * </code></pre>
20104      */
20105     getSortState : function(){
20106         return this.sortInfo;
20107     },
20108
20109     // private
20110     applySort : function(){
20111         if(this.sortInfo && !this.remoteSort){
20112             var s = this.sortInfo, f = s.field;
20113             var st = this.fields.get(f).sortType;
20114             var fn = function(r1, r2){
20115                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
20116                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
20117             };
20118             this.data.sort(s.direction, fn);
20119             if(this.snapshot && this.snapshot != this.data){
20120                 this.snapshot.sort(s.direction, fn);
20121             }
20122         }
20123     },
20124
20125     /**
20126      * Sets the default sort column and order to be used by the next load operation.
20127      * @param {String} fieldName The name of the field to sort by.
20128      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
20129      */
20130     setDefaultSort : function(field, dir){
20131         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
20132     },
20133
20134     /**
20135      * Sort the Records.
20136      * If remote sorting is used, the sort is performed on the server, and the cache is
20137      * reloaded. If local sorting is used, the cache is sorted internally.
20138      * @param {String} fieldName The name of the field to sort by.
20139      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
20140      */
20141     sort : function(fieldName, dir){
20142         var f = this.fields.get(fieldName);
20143         if(!dir){
20144             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
20145             
20146             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
20147                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
20148             }else{
20149                 dir = f.sortDir;
20150             }
20151         }
20152         this.sortToggle[f.name] = dir;
20153         this.sortInfo = {field: f.name, direction: dir};
20154         if(!this.remoteSort){
20155             this.applySort();
20156             this.fireEvent("datachanged", this);
20157         }else{
20158             this.load(this.lastOptions);
20159         }
20160     },
20161
20162     /**
20163      * Calls the specified function for each of the Records in the cache.
20164      * @param {Function} fn The function to call. The Record is passed as the first parameter.
20165      * Returning <em>false</em> aborts and exits the iteration.
20166      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
20167      */
20168     each : function(fn, scope){
20169         this.data.each(fn, scope);
20170     },
20171
20172     /**
20173      * Gets all records modified since the last commit.  Modified records are persisted across load operations
20174      * (e.g., during paging).
20175      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
20176      */
20177     getModifiedRecords : function(){
20178         return this.modified;
20179     },
20180
20181     // private
20182     createFilterFn : function(property, value, anyMatch){
20183         if(!value.exec){ // not a regex
20184             value = String(value);
20185             if(value.length == 0){
20186                 return false;
20187             }
20188             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
20189         }
20190         return function(r){
20191             return value.test(r.data[property]);
20192         };
20193     },
20194
20195     /**
20196      * Sums the value of <i>property</i> for each record between start and end and returns the result.
20197      * @param {String} property A field on your records
20198      * @param {Number} start The record index to start at (defaults to 0)
20199      * @param {Number} end The last record index to include (defaults to length - 1)
20200      * @return {Number} The sum
20201      */
20202     sum : function(property, start, end){
20203         var rs = this.data.items, v = 0;
20204         start = start || 0;
20205         end = (end || end === 0) ? end : rs.length-1;
20206
20207         for(var i = start; i <= end; i++){
20208             v += (rs[i].data[property] || 0);
20209         }
20210         return v;
20211     },
20212
20213     /**
20214      * Filter the records by a specified property.
20215      * @param {String} field A field on your records
20216      * @param {String/RegExp} value Either a string that the field
20217      * should start with or a RegExp to test against the field
20218      * @param {Boolean} anyMatch True to match any part not just the beginning
20219      */
20220     filter : function(property, value, anyMatch){
20221         var fn = this.createFilterFn(property, value, anyMatch);
20222         return fn ? this.filterBy(fn) : this.clearFilter();
20223     },
20224
20225     /**
20226      * Filter by a function. The specified function will be called with each
20227      * record in this data source. If the function returns true the record is included,
20228      * otherwise it is filtered.
20229      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
20230      * @param {Object} scope (optional) The scope of the function (defaults to this)
20231      */
20232     filterBy : function(fn, scope){
20233         this.snapshot = this.snapshot || this.data;
20234         this.data = this.queryBy(fn, scope||this);
20235         this.fireEvent("datachanged", this);
20236     },
20237
20238     /**
20239      * Query the records by a specified property.
20240      * @param {String} field A field on your records
20241      * @param {String/RegExp} value Either a string that the field
20242      * should start with or a RegExp to test against the field
20243      * @param {Boolean} anyMatch True to match any part not just the beginning
20244      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
20245      */
20246     query : function(property, value, anyMatch){
20247         var fn = this.createFilterFn(property, value, anyMatch);
20248         return fn ? this.queryBy(fn) : this.data.clone();
20249     },
20250
20251     /**
20252      * Query by a function. The specified function will be called with each
20253      * record in this data source. If the function returns true the record is included
20254      * in the results.
20255      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
20256      * @param {Object} scope (optional) The scope of the function (defaults to this)
20257       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
20258      **/
20259     queryBy : function(fn, scope){
20260         var data = this.snapshot || this.data;
20261         return data.filterBy(fn, scope||this);
20262     },
20263
20264     /**
20265      * Collects unique values for a particular dataIndex from this store.
20266      * @param {String} dataIndex The property to collect
20267      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
20268      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
20269      * @return {Array} An array of the unique values
20270      **/
20271     collect : function(dataIndex, allowNull, bypassFilter){
20272         var d = (bypassFilter === true && this.snapshot) ?
20273                 this.snapshot.items : this.data.items;
20274         var v, sv, r = [], l = {};
20275         for(var i = 0, len = d.length; i < len; i++){
20276             v = d[i].data[dataIndex];
20277             sv = String(v);
20278             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
20279                 l[sv] = true;
20280                 r[r.length] = v;
20281             }
20282         }
20283         return r;
20284     },
20285
20286     /**
20287      * Revert to a view of the Record cache with no filtering applied.
20288      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
20289      */
20290     clearFilter : function(suppressEvent){
20291         if(this.snapshot && this.snapshot != this.data){
20292             this.data = this.snapshot;
20293             delete this.snapshot;
20294             if(suppressEvent !== true){
20295                 this.fireEvent("datachanged", this);
20296             }
20297         }
20298     },
20299
20300     // private
20301     afterEdit : function(record){
20302         if(this.modified.indexOf(record) == -1){
20303             this.modified.push(record);
20304         }
20305         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
20306     },
20307     
20308     // private
20309     afterReject : function(record){
20310         this.modified.remove(record);
20311         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
20312     },
20313
20314     // private
20315     afterCommit : function(record){
20316         this.modified.remove(record);
20317         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
20318     },
20319
20320     /**
20321      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
20322      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
20323      */
20324     commitChanges : function(){
20325         var m = this.modified.slice(0);
20326         this.modified = [];
20327         for(var i = 0, len = m.length; i < len; i++){
20328             m[i].commit();
20329         }
20330     },
20331
20332     /**
20333      * Cancel outstanding changes on all changed records.
20334      */
20335     rejectChanges : function(){
20336         var m = this.modified.slice(0);
20337         this.modified = [];
20338         for(var i = 0, len = m.length; i < len; i++){
20339             m[i].reject();
20340         }
20341     },
20342
20343     onMetaChange : function(meta, rtype, o){
20344         this.recordType = rtype;
20345         this.fields = rtype.prototype.fields;
20346         delete this.snapshot;
20347         this.sortInfo = meta.sortInfo || this.sortInfo;
20348         this.modified = [];
20349         this.fireEvent('metachange', this, this.reader.meta);
20350     }
20351 });/*
20352  * Based on:
20353  * Ext JS Library 1.1.1
20354  * Copyright(c) 2006-2007, Ext JS, LLC.
20355  *
20356  * Originally Released Under LGPL - original licence link has changed is not relivant.
20357  *
20358  * Fork - LGPL
20359  * <script type="text/javascript">
20360  */
20361
20362 /**
20363  * @class Roo.data.SimpleStore
20364  * @extends Roo.data.Store
20365  * Small helper class to make creating Stores from Array data easier.
20366  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
20367  * @cfg {Array} fields An array of field definition objects, or field name strings.
20368  * @cfg {Array} data The multi-dimensional array of data
20369  * @constructor
20370  * @param {Object} config
20371  */
20372 Roo.data.SimpleStore = function(config){
20373     Roo.data.SimpleStore.superclass.constructor.call(this, {
20374         isLocal : true,
20375         reader: new Roo.data.ArrayReader({
20376                 id: config.id
20377             },
20378             Roo.data.Record.create(config.fields)
20379         ),
20380         proxy : new Roo.data.MemoryProxy(config.data)
20381     });
20382     this.load();
20383 };
20384 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
20385  * Based on:
20386  * Ext JS Library 1.1.1
20387  * Copyright(c) 2006-2007, Ext JS, LLC.
20388  *
20389  * Originally Released Under LGPL - original licence link has changed is not relivant.
20390  *
20391  * Fork - LGPL
20392  * <script type="text/javascript">
20393  */
20394
20395 /**
20396 /**
20397  * @extends Roo.data.Store
20398  * @class Roo.data.JsonStore
20399  * Small helper class to make creating Stores for JSON data easier. <br/>
20400 <pre><code>
20401 var store = new Roo.data.JsonStore({
20402     url: 'get-images.php',
20403     root: 'images',
20404     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
20405 });
20406 </code></pre>
20407  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
20408  * JsonReader and HttpProxy (unless inline data is provided).</b>
20409  * @cfg {Array} fields An array of field definition objects, or field name strings.
20410  * @constructor
20411  * @param {Object} config
20412  */
20413 Roo.data.JsonStore = function(c){
20414     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
20415         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
20416         reader: new Roo.data.JsonReader(c, c.fields)
20417     }));
20418 };
20419 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
20420  * Based on:
20421  * Ext JS Library 1.1.1
20422  * Copyright(c) 2006-2007, Ext JS, LLC.
20423  *
20424  * Originally Released Under LGPL - original licence link has changed is not relivant.
20425  *
20426  * Fork - LGPL
20427  * <script type="text/javascript">
20428  */
20429
20430  
20431 Roo.data.Field = function(config){
20432     if(typeof config == "string"){
20433         config = {name: config};
20434     }
20435     Roo.apply(this, config);
20436     
20437     if(!this.type){
20438         this.type = "auto";
20439     }
20440     
20441     var st = Roo.data.SortTypes;
20442     // named sortTypes are supported, here we look them up
20443     if(typeof this.sortType == "string"){
20444         this.sortType = st[this.sortType];
20445     }
20446     
20447     // set default sortType for strings and dates
20448     if(!this.sortType){
20449         switch(this.type){
20450             case "string":
20451                 this.sortType = st.asUCString;
20452                 break;
20453             case "date":
20454                 this.sortType = st.asDate;
20455                 break;
20456             default:
20457                 this.sortType = st.none;
20458         }
20459     }
20460
20461     // define once
20462     var stripRe = /[\$,%]/g;
20463
20464     // prebuilt conversion function for this field, instead of
20465     // switching every time we're reading a value
20466     if(!this.convert){
20467         var cv, dateFormat = this.dateFormat;
20468         switch(this.type){
20469             case "":
20470             case "auto":
20471             case undefined:
20472                 cv = function(v){ return v; };
20473                 break;
20474             case "string":
20475                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
20476                 break;
20477             case "int":
20478                 cv = function(v){
20479                     return v !== undefined && v !== null && v !== '' ?
20480                            parseInt(String(v).replace(stripRe, ""), 10) : '';
20481                     };
20482                 break;
20483             case "float":
20484                 cv = function(v){
20485                     return v !== undefined && v !== null && v !== '' ?
20486                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
20487                     };
20488                 break;
20489             case "bool":
20490             case "boolean":
20491                 cv = function(v){ return v === true || v === "true" || v == 1; };
20492                 break;
20493             case "date":
20494                 cv = function(v){
20495                     if(!v){
20496                         return '';
20497                     }
20498                     if(v instanceof Date){
20499                         return v;
20500                     }
20501                     if(dateFormat){
20502                         if(dateFormat == "timestamp"){
20503                             return new Date(v*1000);
20504                         }
20505                         return Date.parseDate(v, dateFormat);
20506                     }
20507                     var parsed = Date.parse(v);
20508                     return parsed ? new Date(parsed) : null;
20509                 };
20510              break;
20511             
20512         }
20513         this.convert = cv;
20514     }
20515 };
20516
20517 Roo.data.Field.prototype = {
20518     dateFormat: null,
20519     defaultValue: "",
20520     mapping: null,
20521     sortType : null,
20522     sortDir : "ASC"
20523 };/*
20524  * Based on:
20525  * Ext JS Library 1.1.1
20526  * Copyright(c) 2006-2007, Ext JS, LLC.
20527  *
20528  * Originally Released Under LGPL - original licence link has changed is not relivant.
20529  *
20530  * Fork - LGPL
20531  * <script type="text/javascript">
20532  */
20533  
20534 // Base class for reading structured data from a data source.  This class is intended to be
20535 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
20536
20537 /**
20538  * @class Roo.data.DataReader
20539  * Base class for reading structured data from a data source.  This class is intended to be
20540  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
20541  */
20542
20543 Roo.data.DataReader = function(meta, recordType){
20544     
20545     this.meta = meta;
20546     
20547     this.recordType = recordType instanceof Array ? 
20548         Roo.data.Record.create(recordType) : recordType;
20549 };
20550
20551 Roo.data.DataReader.prototype = {
20552      /**
20553      * Create an empty record
20554      * @param {Object} data (optional) - overlay some values
20555      * @return {Roo.data.Record} record created.
20556      */
20557     newRow :  function(d) {
20558         var da =  {};
20559         this.recordType.prototype.fields.each(function(c) {
20560             switch( c.type) {
20561                 case 'int' : da[c.name] = 0; break;
20562                 case 'date' : da[c.name] = new Date(); break;
20563                 case 'float' : da[c.name] = 0.0; break;
20564                 case 'boolean' : da[c.name] = false; break;
20565                 default : da[c.name] = ""; break;
20566             }
20567             
20568         });
20569         return new this.recordType(Roo.apply(da, d));
20570     }
20571     
20572 };/*
20573  * Based on:
20574  * Ext JS Library 1.1.1
20575  * Copyright(c) 2006-2007, Ext JS, LLC.
20576  *
20577  * Originally Released Under LGPL - original licence link has changed is not relivant.
20578  *
20579  * Fork - LGPL
20580  * <script type="text/javascript">
20581  */
20582
20583 /**
20584  * @class Roo.data.DataProxy
20585  * @extends Roo.data.Observable
20586  * This class is an abstract base class for implementations which provide retrieval of
20587  * unformatted data objects.<br>
20588  * <p>
20589  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
20590  * (of the appropriate type which knows how to parse the data object) to provide a block of
20591  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
20592  * <p>
20593  * Custom implementations must implement the load method as described in
20594  * {@link Roo.data.HttpProxy#load}.
20595  */
20596 Roo.data.DataProxy = function(){
20597     this.addEvents({
20598         /**
20599          * @event beforeload
20600          * Fires before a network request is made to retrieve a data object.
20601          * @param {Object} This DataProxy object.
20602          * @param {Object} params The params parameter to the load function.
20603          */
20604         beforeload : true,
20605         /**
20606          * @event load
20607          * Fires before the load method's callback is called.
20608          * @param {Object} This DataProxy object.
20609          * @param {Object} o The data object.
20610          * @param {Object} arg The callback argument object passed to the load function.
20611          */
20612         load : true,
20613         /**
20614          * @event loadexception
20615          * Fires if an Exception occurs during data retrieval.
20616          * @param {Object} This DataProxy object.
20617          * @param {Object} o The data object.
20618          * @param {Object} arg The callback argument object passed to the load function.
20619          * @param {Object} e The Exception.
20620          */
20621         loadexception : true
20622     });
20623     Roo.data.DataProxy.superclass.constructor.call(this);
20624 };
20625
20626 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
20627
20628     /**
20629      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
20630      */
20631 /*
20632  * Based on:
20633  * Ext JS Library 1.1.1
20634  * Copyright(c) 2006-2007, Ext JS, LLC.
20635  *
20636  * Originally Released Under LGPL - original licence link has changed is not relivant.
20637  *
20638  * Fork - LGPL
20639  * <script type="text/javascript">
20640  */
20641 /**
20642  * @class Roo.data.MemoryProxy
20643  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
20644  * to the Reader when its load method is called.
20645  * @constructor
20646  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
20647  */
20648 Roo.data.MemoryProxy = function(data){
20649     if (data.data) {
20650         data = data.data;
20651     }
20652     Roo.data.MemoryProxy.superclass.constructor.call(this);
20653     this.data = data;
20654 };
20655
20656 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
20657     /**
20658      * Load data from the requested source (in this case an in-memory
20659      * data object passed to the constructor), read the data object into
20660      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
20661      * process that block using the passed callback.
20662      * @param {Object} params This parameter is not used by the MemoryProxy class.
20663      * @param {Roo.data.DataReader} reader The Reader object which converts the data
20664      * object into a block of Roo.data.Records.
20665      * @param {Function} callback The function into which to pass the block of Roo.data.records.
20666      * The function must be passed <ul>
20667      * <li>The Record block object</li>
20668      * <li>The "arg" argument from the load function</li>
20669      * <li>A boolean success indicator</li>
20670      * </ul>
20671      * @param {Object} scope The scope in which to call the callback
20672      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
20673      */
20674     load : function(params, reader, callback, scope, arg){
20675         params = params || {};
20676         var result;
20677         try {
20678             result = reader.readRecords(this.data);
20679         }catch(e){
20680             this.fireEvent("loadexception", this, arg, null, e);
20681             callback.call(scope, null, arg, false);
20682             return;
20683         }
20684         callback.call(scope, result, arg, true);
20685     },
20686     
20687     // private
20688     update : function(params, records){
20689         
20690     }
20691 });/*
20692  * Based on:
20693  * Ext JS Library 1.1.1
20694  * Copyright(c) 2006-2007, Ext JS, LLC.
20695  *
20696  * Originally Released Under LGPL - original licence link has changed is not relivant.
20697  *
20698  * Fork - LGPL
20699  * <script type="text/javascript">
20700  */
20701 /**
20702  * @class Roo.data.HttpProxy
20703  * @extends Roo.data.DataProxy
20704  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
20705  * configured to reference a certain URL.<br><br>
20706  * <p>
20707  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
20708  * from which the running page was served.<br><br>
20709  * <p>
20710  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
20711  * <p>
20712  * Be aware that to enable the browser to parse an XML document, the server must set
20713  * the Content-Type header in the HTTP response to "text/xml".
20714  * @constructor
20715  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
20716  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
20717  * will be used to make the request.
20718  */
20719 Roo.data.HttpProxy = function(conn){
20720     Roo.data.HttpProxy.superclass.constructor.call(this);
20721     // is conn a conn config or a real conn?
20722     this.conn = conn;
20723     this.useAjax = !conn || !conn.events;
20724   
20725 };
20726
20727 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
20728     // thse are take from connection...
20729     
20730     /**
20731      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
20732      */
20733     /**
20734      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
20735      * extra parameters to each request made by this object. (defaults to undefined)
20736      */
20737     /**
20738      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
20739      *  to each request made by this object. (defaults to undefined)
20740      */
20741     /**
20742      * @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)
20743      */
20744     /**
20745      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
20746      */
20747      /**
20748      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
20749      * @type Boolean
20750      */
20751   
20752
20753     /**
20754      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
20755      * @type Boolean
20756      */
20757     /**
20758      * Return the {@link Roo.data.Connection} object being used by this Proxy.
20759      * @return {Connection} The Connection object. This object may be used to subscribe to events on
20760      * a finer-grained basis than the DataProxy events.
20761      */
20762     getConnection : function(){
20763         return this.useAjax ? Roo.Ajax : this.conn;
20764     },
20765
20766     /**
20767      * Load data from the configured {@link Roo.data.Connection}, read the data object into
20768      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
20769      * process that block using the passed callback.
20770      * @param {Object} params An object containing properties which are to be used as HTTP parameters
20771      * for the request to the remote server.
20772      * @param {Roo.data.DataReader} reader The Reader object which converts the data
20773      * object into a block of Roo.data.Records.
20774      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
20775      * The function must be passed <ul>
20776      * <li>The Record block object</li>
20777      * <li>The "arg" argument from the load function</li>
20778      * <li>A boolean success indicator</li>
20779      * </ul>
20780      * @param {Object} scope The scope in which to call the callback
20781      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
20782      */
20783     load : function(params, reader, callback, scope, arg){
20784         if(this.fireEvent("beforeload", this, params) !== false){
20785             var  o = {
20786                 params : params || {},
20787                 request: {
20788                     callback : callback,
20789                     scope : scope,
20790                     arg : arg
20791                 },
20792                 reader: reader,
20793                 callback : this.loadResponse,
20794                 scope: this
20795             };
20796             if(this.useAjax){
20797                 Roo.applyIf(o, this.conn);
20798                 if(this.activeRequest){
20799                     Roo.Ajax.abort(this.activeRequest);
20800                 }
20801                 this.activeRequest = Roo.Ajax.request(o);
20802             }else{
20803                 this.conn.request(o);
20804             }
20805         }else{
20806             callback.call(scope||this, null, arg, false);
20807         }
20808     },
20809
20810     // private
20811     loadResponse : function(o, success, response){
20812         delete this.activeRequest;
20813         if(!success){
20814             this.fireEvent("loadexception", this, o, response);
20815             o.request.callback.call(o.request.scope, null, o.request.arg, false);
20816             return;
20817         }
20818         var result;
20819         try {
20820             result = o.reader.read(response);
20821         }catch(e){
20822             this.fireEvent("loadexception", this, o, response, e);
20823             o.request.callback.call(o.request.scope, null, o.request.arg, false);
20824             return;
20825         }
20826         
20827         this.fireEvent("load", this, o, o.request.arg);
20828         o.request.callback.call(o.request.scope, result, o.request.arg, true);
20829     },
20830
20831     // private
20832     update : function(dataSet){
20833
20834     },
20835
20836     // private
20837     updateResponse : function(dataSet){
20838
20839     }
20840 });/*
20841  * Based on:
20842  * Ext JS Library 1.1.1
20843  * Copyright(c) 2006-2007, Ext JS, LLC.
20844  *
20845  * Originally Released Under LGPL - original licence link has changed is not relivant.
20846  *
20847  * Fork - LGPL
20848  * <script type="text/javascript">
20849  */
20850
20851 /**
20852  * @class Roo.data.ScriptTagProxy
20853  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
20854  * other than the originating domain of the running page.<br><br>
20855  * <p>
20856  * <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
20857  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
20858  * <p>
20859  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
20860  * source code that is used as the source inside a &lt;script> tag.<br><br>
20861  * <p>
20862  * In order for the browser to process the returned data, the server must wrap the data object
20863  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
20864  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
20865  * depending on whether the callback name was passed:
20866  * <p>
20867  * <pre><code>
20868 boolean scriptTag = false;
20869 String cb = request.getParameter("callback");
20870 if (cb != null) {
20871     scriptTag = true;
20872     response.setContentType("text/javascript");
20873 } else {
20874     response.setContentType("application/x-json");
20875 }
20876 Writer out = response.getWriter();
20877 if (scriptTag) {
20878     out.write(cb + "(");
20879 }
20880 out.print(dataBlock.toJsonString());
20881 if (scriptTag) {
20882     out.write(");");
20883 }
20884 </pre></code>
20885  *
20886  * @constructor
20887  * @param {Object} config A configuration object.
20888  */
20889 Roo.data.ScriptTagProxy = function(config){
20890     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
20891     Roo.apply(this, config);
20892     this.head = document.getElementsByTagName("head")[0];
20893 };
20894
20895 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
20896
20897 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
20898     /**
20899      * @cfg {String} url The URL from which to request the data object.
20900      */
20901     /**
20902      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
20903      */
20904     timeout : 30000,
20905     /**
20906      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
20907      * the server the name of the callback function set up by the load call to process the returned data object.
20908      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
20909      * javascript output which calls this named function passing the data object as its only parameter.
20910      */
20911     callbackParam : "callback",
20912     /**
20913      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
20914      * name to the request.
20915      */
20916     nocache : true,
20917
20918     /**
20919      * Load data from the configured URL, read the data object into
20920      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
20921      * process that block using the passed callback.
20922      * @param {Object} params An object containing properties which are to be used as HTTP parameters
20923      * for the request to the remote server.
20924      * @param {Roo.data.DataReader} reader The Reader object which converts the data
20925      * object into a block of Roo.data.Records.
20926      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
20927      * The function must be passed <ul>
20928      * <li>The Record block object</li>
20929      * <li>The "arg" argument from the load function</li>
20930      * <li>A boolean success indicator</li>
20931      * </ul>
20932      * @param {Object} scope The scope in which to call the callback
20933      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
20934      */
20935     load : function(params, reader, callback, scope, arg){
20936         if(this.fireEvent("beforeload", this, params) !== false){
20937
20938             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
20939
20940             var url = this.url;
20941             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
20942             if(this.nocache){
20943                 url += "&_dc=" + (new Date().getTime());
20944             }
20945             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
20946             var trans = {
20947                 id : transId,
20948                 cb : "stcCallback"+transId,
20949                 scriptId : "stcScript"+transId,
20950                 params : params,
20951                 arg : arg,
20952                 url : url,
20953                 callback : callback,
20954                 scope : scope,
20955                 reader : reader
20956             };
20957             var conn = this;
20958
20959             window[trans.cb] = function(o){
20960                 conn.handleResponse(o, trans);
20961             };
20962
20963             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
20964
20965             if(this.autoAbort !== false){
20966                 this.abort();
20967             }
20968
20969             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
20970
20971             var script = document.createElement("script");
20972             script.setAttribute("src", url);
20973             script.setAttribute("type", "text/javascript");
20974             script.setAttribute("id", trans.scriptId);
20975             this.head.appendChild(script);
20976
20977             this.trans = trans;
20978         }else{
20979             callback.call(scope||this, null, arg, false);
20980         }
20981     },
20982
20983     // private
20984     isLoading : function(){
20985         return this.trans ? true : false;
20986     },
20987
20988     /**
20989      * Abort the current server request.
20990      */
20991     abort : function(){
20992         if(this.isLoading()){
20993             this.destroyTrans(this.trans);
20994         }
20995     },
20996
20997     // private
20998     destroyTrans : function(trans, isLoaded){
20999         this.head.removeChild(document.getElementById(trans.scriptId));
21000         clearTimeout(trans.timeoutId);
21001         if(isLoaded){
21002             window[trans.cb] = undefined;
21003             try{
21004                 delete window[trans.cb];
21005             }catch(e){}
21006         }else{
21007             // if hasn't been loaded, wait for load to remove it to prevent script error
21008             window[trans.cb] = function(){
21009                 window[trans.cb] = undefined;
21010                 try{
21011                     delete window[trans.cb];
21012                 }catch(e){}
21013             };
21014         }
21015     },
21016
21017     // private
21018     handleResponse : function(o, trans){
21019         this.trans = false;
21020         this.destroyTrans(trans, true);
21021         var result;
21022         try {
21023             result = trans.reader.readRecords(o);
21024         }catch(e){
21025             this.fireEvent("loadexception", this, o, trans.arg, e);
21026             trans.callback.call(trans.scope||window, null, trans.arg, false);
21027             return;
21028         }
21029         this.fireEvent("load", this, o, trans.arg);
21030         trans.callback.call(trans.scope||window, result, trans.arg, true);
21031     },
21032
21033     // private
21034     handleFailure : function(trans){
21035         this.trans = false;
21036         this.destroyTrans(trans, false);
21037         this.fireEvent("loadexception", this, null, trans.arg);
21038         trans.callback.call(trans.scope||window, null, trans.arg, false);
21039     }
21040 });/*
21041  * Based on:
21042  * Ext JS Library 1.1.1
21043  * Copyright(c) 2006-2007, Ext JS, LLC.
21044  *
21045  * Originally Released Under LGPL - original licence link has changed is not relivant.
21046  *
21047  * Fork - LGPL
21048  * <script type="text/javascript">
21049  */
21050
21051 /**
21052  * @class Roo.data.JsonReader
21053  * @extends Roo.data.DataReader
21054  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
21055  * based on mappings in a provided Roo.data.Record constructor.
21056  * 
21057  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
21058  * in the reply previously. 
21059  * 
21060  * <p>
21061  * Example code:
21062  * <pre><code>
21063 var RecordDef = Roo.data.Record.create([
21064     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
21065     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
21066 ]);
21067 var myReader = new Roo.data.JsonReader({
21068     totalProperty: "results",    // The property which contains the total dataset size (optional)
21069     root: "rows",                // The property which contains an Array of row objects
21070     id: "id"                     // The property within each row object that provides an ID for the record (optional)
21071 }, RecordDef);
21072 </code></pre>
21073  * <p>
21074  * This would consume a JSON file like this:
21075  * <pre><code>
21076 { 'results': 2, 'rows': [
21077     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
21078     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
21079 }
21080 </code></pre>
21081  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
21082  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
21083  * paged from the remote server.
21084  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
21085  * @cfg {String} root name of the property which contains the Array of row objects.
21086  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
21087  * @constructor
21088  * Create a new JsonReader
21089  * @param {Object} meta Metadata configuration options
21090  * @param {Object} recordType Either an Array of field definition objects,
21091  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
21092  */
21093 Roo.data.JsonReader = function(meta, recordType){
21094     
21095     meta = meta || {};
21096     // set some defaults:
21097     Roo.applyIf(meta, {
21098         totalProperty: 'total',
21099         successProperty : 'success',
21100         root : 'data',
21101         id : 'id'
21102     });
21103     
21104     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
21105 };
21106 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
21107     
21108     /**
21109      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
21110      * Used by Store query builder to append _requestMeta to params.
21111      * 
21112      */
21113     metaFromRemote : false,
21114     /**
21115      * This method is only used by a DataProxy which has retrieved data from a remote server.
21116      * @param {Object} response The XHR object which contains the JSON data in its responseText.
21117      * @return {Object} data A data block which is used by an Roo.data.Store object as
21118      * a cache of Roo.data.Records.
21119      */
21120     read : function(response){
21121         var json = response.responseText;
21122        
21123         var o = /* eval:var:o */ eval("("+json+")");
21124         if(!o) {
21125             throw {message: "JsonReader.read: Json object not found"};
21126         }
21127         
21128         if(o.metaData){
21129             
21130             delete this.ef;
21131             this.metaFromRemote = true;
21132             this.meta = o.metaData;
21133             this.recordType = Roo.data.Record.create(o.metaData.fields);
21134             this.onMetaChange(this.meta, this.recordType, o);
21135         }
21136         return this.readRecords(o);
21137     },
21138
21139     // private function a store will implement
21140     onMetaChange : function(meta, recordType, o){
21141
21142     },
21143
21144     /**
21145          * @ignore
21146          */
21147     simpleAccess: function(obj, subsc) {
21148         return obj[subsc];
21149     },
21150
21151         /**
21152          * @ignore
21153          */
21154     getJsonAccessor: function(){
21155         var re = /[\[\.]/;
21156         return function(expr) {
21157             try {
21158                 return(re.test(expr))
21159                     ? new Function("obj", "return obj." + expr)
21160                     : function(obj){
21161                         return obj[expr];
21162                     };
21163             } catch(e){}
21164             return Roo.emptyFn;
21165         };
21166     }(),
21167
21168     /**
21169      * Create a data block containing Roo.data.Records from an XML document.
21170      * @param {Object} o An object which contains an Array of row objects in the property specified
21171      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
21172      * which contains the total size of the dataset.
21173      * @return {Object} data A data block which is used by an Roo.data.Store object as
21174      * a cache of Roo.data.Records.
21175      */
21176     readRecords : function(o){
21177         /**
21178          * After any data loads, the raw JSON data is available for further custom processing.
21179          * @type Object
21180          */
21181         this.o = o;
21182         var s = this.meta, Record = this.recordType,
21183             f = Record.prototype.fields, fi = f.items, fl = f.length;
21184
21185 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
21186         if (!this.ef) {
21187             if(s.totalProperty) {
21188                     this.getTotal = this.getJsonAccessor(s.totalProperty);
21189                 }
21190                 if(s.successProperty) {
21191                     this.getSuccess = this.getJsonAccessor(s.successProperty);
21192                 }
21193                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
21194                 if (s.id) {
21195                         var g = this.getJsonAccessor(s.id);
21196                         this.getId = function(rec) {
21197                                 var r = g(rec);
21198                                 return (r === undefined || r === "") ? null : r;
21199                         };
21200                 } else {
21201                         this.getId = function(){return null;};
21202                 }
21203             this.ef = [];
21204             for(var jj = 0; jj < fl; jj++){
21205                 f = fi[jj];
21206                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
21207                 this.ef[jj] = this.getJsonAccessor(map);
21208             }
21209         }
21210
21211         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
21212         if(s.totalProperty){
21213             var vt = parseInt(this.getTotal(o), 10);
21214             if(!isNaN(vt)){
21215                 totalRecords = vt;
21216             }
21217         }
21218         if(s.successProperty){
21219             var vs = this.getSuccess(o);
21220             if(vs === false || vs === 'false'){
21221                 success = false;
21222             }
21223         }
21224         var records = [];
21225             for(var i = 0; i < c; i++){
21226                     var n = root[i];
21227                 var values = {};
21228                 var id = this.getId(n);
21229                 for(var j = 0; j < fl; j++){
21230                     f = fi[j];
21231                 var v = this.ef[j](n);
21232                 if (!f.convert) {
21233                     Roo.log('missing convert for ' + f.name);
21234                     Roo.log(f);
21235                     continue;
21236                 }
21237                 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
21238                 }
21239                 var record = new Record(values, id);
21240                 record.json = n;
21241                 records[i] = record;
21242             }
21243             return {
21244             raw : o,
21245                 success : success,
21246                 records : records,
21247                 totalRecords : totalRecords
21248             };
21249     }
21250 });/*
21251  * Based on:
21252  * Ext JS Library 1.1.1
21253  * Copyright(c) 2006-2007, Ext JS, LLC.
21254  *
21255  * Originally Released Under LGPL - original licence link has changed is not relivant.
21256  *
21257  * Fork - LGPL
21258  * <script type="text/javascript">
21259  */
21260
21261 /**
21262  * @class Roo.data.XmlReader
21263  * @extends Roo.data.DataReader
21264  * Data reader class to create an Array of {@link Roo.data.Record} objects from an XML document
21265  * based on mappings in a provided Roo.data.Record constructor.<br><br>
21266  * <p>
21267  * <em>Note that in order for the browser to parse a returned XML document, the Content-Type
21268  * header in the HTTP response must be set to "text/xml".</em>
21269  * <p>
21270  * Example code:
21271  * <pre><code>
21272 var RecordDef = Roo.data.Record.create([
21273    {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
21274    {name: 'occupation'}                 // This field will use "occupation" as the mapping.
21275 ]);
21276 var myReader = new Roo.data.XmlReader({
21277    totalRecords: "results", // The element which contains the total dataset size (optional)
21278    record: "row",           // The repeated element which contains row information
21279    id: "id"                 // The element within the row that provides an ID for the record (optional)
21280 }, RecordDef);
21281 </code></pre>
21282  * <p>
21283  * This would consume an XML file like this:
21284  * <pre><code>
21285 &lt;?xml?>
21286 &lt;dataset>
21287  &lt;results>2&lt;/results>
21288  &lt;row>
21289    &lt;id>1&lt;/id>
21290    &lt;name>Bill&lt;/name>
21291    &lt;occupation>Gardener&lt;/occupation>
21292  &lt;/row>
21293  &lt;row>
21294    &lt;id>2&lt;/id>
21295    &lt;name>Ben&lt;/name>
21296    &lt;occupation>Horticulturalist&lt;/occupation>
21297  &lt;/row>
21298 &lt;/dataset>
21299 </code></pre>
21300  * @cfg {String} totalRecords The DomQuery path from which to retrieve the total number of records
21301  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
21302  * paged from the remote server.
21303  * @cfg {String} record The DomQuery path to the repeated element which contains record information.
21304  * @cfg {String} success The DomQuery path to the success attribute used by forms.
21305  * @cfg {String} id The DomQuery path relative from the record element to the element that contains
21306  * a record identifier value.
21307  * @constructor
21308  * Create a new XmlReader
21309  * @param {Object} meta Metadata configuration options
21310  * @param {Mixed} recordType The definition of the data record type to produce.  This can be either a valid
21311  * Record subclass created with {@link Roo.data.Record#create}, or an array of objects with which to call
21312  * Roo.data.Record.create.  See the {@link Roo.data.Record} class for more details.
21313  */
21314 Roo.data.XmlReader = function(meta, recordType){
21315     meta = meta || {};
21316     Roo.data.XmlReader.superclass.constructor.call(this, meta, recordType||meta.fields);
21317 };
21318 Roo.extend(Roo.data.XmlReader, Roo.data.DataReader, {
21319     /**
21320      * This method is only used by a DataProxy which has retrieved data from a remote server.
21321          * @param {Object} response The XHR object which contains the parsed XML document.  The response is expected
21322          * to contain a method called 'responseXML' that returns an XML document object.
21323      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
21324      * a cache of Roo.data.Records.
21325      */
21326     read : function(response){
21327         var doc = response.responseXML;
21328         if(!doc) {
21329             throw {message: "XmlReader.read: XML Document not available"};
21330         }
21331         return this.readRecords(doc);
21332     },
21333
21334     /**
21335      * Create a data block containing Roo.data.Records from an XML document.
21336          * @param {Object} doc A parsed XML document.
21337      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
21338      * a cache of Roo.data.Records.
21339      */
21340     readRecords : function(doc){
21341         /**
21342          * After any data loads/reads, the raw XML Document is available for further custom processing.
21343          * @type XMLDocument
21344          */
21345         this.xmlData = doc;
21346         var root = doc.documentElement || doc;
21347         var q = Roo.DomQuery;
21348         var recordType = this.recordType, fields = recordType.prototype.fields;
21349         var sid = this.meta.id;
21350         var totalRecords = 0, success = true;
21351         if(this.meta.totalRecords){
21352             totalRecords = q.selectNumber(this.meta.totalRecords, root, 0);
21353         }
21354         
21355         if(this.meta.success){
21356             var sv = q.selectValue(this.meta.success, root, true);
21357             success = sv !== false && sv !== 'false';
21358         }
21359         var records = [];
21360         var ns = q.select(this.meta.record, root);
21361         for(var i = 0, len = ns.length; i < len; i++) {
21362                 var n = ns[i];
21363                 var values = {};
21364                 var id = sid ? q.selectValue(sid, n) : undefined;
21365                 for(var j = 0, jlen = fields.length; j < jlen; j++){
21366                     var f = fields.items[j];
21367                 var v = q.selectValue(f.mapping || f.name, n, f.defaultValue);
21368                     v = f.convert(v);
21369                     values[f.name] = v;
21370                 }
21371                 var record = new recordType(values, id);
21372                 record.node = n;
21373                 records[records.length] = record;
21374             }
21375
21376             return {
21377                 success : success,
21378                 records : records,
21379                 totalRecords : totalRecords || records.length
21380             };
21381     }
21382 });/*
21383  * Based on:
21384  * Ext JS Library 1.1.1
21385  * Copyright(c) 2006-2007, Ext JS, LLC.
21386  *
21387  * Originally Released Under LGPL - original licence link has changed is not relivant.
21388  *
21389  * Fork - LGPL
21390  * <script type="text/javascript">
21391  */
21392
21393 /**
21394  * @class Roo.data.ArrayReader
21395  * @extends Roo.data.DataReader
21396  * Data reader class to create an Array of Roo.data.Record objects from an Array.
21397  * Each element of that Array represents a row of data fields. The
21398  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
21399  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
21400  * <p>
21401  * Example code:.
21402  * <pre><code>
21403 var RecordDef = Roo.data.Record.create([
21404     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
21405     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
21406 ]);
21407 var myReader = new Roo.data.ArrayReader({
21408     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
21409 }, RecordDef);
21410 </code></pre>
21411  * <p>
21412  * This would consume an Array like this:
21413  * <pre><code>
21414 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
21415   </code></pre>
21416  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
21417  * @constructor
21418  * Create a new JsonReader
21419  * @param {Object} meta Metadata configuration options.
21420  * @param {Object} recordType Either an Array of field definition objects
21421  * as specified to {@link Roo.data.Record#create},
21422  * or an {@link Roo.data.Record} object
21423  * created using {@link Roo.data.Record#create}.
21424  */
21425 Roo.data.ArrayReader = function(meta, recordType){
21426     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
21427 };
21428
21429 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
21430     /**
21431      * Create a data block containing Roo.data.Records from an XML document.
21432      * @param {Object} o An Array of row objects which represents the dataset.
21433      * @return {Object} data A data block which is used by an Roo.data.Store object as
21434      * a cache of Roo.data.Records.
21435      */
21436     readRecords : function(o){
21437         var sid = this.meta ? this.meta.id : null;
21438         var recordType = this.recordType, fields = recordType.prototype.fields;
21439         var records = [];
21440         var root = o;
21441             for(var i = 0; i < root.length; i++){
21442                     var n = root[i];
21443                 var values = {};
21444                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
21445                 for(var j = 0, jlen = fields.length; j < jlen; j++){
21446                 var f = fields.items[j];
21447                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
21448                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
21449                 v = f.convert(v);
21450                 values[f.name] = v;
21451             }
21452                 var record = new recordType(values, id);
21453                 record.json = n;
21454                 records[records.length] = record;
21455             }
21456             return {
21457                 records : records,
21458                 totalRecords : records.length
21459             };
21460     }
21461 });/*
21462  * Based on:
21463  * Ext JS Library 1.1.1
21464  * Copyright(c) 2006-2007, Ext JS, LLC.
21465  *
21466  * Originally Released Under LGPL - original licence link has changed is not relivant.
21467  *
21468  * Fork - LGPL
21469  * <script type="text/javascript">
21470  */
21471
21472
21473 /**
21474  * @class Roo.data.Tree
21475  * @extends Roo.util.Observable
21476  * Represents a tree data structure and bubbles all the events for its nodes. The nodes
21477  * in the tree have most standard DOM functionality.
21478  * @constructor
21479  * @param {Node} root (optional) The root node
21480  */
21481 Roo.data.Tree = function(root){
21482    this.nodeHash = {};
21483    /**
21484     * The root node for this tree
21485     * @type Node
21486     */
21487    this.root = null;
21488    if(root){
21489        this.setRootNode(root);
21490    }
21491    this.addEvents({
21492        /**
21493         * @event append
21494         * Fires when a new child node is appended to a node in this tree.
21495         * @param {Tree} tree The owner tree
21496         * @param {Node} parent The parent node
21497         * @param {Node} node The newly appended node
21498         * @param {Number} index The index of the newly appended node
21499         */
21500        "append" : true,
21501        /**
21502         * @event remove
21503         * Fires when a child node is removed from a node in this tree.
21504         * @param {Tree} tree The owner tree
21505         * @param {Node} parent The parent node
21506         * @param {Node} node The child node removed
21507         */
21508        "remove" : true,
21509        /**
21510         * @event move
21511         * Fires when a node is moved to a new location in the tree
21512         * @param {Tree} tree The owner tree
21513         * @param {Node} node The node moved
21514         * @param {Node} oldParent The old parent of this node
21515         * @param {Node} newParent The new parent of this node
21516         * @param {Number} index The index it was moved to
21517         */
21518        "move" : true,
21519        /**
21520         * @event insert
21521         * Fires when a new child node is inserted in a node in this tree.
21522         * @param {Tree} tree The owner tree
21523         * @param {Node} parent The parent node
21524         * @param {Node} node The child node inserted
21525         * @param {Node} refNode The child node the node was inserted before
21526         */
21527        "insert" : true,
21528        /**
21529         * @event beforeappend
21530         * Fires before a new child is appended to a node in this tree, return false to cancel the append.
21531         * @param {Tree} tree The owner tree
21532         * @param {Node} parent The parent node
21533         * @param {Node} node The child node to be appended
21534         */
21535        "beforeappend" : true,
21536        /**
21537         * @event beforeremove
21538         * Fires before a child is removed from a node in this tree, return false to cancel the remove.
21539         * @param {Tree} tree The owner tree
21540         * @param {Node} parent The parent node
21541         * @param {Node} node The child node to be removed
21542         */
21543        "beforeremove" : true,
21544        /**
21545         * @event beforemove
21546         * Fires before a node is moved to a new location in the tree. Return false to cancel the move.
21547         * @param {Tree} tree The owner tree
21548         * @param {Node} node The node being moved
21549         * @param {Node} oldParent The parent of the node
21550         * @param {Node} newParent The new parent the node is moving to
21551         * @param {Number} index The index it is being moved to
21552         */
21553        "beforemove" : true,
21554        /**
21555         * @event beforeinsert
21556         * Fires before a new child is inserted in a node in this tree, return false to cancel the insert.
21557         * @param {Tree} tree The owner tree
21558         * @param {Node} parent The parent node
21559         * @param {Node} node The child node to be inserted
21560         * @param {Node} refNode The child node the node is being inserted before
21561         */
21562        "beforeinsert" : true
21563    });
21564
21565     Roo.data.Tree.superclass.constructor.call(this);
21566 };
21567
21568 Roo.extend(Roo.data.Tree, Roo.util.Observable, {
21569     pathSeparator: "/",
21570
21571     proxyNodeEvent : function(){
21572         return this.fireEvent.apply(this, arguments);
21573     },
21574
21575     /**
21576      * Returns the root node for this tree.
21577      * @return {Node}
21578      */
21579     getRootNode : function(){
21580         return this.root;
21581     },
21582
21583     /**
21584      * Sets the root node for this tree.
21585      * @param {Node} node
21586      * @return {Node}
21587      */
21588     setRootNode : function(node){
21589         this.root = node;
21590         node.ownerTree = this;
21591         node.isRoot = true;
21592         this.registerNode(node);
21593         return node;
21594     },
21595
21596     /**
21597      * Gets a node in this tree by its id.
21598      * @param {String} id
21599      * @return {Node}
21600      */
21601     getNodeById : function(id){
21602         return this.nodeHash[id];
21603     },
21604
21605     registerNode : function(node){
21606         this.nodeHash[node.id] = node;
21607     },
21608
21609     unregisterNode : function(node){
21610         delete this.nodeHash[node.id];
21611     },
21612
21613     toString : function(){
21614         return "[Tree"+(this.id?" "+this.id:"")+"]";
21615     }
21616 });
21617
21618 /**
21619  * @class Roo.data.Node
21620  * @extends Roo.util.Observable
21621  * @cfg {Boolean} leaf true if this node is a leaf and does not have children
21622  * @cfg {String} id The id for this node. If one is not specified, one is generated.
21623  * @constructor
21624  * @param {Object} attributes The attributes/config for the node
21625  */
21626 Roo.data.Node = function(attributes){
21627     /**
21628      * The attributes supplied for the node. You can use this property to access any custom attributes you supplied.
21629      * @type {Object}
21630      */
21631     this.attributes = attributes || {};
21632     this.leaf = this.attributes.leaf;
21633     /**
21634      * The node id. @type String
21635      */
21636     this.id = this.attributes.id;
21637     if(!this.id){
21638         this.id = Roo.id(null, "ynode-");
21639         this.attributes.id = this.id;
21640     }
21641      
21642     
21643     /**
21644      * All child nodes of this node. @type Array
21645      */
21646     this.childNodes = [];
21647     if(!this.childNodes.indexOf){ // indexOf is a must
21648         this.childNodes.indexOf = function(o){
21649             for(var i = 0, len = this.length; i < len; i++){
21650                 if(this[i] == o) {
21651                     return i;
21652                 }
21653             }
21654             return -1;
21655         };
21656     }
21657     /**
21658      * The parent node for this node. @type Node
21659      */
21660     this.parentNode = null;
21661     /**
21662      * The first direct child node of this node, or null if this node has no child nodes. @type Node
21663      */
21664     this.firstChild = null;
21665     /**
21666      * The last direct child node of this node, or null if this node has no child nodes. @type Node
21667      */
21668     this.lastChild = null;
21669     /**
21670      * The node immediately preceding this node in the tree, or null if there is no sibling node. @type Node
21671      */
21672     this.previousSibling = null;
21673     /**
21674      * The node immediately following this node in the tree, or null if there is no sibling node. @type Node
21675      */
21676     this.nextSibling = null;
21677
21678     this.addEvents({
21679        /**
21680         * @event append
21681         * Fires when a new child node is appended
21682         * @param {Tree} tree The owner tree
21683         * @param {Node} this This node
21684         * @param {Node} node The newly appended node
21685         * @param {Number} index The index of the newly appended node
21686         */
21687        "append" : true,
21688        /**
21689         * @event remove
21690         * Fires when a child node is removed
21691         * @param {Tree} tree The owner tree
21692         * @param {Node} this This node
21693         * @param {Node} node The removed node
21694         */
21695        "remove" : true,
21696        /**
21697         * @event move
21698         * Fires when this node is moved to a new location in the tree
21699         * @param {Tree} tree The owner tree
21700         * @param {Node} this This node
21701         * @param {Node} oldParent The old parent of this node
21702         * @param {Node} newParent The new parent of this node
21703         * @param {Number} index The index it was moved to
21704         */
21705        "move" : true,
21706        /**
21707         * @event insert
21708         * Fires when a new child node is inserted.
21709         * @param {Tree} tree The owner tree
21710         * @param {Node} this This node
21711         * @param {Node} node The child node inserted
21712         * @param {Node} refNode The child node the node was inserted before
21713         */
21714        "insert" : true,
21715        /**
21716         * @event beforeappend
21717         * Fires before a new child is appended, return false to cancel the append.
21718         * @param {Tree} tree The owner tree
21719         * @param {Node} this This node
21720         * @param {Node} node The child node to be appended
21721         */
21722        "beforeappend" : true,
21723        /**
21724         * @event beforeremove
21725         * Fires before a child is removed, return false to cancel the remove.
21726         * @param {Tree} tree The owner tree
21727         * @param {Node} this This node
21728         * @param {Node} node The child node to be removed
21729         */
21730        "beforeremove" : true,
21731        /**
21732         * @event beforemove
21733         * Fires before this node is moved to a new location in the tree. Return false to cancel the move.
21734         * @param {Tree} tree The owner tree
21735         * @param {Node} this This node
21736         * @param {Node} oldParent The parent of this node
21737         * @param {Node} newParent The new parent this node is moving to
21738         * @param {Number} index The index it is being moved to
21739         */
21740        "beforemove" : true,
21741        /**
21742         * @event beforeinsert
21743         * Fires before a new child is inserted, return false to cancel the insert.
21744         * @param {Tree} tree The owner tree
21745         * @param {Node} this This node
21746         * @param {Node} node The child node to be inserted
21747         * @param {Node} refNode The child node the node is being inserted before
21748         */
21749        "beforeinsert" : true
21750    });
21751     this.listeners = this.attributes.listeners;
21752     Roo.data.Node.superclass.constructor.call(this);
21753 };
21754
21755 Roo.extend(Roo.data.Node, Roo.util.Observable, {
21756     fireEvent : function(evtName){
21757         // first do standard event for this node
21758         if(Roo.data.Node.superclass.fireEvent.apply(this, arguments) === false){
21759             return false;
21760         }
21761         // then bubble it up to the tree if the event wasn't cancelled
21762         var ot = this.getOwnerTree();
21763         if(ot){
21764             if(ot.proxyNodeEvent.apply(ot, arguments) === false){
21765                 return false;
21766             }
21767         }
21768         return true;
21769     },
21770
21771     /**
21772      * Returns true if this node is a leaf
21773      * @return {Boolean}
21774      */
21775     isLeaf : function(){
21776         return this.leaf === true;
21777     },
21778
21779     // private
21780     setFirstChild : function(node){
21781         this.firstChild = node;
21782     },
21783
21784     //private
21785     setLastChild : function(node){
21786         this.lastChild = node;
21787     },
21788
21789
21790     /**
21791      * Returns true if this node is the last child of its parent
21792      * @return {Boolean}
21793      */
21794     isLast : function(){
21795        return (!this.parentNode ? true : this.parentNode.lastChild == this);
21796     },
21797
21798     /**
21799      * Returns true if this node is the first child of its parent
21800      * @return {Boolean}
21801      */
21802     isFirst : function(){
21803        return (!this.parentNode ? true : this.parentNode.firstChild == this);
21804     },
21805
21806     hasChildNodes : function(){
21807         return !this.isLeaf() && this.childNodes.length > 0;
21808     },
21809
21810     /**
21811      * Insert node(s) as the last child node of this node.
21812      * @param {Node/Array} node The node or Array of nodes to append
21813      * @return {Node} The appended node if single append, or null if an array was passed
21814      */
21815     appendChild : function(node){
21816         var multi = false;
21817         if(node instanceof Array){
21818             multi = node;
21819         }else if(arguments.length > 1){
21820             multi = arguments;
21821         }
21822         // if passed an array or multiple args do them one by one
21823         if(multi){
21824             for(var i = 0, len = multi.length; i < len; i++) {
21825                 this.appendChild(multi[i]);
21826             }
21827         }else{
21828             if(this.fireEvent("beforeappend", this.ownerTree, this, node) === false){
21829                 return false;
21830             }
21831             var index = this.childNodes.length;
21832             var oldParent = node.parentNode;
21833             // it's a move, make sure we move it cleanly
21834             if(oldParent){
21835                 if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index) === false){
21836                     return false;
21837                 }
21838                 oldParent.removeChild(node);
21839             }
21840             index = this.childNodes.length;
21841             if(index == 0){
21842                 this.setFirstChild(node);
21843             }
21844             this.childNodes.push(node);
21845             node.parentNode = this;
21846             var ps = this.childNodes[index-1];
21847             if(ps){
21848                 node.previousSibling = ps;
21849                 ps.nextSibling = node;
21850             }else{
21851                 node.previousSibling = null;
21852             }
21853             node.nextSibling = null;
21854             this.setLastChild(node);
21855             node.setOwnerTree(this.getOwnerTree());
21856             this.fireEvent("append", this.ownerTree, this, node, index);
21857             if(oldParent){
21858                 node.fireEvent("move", this.ownerTree, node, oldParent, this, index);
21859             }
21860             return node;
21861         }
21862     },
21863
21864     /**
21865      * Removes a child node from this node.
21866      * @param {Node} node The node to remove
21867      * @return {Node} The removed node
21868      */
21869     removeChild : function(node){
21870         var index = this.childNodes.indexOf(node);
21871         if(index == -1){
21872             return false;
21873         }
21874         if(this.fireEvent("beforeremove", this.ownerTree, this, node) === false){
21875             return false;
21876         }
21877
21878         // remove it from childNodes collection
21879         this.childNodes.splice(index, 1);
21880
21881         // update siblings
21882         if(node.previousSibling){
21883             node.previousSibling.nextSibling = node.nextSibling;
21884         }
21885         if(node.nextSibling){
21886             node.nextSibling.previousSibling = node.previousSibling;
21887         }
21888
21889         // update child refs
21890         if(this.firstChild == node){
21891             this.setFirstChild(node.nextSibling);
21892         }
21893         if(this.lastChild == node){
21894             this.setLastChild(node.previousSibling);
21895         }
21896
21897         node.setOwnerTree(null);
21898         // clear any references from the node
21899         node.parentNode = null;
21900         node.previousSibling = null;
21901         node.nextSibling = null;
21902         this.fireEvent("remove", this.ownerTree, this, node);
21903         return node;
21904     },
21905
21906     /**
21907      * Inserts the first node before the second node in this nodes childNodes collection.
21908      * @param {Node} node The node to insert
21909      * @param {Node} refNode The node to insert before (if null the node is appended)
21910      * @return {Node} The inserted node
21911      */
21912     insertBefore : function(node, refNode){
21913         if(!refNode){ // like standard Dom, refNode can be null for append
21914             return this.appendChild(node);
21915         }
21916         // nothing to do
21917         if(node == refNode){
21918             return false;
21919         }
21920
21921         if(this.fireEvent("beforeinsert", this.ownerTree, this, node, refNode) === false){
21922             return false;
21923         }
21924         var index = this.childNodes.indexOf(refNode);
21925         var oldParent = node.parentNode;
21926         var refIndex = index;
21927
21928         // when moving internally, indexes will change after remove
21929         if(oldParent == this && this.childNodes.indexOf(node) < index){
21930             refIndex--;
21931         }
21932
21933         // it's a move, make sure we move it cleanly
21934         if(oldParent){
21935             if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index, refNode) === false){
21936                 return false;
21937             }
21938             oldParent.removeChild(node);
21939         }
21940         if(refIndex == 0){
21941             this.setFirstChild(node);
21942         }
21943         this.childNodes.splice(refIndex, 0, node);
21944         node.parentNode = this;
21945         var ps = this.childNodes[refIndex-1];
21946         if(ps){
21947             node.previousSibling = ps;
21948             ps.nextSibling = node;
21949         }else{
21950             node.previousSibling = null;
21951         }
21952         node.nextSibling = refNode;
21953         refNode.previousSibling = node;
21954         node.setOwnerTree(this.getOwnerTree());
21955         this.fireEvent("insert", this.ownerTree, this, node, refNode);
21956         if(oldParent){
21957             node.fireEvent("move", this.ownerTree, node, oldParent, this, refIndex, refNode);
21958         }
21959         return node;
21960     },
21961
21962     /**
21963      * Returns the child node at the specified index.
21964      * @param {Number} index
21965      * @return {Node}
21966      */
21967     item : function(index){
21968         return this.childNodes[index];
21969     },
21970
21971     /**
21972      * Replaces one child node in this node with another.
21973      * @param {Node} newChild The replacement node
21974      * @param {Node} oldChild The node to replace
21975      * @return {Node} The replaced node
21976      */
21977     replaceChild : function(newChild, oldChild){
21978         this.insertBefore(newChild, oldChild);
21979         this.removeChild(oldChild);
21980         return oldChild;
21981     },
21982
21983     /**
21984      * Returns the index of a child node
21985      * @param {Node} node
21986      * @return {Number} The index of the node or -1 if it was not found
21987      */
21988     indexOf : function(child){
21989         return this.childNodes.indexOf(child);
21990     },
21991
21992     /**
21993      * Returns the tree this node is in.
21994      * @return {Tree}
21995      */
21996     getOwnerTree : function(){
21997         // if it doesn't have one, look for one
21998         if(!this.ownerTree){
21999             var p = this;
22000             while(p){
22001                 if(p.ownerTree){
22002                     this.ownerTree = p.ownerTree;
22003                     break;
22004                 }
22005                 p = p.parentNode;
22006             }
22007         }
22008         return this.ownerTree;
22009     },
22010
22011     /**
22012      * Returns depth of this node (the root node has a depth of 0)
22013      * @return {Number}
22014      */
22015     getDepth : function(){
22016         var depth = 0;
22017         var p = this;
22018         while(p.parentNode){
22019             ++depth;
22020             p = p.parentNode;
22021         }
22022         return depth;
22023     },
22024
22025     // private
22026     setOwnerTree : function(tree){
22027         // if it's move, we need to update everyone
22028         if(tree != this.ownerTree){
22029             if(this.ownerTree){
22030                 this.ownerTree.unregisterNode(this);
22031             }
22032             this.ownerTree = tree;
22033             var cs = this.childNodes;
22034             for(var i = 0, len = cs.length; i < len; i++) {
22035                 cs[i].setOwnerTree(tree);
22036             }
22037             if(tree){
22038                 tree.registerNode(this);
22039             }
22040         }
22041     },
22042
22043     /**
22044      * Returns the path for this node. The path can be used to expand or select this node programmatically.
22045      * @param {String} attr (optional) The attr to use for the path (defaults to the node's id)
22046      * @return {String} The path
22047      */
22048     getPath : function(attr){
22049         attr = attr || "id";
22050         var p = this.parentNode;
22051         var b = [this.attributes[attr]];
22052         while(p){
22053             b.unshift(p.attributes[attr]);
22054             p = p.parentNode;
22055         }
22056         var sep = this.getOwnerTree().pathSeparator;
22057         return sep + b.join(sep);
22058     },
22059
22060     /**
22061      * Bubbles up the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
22062      * function call will be the scope provided or the current node. The arguments to the function
22063      * will be the args provided or the current node. If the function returns false at any point,
22064      * the bubble is stopped.
22065      * @param {Function} fn The function to call
22066      * @param {Object} scope (optional) The scope of the function (defaults to current node)
22067      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
22068      */
22069     bubble : function(fn, scope, args){
22070         var p = this;
22071         while(p){
22072             if(fn.call(scope || p, args || p) === false){
22073                 break;
22074             }
22075             p = p.parentNode;
22076         }
22077     },
22078
22079     /**
22080      * Cascades down the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
22081      * function call will be the scope provided or the current node. The arguments to the function
22082      * will be the args provided or the current node. If the function returns false at any point,
22083      * the cascade is stopped on that branch.
22084      * @param {Function} fn The function to call
22085      * @param {Object} scope (optional) The scope of the function (defaults to current node)
22086      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
22087      */
22088     cascade : function(fn, scope, args){
22089         if(fn.call(scope || this, args || this) !== false){
22090             var cs = this.childNodes;
22091             for(var i = 0, len = cs.length; i < len; i++) {
22092                 cs[i].cascade(fn, scope, args);
22093             }
22094         }
22095     },
22096
22097     /**
22098      * Interates the child nodes of this node, calling the specified function with each node. The scope (<i>this</i>) of
22099      * function call will be the scope provided or the current node. The arguments to the function
22100      * will be the args provided or the current node. If the function returns false at any point,
22101      * the iteration stops.
22102      * @param {Function} fn The function to call
22103      * @param {Object} scope (optional) The scope of the function (defaults to current node)
22104      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
22105      */
22106     eachChild : function(fn, scope, args){
22107         var cs = this.childNodes;
22108         for(var i = 0, len = cs.length; i < len; i++) {
22109                 if(fn.call(scope || this, args || cs[i]) === false){
22110                     break;
22111                 }
22112         }
22113     },
22114
22115     /**
22116      * Finds the first child that has the attribute with the specified value.
22117      * @param {String} attribute The attribute name
22118      * @param {Mixed} value The value to search for
22119      * @return {Node} The found child or null if none was found
22120      */
22121     findChild : function(attribute, value){
22122         var cs = this.childNodes;
22123         for(var i = 0, len = cs.length; i < len; i++) {
22124                 if(cs[i].attributes[attribute] == value){
22125                     return cs[i];
22126                 }
22127         }
22128         return null;
22129     },
22130
22131     /**
22132      * Finds the first child by a custom function. The child matches if the function passed
22133      * returns true.
22134      * @param {Function} fn
22135      * @param {Object} scope (optional)
22136      * @return {Node} The found child or null if none was found
22137      */
22138     findChildBy : function(fn, scope){
22139         var cs = this.childNodes;
22140         for(var i = 0, len = cs.length; i < len; i++) {
22141                 if(fn.call(scope||cs[i], cs[i]) === true){
22142                     return cs[i];
22143                 }
22144         }
22145         return null;
22146     },
22147
22148     /**
22149      * Sorts this nodes children using the supplied sort function
22150      * @param {Function} fn
22151      * @param {Object} scope (optional)
22152      */
22153     sort : function(fn, scope){
22154         var cs = this.childNodes;
22155         var len = cs.length;
22156         if(len > 0){
22157             var sortFn = scope ? function(){fn.apply(scope, arguments);} : fn;
22158             cs.sort(sortFn);
22159             for(var i = 0; i < len; i++){
22160                 var n = cs[i];
22161                 n.previousSibling = cs[i-1];
22162                 n.nextSibling = cs[i+1];
22163                 if(i == 0){
22164                     this.setFirstChild(n);
22165                 }
22166                 if(i == len-1){
22167                     this.setLastChild(n);
22168                 }
22169             }
22170         }
22171     },
22172
22173     /**
22174      * Returns true if this node is an ancestor (at any point) of the passed node.
22175      * @param {Node} node
22176      * @return {Boolean}
22177      */
22178     contains : function(node){
22179         return node.isAncestor(this);
22180     },
22181
22182     /**
22183      * Returns true if the passed node is an ancestor (at any point) of this node.
22184      * @param {Node} node
22185      * @return {Boolean}
22186      */
22187     isAncestor : function(node){
22188         var p = this.parentNode;
22189         while(p){
22190             if(p == node){
22191                 return true;
22192             }
22193             p = p.parentNode;
22194         }
22195         return false;
22196     },
22197
22198     toString : function(){
22199         return "[Node"+(this.id?" "+this.id:"")+"]";
22200     }
22201 });/*
22202  * Based on:
22203  * Ext JS Library 1.1.1
22204  * Copyright(c) 2006-2007, Ext JS, LLC.
22205  *
22206  * Originally Released Under LGPL - original licence link has changed is not relivant.
22207  *
22208  * Fork - LGPL
22209  * <script type="text/javascript">
22210  */
22211  
22212
22213 /**
22214  * @class Roo.ComponentMgr
22215  * Provides a common registry of all components on a page so that they can be easily accessed by component id (see {@link Roo.getCmp}).
22216  * @singleton
22217  */
22218 Roo.ComponentMgr = function(){
22219     var all = new Roo.util.MixedCollection();
22220
22221     return {
22222         /**
22223          * Registers a component.
22224          * @param {Roo.Component} c The component
22225          */
22226         register : function(c){
22227             all.add(c);
22228         },
22229
22230         /**
22231          * Unregisters a component.
22232          * @param {Roo.Component} c The component
22233          */
22234         unregister : function(c){
22235             all.remove(c);
22236         },
22237
22238         /**
22239          * Returns a component by id
22240          * @param {String} id The component id
22241          */
22242         get : function(id){
22243             return all.get(id);
22244         },
22245
22246         /**
22247          * Registers a function that will be called when a specified component is added to ComponentMgr
22248          * @param {String} id The component id
22249          * @param {Funtction} fn The callback function
22250          * @param {Object} scope The scope of the callback
22251          */
22252         onAvailable : function(id, fn, scope){
22253             all.on("add", function(index, o){
22254                 if(o.id == id){
22255                     fn.call(scope || o, o);
22256                     all.un("add", fn, scope);
22257                 }
22258             });
22259         }
22260     };
22261 }();/*
22262  * Based on:
22263  * Ext JS Library 1.1.1
22264  * Copyright(c) 2006-2007, Ext JS, LLC.
22265  *
22266  * Originally Released Under LGPL - original licence link has changed is not relivant.
22267  *
22268  * Fork - LGPL
22269  * <script type="text/javascript">
22270  */
22271  
22272 /**
22273  * @class Roo.Component
22274  * @extends Roo.util.Observable
22275  * Base class for all major Roo components.  All subclasses of Component can automatically participate in the standard
22276  * Roo component lifecycle of creation, rendering and destruction.  They also have automatic support for basic hide/show
22277  * and enable/disable behavior.  Component allows any subclass to be lazy-rendered into any {@link Roo.Container} and
22278  * to be automatically registered with the {@link Roo.ComponentMgr} so that it can be referenced at any time via {@link Roo.getCmp}.
22279  * All visual components (widgets) that require rendering into a layout should subclass Component.
22280  * @constructor
22281  * @param {Roo.Element/String/Object} config The configuration options.  If an element is passed, it is set as the internal
22282  * 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
22283  * and is used as the component id.  Otherwise, it is assumed to be a standard config object and is applied to the component.
22284  */
22285 Roo.Component = function(config){
22286     config = config || {};
22287     if(config.tagName || config.dom || typeof config == "string"){ // element object
22288         config = {el: config, id: config.id || config};
22289     }
22290     this.initialConfig = config;
22291
22292     Roo.apply(this, config);
22293     this.addEvents({
22294         /**
22295          * @event disable
22296          * Fires after the component is disabled.
22297              * @param {Roo.Component} this
22298              */
22299         disable : true,
22300         /**
22301          * @event enable
22302          * Fires after the component is enabled.
22303              * @param {Roo.Component} this
22304              */
22305         enable : true,
22306         /**
22307          * @event beforeshow
22308          * Fires before the component is shown.  Return false to stop the show.
22309              * @param {Roo.Component} this
22310              */
22311         beforeshow : true,
22312         /**
22313          * @event show
22314          * Fires after the component is shown.
22315              * @param {Roo.Component} this
22316              */
22317         show : true,
22318         /**
22319          * @event beforehide
22320          * Fires before the component is hidden. Return false to stop the hide.
22321              * @param {Roo.Component} this
22322              */
22323         beforehide : true,
22324         /**
22325          * @event hide
22326          * Fires after the component is hidden.
22327              * @param {Roo.Component} this
22328              */
22329         hide : true,
22330         /**
22331          * @event beforerender
22332          * Fires before the component is rendered. Return false to stop the render.
22333              * @param {Roo.Component} this
22334              */
22335         beforerender : true,
22336         /**
22337          * @event render
22338          * Fires after the component is rendered.
22339              * @param {Roo.Component} this
22340              */
22341         render : true,
22342         /**
22343          * @event beforedestroy
22344          * Fires before the component is destroyed. Return false to stop the destroy.
22345              * @param {Roo.Component} this
22346              */
22347         beforedestroy : true,
22348         /**
22349          * @event destroy
22350          * Fires after the component is destroyed.
22351              * @param {Roo.Component} this
22352              */
22353         destroy : true
22354     });
22355     if(!this.id){
22356         this.id = "ext-comp-" + (++Roo.Component.AUTO_ID);
22357     }
22358     Roo.ComponentMgr.register(this);
22359     Roo.Component.superclass.constructor.call(this);
22360     this.initComponent();
22361     if(this.renderTo){ // not supported by all components yet. use at your own risk!
22362         this.render(this.renderTo);
22363         delete this.renderTo;
22364     }
22365 };
22366
22367 /** @private */
22368 Roo.Component.AUTO_ID = 1000;
22369
22370 Roo.extend(Roo.Component, Roo.util.Observable, {
22371     /**
22372      * @scope Roo.Component.prototype
22373      * @type {Boolean}
22374      * true if this component is hidden. Read-only.
22375      */
22376     hidden : false,
22377     /**
22378      * @type {Boolean}
22379      * true if this component is disabled. Read-only.
22380      */
22381     disabled : false,
22382     /**
22383      * @type {Boolean}
22384      * true if this component has been rendered. Read-only.
22385      */
22386     rendered : false,
22387     
22388     /** @cfg {String} disableClass
22389      * CSS class added to the component when it is disabled (defaults to "x-item-disabled").
22390      */
22391     disabledClass : "x-item-disabled",
22392         /** @cfg {Boolean} allowDomMove
22393          * Whether the component can move the Dom node when rendering (defaults to true).
22394          */
22395     allowDomMove : true,
22396     /** @cfg {String} hideMode
22397      * How this component should hidden. Supported values are
22398      * "visibility" (css visibility), "offsets" (negative offset position) and
22399      * "display" (css display) - defaults to "display".
22400      */
22401     hideMode: 'display',
22402
22403     /** @private */
22404     ctype : "Roo.Component",
22405
22406     /**
22407      * @cfg {String} actionMode 
22408      * which property holds the element that used for  hide() / show() / disable() / enable()
22409      * default is 'el' 
22410      */
22411     actionMode : "el",
22412
22413     /** @private */
22414     getActionEl : function(){
22415         return this[this.actionMode];
22416     },
22417
22418     initComponent : Roo.emptyFn,
22419     /**
22420      * If this is a lazy rendering component, render it to its container element.
22421      * @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.
22422      */
22423     render : function(container, position){
22424         if(!this.rendered && this.fireEvent("beforerender", this) !== false){
22425             if(!container && this.el){
22426                 this.el = Roo.get(this.el);
22427                 container = this.el.dom.parentNode;
22428                 this.allowDomMove = false;
22429             }
22430             this.container = Roo.get(container);
22431             this.rendered = true;
22432             if(position !== undefined){
22433                 if(typeof position == 'number'){
22434                     position = this.container.dom.childNodes[position];
22435                 }else{
22436                     position = Roo.getDom(position);
22437                 }
22438             }
22439             this.onRender(this.container, position || null);
22440             if(this.cls){
22441                 this.el.addClass(this.cls);
22442                 delete this.cls;
22443             }
22444             if(this.style){
22445                 this.el.applyStyles(this.style);
22446                 delete this.style;
22447             }
22448             this.fireEvent("render", this);
22449             this.afterRender(this.container);
22450             if(this.hidden){
22451                 this.hide();
22452             }
22453             if(this.disabled){
22454                 this.disable();
22455             }
22456         }
22457         return this;
22458     },
22459
22460     /** @private */
22461     // default function is not really useful
22462     onRender : function(ct, position){
22463         if(this.el){
22464             this.el = Roo.get(this.el);
22465             if(this.allowDomMove !== false){
22466                 ct.dom.insertBefore(this.el.dom, position);
22467             }
22468         }
22469     },
22470
22471     /** @private */
22472     getAutoCreate : function(){
22473         var cfg = typeof this.autoCreate == "object" ?
22474                       this.autoCreate : Roo.apply({}, this.defaultAutoCreate);
22475         if(this.id && !cfg.id){
22476             cfg.id = this.id;
22477         }
22478         return cfg;
22479     },
22480
22481     /** @private */
22482     afterRender : Roo.emptyFn,
22483
22484     /**
22485      * Destroys this component by purging any event listeners, removing the component's element from the DOM,
22486      * removing the component from its {@link Roo.Container} (if applicable) and unregistering it from {@link Roo.ComponentMgr}.
22487      */
22488     destroy : function(){
22489         if(this.fireEvent("beforedestroy", this) !== false){
22490             this.purgeListeners();
22491             this.beforeDestroy();
22492             if(this.rendered){
22493                 this.el.removeAllListeners();
22494                 this.el.remove();
22495                 if(this.actionMode == "container"){
22496                     this.container.remove();
22497                 }
22498             }
22499             this.onDestroy();
22500             Roo.ComponentMgr.unregister(this);
22501             this.fireEvent("destroy", this);
22502         }
22503     },
22504
22505         /** @private */
22506     beforeDestroy : function(){
22507
22508     },
22509
22510         /** @private */
22511         onDestroy : function(){
22512
22513     },
22514
22515     /**
22516      * Returns the underlying {@link Roo.Element}.
22517      * @return {Roo.Element} The element
22518      */
22519     getEl : function(){
22520         return this.el;
22521     },
22522
22523     /**
22524      * Returns the id of this component.
22525      * @return {String}
22526      */
22527     getId : function(){
22528         return this.id;
22529     },
22530
22531     /**
22532      * Try to focus this component.
22533      * @param {Boolean} selectText True to also select the text in this component (if applicable)
22534      * @return {Roo.Component} this
22535      */
22536     focus : function(selectText){
22537         if(this.rendered){
22538             this.el.focus();
22539             if(selectText === true){
22540                 this.el.dom.select();
22541             }
22542         }
22543         return this;
22544     },
22545
22546     /** @private */
22547     blur : function(){
22548         if(this.rendered){
22549             this.el.blur();
22550         }
22551         return this;
22552     },
22553
22554     /**
22555      * Disable this component.
22556      * @return {Roo.Component} this
22557      */
22558     disable : function(){
22559         if(this.rendered){
22560             this.onDisable();
22561         }
22562         this.disabled = true;
22563         this.fireEvent("disable", this);
22564         return this;
22565     },
22566
22567         // private
22568     onDisable : function(){
22569         this.getActionEl().addClass(this.disabledClass);
22570         this.el.dom.disabled = true;
22571     },
22572
22573     /**
22574      * Enable this component.
22575      * @return {Roo.Component} this
22576      */
22577     enable : function(){
22578         if(this.rendered){
22579             this.onEnable();
22580         }
22581         this.disabled = false;
22582         this.fireEvent("enable", this);
22583         return this;
22584     },
22585
22586         // private
22587     onEnable : function(){
22588         this.getActionEl().removeClass(this.disabledClass);
22589         this.el.dom.disabled = false;
22590     },
22591
22592     /**
22593      * Convenience function for setting disabled/enabled by boolean.
22594      * @param {Boolean} disabled
22595      */
22596     setDisabled : function(disabled){
22597         this[disabled ? "disable" : "enable"]();
22598     },
22599
22600     /**
22601      * Show this component.
22602      * @return {Roo.Component} this
22603      */
22604     show: function(){
22605         if(this.fireEvent("beforeshow", this) !== false){
22606             this.hidden = false;
22607             if(this.rendered){
22608                 this.onShow();
22609             }
22610             this.fireEvent("show", this);
22611         }
22612         return this;
22613     },
22614
22615     // private
22616     onShow : function(){
22617         var ae = this.getActionEl();
22618         if(this.hideMode == 'visibility'){
22619             ae.dom.style.visibility = "visible";
22620         }else if(this.hideMode == 'offsets'){
22621             ae.removeClass('x-hidden');
22622         }else{
22623             ae.dom.style.display = "";
22624         }
22625     },
22626
22627     /**
22628      * Hide this component.
22629      * @return {Roo.Component} this
22630      */
22631     hide: function(){
22632         if(this.fireEvent("beforehide", this) !== false){
22633             this.hidden = true;
22634             if(this.rendered){
22635                 this.onHide();
22636             }
22637             this.fireEvent("hide", this);
22638         }
22639         return this;
22640     },
22641
22642     // private
22643     onHide : function(){
22644         var ae = this.getActionEl();
22645         if(this.hideMode == 'visibility'){
22646             ae.dom.style.visibility = "hidden";
22647         }else if(this.hideMode == 'offsets'){
22648             ae.addClass('x-hidden');
22649         }else{
22650             ae.dom.style.display = "none";
22651         }
22652     },
22653
22654     /**
22655      * Convenience function to hide or show this component by boolean.
22656      * @param {Boolean} visible True to show, false to hide
22657      * @return {Roo.Component} this
22658      */
22659     setVisible: function(visible){
22660         if(visible) {
22661             this.show();
22662         }else{
22663             this.hide();
22664         }
22665         return this;
22666     },
22667
22668     /**
22669      * Returns true if this component is visible.
22670      */
22671     isVisible : function(){
22672         return this.getActionEl().isVisible();
22673     },
22674
22675     cloneConfig : function(overrides){
22676         overrides = overrides || {};
22677         var id = overrides.id || Roo.id();
22678         var cfg = Roo.applyIf(overrides, this.initialConfig);
22679         cfg.id = id; // prevent dup id
22680         return new this.constructor(cfg);
22681     }
22682 });/*
22683  * Based on:
22684  * Ext JS Library 1.1.1
22685  * Copyright(c) 2006-2007, Ext JS, LLC.
22686  *
22687  * Originally Released Under LGPL - original licence link has changed is not relivant.
22688  *
22689  * Fork - LGPL
22690  * <script type="text/javascript">
22691  */
22692  (function(){ 
22693 /**
22694  * @class Roo.Layer
22695  * @extends Roo.Element
22696  * An extended {@link Roo.Element} object that supports a shadow and shim, constrain to viewport and
22697  * automatic maintaining of shadow/shim positions.
22698  * @cfg {Boolean} shim False to disable the iframe shim in browsers which need one (defaults to true)
22699  * @cfg {String/Boolean} shadow True to create a shadow element with default class "x-layer-shadow", or
22700  * you can pass a string with a CSS class name. False turns off the shadow.
22701  * @cfg {Object} dh DomHelper object config to create element with (defaults to {tag: "div", cls: "x-layer"}).
22702  * @cfg {Boolean} constrain False to disable constrain to viewport (defaults to true)
22703  * @cfg {String} cls CSS class to add to the element
22704  * @cfg {Number} zindex Starting z-index (defaults to 11000)
22705  * @cfg {Number} shadowOffset Number of pixels to offset the shadow (defaults to 3)
22706  * @constructor
22707  * @param {Object} config An object with config options.
22708  * @param {String/HTMLElement} existingEl (optional) Uses an existing DOM element. If the element is not found it creates it.
22709  */
22710
22711 Roo.Layer = function(config, existingEl){
22712     config = config || {};
22713     var dh = Roo.DomHelper;
22714     var cp = config.parentEl, pel = cp ? Roo.getDom(cp) : document.body;
22715     if(existingEl){
22716         this.dom = Roo.getDom(existingEl);
22717     }
22718     if(!this.dom){
22719         var o = config.dh || {tag: "div", cls: "x-layer"};
22720         this.dom = dh.append(pel, o);
22721     }
22722     if(config.cls){
22723         this.addClass(config.cls);
22724     }
22725     this.constrain = config.constrain !== false;
22726     this.visibilityMode = Roo.Element.VISIBILITY;
22727     if(config.id){
22728         this.id = this.dom.id = config.id;
22729     }else{
22730         this.id = Roo.id(this.dom);
22731     }
22732     this.zindex = config.zindex || this.getZIndex();
22733     this.position("absolute", this.zindex);
22734     if(config.shadow){
22735         this.shadowOffset = config.shadowOffset || 4;
22736         this.shadow = new Roo.Shadow({
22737             offset : this.shadowOffset,
22738             mode : config.shadow
22739         });
22740     }else{
22741         this.shadowOffset = 0;
22742     }
22743     this.useShim = config.shim !== false && Roo.useShims;
22744     this.useDisplay = config.useDisplay;
22745     this.hide();
22746 };
22747
22748 var supr = Roo.Element.prototype;
22749
22750 // shims are shared among layer to keep from having 100 iframes
22751 var shims = [];
22752
22753 Roo.extend(Roo.Layer, Roo.Element, {
22754
22755     getZIndex : function(){
22756         return this.zindex || parseInt(this.getStyle("z-index"), 10) || 11000;
22757     },
22758
22759     getShim : function(){
22760         if(!this.useShim){
22761             return null;
22762         }
22763         if(this.shim){
22764             return this.shim;
22765         }
22766         var shim = shims.shift();
22767         if(!shim){
22768             shim = this.createShim();
22769             shim.enableDisplayMode('block');
22770             shim.dom.style.display = 'none';
22771             shim.dom.style.visibility = 'visible';
22772         }
22773         var pn = this.dom.parentNode;
22774         if(shim.dom.parentNode != pn){
22775             pn.insertBefore(shim.dom, this.dom);
22776         }
22777         shim.setStyle('z-index', this.getZIndex()-2);
22778         this.shim = shim;
22779         return shim;
22780     },
22781
22782     hideShim : function(){
22783         if(this.shim){
22784             this.shim.setDisplayed(false);
22785             shims.push(this.shim);
22786             delete this.shim;
22787         }
22788     },
22789
22790     disableShadow : function(){
22791         if(this.shadow){
22792             this.shadowDisabled = true;
22793             this.shadow.hide();
22794             this.lastShadowOffset = this.shadowOffset;
22795             this.shadowOffset = 0;
22796         }
22797     },
22798
22799     enableShadow : function(show){
22800         if(this.shadow){
22801             this.shadowDisabled = false;
22802             this.shadowOffset = this.lastShadowOffset;
22803             delete this.lastShadowOffset;
22804             if(show){
22805                 this.sync(true);
22806             }
22807         }
22808     },
22809
22810     // private
22811     // this code can execute repeatedly in milliseconds (i.e. during a drag) so
22812     // code size was sacrificed for effeciency (e.g. no getBox/setBox, no XY calls)
22813     sync : function(doShow){
22814         var sw = this.shadow;
22815         if(!this.updating && this.isVisible() && (sw || this.useShim)){
22816             var sh = this.getShim();
22817
22818             var w = this.getWidth(),
22819                 h = this.getHeight();
22820
22821             var l = this.getLeft(true),
22822                 t = this.getTop(true);
22823
22824             if(sw && !this.shadowDisabled){
22825                 if(doShow && !sw.isVisible()){
22826                     sw.show(this);
22827                 }else{
22828                     sw.realign(l, t, w, h);
22829                 }
22830                 if(sh){
22831                     if(doShow){
22832                        sh.show();
22833                     }
22834                     // fit the shim behind the shadow, so it is shimmed too
22835                     var a = sw.adjusts, s = sh.dom.style;
22836                     s.left = (Math.min(l, l+a.l))+"px";
22837                     s.top = (Math.min(t, t+a.t))+"px";
22838                     s.width = (w+a.w)+"px";
22839                     s.height = (h+a.h)+"px";
22840                 }
22841             }else if(sh){
22842                 if(doShow){
22843                    sh.show();
22844                 }
22845                 sh.setSize(w, h);
22846                 sh.setLeftTop(l, t);
22847             }
22848             
22849         }
22850     },
22851
22852     // private
22853     destroy : function(){
22854         this.hideShim();
22855         if(this.shadow){
22856             this.shadow.hide();
22857         }
22858         this.removeAllListeners();
22859         var pn = this.dom.parentNode;
22860         if(pn){
22861             pn.removeChild(this.dom);
22862         }
22863         Roo.Element.uncache(this.id);
22864     },
22865
22866     remove : function(){
22867         this.destroy();
22868     },
22869
22870     // private
22871     beginUpdate : function(){
22872         this.updating = true;
22873     },
22874
22875     // private
22876     endUpdate : function(){
22877         this.updating = false;
22878         this.sync(true);
22879     },
22880
22881     // private
22882     hideUnders : function(negOffset){
22883         if(this.shadow){
22884             this.shadow.hide();
22885         }
22886         this.hideShim();
22887     },
22888
22889     // private
22890     constrainXY : function(){
22891         if(this.constrain){
22892             var vw = Roo.lib.Dom.getViewWidth(),
22893                 vh = Roo.lib.Dom.getViewHeight();
22894             var s = Roo.get(document).getScroll();
22895
22896             var xy = this.getXY();
22897             var x = xy[0], y = xy[1];   
22898             var w = this.dom.offsetWidth+this.shadowOffset, h = this.dom.offsetHeight+this.shadowOffset;
22899             // only move it if it needs it
22900             var moved = false;
22901             // first validate right/bottom
22902             if((x + w) > vw+s.left){
22903                 x = vw - w - this.shadowOffset;
22904                 moved = true;
22905             }
22906             if((y + h) > vh+s.top){
22907                 y = vh - h - this.shadowOffset;
22908                 moved = true;
22909             }
22910             // then make sure top/left isn't negative
22911             if(x < s.left){
22912                 x = s.left;
22913                 moved = true;
22914             }
22915             if(y < s.top){
22916                 y = s.top;
22917                 moved = true;
22918             }
22919             if(moved){
22920                 if(this.avoidY){
22921                     var ay = this.avoidY;
22922                     if(y <= ay && (y+h) >= ay){
22923                         y = ay-h-5;   
22924                     }
22925                 }
22926                 xy = [x, y];
22927                 this.storeXY(xy);
22928                 supr.setXY.call(this, xy);
22929                 this.sync();
22930             }
22931         }
22932     },
22933
22934     isVisible : function(){
22935         return this.visible;    
22936     },
22937
22938     // private
22939     showAction : function(){
22940         this.visible = true; // track visibility to prevent getStyle calls
22941         if(this.useDisplay === true){
22942             this.setDisplayed("");
22943         }else if(this.lastXY){
22944             supr.setXY.call(this, this.lastXY);
22945         }else if(this.lastLT){
22946             supr.setLeftTop.call(this, this.lastLT[0], this.lastLT[1]);
22947         }
22948     },
22949
22950     // private
22951     hideAction : function(){
22952         this.visible = false;
22953         if(this.useDisplay === true){
22954             this.setDisplayed(false);
22955         }else{
22956             this.setLeftTop(-10000,-10000);
22957         }
22958     },
22959
22960     // overridden Element method
22961     setVisible : function(v, a, d, c, e){
22962         if(v){
22963             this.showAction();
22964         }
22965         if(a && v){
22966             var cb = function(){
22967                 this.sync(true);
22968                 if(c){
22969                     c();
22970                 }
22971             }.createDelegate(this);
22972             supr.setVisible.call(this, true, true, d, cb, e);
22973         }else{
22974             if(!v){
22975                 this.hideUnders(true);
22976             }
22977             var cb = c;
22978             if(a){
22979                 cb = function(){
22980                     this.hideAction();
22981                     if(c){
22982                         c();
22983                     }
22984                 }.createDelegate(this);
22985             }
22986             supr.setVisible.call(this, v, a, d, cb, e);
22987             if(v){
22988                 this.sync(true);
22989             }else if(!a){
22990                 this.hideAction();
22991             }
22992         }
22993     },
22994
22995     storeXY : function(xy){
22996         delete this.lastLT;
22997         this.lastXY = xy;
22998     },
22999
23000     storeLeftTop : function(left, top){
23001         delete this.lastXY;
23002         this.lastLT = [left, top];
23003     },
23004
23005     // private
23006     beforeFx : function(){
23007         this.beforeAction();
23008         return Roo.Layer.superclass.beforeFx.apply(this, arguments);
23009     },
23010
23011     // private
23012     afterFx : function(){
23013         Roo.Layer.superclass.afterFx.apply(this, arguments);
23014         this.sync(this.isVisible());
23015     },
23016
23017     // private
23018     beforeAction : function(){
23019         if(!this.updating && this.shadow){
23020             this.shadow.hide();
23021         }
23022     },
23023
23024     // overridden Element method
23025     setLeft : function(left){
23026         this.storeLeftTop(left, this.getTop(true));
23027         supr.setLeft.apply(this, arguments);
23028         this.sync();
23029     },
23030
23031     setTop : function(top){
23032         this.storeLeftTop(this.getLeft(true), top);
23033         supr.setTop.apply(this, arguments);
23034         this.sync();
23035     },
23036
23037     setLeftTop : function(left, top){
23038         this.storeLeftTop(left, top);
23039         supr.setLeftTop.apply(this, arguments);
23040         this.sync();
23041     },
23042
23043     setXY : function(xy, a, d, c, e){
23044         this.fixDisplay();
23045         this.beforeAction();
23046         this.storeXY(xy);
23047         var cb = this.createCB(c);
23048         supr.setXY.call(this, xy, a, d, cb, e);
23049         if(!a){
23050             cb();
23051         }
23052     },
23053
23054     // private
23055     createCB : function(c){
23056         var el = this;
23057         return function(){
23058             el.constrainXY();
23059             el.sync(true);
23060             if(c){
23061                 c();
23062             }
23063         };
23064     },
23065
23066     // overridden Element method
23067     setX : function(x, a, d, c, e){
23068         this.setXY([x, this.getY()], a, d, c, e);
23069     },
23070
23071     // overridden Element method
23072     setY : function(y, a, d, c, e){
23073         this.setXY([this.getX(), y], a, d, c, e);
23074     },
23075
23076     // overridden Element method
23077     setSize : function(w, h, a, d, c, e){
23078         this.beforeAction();
23079         var cb = this.createCB(c);
23080         supr.setSize.call(this, w, h, a, d, cb, e);
23081         if(!a){
23082             cb();
23083         }
23084     },
23085
23086     // overridden Element method
23087     setWidth : function(w, a, d, c, e){
23088         this.beforeAction();
23089         var cb = this.createCB(c);
23090         supr.setWidth.call(this, w, a, d, cb, e);
23091         if(!a){
23092             cb();
23093         }
23094     },
23095
23096     // overridden Element method
23097     setHeight : function(h, a, d, c, e){
23098         this.beforeAction();
23099         var cb = this.createCB(c);
23100         supr.setHeight.call(this, h, a, d, cb, e);
23101         if(!a){
23102             cb();
23103         }
23104     },
23105
23106     // overridden Element method
23107     setBounds : function(x, y, w, h, a, d, c, e){
23108         this.beforeAction();
23109         var cb = this.createCB(c);
23110         if(!a){
23111             this.storeXY([x, y]);
23112             supr.setXY.call(this, [x, y]);
23113             supr.setSize.call(this, w, h, a, d, cb, e);
23114             cb();
23115         }else{
23116             supr.setBounds.call(this, x, y, w, h, a, d, cb, e);
23117         }
23118         return this;
23119     },
23120     
23121     /**
23122      * Sets the z-index of this layer and adjusts any shadow and shim z-indexes. The layer z-index is automatically
23123      * incremented by two more than the value passed in so that it always shows above any shadow or shim (the shadow
23124      * element, if any, will be assigned z-index + 1, and the shim element, if any, will be assigned the unmodified z-index).
23125      * @param {Number} zindex The new z-index to set
23126      * @return {this} The Layer
23127      */
23128     setZIndex : function(zindex){
23129         this.zindex = zindex;
23130         this.setStyle("z-index", zindex + 2);
23131         if(this.shadow){
23132             this.shadow.setZIndex(zindex + 1);
23133         }
23134         if(this.shim){
23135             this.shim.setStyle("z-index", zindex);
23136         }
23137     }
23138 });
23139 })();/*
23140  * Based on:
23141  * Ext JS Library 1.1.1
23142  * Copyright(c) 2006-2007, Ext JS, LLC.
23143  *
23144  * Originally Released Under LGPL - original licence link has changed is not relivant.
23145  *
23146  * Fork - LGPL
23147  * <script type="text/javascript">
23148  */
23149
23150
23151 /**
23152  * @class Roo.Shadow
23153  * Simple class that can provide a shadow effect for any element.  Note that the element MUST be absolutely positioned,
23154  * and the shadow does not provide any shimming.  This should be used only in simple cases -- for more advanced
23155  * functionality that can also provide the same shadow effect, see the {@link Roo.Layer} class.
23156  * @constructor
23157  * Create a new Shadow
23158  * @param {Object} config The config object
23159  */
23160 Roo.Shadow = function(config){
23161     Roo.apply(this, config);
23162     if(typeof this.mode != "string"){
23163         this.mode = this.defaultMode;
23164     }
23165     var o = this.offset, a = {h: 0};
23166     var rad = Math.floor(this.offset/2);
23167     switch(this.mode.toLowerCase()){ // all this hideous nonsense calculates the various offsets for shadows
23168         case "drop":
23169             a.w = 0;
23170             a.l = a.t = o;
23171             a.t -= 1;
23172             if(Roo.isIE){
23173                 a.l -= this.offset + rad;
23174                 a.t -= this.offset + rad;
23175                 a.w -= rad;
23176                 a.h -= rad;
23177                 a.t += 1;
23178             }
23179         break;
23180         case "sides":
23181             a.w = (o*2);
23182             a.l = -o;
23183             a.t = o-1;
23184             if(Roo.isIE){
23185                 a.l -= (this.offset - rad);
23186                 a.t -= this.offset + rad;
23187                 a.l += 1;
23188                 a.w -= (this.offset - rad)*2;
23189                 a.w -= rad + 1;
23190                 a.h -= 1;
23191             }
23192         break;
23193         case "frame":
23194             a.w = a.h = (o*2);
23195             a.l = a.t = -o;
23196             a.t += 1;
23197             a.h -= 2;
23198             if(Roo.isIE){
23199                 a.l -= (this.offset - rad);
23200                 a.t -= (this.offset - rad);
23201                 a.l += 1;
23202                 a.w -= (this.offset + rad + 1);
23203                 a.h -= (this.offset + rad);
23204                 a.h += 1;
23205             }
23206         break;
23207     };
23208
23209     this.adjusts = a;
23210 };
23211
23212 Roo.Shadow.prototype = {
23213     /**
23214      * @cfg {String} mode
23215      * The shadow display mode.  Supports the following options:<br />
23216      * sides: Shadow displays on both sides and bottom only<br />
23217      * frame: Shadow displays equally on all four sides<br />
23218      * drop: Traditional bottom-right drop shadow (default)
23219      */
23220     /**
23221      * @cfg {String} offset
23222      * The number of pixels to offset the shadow from the element (defaults to 4)
23223      */
23224     offset: 4,
23225
23226     // private
23227     defaultMode: "drop",
23228
23229     /**
23230      * Displays the shadow under the target element
23231      * @param {String/HTMLElement/Element} targetEl The id or element under which the shadow should display
23232      */
23233     show : function(target){
23234         target = Roo.get(target);
23235         if(!this.el){
23236             this.el = Roo.Shadow.Pool.pull();
23237             if(this.el.dom.nextSibling != target.dom){
23238                 this.el.insertBefore(target);
23239             }
23240         }
23241         this.el.setStyle("z-index", this.zIndex || parseInt(target.getStyle("z-index"), 10)-1);
23242         if(Roo.isIE){
23243             this.el.dom.style.filter="progid:DXImageTransform.Microsoft.alpha(opacity=50) progid:DXImageTransform.Microsoft.Blur(pixelradius="+(this.offset)+")";
23244         }
23245         this.realign(
23246             target.getLeft(true),
23247             target.getTop(true),
23248             target.getWidth(),
23249             target.getHeight()
23250         );
23251         this.el.dom.style.display = "block";
23252     },
23253
23254     /**
23255      * Returns true if the shadow is visible, else false
23256      */
23257     isVisible : function(){
23258         return this.el ? true : false;  
23259     },
23260
23261     /**
23262      * Direct alignment when values are already available. Show must be called at least once before
23263      * calling this method to ensure it is initialized.
23264      * @param {Number} left The target element left position
23265      * @param {Number} top The target element top position
23266      * @param {Number} width The target element width
23267      * @param {Number} height The target element height
23268      */
23269     realign : function(l, t, w, h){
23270         if(!this.el){
23271             return;
23272         }
23273         var a = this.adjusts, d = this.el.dom, s = d.style;
23274         var iea = 0;
23275         s.left = (l+a.l)+"px";
23276         s.top = (t+a.t)+"px";
23277         var sw = (w+a.w), sh = (h+a.h), sws = sw +"px", shs = sh + "px";
23278  
23279         if(s.width != sws || s.height != shs){
23280             s.width = sws;
23281             s.height = shs;
23282             if(!Roo.isIE){
23283                 var cn = d.childNodes;
23284                 var sww = Math.max(0, (sw-12))+"px";
23285                 cn[0].childNodes[1].style.width = sww;
23286                 cn[1].childNodes[1].style.width = sww;
23287                 cn[2].childNodes[1].style.width = sww;
23288                 cn[1].style.height = Math.max(0, (sh-12))+"px";
23289             }
23290         }
23291     },
23292
23293     /**
23294      * Hides this shadow
23295      */
23296     hide : function(){
23297         if(this.el){
23298             this.el.dom.style.display = "none";
23299             Roo.Shadow.Pool.push(this.el);
23300             delete this.el;
23301         }
23302     },
23303
23304     /**
23305      * Adjust the z-index of this shadow
23306      * @param {Number} zindex The new z-index
23307      */
23308     setZIndex : function(z){
23309         this.zIndex = z;
23310         if(this.el){
23311             this.el.setStyle("z-index", z);
23312         }
23313     }
23314 };
23315
23316 // Private utility class that manages the internal Shadow cache
23317 Roo.Shadow.Pool = function(){
23318     var p = [];
23319     var markup = Roo.isIE ?
23320                  '<div class="x-ie-shadow"></div>' :
23321                  '<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>';
23322     return {
23323         pull : function(){
23324             var sh = p.shift();
23325             if(!sh){
23326                 sh = Roo.get(Roo.DomHelper.insertHtml("beforeBegin", document.body.firstChild, markup));
23327                 sh.autoBoxAdjust = false;
23328             }
23329             return sh;
23330         },
23331
23332         push : function(sh){
23333             p.push(sh);
23334         }
23335     };
23336 }();/*
23337  * Based on:
23338  * Ext JS Library 1.1.1
23339  * Copyright(c) 2006-2007, Ext JS, LLC.
23340  *
23341  * Originally Released Under LGPL - original licence link has changed is not relivant.
23342  *
23343  * Fork - LGPL
23344  * <script type="text/javascript">
23345  */
23346
23347 /**
23348  * @class Roo.BoxComponent
23349  * @extends Roo.Component
23350  * Base class for any visual {@link Roo.Component} that uses a box container.  BoxComponent provides automatic box
23351  * model adjustments for sizing and positioning and will work correctly withnin the Component rendering model.  All
23352  * container classes should subclass BoxComponent so that they will work consistently when nested within other Ext
23353  * layout containers.
23354  * @constructor
23355  * @param {Roo.Element/String/Object} config The configuration options.
23356  */
23357 Roo.BoxComponent = function(config){
23358     Roo.Component.call(this, config);
23359     this.addEvents({
23360         /**
23361          * @event resize
23362          * Fires after the component is resized.
23363              * @param {Roo.Component} this
23364              * @param {Number} adjWidth The box-adjusted width that was set
23365              * @param {Number} adjHeight The box-adjusted height that was set
23366              * @param {Number} rawWidth The width that was originally specified
23367              * @param {Number} rawHeight The height that was originally specified
23368              */
23369         resize : true,
23370         /**
23371          * @event move
23372          * Fires after the component is moved.
23373              * @param {Roo.Component} this
23374              * @param {Number} x The new x position
23375              * @param {Number} y The new y position
23376              */
23377         move : true
23378     });
23379 };
23380
23381 Roo.extend(Roo.BoxComponent, Roo.Component, {
23382     // private, set in afterRender to signify that the component has been rendered
23383     boxReady : false,
23384     // private, used to defer height settings to subclasses
23385     deferHeight: false,
23386     /** @cfg {Number} width
23387      * width (optional) size of component
23388      */
23389      /** @cfg {Number} height
23390      * height (optional) size of component
23391      */
23392      
23393     /**
23394      * Sets the width and height of the component.  This method fires the resize event.  This method can accept
23395      * either width and height as separate numeric arguments, or you can pass a size object like {width:10, height:20}.
23396      * @param {Number/Object} width The new width to set, or a size object in the format {width, height}
23397      * @param {Number} height The new height to set (not required if a size object is passed as the first arg)
23398      * @return {Roo.BoxComponent} this
23399      */
23400     setSize : function(w, h){
23401         // support for standard size objects
23402         if(typeof w == 'object'){
23403             h = w.height;
23404             w = w.width;
23405         }
23406         // not rendered
23407         if(!this.boxReady){
23408             this.width = w;
23409             this.height = h;
23410             return this;
23411         }
23412
23413         // prevent recalcs when not needed
23414         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
23415             return this;
23416         }
23417         this.lastSize = {width: w, height: h};
23418
23419         var adj = this.adjustSize(w, h);
23420         var aw = adj.width, ah = adj.height;
23421         if(aw !== undefined || ah !== undefined){ // this code is nasty but performs better with floaters
23422             var rz = this.getResizeEl();
23423             if(!this.deferHeight && aw !== undefined && ah !== undefined){
23424                 rz.setSize(aw, ah);
23425             }else if(!this.deferHeight && ah !== undefined){
23426                 rz.setHeight(ah);
23427             }else if(aw !== undefined){
23428                 rz.setWidth(aw);
23429             }
23430             this.onResize(aw, ah, w, h);
23431             this.fireEvent('resize', this, aw, ah, w, h);
23432         }
23433         return this;
23434     },
23435
23436     /**
23437      * Gets the current size of the component's underlying element.
23438      * @return {Object} An object containing the element's size {width: (element width), height: (element height)}
23439      */
23440     getSize : function(){
23441         return this.el.getSize();
23442     },
23443
23444     /**
23445      * Gets the current XY position of the component's underlying element.
23446      * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
23447      * @return {Array} The XY position of the element (e.g., [100, 200])
23448      */
23449     getPosition : function(local){
23450         if(local === true){
23451             return [this.el.getLeft(true), this.el.getTop(true)];
23452         }
23453         return this.xy || this.el.getXY();
23454     },
23455
23456     /**
23457      * Gets the current box measurements of the component's underlying element.
23458      * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
23459      * @returns {Object} box An object in the format {x, y, width, height}
23460      */
23461     getBox : function(local){
23462         var s = this.el.getSize();
23463         if(local){
23464             s.x = this.el.getLeft(true);
23465             s.y = this.el.getTop(true);
23466         }else{
23467             var xy = this.xy || this.el.getXY();
23468             s.x = xy[0];
23469             s.y = xy[1];
23470         }
23471         return s;
23472     },
23473
23474     /**
23475      * Sets the current box measurements of the component's underlying element.
23476      * @param {Object} box An object in the format {x, y, width, height}
23477      * @returns {Roo.BoxComponent} this
23478      */
23479     updateBox : function(box){
23480         this.setSize(box.width, box.height);
23481         this.setPagePosition(box.x, box.y);
23482         return this;
23483     },
23484
23485     // protected
23486     getResizeEl : function(){
23487         return this.resizeEl || this.el;
23488     },
23489
23490     // protected
23491     getPositionEl : function(){
23492         return this.positionEl || this.el;
23493     },
23494
23495     /**
23496      * Sets the left and top of the component.  To set the page XY position instead, use {@link #setPagePosition}.
23497      * This method fires the move event.
23498      * @param {Number} left The new left
23499      * @param {Number} top The new top
23500      * @returns {Roo.BoxComponent} this
23501      */
23502     setPosition : function(x, y){
23503         this.x = x;
23504         this.y = y;
23505         if(!this.boxReady){
23506             return this;
23507         }
23508         var adj = this.adjustPosition(x, y);
23509         var ax = adj.x, ay = adj.y;
23510
23511         var el = this.getPositionEl();
23512         if(ax !== undefined || ay !== undefined){
23513             if(ax !== undefined && ay !== undefined){
23514                 el.setLeftTop(ax, ay);
23515             }else if(ax !== undefined){
23516                 el.setLeft(ax);
23517             }else if(ay !== undefined){
23518                 el.setTop(ay);
23519             }
23520             this.onPosition(ax, ay);
23521             this.fireEvent('move', this, ax, ay);
23522         }
23523         return this;
23524     },
23525
23526     /**
23527      * Sets the page XY position of the component.  To set the left and top instead, use {@link #setPosition}.
23528      * This method fires the move event.
23529      * @param {Number} x The new x position
23530      * @param {Number} y The new y position
23531      * @returns {Roo.BoxComponent} this
23532      */
23533     setPagePosition : function(x, y){
23534         this.pageX = x;
23535         this.pageY = y;
23536         if(!this.boxReady){
23537             return;
23538         }
23539         if(x === undefined || y === undefined){ // cannot translate undefined points
23540             return;
23541         }
23542         var p = this.el.translatePoints(x, y);
23543         this.setPosition(p.left, p.top);
23544         return this;
23545     },
23546
23547     // private
23548     onRender : function(ct, position){
23549         Roo.BoxComponent.superclass.onRender.call(this, ct, position);
23550         if(this.resizeEl){
23551             this.resizeEl = Roo.get(this.resizeEl);
23552         }
23553         if(this.positionEl){
23554             this.positionEl = Roo.get(this.positionEl);
23555         }
23556     },
23557
23558     // private
23559     afterRender : function(){
23560         Roo.BoxComponent.superclass.afterRender.call(this);
23561         this.boxReady = true;
23562         this.setSize(this.width, this.height);
23563         if(this.x || this.y){
23564             this.setPosition(this.x, this.y);
23565         }
23566         if(this.pageX || this.pageY){
23567             this.setPagePosition(this.pageX, this.pageY);
23568         }
23569     },
23570
23571     /**
23572      * Force the component's size to recalculate based on the underlying element's current height and width.
23573      * @returns {Roo.BoxComponent} this
23574      */
23575     syncSize : function(){
23576         delete this.lastSize;
23577         this.setSize(this.el.getWidth(), this.el.getHeight());
23578         return this;
23579     },
23580
23581     /**
23582      * Called after the component is resized, this method is empty by default but can be implemented by any
23583      * subclass that needs to perform custom logic after a resize occurs.
23584      * @param {Number} adjWidth The box-adjusted width that was set
23585      * @param {Number} adjHeight The box-adjusted height that was set
23586      * @param {Number} rawWidth The width that was originally specified
23587      * @param {Number} rawHeight The height that was originally specified
23588      */
23589     onResize : function(adjWidth, adjHeight, rawWidth, rawHeight){
23590
23591     },
23592
23593     /**
23594      * Called after the component is moved, this method is empty by default but can be implemented by any
23595      * subclass that needs to perform custom logic after a move occurs.
23596      * @param {Number} x The new x position
23597      * @param {Number} y The new y position
23598      */
23599     onPosition : function(x, y){
23600
23601     },
23602
23603     // private
23604     adjustSize : function(w, h){
23605         if(this.autoWidth){
23606             w = 'auto';
23607         }
23608         if(this.autoHeight){
23609             h = 'auto';
23610         }
23611         return {width : w, height: h};
23612     },
23613
23614     // private
23615     adjustPosition : function(x, y){
23616         return {x : x, y: y};
23617     }
23618 });/*
23619  * Based on:
23620  * Ext JS Library 1.1.1
23621  * Copyright(c) 2006-2007, Ext JS, LLC.
23622  *
23623  * Originally Released Under LGPL - original licence link has changed is not relivant.
23624  *
23625  * Fork - LGPL
23626  * <script type="text/javascript">
23627  */
23628
23629
23630 /**
23631  * @class Roo.SplitBar
23632  * @extends Roo.util.Observable
23633  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
23634  * <br><br>
23635  * Usage:
23636  * <pre><code>
23637 var split = new Roo.SplitBar("elementToDrag", "elementToSize",
23638                    Roo.SplitBar.HORIZONTAL, Roo.SplitBar.LEFT);
23639 split.setAdapter(new Roo.SplitBar.AbsoluteLayoutAdapter("container"));
23640 split.minSize = 100;
23641 split.maxSize = 600;
23642 split.animate = true;
23643 split.on('moved', splitterMoved);
23644 </code></pre>
23645  * @constructor
23646  * Create a new SplitBar
23647  * @param {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
23648  * @param {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
23649  * @param {Number} orientation (optional) Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
23650  * @param {Number} placement (optional) Either Roo.SplitBar.LEFT or Roo.SplitBar.RIGHT for horizontal or  
23651                         Roo.SplitBar.TOP or Roo.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
23652                         position of the SplitBar).
23653  */
23654 Roo.SplitBar = function(dragElement, resizingElement, orientation, placement, existingProxy){
23655     
23656     /** @private */
23657     this.el = Roo.get(dragElement, true);
23658     this.el.dom.unselectable = "on";
23659     /** @private */
23660     this.resizingEl = Roo.get(resizingElement, true);
23661
23662     /**
23663      * @private
23664      * The orientation of the split. Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
23665      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
23666      * @type Number
23667      */
23668     this.orientation = orientation || Roo.SplitBar.HORIZONTAL;
23669     
23670     /**
23671      * The minimum size of the resizing element. (Defaults to 0)
23672      * @type Number
23673      */
23674     this.minSize = 0;
23675     
23676     /**
23677      * The maximum size of the resizing element. (Defaults to 2000)
23678      * @type Number
23679      */
23680     this.maxSize = 2000;
23681     
23682     /**
23683      * Whether to animate the transition to the new size
23684      * @type Boolean
23685      */
23686     this.animate = false;
23687     
23688     /**
23689      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
23690      * @type Boolean
23691      */
23692     this.useShim = false;
23693     
23694     /** @private */
23695     this.shim = null;
23696     
23697     if(!existingProxy){
23698         /** @private */
23699         this.proxy = Roo.SplitBar.createProxy(this.orientation);
23700     }else{
23701         this.proxy = Roo.get(existingProxy).dom;
23702     }
23703     /** @private */
23704     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
23705     
23706     /** @private */
23707     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
23708     
23709     /** @private */
23710     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
23711     
23712     /** @private */
23713     this.dragSpecs = {};
23714     
23715     /**
23716      * @private The adapter to use to positon and resize elements
23717      */
23718     this.adapter = new Roo.SplitBar.BasicLayoutAdapter();
23719     this.adapter.init(this);
23720     
23721     if(this.orientation == Roo.SplitBar.HORIZONTAL){
23722         /** @private */
23723         this.placement = placement || (this.el.getX() > this.resizingEl.getX() ? Roo.SplitBar.LEFT : Roo.SplitBar.RIGHT);
23724         this.el.addClass("x-splitbar-h");
23725     }else{
23726         /** @private */
23727         this.placement = placement || (this.el.getY() > this.resizingEl.getY() ? Roo.SplitBar.TOP : Roo.SplitBar.BOTTOM);
23728         this.el.addClass("x-splitbar-v");
23729     }
23730     
23731     this.addEvents({
23732         /**
23733          * @event resize
23734          * Fires when the splitter is moved (alias for {@link #event-moved})
23735          * @param {Roo.SplitBar} this
23736          * @param {Number} newSize the new width or height
23737          */
23738         "resize" : true,
23739         /**
23740          * @event moved
23741          * Fires when the splitter is moved
23742          * @param {Roo.SplitBar} this
23743          * @param {Number} newSize the new width or height
23744          */
23745         "moved" : true,
23746         /**
23747          * @event beforeresize
23748          * Fires before the splitter is dragged
23749          * @param {Roo.SplitBar} this
23750          */
23751         "beforeresize" : true,
23752
23753         "beforeapply" : true
23754     });
23755
23756     Roo.util.Observable.call(this);
23757 };
23758
23759 Roo.extend(Roo.SplitBar, Roo.util.Observable, {
23760     onStartProxyDrag : function(x, y){
23761         this.fireEvent("beforeresize", this);
23762         if(!this.overlay){
23763             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "x-drag-overlay", html: "&#160;"}, true);
23764             o.unselectable();
23765             o.enableDisplayMode("block");
23766             // all splitbars share the same overlay
23767             Roo.SplitBar.prototype.overlay = o;
23768         }
23769         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
23770         this.overlay.show();
23771         Roo.get(this.proxy).setDisplayed("block");
23772         var size = this.adapter.getElementSize(this);
23773         this.activeMinSize = this.getMinimumSize();;
23774         this.activeMaxSize = this.getMaximumSize();;
23775         var c1 = size - this.activeMinSize;
23776         var c2 = Math.max(this.activeMaxSize - size, 0);
23777         if(this.orientation == Roo.SplitBar.HORIZONTAL){
23778             this.dd.resetConstraints();
23779             this.dd.setXConstraint(
23780                 this.placement == Roo.SplitBar.LEFT ? c1 : c2, 
23781                 this.placement == Roo.SplitBar.LEFT ? c2 : c1
23782             );
23783             this.dd.setYConstraint(0, 0);
23784         }else{
23785             this.dd.resetConstraints();
23786             this.dd.setXConstraint(0, 0);
23787             this.dd.setYConstraint(
23788                 this.placement == Roo.SplitBar.TOP ? c1 : c2, 
23789                 this.placement == Roo.SplitBar.TOP ? c2 : c1
23790             );
23791          }
23792         this.dragSpecs.startSize = size;
23793         this.dragSpecs.startPoint = [x, y];
23794         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
23795     },
23796     
23797     /** 
23798      * @private Called after the drag operation by the DDProxy
23799      */
23800     onEndProxyDrag : function(e){
23801         Roo.get(this.proxy).setDisplayed(false);
23802         var endPoint = Roo.lib.Event.getXY(e);
23803         if(this.overlay){
23804             this.overlay.hide();
23805         }
23806         var newSize;
23807         if(this.orientation == Roo.SplitBar.HORIZONTAL){
23808             newSize = this.dragSpecs.startSize + 
23809                 (this.placement == Roo.SplitBar.LEFT ?
23810                     endPoint[0] - this.dragSpecs.startPoint[0] :
23811                     this.dragSpecs.startPoint[0] - endPoint[0]
23812                 );
23813         }else{
23814             newSize = this.dragSpecs.startSize + 
23815                 (this.placement == Roo.SplitBar.TOP ?
23816                     endPoint[1] - this.dragSpecs.startPoint[1] :
23817                     this.dragSpecs.startPoint[1] - endPoint[1]
23818                 );
23819         }
23820         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
23821         if(newSize != this.dragSpecs.startSize){
23822             if(this.fireEvent('beforeapply', this, newSize) !== false){
23823                 this.adapter.setElementSize(this, newSize);
23824                 this.fireEvent("moved", this, newSize);
23825                 this.fireEvent("resize", this, newSize);
23826             }
23827         }
23828     },
23829     
23830     /**
23831      * Get the adapter this SplitBar uses
23832      * @return The adapter object
23833      */
23834     getAdapter : function(){
23835         return this.adapter;
23836     },
23837     
23838     /**
23839      * Set the adapter this SplitBar uses
23840      * @param {Object} adapter A SplitBar adapter object
23841      */
23842     setAdapter : function(adapter){
23843         this.adapter = adapter;
23844         this.adapter.init(this);
23845     },
23846     
23847     /**
23848      * Gets the minimum size for the resizing element
23849      * @return {Number} The minimum size
23850      */
23851     getMinimumSize : function(){
23852         return this.minSize;
23853     },
23854     
23855     /**
23856      * Sets the minimum size for the resizing element
23857      * @param {Number} minSize The minimum size
23858      */
23859     setMinimumSize : function(minSize){
23860         this.minSize = minSize;
23861     },
23862     
23863     /**
23864      * Gets the maximum size for the resizing element
23865      * @return {Number} The maximum size
23866      */
23867     getMaximumSize : function(){
23868         return this.maxSize;
23869     },
23870     
23871     /**
23872      * Sets the maximum size for the resizing element
23873      * @param {Number} maxSize The maximum size
23874      */
23875     setMaximumSize : function(maxSize){
23876         this.maxSize = maxSize;
23877     },
23878     
23879     /**
23880      * Sets the initialize size for the resizing element
23881      * @param {Number} size The initial size
23882      */
23883     setCurrentSize : function(size){
23884         var oldAnimate = this.animate;
23885         this.animate = false;
23886         this.adapter.setElementSize(this, size);
23887         this.animate = oldAnimate;
23888     },
23889     
23890     /**
23891      * Destroy this splitbar. 
23892      * @param {Boolean} removeEl True to remove the element
23893      */
23894     destroy : function(removeEl){
23895         if(this.shim){
23896             this.shim.remove();
23897         }
23898         this.dd.unreg();
23899         this.proxy.parentNode.removeChild(this.proxy);
23900         if(removeEl){
23901             this.el.remove();
23902         }
23903     }
23904 });
23905
23906 /**
23907  * @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.
23908  */
23909 Roo.SplitBar.createProxy = function(dir){
23910     var proxy = new Roo.Element(document.createElement("div"));
23911     proxy.unselectable();
23912     var cls = 'x-splitbar-proxy';
23913     proxy.addClass(cls + ' ' + (dir == Roo.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
23914     document.body.appendChild(proxy.dom);
23915     return proxy.dom;
23916 };
23917
23918 /** 
23919  * @class Roo.SplitBar.BasicLayoutAdapter
23920  * Default Adapter. It assumes the splitter and resizing element are not positioned
23921  * elements and only gets/sets the width of the element. Generally used for table based layouts.
23922  */
23923 Roo.SplitBar.BasicLayoutAdapter = function(){
23924 };
23925
23926 Roo.SplitBar.BasicLayoutAdapter.prototype = {
23927     // do nothing for now
23928     init : function(s){
23929     
23930     },
23931     /**
23932      * Called before drag operations to get the current size of the resizing element. 
23933      * @param {Roo.SplitBar} s The SplitBar using this adapter
23934      */
23935      getElementSize : function(s){
23936         if(s.orientation == Roo.SplitBar.HORIZONTAL){
23937             return s.resizingEl.getWidth();
23938         }else{
23939             return s.resizingEl.getHeight();
23940         }
23941     },
23942     
23943     /**
23944      * Called after drag operations to set the size of the resizing element.
23945      * @param {Roo.SplitBar} s The SplitBar using this adapter
23946      * @param {Number} newSize The new size to set
23947      * @param {Function} onComplete A function to be invoked when resizing is complete
23948      */
23949     setElementSize : function(s, newSize, onComplete){
23950         if(s.orientation == Roo.SplitBar.HORIZONTAL){
23951             if(!s.animate){
23952                 s.resizingEl.setWidth(newSize);
23953                 if(onComplete){
23954                     onComplete(s, newSize);
23955                 }
23956             }else{
23957                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
23958             }
23959         }else{
23960             
23961             if(!s.animate){
23962                 s.resizingEl.setHeight(newSize);
23963                 if(onComplete){
23964                     onComplete(s, newSize);
23965                 }
23966             }else{
23967                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
23968             }
23969         }
23970     }
23971 };
23972
23973 /** 
23974  *@class Roo.SplitBar.AbsoluteLayoutAdapter
23975  * @extends Roo.SplitBar.BasicLayoutAdapter
23976  * Adapter that  moves the splitter element to align with the resized sizing element. 
23977  * Used with an absolute positioned SplitBar.
23978  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
23979  * document.body, make sure you assign an id to the body element.
23980  */
23981 Roo.SplitBar.AbsoluteLayoutAdapter = function(container){
23982     this.basic = new Roo.SplitBar.BasicLayoutAdapter();
23983     this.container = Roo.get(container);
23984 };
23985
23986 Roo.SplitBar.AbsoluteLayoutAdapter.prototype = {
23987     init : function(s){
23988         this.basic.init(s);
23989     },
23990     
23991     getElementSize : function(s){
23992         return this.basic.getElementSize(s);
23993     },
23994     
23995     setElementSize : function(s, newSize, onComplete){
23996         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
23997     },
23998     
23999     moveSplitter : function(s){
24000         var yes = Roo.SplitBar;
24001         switch(s.placement){
24002             case yes.LEFT:
24003                 s.el.setX(s.resizingEl.getRight());
24004                 break;
24005             case yes.RIGHT:
24006                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
24007                 break;
24008             case yes.TOP:
24009                 s.el.setY(s.resizingEl.getBottom());
24010                 break;
24011             case yes.BOTTOM:
24012                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
24013                 break;
24014         }
24015     }
24016 };
24017
24018 /**
24019  * Orientation constant - Create a vertical SplitBar
24020  * @static
24021  * @type Number
24022  */
24023 Roo.SplitBar.VERTICAL = 1;
24024
24025 /**
24026  * Orientation constant - Create a horizontal SplitBar
24027  * @static
24028  * @type Number
24029  */
24030 Roo.SplitBar.HORIZONTAL = 2;
24031
24032 /**
24033  * Placement constant - The resizing element is to the left of the splitter element
24034  * @static
24035  * @type Number
24036  */
24037 Roo.SplitBar.LEFT = 1;
24038
24039 /**
24040  * Placement constant - The resizing element is to the right of the splitter element
24041  * @static
24042  * @type Number
24043  */
24044 Roo.SplitBar.RIGHT = 2;
24045
24046 /**
24047  * Placement constant - The resizing element is positioned above the splitter element
24048  * @static
24049  * @type Number
24050  */
24051 Roo.SplitBar.TOP = 3;
24052
24053 /**
24054  * Placement constant - The resizing element is positioned under splitter element
24055  * @static
24056  * @type Number
24057  */
24058 Roo.SplitBar.BOTTOM = 4;
24059 /*
24060  * Based on:
24061  * Ext JS Library 1.1.1
24062  * Copyright(c) 2006-2007, Ext JS, LLC.
24063  *
24064  * Originally Released Under LGPL - original licence link has changed is not relivant.
24065  *
24066  * Fork - LGPL
24067  * <script type="text/javascript">
24068  */
24069
24070 /**
24071  * @class Roo.View
24072  * @extends Roo.util.Observable
24073  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
24074  * This class also supports single and multi selection modes. <br>
24075  * Create a data model bound view:
24076  <pre><code>
24077  var store = new Roo.data.Store(...);
24078
24079  var view = new Roo.View({
24080     el : "my-element",
24081     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
24082  
24083     singleSelect: true,
24084     selectedClass: "ydataview-selected",
24085     store: store
24086  });
24087
24088  // listen for node click?
24089  view.on("click", function(vw, index, node, e){
24090  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
24091  });
24092
24093  // load XML data
24094  dataModel.load("foobar.xml");
24095  </code></pre>
24096  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
24097  * <br><br>
24098  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
24099  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
24100  * 
24101  * Note: old style constructor is still suported (container, template, config)
24102  * 
24103  * @constructor
24104  * Create a new View
24105  * @param {Object} config The config object
24106  * 
24107  */
24108 Roo.View = function(config, depreciated_tpl, depreciated_config){
24109     
24110     if (typeof(depreciated_tpl) == 'undefined') {
24111         // new way.. - universal constructor.
24112         Roo.apply(this, config);
24113         this.el  = Roo.get(this.el);
24114     } else {
24115         // old format..
24116         this.el  = Roo.get(config);
24117         this.tpl = depreciated_tpl;
24118         Roo.apply(this, depreciated_config);
24119     }
24120     this.wrapEl  = this.el.wrap().wrap();
24121     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
24122     
24123     
24124     if(typeof(this.tpl) == "string"){
24125         this.tpl = new Roo.Template(this.tpl);
24126     } else {
24127         // support xtype ctors..
24128         this.tpl = new Roo.factory(this.tpl, Roo);
24129     }
24130     
24131     
24132     this.tpl.compile();
24133    
24134   
24135     
24136      
24137     /** @private */
24138     this.addEvents({
24139         /**
24140          * @event beforeclick
24141          * Fires before a click is processed. Returns false to cancel the default action.
24142          * @param {Roo.View} this
24143          * @param {Number} index The index of the target node
24144          * @param {HTMLElement} node The target node
24145          * @param {Roo.EventObject} e The raw event object
24146          */
24147             "beforeclick" : true,
24148         /**
24149          * @event click
24150          * Fires when a template node is clicked.
24151          * @param {Roo.View} this
24152          * @param {Number} index The index of the target node
24153          * @param {HTMLElement} node The target node
24154          * @param {Roo.EventObject} e The raw event object
24155          */
24156             "click" : true,
24157         /**
24158          * @event dblclick
24159          * Fires when a template node is double clicked.
24160          * @param {Roo.View} this
24161          * @param {Number} index The index of the target node
24162          * @param {HTMLElement} node The target node
24163          * @param {Roo.EventObject} e The raw event object
24164          */
24165             "dblclick" : true,
24166         /**
24167          * @event contextmenu
24168          * Fires when a template node is right clicked.
24169          * @param {Roo.View} this
24170          * @param {Number} index The index of the target node
24171          * @param {HTMLElement} node The target node
24172          * @param {Roo.EventObject} e The raw event object
24173          */
24174             "contextmenu" : true,
24175         /**
24176          * @event selectionchange
24177          * Fires when the selected nodes change.
24178          * @param {Roo.View} this
24179          * @param {Array} selections Array of the selected nodes
24180          */
24181             "selectionchange" : true,
24182     
24183         /**
24184          * @event beforeselect
24185          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
24186          * @param {Roo.View} this
24187          * @param {HTMLElement} node The node to be selected
24188          * @param {Array} selections Array of currently selected nodes
24189          */
24190             "beforeselect" : true,
24191         /**
24192          * @event preparedata
24193          * Fires on every row to render, to allow you to change the data.
24194          * @param {Roo.View} this
24195          * @param {Object} data to be rendered (change this)
24196          */
24197           "preparedata" : true
24198           
24199           
24200         });
24201
24202
24203
24204     this.el.on({
24205         "click": this.onClick,
24206         "dblclick": this.onDblClick,
24207         "contextmenu": this.onContextMenu,
24208         scope:this
24209     });
24210
24211     this.selections = [];
24212     this.nodes = [];
24213     this.cmp = new Roo.CompositeElementLite([]);
24214     if(this.store){
24215         this.store = Roo.factory(this.store, Roo.data);
24216         this.setStore(this.store, true);
24217     }
24218     
24219     if ( this.footer && this.footer.xtype) {
24220            
24221          var fctr = this.wrapEl.appendChild(document.createElement("div"));
24222         
24223         this.footer.dataSource = this.store
24224         this.footer.container = fctr;
24225         this.footer = Roo.factory(this.footer, Roo);
24226         fctr.insertFirst(this.el);
24227         
24228         // this is a bit insane - as the paging toolbar seems to detach the el..
24229 //        dom.parentNode.parentNode.parentNode
24230          // they get detached?
24231     }
24232     
24233     
24234     Roo.View.superclass.constructor.call(this);
24235     
24236     
24237 };
24238
24239 Roo.extend(Roo.View, Roo.util.Observable, {
24240     
24241      /**
24242      * @cfg {Roo.data.Store} store Data store to load data from.
24243      */
24244     store : false,
24245     
24246     /**
24247      * @cfg {String|Roo.Element} el The container element.
24248      */
24249     el : '',
24250     
24251     /**
24252      * @cfg {String|Roo.Template} tpl The template used by this View 
24253      */
24254     tpl : false,
24255     /**
24256      * @cfg {String} dataName the named area of the template to use as the data area
24257      *                          Works with domtemplates roo-name="name"
24258      */
24259     dataName: false,
24260     /**
24261      * @cfg {String} selectedClass The css class to add to selected nodes
24262      */
24263     selectedClass : "x-view-selected",
24264      /**
24265      * @cfg {String} emptyText The empty text to show when nothing is loaded.
24266      */
24267     emptyText : "",
24268     
24269     /**
24270      * @cfg {String} text to display on mask (default Loading)
24271      */
24272     mask : false,
24273     /**
24274      * @cfg {Boolean} multiSelect Allow multiple selection
24275      */
24276     multiSelect : false,
24277     /**
24278      * @cfg {Boolean} singleSelect Allow single selection
24279      */
24280     singleSelect:  false,
24281     
24282     /**
24283      * @cfg {Boolean} toggleSelect - selecting 
24284      */
24285     toggleSelect : false,
24286     
24287     /**
24288      * Returns the element this view is bound to.
24289      * @return {Roo.Element}
24290      */
24291     getEl : function(){
24292         return this.wrapEl;
24293     },
24294     
24295     
24296
24297     /**
24298      * Refreshes the view. - called by datachanged on the store. - do not call directly.
24299      */
24300     refresh : function(){
24301         var t = this.tpl;
24302         
24303         // if we are using something like 'domtemplate', then
24304         // the what gets used is:
24305         // t.applySubtemplate(NAME, data, wrapping data..)
24306         // the outer template then get' applied with
24307         //     the store 'extra data'
24308         // and the body get's added to the
24309         //      roo-name="data" node?
24310         //      <span class='roo-tpl-{name}'></span> ?????
24311         
24312         
24313         
24314         this.clearSelections();
24315         this.el.update("");
24316         var html = [];
24317         var records = this.store.getRange();
24318         if(records.length < 1) {
24319             
24320             // is this valid??  = should it render a template??
24321             
24322             this.el.update(this.emptyText);
24323             return;
24324         }
24325         var el = this.el;
24326         if (this.dataName) {
24327             this.el.update(t.apply(this.store.meta)); //????
24328             el = this.el.child('.roo-tpl-' + this.dataName);
24329         }
24330         
24331         for(var i = 0, len = records.length; i < len; i++){
24332             var data = this.prepareData(records[i].data, i, records[i]);
24333             this.fireEvent("preparedata", this, data, i, records[i]);
24334             html[html.length] = Roo.util.Format.trim(
24335                 this.dataName ?
24336                     t.applySubtemplate(this.dataName, data, this.store.meta) :
24337                     t.apply(data)
24338             );
24339         }
24340         
24341         
24342         
24343         el.update(html.join(""));
24344         this.nodes = el.dom.childNodes;
24345         this.updateIndexes(0);
24346     },
24347
24348     /**
24349      * Function to override to reformat the data that is sent to
24350      * the template for each node.
24351      * DEPRICATED - use the preparedata event handler.
24352      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
24353      * a JSON object for an UpdateManager bound view).
24354      */
24355     prepareData : function(data, index, record)
24356     {
24357         this.fireEvent("preparedata", this, data, index, record);
24358         return data;
24359     },
24360
24361     onUpdate : function(ds, record){
24362         this.clearSelections();
24363         var index = this.store.indexOf(record);
24364         var n = this.nodes[index];
24365         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
24366         n.parentNode.removeChild(n);
24367         this.updateIndexes(index, index);
24368     },
24369
24370     
24371     
24372 // --------- FIXME     
24373     onAdd : function(ds, records, index)
24374     {
24375         this.clearSelections();
24376         if(this.nodes.length == 0){
24377             this.refresh();
24378             return;
24379         }
24380         var n = this.nodes[index];
24381         for(var i = 0, len = records.length; i < len; i++){
24382             var d = this.prepareData(records[i].data, i, records[i]);
24383             if(n){
24384                 this.tpl.insertBefore(n, d);
24385             }else{
24386                 
24387                 this.tpl.append(this.el, d);
24388             }
24389         }
24390         this.updateIndexes(index);
24391     },
24392
24393     onRemove : function(ds, record, index){
24394         this.clearSelections();
24395         var el = this.dataName  ?
24396             this.el.child('.roo-tpl-' + this.dataName) :
24397             this.el; 
24398         el.dom.removeChild(this.nodes[index]);
24399         this.updateIndexes(index);
24400     },
24401
24402     /**
24403      * Refresh an individual node.
24404      * @param {Number} index
24405      */
24406     refreshNode : function(index){
24407         this.onUpdate(this.store, this.store.getAt(index));
24408     },
24409
24410     updateIndexes : function(startIndex, endIndex){
24411         var ns = this.nodes;
24412         startIndex = startIndex || 0;
24413         endIndex = endIndex || ns.length - 1;
24414         for(var i = startIndex; i <= endIndex; i++){
24415             ns[i].nodeIndex = i;
24416         }
24417     },
24418
24419     /**
24420      * Changes the data store this view uses and refresh the view.
24421      * @param {Store} store
24422      */
24423     setStore : function(store, initial){
24424         if(!initial && this.store){
24425             this.store.un("datachanged", this.refresh);
24426             this.store.un("add", this.onAdd);
24427             this.store.un("remove", this.onRemove);
24428             this.store.un("update", this.onUpdate);
24429             this.store.un("clear", this.refresh);
24430             this.store.un("beforeload", this.onBeforeLoad);
24431             this.store.un("load", this.onLoad);
24432             this.store.un("loadexception", this.onLoad);
24433         }
24434         if(store){
24435           
24436             store.on("datachanged", this.refresh, this);
24437             store.on("add", this.onAdd, this);
24438             store.on("remove", this.onRemove, this);
24439             store.on("update", this.onUpdate, this);
24440             store.on("clear", this.refresh, this);
24441             store.on("beforeload", this.onBeforeLoad, this);
24442             store.on("load", this.onLoad, this);
24443             store.on("loadexception", this.onLoad, this);
24444         }
24445         
24446         if(store){
24447             this.refresh();
24448         }
24449     },
24450     /**
24451      * onbeforeLoad - masks the loading area.
24452      *
24453      */
24454     onBeforeLoad : function()
24455     {
24456         this.el.update("");
24457         this.el.mask(this.mask ? this.mask : "Loading" ); 
24458     },
24459     onLoad : function ()
24460     {
24461         this.el.unmask();
24462     },
24463     
24464
24465     /**
24466      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
24467      * @param {HTMLElement} node
24468      * @return {HTMLElement} The template node
24469      */
24470     findItemFromChild : function(node){
24471         var el = this.dataName  ?
24472             this.el.child('.roo-tpl-' + this.dataName,true) :
24473             this.el.dom; 
24474         
24475         if(!node || node.parentNode == el){
24476                     return node;
24477             }
24478             var p = node.parentNode;
24479             while(p && p != el){
24480             if(p.parentNode == el){
24481                 return p;
24482             }
24483             p = p.parentNode;
24484         }
24485             return null;
24486     },
24487
24488     /** @ignore */
24489     onClick : function(e){
24490         var item = this.findItemFromChild(e.getTarget());
24491         if(item){
24492             var index = this.indexOf(item);
24493             if(this.onItemClick(item, index, e) !== false){
24494                 this.fireEvent("click", this, index, item, e);
24495             }
24496         }else{
24497             this.clearSelections();
24498         }
24499     },
24500
24501     /** @ignore */
24502     onContextMenu : function(e){
24503         var item = this.findItemFromChild(e.getTarget());
24504         if(item){
24505             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
24506         }
24507     },
24508
24509     /** @ignore */
24510     onDblClick : function(e){
24511         var item = this.findItemFromChild(e.getTarget());
24512         if(item){
24513             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
24514         }
24515     },
24516
24517     onItemClick : function(item, index, e)
24518     {
24519         if(this.fireEvent("beforeclick", this, index, item, e) === false){
24520             return false;
24521         }
24522         if (this.toggleSelect) {
24523             var m = this.isSelected(item) ? 'unselect' : 'select';
24524             Roo.log(m);
24525             var _t = this;
24526             _t[m](item, true, false);
24527             return true;
24528         }
24529         if(this.multiSelect || this.singleSelect){
24530             if(this.multiSelect && e.shiftKey && this.lastSelection){
24531                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
24532             }else{
24533                 this.select(item, this.multiSelect && e.ctrlKey);
24534                 this.lastSelection = item;
24535             }
24536             e.preventDefault();
24537         }
24538         return true;
24539     },
24540
24541     /**
24542      * Get the number of selected nodes.
24543      * @return {Number}
24544      */
24545     getSelectionCount : function(){
24546         return this.selections.length;
24547     },
24548
24549     /**
24550      * Get the currently selected nodes.
24551      * @return {Array} An array of HTMLElements
24552      */
24553     getSelectedNodes : function(){
24554         return this.selections;
24555     },
24556
24557     /**
24558      * Get the indexes of the selected nodes.
24559      * @return {Array}
24560      */
24561     getSelectedIndexes : function(){
24562         var indexes = [], s = this.selections;
24563         for(var i = 0, len = s.length; i < len; i++){
24564             indexes.push(s[i].nodeIndex);
24565         }
24566         return indexes;
24567     },
24568
24569     /**
24570      * Clear all selections
24571      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
24572      */
24573     clearSelections : function(suppressEvent){
24574         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
24575             this.cmp.elements = this.selections;
24576             this.cmp.removeClass(this.selectedClass);
24577             this.selections = [];
24578             if(!suppressEvent){
24579                 this.fireEvent("selectionchange", this, this.selections);
24580             }
24581         }
24582     },
24583
24584     /**
24585      * Returns true if the passed node is selected
24586      * @param {HTMLElement/Number} node The node or node index
24587      * @return {Boolean}
24588      */
24589     isSelected : function(node){
24590         var s = this.selections;
24591         if(s.length < 1){
24592             return false;
24593         }
24594         node = this.getNode(node);
24595         return s.indexOf(node) !== -1;
24596     },
24597
24598     /**
24599      * Selects nodes.
24600      * @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
24601      * @param {Boolean} keepExisting (optional) true to keep existing selections
24602      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
24603      */
24604     select : function(nodeInfo, keepExisting, suppressEvent){
24605         if(nodeInfo instanceof Array){
24606             if(!keepExisting){
24607                 this.clearSelections(true);
24608             }
24609             for(var i = 0, len = nodeInfo.length; i < len; i++){
24610                 this.select(nodeInfo[i], true, true);
24611             }
24612             return;
24613         } 
24614         var node = this.getNode(nodeInfo);
24615         if(!node || this.isSelected(node)){
24616             return; // already selected.
24617         }
24618         if(!keepExisting){
24619             this.clearSelections(true);
24620         }
24621         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
24622             Roo.fly(node).addClass(this.selectedClass);
24623             this.selections.push(node);
24624             if(!suppressEvent){
24625                 this.fireEvent("selectionchange", this, this.selections);
24626             }
24627         }
24628         
24629         
24630     },
24631       /**
24632      * Unselects nodes.
24633      * @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
24634      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
24635      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
24636      */
24637     unselect : function(nodeInfo, keepExisting, suppressEvent)
24638     {
24639         if(nodeInfo instanceof Array){
24640             Roo.each(this.selections, function(s) {
24641                 this.unselect(s, nodeInfo);
24642             }, this);
24643             return;
24644         }
24645         var node = this.getNode(nodeInfo);
24646         if(!node || !this.isSelected(node)){
24647             Roo.log("not selected");
24648             return; // not selected.
24649         }
24650         // fireevent???
24651         var ns = [];
24652         Roo.each(this.selections, function(s) {
24653             if (s == node ) {
24654                 Roo.fly(node).removeClass(this.selectedClass);
24655
24656                 return;
24657             }
24658             ns.push(s);
24659         },this);
24660         
24661         this.selections= ns;
24662         this.fireEvent("selectionchange", this, this.selections);
24663     },
24664
24665     /**
24666      * Gets a template node.
24667      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
24668      * @return {HTMLElement} The node or null if it wasn't found
24669      */
24670     getNode : function(nodeInfo){
24671         if(typeof nodeInfo == "string"){
24672             return document.getElementById(nodeInfo);
24673         }else if(typeof nodeInfo == "number"){
24674             return this.nodes[nodeInfo];
24675         }
24676         return nodeInfo;
24677     },
24678
24679     /**
24680      * Gets a range template nodes.
24681      * @param {Number} startIndex
24682      * @param {Number} endIndex
24683      * @return {Array} An array of nodes
24684      */
24685     getNodes : function(start, end){
24686         var ns = this.nodes;
24687         start = start || 0;
24688         end = typeof end == "undefined" ? ns.length - 1 : end;
24689         var nodes = [];
24690         if(start <= end){
24691             for(var i = start; i <= end; i++){
24692                 nodes.push(ns[i]);
24693             }
24694         } else{
24695             for(var i = start; i >= end; i--){
24696                 nodes.push(ns[i]);
24697             }
24698         }
24699         return nodes;
24700     },
24701
24702     /**
24703      * Finds the index of the passed node
24704      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
24705      * @return {Number} The index of the node or -1
24706      */
24707     indexOf : function(node){
24708         node = this.getNode(node);
24709         if(typeof node.nodeIndex == "number"){
24710             return node.nodeIndex;
24711         }
24712         var ns = this.nodes;
24713         for(var i = 0, len = ns.length; i < len; i++){
24714             if(ns[i] == node){
24715                 return i;
24716             }
24717         }
24718         return -1;
24719     }
24720 });
24721 /*
24722  * Based on:
24723  * Ext JS Library 1.1.1
24724  * Copyright(c) 2006-2007, Ext JS, LLC.
24725  *
24726  * Originally Released Under LGPL - original licence link has changed is not relivant.
24727  *
24728  * Fork - LGPL
24729  * <script type="text/javascript">
24730  */
24731
24732 /**
24733  * @class Roo.JsonView
24734  * @extends Roo.View
24735  * Shortcut class to create a JSON + {@link Roo.UpdateManager} template view. Usage:
24736 <pre><code>
24737 var view = new Roo.JsonView({
24738     container: "my-element",
24739     tpl: '&lt;div id="{id}"&gt;{foo} - {bar}&lt;/div&gt;', // auto create template
24740     multiSelect: true, 
24741     jsonRoot: "data" 
24742 });
24743
24744 // listen for node click?
24745 view.on("click", function(vw, index, node, e){
24746     alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
24747 });
24748
24749 // direct load of JSON data
24750 view.load("foobar.php");
24751
24752 // Example from my blog list
24753 var tpl = new Roo.Template(
24754     '&lt;div class="entry"&gt;' +
24755     '&lt;a class="entry-title" href="{link}"&gt;{title}&lt;/a&gt;' +
24756     "&lt;h4&gt;{date} by {author} | {comments} Comments&lt;/h4&gt;{description}" +
24757     "&lt;/div&gt;&lt;hr /&gt;"
24758 );
24759
24760 var moreView = new Roo.JsonView({
24761     container :  "entry-list", 
24762     template : tpl,
24763     jsonRoot: "posts"
24764 });
24765 moreView.on("beforerender", this.sortEntries, this);
24766 moreView.load({
24767     url: "/blog/get-posts.php",
24768     params: "allposts=true",
24769     text: "Loading Blog Entries..."
24770 });
24771 </code></pre>
24772
24773 * Note: old code is supported with arguments : (container, template, config)
24774
24775
24776  * @constructor
24777  * Create a new JsonView
24778  * 
24779  * @param {Object} config The config object
24780  * 
24781  */
24782 Roo.JsonView = function(config, depreciated_tpl, depreciated_config){
24783     
24784     
24785     Roo.JsonView.superclass.constructor.call(this, config, depreciated_tpl, depreciated_config);
24786
24787     var um = this.el.getUpdateManager();
24788     um.setRenderer(this);
24789     um.on("update", this.onLoad, this);
24790     um.on("failure", this.onLoadException, this);
24791
24792     /**
24793      * @event beforerender
24794      * Fires before rendering of the downloaded JSON data.
24795      * @param {Roo.JsonView} this
24796      * @param {Object} data The JSON data loaded
24797      */
24798     /**
24799      * @event load
24800      * Fires when data is loaded.
24801      * @param {Roo.JsonView} this
24802      * @param {Object} data The JSON data loaded
24803      * @param {Object} response The raw Connect response object
24804      */
24805     /**
24806      * @event loadexception
24807      * Fires when loading fails.
24808      * @param {Roo.JsonView} this
24809      * @param {Object} response The raw Connect response object
24810      */
24811     this.addEvents({
24812         'beforerender' : true,
24813         'load' : true,
24814         'loadexception' : true
24815     });
24816 };
24817 Roo.extend(Roo.JsonView, Roo.View, {
24818     /**
24819      * @type {String} The root property in the loaded JSON object that contains the data
24820      */
24821     jsonRoot : "",
24822
24823     /**
24824      * Refreshes the view.
24825      */
24826     refresh : function(){
24827         this.clearSelections();
24828         this.el.update("");
24829         var html = [];
24830         var o = this.jsonData;
24831         if(o && o.length > 0){
24832             for(var i = 0, len = o.length; i < len; i++){
24833                 var data = this.prepareData(o[i], i, o);
24834                 html[html.length] = this.tpl.apply(data);
24835             }
24836         }else{
24837             html.push(this.emptyText);
24838         }
24839         this.el.update(html.join(""));
24840         this.nodes = this.el.dom.childNodes;
24841         this.updateIndexes(0);
24842     },
24843
24844     /**
24845      * 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.
24846      * @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:
24847      <pre><code>
24848      view.load({
24849          url: "your-url.php",
24850          params: {param1: "foo", param2: "bar"}, // or a URL encoded string
24851          callback: yourFunction,
24852          scope: yourObject, //(optional scope)
24853          discardUrl: false,
24854          nocache: false,
24855          text: "Loading...",
24856          timeout: 30,
24857          scripts: false
24858      });
24859      </code></pre>
24860      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
24861      * 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.
24862      * @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}
24863      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
24864      * @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.
24865      */
24866     load : function(){
24867         var um = this.el.getUpdateManager();
24868         um.update.apply(um, arguments);
24869     },
24870
24871     render : function(el, response){
24872         this.clearSelections();
24873         this.el.update("");
24874         var o;
24875         try{
24876             o = Roo.util.JSON.decode(response.responseText);
24877             if(this.jsonRoot){
24878                 
24879                 o = o[this.jsonRoot];
24880             }
24881         } catch(e){
24882         }
24883         /**
24884          * The current JSON data or null
24885          */
24886         this.jsonData = o;
24887         this.beforeRender();
24888         this.refresh();
24889     },
24890
24891 /**
24892  * Get the number of records in the current JSON dataset
24893  * @return {Number}
24894  */
24895     getCount : function(){
24896         return this.jsonData ? this.jsonData.length : 0;
24897     },
24898
24899 /**
24900  * Returns the JSON object for the specified node(s)
24901  * @param {HTMLElement/Array} node The node or an array of nodes
24902  * @return {Object/Array} If you pass in an array, you get an array back, otherwise
24903  * you get the JSON object for the node
24904  */
24905     getNodeData : function(node){
24906         if(node instanceof Array){
24907             var data = [];
24908             for(var i = 0, len = node.length; i < len; i++){
24909                 data.push(this.getNodeData(node[i]));
24910             }
24911             return data;
24912         }
24913         return this.jsonData[this.indexOf(node)] || null;
24914     },
24915
24916     beforeRender : function(){
24917         this.snapshot = this.jsonData;
24918         if(this.sortInfo){
24919             this.sort.apply(this, this.sortInfo);
24920         }
24921         this.fireEvent("beforerender", this, this.jsonData);
24922     },
24923
24924     onLoad : function(el, o){
24925         this.fireEvent("load", this, this.jsonData, o);
24926     },
24927
24928     onLoadException : function(el, o){
24929         this.fireEvent("loadexception", this, o);
24930     },
24931
24932 /**
24933  * Filter the data by a specific property.
24934  * @param {String} property A property on your JSON objects
24935  * @param {String/RegExp} value Either string that the property values
24936  * should start with, or a RegExp to test against the property
24937  */
24938     filter : function(property, value){
24939         if(this.jsonData){
24940             var data = [];
24941             var ss = this.snapshot;
24942             if(typeof value == "string"){
24943                 var vlen = value.length;
24944                 if(vlen == 0){
24945                     this.clearFilter();
24946                     return;
24947                 }
24948                 value = value.toLowerCase();
24949                 for(var i = 0, len = ss.length; i < len; i++){
24950                     var o = ss[i];
24951                     if(o[property].substr(0, vlen).toLowerCase() == value){
24952                         data.push(o);
24953                     }
24954                 }
24955             } else if(value.exec){ // regex?
24956                 for(var i = 0, len = ss.length; i < len; i++){
24957                     var o = ss[i];
24958                     if(value.test(o[property])){
24959                         data.push(o);
24960                     }
24961                 }
24962             } else{
24963                 return;
24964             }
24965             this.jsonData = data;
24966             this.refresh();
24967         }
24968     },
24969
24970 /**
24971  * Filter by a function. The passed function will be called with each
24972  * object in the current dataset. If the function returns true the value is kept,
24973  * otherwise it is filtered.
24974  * @param {Function} fn
24975  * @param {Object} scope (optional) The scope of the function (defaults to this JsonView)
24976  */
24977     filterBy : function(fn, scope){
24978         if(this.jsonData){
24979             var data = [];
24980             var ss = this.snapshot;
24981             for(var i = 0, len = ss.length; i < len; i++){
24982                 var o = ss[i];
24983                 if(fn.call(scope || this, o)){
24984                     data.push(o);
24985                 }
24986             }
24987             this.jsonData = data;
24988             this.refresh();
24989         }
24990     },
24991
24992 /**
24993  * Clears the current filter.
24994  */
24995     clearFilter : function(){
24996         if(this.snapshot && this.jsonData != this.snapshot){
24997             this.jsonData = this.snapshot;
24998             this.refresh();
24999         }
25000     },
25001
25002
25003 /**
25004  * Sorts the data for this view and refreshes it.
25005  * @param {String} property A property on your JSON objects to sort on
25006  * @param {String} direction (optional) "desc" or "asc" (defaults to "asc")
25007  * @param {Function} sortType (optional) A function to call to convert the data to a sortable value.
25008  */
25009     sort : function(property, dir, sortType){
25010         this.sortInfo = Array.prototype.slice.call(arguments, 0);
25011         if(this.jsonData){
25012             var p = property;
25013             var dsc = dir && dir.toLowerCase() == "desc";
25014             var f = function(o1, o2){
25015                 var v1 = sortType ? sortType(o1[p]) : o1[p];
25016                 var v2 = sortType ? sortType(o2[p]) : o2[p];
25017                 ;
25018                 if(v1 < v2){
25019                     return dsc ? +1 : -1;
25020                 } else if(v1 > v2){
25021                     return dsc ? -1 : +1;
25022                 } else{
25023                     return 0;
25024                 }
25025             };
25026             this.jsonData.sort(f);
25027             this.refresh();
25028             if(this.jsonData != this.snapshot){
25029                 this.snapshot.sort(f);
25030             }
25031         }
25032     }
25033 });/*
25034  * Based on:
25035  * Ext JS Library 1.1.1
25036  * Copyright(c) 2006-2007, Ext JS, LLC.
25037  *
25038  * Originally Released Under LGPL - original licence link has changed is not relivant.
25039  *
25040  * Fork - LGPL
25041  * <script type="text/javascript">
25042  */
25043  
25044
25045 /**
25046  * @class Roo.ColorPalette
25047  * @extends Roo.Component
25048  * Simple color palette class for choosing colors.  The palette can be rendered to any container.<br />
25049  * Here's an example of typical usage:
25050  * <pre><code>
25051 var cp = new Roo.ColorPalette({value:'993300'});  // initial selected color
25052 cp.render('my-div');
25053
25054 cp.on('select', function(palette, selColor){
25055     // do something with selColor
25056 });
25057 </code></pre>
25058  * @constructor
25059  * Create a new ColorPalette
25060  * @param {Object} config The config object
25061  */
25062 Roo.ColorPalette = function(config){
25063     Roo.ColorPalette.superclass.constructor.call(this, config);
25064     this.addEvents({
25065         /**
25066              * @event select
25067              * Fires when a color is selected
25068              * @param {ColorPalette} this
25069              * @param {String} color The 6-digit color hex code (without the # symbol)
25070              */
25071         select: true
25072     });
25073
25074     if(this.handler){
25075         this.on("select", this.handler, this.scope, true);
25076     }
25077 };
25078 Roo.extend(Roo.ColorPalette, Roo.Component, {
25079     /**
25080      * @cfg {String} itemCls
25081      * The CSS class to apply to the containing element (defaults to "x-color-palette")
25082      */
25083     itemCls : "x-color-palette",
25084     /**
25085      * @cfg {String} value
25086      * The initial color to highlight (should be a valid 6-digit color hex code without the # symbol).  Note that
25087      * the hex codes are case-sensitive.
25088      */
25089     value : null,
25090     clickEvent:'click',
25091     // private
25092     ctype: "Roo.ColorPalette",
25093
25094     /**
25095      * @cfg {Boolean} allowReselect If set to true then reselecting a color that is already selected fires the selection event
25096      */
25097     allowReselect : false,
25098
25099     /**
25100      * <p>An array of 6-digit color hex code strings (without the # symbol).  This array can contain any number
25101      * of colors, and each hex code should be unique.  The width of the palette is controlled via CSS by adjusting
25102      * the width property of the 'x-color-palette' class (or assigning a custom class), so you can balance the number
25103      * of colors with the width setting until the box is symmetrical.</p>
25104      * <p>You can override individual colors if needed:</p>
25105      * <pre><code>
25106 var cp = new Roo.ColorPalette();
25107 cp.colors[0] = "FF0000";  // change the first box to red
25108 </code></pre>
25109
25110 Or you can provide a custom array of your own for complete control:
25111 <pre><code>
25112 var cp = new Roo.ColorPalette();
25113 cp.colors = ["000000", "993300", "333300"];
25114 </code></pre>
25115      * @type Array
25116      */
25117     colors : [
25118         "000000", "993300", "333300", "003300", "003366", "000080", "333399", "333333",
25119         "800000", "FF6600", "808000", "008000", "008080", "0000FF", "666699", "808080",
25120         "FF0000", "FF9900", "99CC00", "339966", "33CCCC", "3366FF", "800080", "969696",
25121         "FF00FF", "FFCC00", "FFFF00", "00FF00", "00FFFF", "00CCFF", "993366", "C0C0C0",
25122         "FF99CC", "FFCC99", "FFFF99", "CCFFCC", "CCFFFF", "99CCFF", "CC99FF", "FFFFFF"
25123     ],
25124
25125     // private
25126     onRender : function(container, position){
25127         var t = new Roo.MasterTemplate(
25128             '<tpl><a href="#" class="color-{0}" hidefocus="on"><em><span style="background:#{0}" unselectable="on">&#160;</span></em></a></tpl>'
25129         );
25130         var c = this.colors;
25131         for(var i = 0, len = c.length; i < len; i++){
25132             t.add([c[i]]);
25133         }
25134         var el = document.createElement("div");
25135         el.className = this.itemCls;
25136         t.overwrite(el);
25137         container.dom.insertBefore(el, position);
25138         this.el = Roo.get(el);
25139         this.el.on(this.clickEvent, this.handleClick,  this, {delegate: "a"});
25140         if(this.clickEvent != 'click'){
25141             this.el.on('click', Roo.emptyFn,  this, {delegate: "a", preventDefault:true});
25142         }
25143     },
25144
25145     // private
25146     afterRender : function(){
25147         Roo.ColorPalette.superclass.afterRender.call(this);
25148         if(this.value){
25149             var s = this.value;
25150             this.value = null;
25151             this.select(s);
25152         }
25153     },
25154
25155     // private
25156     handleClick : function(e, t){
25157         e.preventDefault();
25158         if(!this.disabled){
25159             var c = t.className.match(/(?:^|\s)color-(.{6})(?:\s|$)/)[1];
25160             this.select(c.toUpperCase());
25161         }
25162     },
25163
25164     /**
25165      * Selects the specified color in the palette (fires the select event)
25166      * @param {String} color A valid 6-digit color hex code (# will be stripped if included)
25167      */
25168     select : function(color){
25169         color = color.replace("#", "");
25170         if(color != this.value || this.allowReselect){
25171             var el = this.el;
25172             if(this.value){
25173                 el.child("a.color-"+this.value).removeClass("x-color-palette-sel");
25174             }
25175             el.child("a.color-"+color).addClass("x-color-palette-sel");
25176             this.value = color;
25177             this.fireEvent("select", this, color);
25178         }
25179     }
25180 });/*
25181  * Based on:
25182  * Ext JS Library 1.1.1
25183  * Copyright(c) 2006-2007, Ext JS, LLC.
25184  *
25185  * Originally Released Under LGPL - original licence link has changed is not relivant.
25186  *
25187  * Fork - LGPL
25188  * <script type="text/javascript">
25189  */
25190  
25191 /**
25192  * @class Roo.DatePicker
25193  * @extends Roo.Component
25194  * Simple date picker class.
25195  * @constructor
25196  * Create a new DatePicker
25197  * @param {Object} config The config object
25198  */
25199 Roo.DatePicker = function(config){
25200     Roo.DatePicker.superclass.constructor.call(this, config);
25201
25202     this.value = config && config.value ?
25203                  config.value.clearTime() : new Date().clearTime();
25204
25205     this.addEvents({
25206         /**
25207              * @event select
25208              * Fires when a date is selected
25209              * @param {DatePicker} this
25210              * @param {Date} date The selected date
25211              */
25212         'select': true,
25213         /**
25214              * @event monthchange
25215              * Fires when the displayed month changes 
25216              * @param {DatePicker} this
25217              * @param {Date} date The selected month
25218              */
25219         'monthchange': true
25220     });
25221
25222     if(this.handler){
25223         this.on("select", this.handler,  this.scope || this);
25224     }
25225     // build the disabledDatesRE
25226     if(!this.disabledDatesRE && this.disabledDates){
25227         var dd = this.disabledDates;
25228         var re = "(?:";
25229         for(var i = 0; i < dd.length; i++){
25230             re += dd[i];
25231             if(i != dd.length-1) re += "|";
25232         }
25233         this.disabledDatesRE = new RegExp(re + ")");
25234     }
25235 };
25236
25237 Roo.extend(Roo.DatePicker, Roo.Component, {
25238     /**
25239      * @cfg {String} todayText
25240      * The text to display on the button that selects the current date (defaults to "Today")
25241      */
25242     todayText : "Today",
25243     /**
25244      * @cfg {String} okText
25245      * The text to display on the ok button
25246      */
25247     okText : "&#160;OK&#160;", // &#160; to give the user extra clicking room
25248     /**
25249      * @cfg {String} cancelText
25250      * The text to display on the cancel button
25251      */
25252     cancelText : "Cancel",
25253     /**
25254      * @cfg {String} todayTip
25255      * The tooltip to display for the button that selects the current date (defaults to "{current date} (Spacebar)")
25256      */
25257     todayTip : "{0} (Spacebar)",
25258     /**
25259      * @cfg {Date} minDate
25260      * Minimum allowable date (JavaScript date object, defaults to null)
25261      */
25262     minDate : null,
25263     /**
25264      * @cfg {Date} maxDate
25265      * Maximum allowable date (JavaScript date object, defaults to null)
25266      */
25267     maxDate : null,
25268     /**
25269      * @cfg {String} minText
25270      * The error text to display if the minDate validation fails (defaults to "This date is before the minimum date")
25271      */
25272     minText : "This date is before the minimum date",
25273     /**
25274      * @cfg {String} maxText
25275      * The error text to display if the maxDate validation fails (defaults to "This date is after the maximum date")
25276      */
25277     maxText : "This date is after the maximum date",
25278     /**
25279      * @cfg {String} format
25280      * The default date format string which can be overriden for localization support.  The format must be
25281      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
25282      */
25283     format : "m/d/y",
25284     /**
25285      * @cfg {Array} disabledDays
25286      * An array of days to disable, 0-based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
25287      */
25288     disabledDays : null,
25289     /**
25290      * @cfg {String} disabledDaysText
25291      * The tooltip to display when the date falls on a disabled day (defaults to "")
25292      */
25293     disabledDaysText : "",
25294     /**
25295      * @cfg {RegExp} disabledDatesRE
25296      * JavaScript regular expression used to disable a pattern of dates (defaults to null)
25297      */
25298     disabledDatesRE : null,
25299     /**
25300      * @cfg {String} disabledDatesText
25301      * The tooltip text to display when the date falls on a disabled date (defaults to "")
25302      */
25303     disabledDatesText : "",
25304     /**
25305      * @cfg {Boolean} constrainToViewport
25306      * True to constrain the date picker to the viewport (defaults to true)
25307      */
25308     constrainToViewport : true,
25309     /**
25310      * @cfg {Array} monthNames
25311      * An array of textual month names which can be overriden for localization support (defaults to Date.monthNames)
25312      */
25313     monthNames : Date.monthNames,
25314     /**
25315      * @cfg {Array} dayNames
25316      * An array of textual day names which can be overriden for localization support (defaults to Date.dayNames)
25317      */
25318     dayNames : Date.dayNames,
25319     /**
25320      * @cfg {String} nextText
25321      * The next month navigation button tooltip (defaults to 'Next Month (Control+Right)')
25322      */
25323     nextText: 'Next Month (Control+Right)',
25324     /**
25325      * @cfg {String} prevText
25326      * The previous month navigation button tooltip (defaults to 'Previous Month (Control+Left)')
25327      */
25328     prevText: 'Previous Month (Control+Left)',
25329     /**
25330      * @cfg {String} monthYearText
25331      * The header month selector tooltip (defaults to 'Choose a month (Control+Up/Down to move years)')
25332      */
25333     monthYearText: 'Choose a month (Control+Up/Down to move years)',
25334     /**
25335      * @cfg {Number} startDay
25336      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
25337      */
25338     startDay : 0,
25339     /**
25340      * @cfg {Bool} showClear
25341      * Show a clear button (usefull for date form elements that can be blank.)
25342      */
25343     
25344     showClear: false,
25345     
25346     /**
25347      * Sets the value of the date field
25348      * @param {Date} value The date to set
25349      */
25350     setValue : function(value){
25351         var old = this.value;
25352         
25353         if (typeof(value) == 'string') {
25354          
25355             value = Date.parseDate(value, this.format);
25356         }
25357         if (!value) {
25358             value = new Date();
25359         }
25360         
25361         this.value = value.clearTime(true);
25362         if(this.el){
25363             this.update(this.value);
25364         }
25365     },
25366
25367     /**
25368      * Gets the current selected value of the date field
25369      * @return {Date} The selected date
25370      */
25371     getValue : function(){
25372         return this.value;
25373     },
25374
25375     // private
25376     focus : function(){
25377         if(this.el){
25378             this.update(this.activeDate);
25379         }
25380     },
25381
25382     // privateval
25383     onRender : function(container, position){
25384         
25385         var m = [
25386              '<table cellspacing="0">',
25387                 '<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>',
25388                 '<tr><td colspan="3"><table class="x-date-inner" cellspacing="0"><thead><tr>'];
25389         var dn = this.dayNames;
25390         for(var i = 0; i < 7; i++){
25391             var d = this.startDay+i;
25392             if(d > 6){
25393                 d = d-7;
25394             }
25395             m.push("<th><span>", dn[d].substr(0,1), "</span></th>");
25396         }
25397         m[m.length] = "</tr></thead><tbody><tr>";
25398         for(var i = 0; i < 42; i++) {
25399             if(i % 7 == 0 && i != 0){
25400                 m[m.length] = "</tr><tr>";
25401             }
25402             m[m.length] = '<td><a href="#" hidefocus="on" class="x-date-date" tabIndex="1"><em><span></span></em></a></td>';
25403         }
25404         m[m.length] = '</tr></tbody></table></td></tr><tr>'+
25405             '<td colspan="3" class="x-date-bottom" align="center"></td></tr></table><div class="x-date-mp"></div>';
25406
25407         var el = document.createElement("div");
25408         el.className = "x-date-picker";
25409         el.innerHTML = m.join("");
25410
25411         container.dom.insertBefore(el, position);
25412
25413         this.el = Roo.get(el);
25414         this.eventEl = Roo.get(el.firstChild);
25415
25416         new Roo.util.ClickRepeater(this.el.child("td.x-date-left a"), {
25417             handler: this.showPrevMonth,
25418             scope: this,
25419             preventDefault:true,
25420             stopDefault:true
25421         });
25422
25423         new Roo.util.ClickRepeater(this.el.child("td.x-date-right a"), {
25424             handler: this.showNextMonth,
25425             scope: this,
25426             preventDefault:true,
25427             stopDefault:true
25428         });
25429
25430         this.eventEl.on("mousewheel", this.handleMouseWheel,  this);
25431
25432         this.monthPicker = this.el.down('div.x-date-mp');
25433         this.monthPicker.enableDisplayMode('block');
25434         
25435         var kn = new Roo.KeyNav(this.eventEl, {
25436             "left" : function(e){
25437                 e.ctrlKey ?
25438                     this.showPrevMonth() :
25439                     this.update(this.activeDate.add("d", -1));
25440             },
25441
25442             "right" : function(e){
25443                 e.ctrlKey ?
25444                     this.showNextMonth() :
25445                     this.update(this.activeDate.add("d", 1));
25446             },
25447
25448             "up" : function(e){
25449                 e.ctrlKey ?
25450                     this.showNextYear() :
25451                     this.update(this.activeDate.add("d", -7));
25452             },
25453
25454             "down" : function(e){
25455                 e.ctrlKey ?
25456                     this.showPrevYear() :
25457                     this.update(this.activeDate.add("d", 7));
25458             },
25459
25460             "pageUp" : function(e){
25461                 this.showNextMonth();
25462             },
25463
25464             "pageDown" : function(e){
25465                 this.showPrevMonth();
25466             },
25467
25468             "enter" : function(e){
25469                 e.stopPropagation();
25470                 return true;
25471             },
25472
25473             scope : this
25474         });
25475
25476         this.eventEl.on("click", this.handleDateClick,  this, {delegate: "a.x-date-date"});
25477
25478         this.eventEl.addKeyListener(Roo.EventObject.SPACE, this.selectToday,  this);
25479
25480         this.el.unselectable();
25481         
25482         this.cells = this.el.select("table.x-date-inner tbody td");
25483         this.textNodes = this.el.query("table.x-date-inner tbody span");
25484
25485         this.mbtn = new Roo.Button(this.el.child("td.x-date-middle", true), {
25486             text: "&#160;",
25487             tooltip: this.monthYearText
25488         });
25489
25490         this.mbtn.on('click', this.showMonthPicker, this);
25491         this.mbtn.el.child(this.mbtn.menuClassTarget).addClass("x-btn-with-menu");
25492
25493
25494         var today = (new Date()).dateFormat(this.format);
25495         
25496         var baseTb = new Roo.Toolbar(this.el.child("td.x-date-bottom", true));
25497         if (this.showClear) {
25498             baseTb.add( new Roo.Toolbar.Fill());
25499         }
25500         baseTb.add({
25501             text: String.format(this.todayText, today),
25502             tooltip: String.format(this.todayTip, today),
25503             handler: this.selectToday,
25504             scope: this
25505         });
25506         
25507         //var todayBtn = new Roo.Button(this.el.child("td.x-date-bottom", true), {
25508             
25509         //});
25510         if (this.showClear) {
25511             
25512             baseTb.add( new Roo.Toolbar.Fill());
25513             baseTb.add({
25514                 text: '&#160;',
25515                 cls: 'x-btn-icon x-btn-clear',
25516                 handler: function() {
25517                     //this.value = '';
25518                     this.fireEvent("select", this, '');
25519                 },
25520                 scope: this
25521             });
25522         }
25523         
25524         
25525         if(Roo.isIE){
25526             this.el.repaint();
25527         }
25528         this.update(this.value);
25529     },
25530
25531     createMonthPicker : function(){
25532         if(!this.monthPicker.dom.firstChild){
25533             var buf = ['<table border="0" cellspacing="0">'];
25534             for(var i = 0; i < 6; i++){
25535                 buf.push(
25536                     '<tr><td class="x-date-mp-month"><a href="#">', this.monthNames[i].substr(0, 3), '</a></td>',
25537                     '<td class="x-date-mp-month x-date-mp-sep"><a href="#">', this.monthNames[i+6].substr(0, 3), '</a></td>',
25538                     i == 0 ?
25539                     '<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>' :
25540                     '<td class="x-date-mp-year"><a href="#"></a></td><td class="x-date-mp-year"><a href="#"></a></td></tr>'
25541                 );
25542             }
25543             buf.push(
25544                 '<tr class="x-date-mp-btns"><td colspan="4"><button type="button" class="x-date-mp-ok">',
25545                     this.okText,
25546                     '</button><button type="button" class="x-date-mp-cancel">',
25547                     this.cancelText,
25548                     '</button></td></tr>',
25549                 '</table>'
25550             );
25551             this.monthPicker.update(buf.join(''));
25552             this.monthPicker.on('click', this.onMonthClick, this);
25553             this.monthPicker.on('dblclick', this.onMonthDblClick, this);
25554
25555             this.mpMonths = this.monthPicker.select('td.x-date-mp-month');
25556             this.mpYears = this.monthPicker.select('td.x-date-mp-year');
25557
25558             this.mpMonths.each(function(m, a, i){
25559                 i += 1;
25560                 if((i%2) == 0){
25561                     m.dom.xmonth = 5 + Math.round(i * .5);
25562                 }else{
25563                     m.dom.xmonth = Math.round((i-1) * .5);
25564                 }
25565             });
25566         }
25567     },
25568
25569     showMonthPicker : function(){
25570         this.createMonthPicker();
25571         var size = this.el.getSize();
25572         this.monthPicker.setSize(size);
25573         this.monthPicker.child('table').setSize(size);
25574
25575         this.mpSelMonth = (this.activeDate || this.value).getMonth();
25576         this.updateMPMonth(this.mpSelMonth);
25577         this.mpSelYear = (this.activeDate || this.value).getFullYear();
25578         this.updateMPYear(this.mpSelYear);
25579
25580         this.monthPicker.slideIn('t', {duration:.2});
25581     },
25582
25583     updateMPYear : function(y){
25584         this.mpyear = y;
25585         var ys = this.mpYears.elements;
25586         for(var i = 1; i <= 10; i++){
25587             var td = ys[i-1], y2;
25588             if((i%2) == 0){
25589                 y2 = y + Math.round(i * .5);
25590                 td.firstChild.innerHTML = y2;
25591                 td.xyear = y2;
25592             }else{
25593                 y2 = y - (5-Math.round(i * .5));
25594                 td.firstChild.innerHTML = y2;
25595                 td.xyear = y2;
25596             }
25597             this.mpYears.item(i-1)[y2 == this.mpSelYear ? 'addClass' : 'removeClass']('x-date-mp-sel');
25598         }
25599     },
25600
25601     updateMPMonth : function(sm){
25602         this.mpMonths.each(function(m, a, i){
25603             m[m.dom.xmonth == sm ? 'addClass' : 'removeClass']('x-date-mp-sel');
25604         });
25605     },
25606
25607     selectMPMonth: function(m){
25608         
25609     },
25610
25611     onMonthClick : function(e, t){
25612         e.stopEvent();
25613         var el = new Roo.Element(t), pn;
25614         if(el.is('button.x-date-mp-cancel')){
25615             this.hideMonthPicker();
25616         }
25617         else if(el.is('button.x-date-mp-ok')){
25618             this.update(new Date(this.mpSelYear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
25619             this.hideMonthPicker();
25620         }
25621         else if(pn = el.up('td.x-date-mp-month', 2)){
25622             this.mpMonths.removeClass('x-date-mp-sel');
25623             pn.addClass('x-date-mp-sel');
25624             this.mpSelMonth = pn.dom.xmonth;
25625         }
25626         else if(pn = el.up('td.x-date-mp-year', 2)){
25627             this.mpYears.removeClass('x-date-mp-sel');
25628             pn.addClass('x-date-mp-sel');
25629             this.mpSelYear = pn.dom.xyear;
25630         }
25631         else if(el.is('a.x-date-mp-prev')){
25632             this.updateMPYear(this.mpyear-10);
25633         }
25634         else if(el.is('a.x-date-mp-next')){
25635             this.updateMPYear(this.mpyear+10);
25636         }
25637     },
25638
25639     onMonthDblClick : function(e, t){
25640         e.stopEvent();
25641         var el = new Roo.Element(t), pn;
25642         if(pn = el.up('td.x-date-mp-month', 2)){
25643             this.update(new Date(this.mpSelYear, pn.dom.xmonth, (this.activeDate || this.value).getDate()));
25644             this.hideMonthPicker();
25645         }
25646         else if(pn = el.up('td.x-date-mp-year', 2)){
25647             this.update(new Date(pn.dom.xyear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
25648             this.hideMonthPicker();
25649         }
25650     },
25651
25652     hideMonthPicker : function(disableAnim){
25653         if(this.monthPicker){
25654             if(disableAnim === true){
25655                 this.monthPicker.hide();
25656             }else{
25657                 this.monthPicker.slideOut('t', {duration:.2});
25658             }
25659         }
25660     },
25661
25662     // private
25663     showPrevMonth : function(e){
25664         this.update(this.activeDate.add("mo", -1));
25665     },
25666
25667     // private
25668     showNextMonth : function(e){
25669         this.update(this.activeDate.add("mo", 1));
25670     },
25671
25672     // private
25673     showPrevYear : function(){
25674         this.update(this.activeDate.add("y", -1));
25675     },
25676
25677     // private
25678     showNextYear : function(){
25679         this.update(this.activeDate.add("y", 1));
25680     },
25681
25682     // private
25683     handleMouseWheel : function(e){
25684         var delta = e.getWheelDelta();
25685         if(delta > 0){
25686             this.showPrevMonth();
25687             e.stopEvent();
25688         } else if(delta < 0){
25689             this.showNextMonth();
25690             e.stopEvent();
25691         }
25692     },
25693
25694     // private
25695     handleDateClick : function(e, t){
25696         e.stopEvent();
25697         if(t.dateValue && !Roo.fly(t.parentNode).hasClass("x-date-disabled")){
25698             this.setValue(new Date(t.dateValue));
25699             this.fireEvent("select", this, this.value);
25700         }
25701     },
25702
25703     // private
25704     selectToday : function(){
25705         this.setValue(new Date().clearTime());
25706         this.fireEvent("select", this, this.value);
25707     },
25708
25709     // private
25710     update : function(date)
25711     {
25712         var vd = this.activeDate;
25713         this.activeDate = date;
25714         if(vd && this.el){
25715             var t = date.getTime();
25716             if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
25717                 this.cells.removeClass("x-date-selected");
25718                 this.cells.each(function(c){
25719                    if(c.dom.firstChild.dateValue == t){
25720                        c.addClass("x-date-selected");
25721                        setTimeout(function(){
25722                             try{c.dom.firstChild.focus();}catch(e){}
25723                        }, 50);
25724                        return false;
25725                    }
25726                 });
25727                 return;
25728             }
25729         }
25730         
25731         var days = date.getDaysInMonth();
25732         var firstOfMonth = date.getFirstDateOfMonth();
25733         var startingPos = firstOfMonth.getDay()-this.startDay;
25734
25735         if(startingPos <= this.startDay){
25736             startingPos += 7;
25737         }
25738
25739         var pm = date.add("mo", -1);
25740         var prevStart = pm.getDaysInMonth()-startingPos;
25741
25742         var cells = this.cells.elements;
25743         var textEls = this.textNodes;
25744         days += startingPos;
25745
25746         // convert everything to numbers so it's fast
25747         var day = 86400000;
25748         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
25749         var today = new Date().clearTime().getTime();
25750         var sel = date.clearTime().getTime();
25751         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
25752         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
25753         var ddMatch = this.disabledDatesRE;
25754         var ddText = this.disabledDatesText;
25755         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
25756         var ddaysText = this.disabledDaysText;
25757         var format = this.format;
25758
25759         var setCellClass = function(cal, cell){
25760             cell.title = "";
25761             var t = d.getTime();
25762             cell.firstChild.dateValue = t;
25763             if(t == today){
25764                 cell.className += " x-date-today";
25765                 cell.title = cal.todayText;
25766             }
25767             if(t == sel){
25768                 cell.className += " x-date-selected";
25769                 setTimeout(function(){
25770                     try{cell.firstChild.focus();}catch(e){}
25771                 }, 50);
25772             }
25773             // disabling
25774             if(t < min) {
25775                 cell.className = " x-date-disabled";
25776                 cell.title = cal.minText;
25777                 return;
25778             }
25779             if(t > max) {
25780                 cell.className = " x-date-disabled";
25781                 cell.title = cal.maxText;
25782                 return;
25783             }
25784             if(ddays){
25785                 if(ddays.indexOf(d.getDay()) != -1){
25786                     cell.title = ddaysText;
25787                     cell.className = " x-date-disabled";
25788                 }
25789             }
25790             if(ddMatch && format){
25791                 var fvalue = d.dateFormat(format);
25792                 if(ddMatch.test(fvalue)){
25793                     cell.title = ddText.replace("%0", fvalue);
25794                     cell.className = " x-date-disabled";
25795                 }
25796             }
25797         };
25798
25799         var i = 0;
25800         for(; i < startingPos; i++) {
25801             textEls[i].innerHTML = (++prevStart);
25802             d.setDate(d.getDate()+1);
25803             cells[i].className = "x-date-prevday";
25804             setCellClass(this, cells[i]);
25805         }
25806         for(; i < days; i++){
25807             intDay = i - startingPos + 1;
25808             textEls[i].innerHTML = (intDay);
25809             d.setDate(d.getDate()+1);
25810             cells[i].className = "x-date-active";
25811             setCellClass(this, cells[i]);
25812         }
25813         var extraDays = 0;
25814         for(; i < 42; i++) {
25815              textEls[i].innerHTML = (++extraDays);
25816              d.setDate(d.getDate()+1);
25817              cells[i].className = "x-date-nextday";
25818              setCellClass(this, cells[i]);
25819         }
25820
25821         this.mbtn.setText(this.monthNames[date.getMonth()] + " " + date.getFullYear());
25822         this.fireEvent('monthchange', this, date);
25823         
25824         if(!this.internalRender){
25825             var main = this.el.dom.firstChild;
25826             var w = main.offsetWidth;
25827             this.el.setWidth(w + this.el.getBorderWidth("lr"));
25828             Roo.fly(main).setWidth(w);
25829             this.internalRender = true;
25830             // opera does not respect the auto grow header center column
25831             // then, after it gets a width opera refuses to recalculate
25832             // without a second pass
25833             if(Roo.isOpera && !this.secondPass){
25834                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
25835                 this.secondPass = true;
25836                 this.update.defer(10, this, [date]);
25837             }
25838         }
25839         
25840         
25841     }
25842 });        /*
25843  * Based on:
25844  * Ext JS Library 1.1.1
25845  * Copyright(c) 2006-2007, Ext JS, LLC.
25846  *
25847  * Originally Released Under LGPL - original licence link has changed is not relivant.
25848  *
25849  * Fork - LGPL
25850  * <script type="text/javascript">
25851  */
25852 /**
25853  * @class Roo.TabPanel
25854  * @extends Roo.util.Observable
25855  * A lightweight tab container.
25856  * <br><br>
25857  * Usage:
25858  * <pre><code>
25859 // basic tabs 1, built from existing content
25860 var tabs = new Roo.TabPanel("tabs1");
25861 tabs.addTab("script", "View Script");
25862 tabs.addTab("markup", "View Markup");
25863 tabs.activate("script");
25864
25865 // more advanced tabs, built from javascript
25866 var jtabs = new Roo.TabPanel("jtabs");
25867 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
25868
25869 // set up the UpdateManager
25870 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
25871 var updater = tab2.getUpdateManager();
25872 updater.setDefaultUrl("ajax1.htm");
25873 tab2.on('activate', updater.refresh, updater, true);
25874
25875 // Use setUrl for Ajax loading
25876 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
25877 tab3.setUrl("ajax2.htm", null, true);
25878
25879 // Disabled tab
25880 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
25881 tab4.disable();
25882
25883 jtabs.activate("jtabs-1");
25884  * </code></pre>
25885  * @constructor
25886  * Create a new TabPanel.
25887  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
25888  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
25889  */
25890 Roo.TabPanel = function(container, config){
25891     /**
25892     * The container element for this TabPanel.
25893     * @type Roo.Element
25894     */
25895     this.el = Roo.get(container, true);
25896     if(config){
25897         if(typeof config == "boolean"){
25898             this.tabPosition = config ? "bottom" : "top";
25899         }else{
25900             Roo.apply(this, config);
25901         }
25902     }
25903     if(this.tabPosition == "bottom"){
25904         this.bodyEl = Roo.get(this.createBody(this.el.dom));
25905         this.el.addClass("x-tabs-bottom");
25906     }
25907     this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
25908     this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
25909     this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
25910     if(Roo.isIE){
25911         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
25912     }
25913     if(this.tabPosition != "bottom"){
25914         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
25915          * @type Roo.Element
25916          */
25917         this.bodyEl = Roo.get(this.createBody(this.el.dom));
25918         this.el.addClass("x-tabs-top");
25919     }
25920     this.items = [];
25921
25922     this.bodyEl.setStyle("position", "relative");
25923
25924     this.active = null;
25925     this.activateDelegate = this.activate.createDelegate(this);
25926
25927     this.addEvents({
25928         /**
25929          * @event tabchange
25930          * Fires when the active tab changes
25931          * @param {Roo.TabPanel} this
25932          * @param {Roo.TabPanelItem} activePanel The new active tab
25933          */
25934         "tabchange": true,
25935         /**
25936          * @event beforetabchange
25937          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
25938          * @param {Roo.TabPanel} this
25939          * @param {Object} e Set cancel to true on this object to cancel the tab change
25940          * @param {Roo.TabPanelItem} tab The tab being changed to
25941          */
25942         "beforetabchange" : true
25943     });
25944
25945     Roo.EventManager.onWindowResize(this.onResize, this);
25946     this.cpad = this.el.getPadding("lr");
25947     this.hiddenCount = 0;
25948
25949
25950     // toolbar on the tabbar support...
25951     if (this.toolbar) {
25952         var tcfg = this.toolbar;
25953         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
25954         this.toolbar = new Roo.Toolbar(tcfg);
25955         if (Roo.isSafari) {
25956             var tbl = tcfg.container.child('table', true);
25957             tbl.setAttribute('width', '100%');
25958         }
25959         
25960     }
25961    
25962
25963
25964     Roo.TabPanel.superclass.constructor.call(this);
25965 };
25966
25967 Roo.extend(Roo.TabPanel, Roo.util.Observable, {
25968     /*
25969      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
25970      */
25971     tabPosition : "top",
25972     /*
25973      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
25974      */
25975     currentTabWidth : 0,
25976     /*
25977      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
25978      */
25979     minTabWidth : 40,
25980     /*
25981      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
25982      */
25983     maxTabWidth : 250,
25984     /*
25985      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
25986      */
25987     preferredTabWidth : 175,
25988     /*
25989      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
25990      */
25991     resizeTabs : false,
25992     /*
25993      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
25994      */
25995     monitorResize : true,
25996     /*
25997      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
25998      */
25999     toolbar : false,
26000
26001     /**
26002      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
26003      * @param {String} id The id of the div to use <b>or create</b>
26004      * @param {String} text The text for the tab
26005      * @param {String} content (optional) Content to put in the TabPanelItem body
26006      * @param {Boolean} closable (optional) True to create a close icon on the tab
26007      * @return {Roo.TabPanelItem} The created TabPanelItem
26008      */
26009     addTab : function(id, text, content, closable){
26010         var item = new Roo.TabPanelItem(this, id, text, closable);
26011         this.addTabItem(item);
26012         if(content){
26013             item.setContent(content);
26014         }
26015         return item;
26016     },
26017
26018     /**
26019      * Returns the {@link Roo.TabPanelItem} with the specified id/index
26020      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
26021      * @return {Roo.TabPanelItem}
26022      */
26023     getTab : function(id){
26024         return this.items[id];
26025     },
26026
26027     /**
26028      * Hides the {@link Roo.TabPanelItem} with the specified id/index
26029      * @param {String/Number} id The id or index of the TabPanelItem to hide.
26030      */
26031     hideTab : function(id){
26032         var t = this.items[id];
26033         if(!t.isHidden()){
26034            t.setHidden(true);
26035            this.hiddenCount++;
26036            this.autoSizeTabs();
26037         }
26038     },
26039
26040     /**
26041      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
26042      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
26043      */
26044     unhideTab : function(id){
26045         var t = this.items[id];
26046         if(t.isHidden()){
26047            t.setHidden(false);
26048            this.hiddenCount--;
26049            this.autoSizeTabs();
26050         }
26051     },
26052
26053     /**
26054      * Adds an existing {@link Roo.TabPanelItem}.
26055      * @param {Roo.TabPanelItem} item The TabPanelItem to add
26056      */
26057     addTabItem : function(item){
26058         this.items[item.id] = item;
26059         this.items.push(item);
26060         if(this.resizeTabs){
26061            item.setWidth(this.currentTabWidth || this.preferredTabWidth);
26062            this.autoSizeTabs();
26063         }else{
26064             item.autoSize();
26065         }
26066     },
26067
26068     /**
26069      * Removes a {@link Roo.TabPanelItem}.
26070      * @param {String/Number} id The id or index of the TabPanelItem to remove.
26071      */
26072     removeTab : function(id){
26073         var items = this.items;
26074         var tab = items[id];
26075         if(!tab) { return; }
26076         var index = items.indexOf(tab);
26077         if(this.active == tab && items.length > 1){
26078             var newTab = this.getNextAvailable(index);
26079             if(newTab) {
26080                 newTab.activate();
26081             }
26082         }
26083         this.stripEl.dom.removeChild(tab.pnode.dom);
26084         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
26085             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
26086         }
26087         items.splice(index, 1);
26088         delete this.items[tab.id];
26089         tab.fireEvent("close", tab);
26090         tab.purgeListeners();
26091         this.autoSizeTabs();
26092     },
26093
26094     getNextAvailable : function(start){
26095         var items = this.items;
26096         var index = start;
26097         // look for a next tab that will slide over to
26098         // replace the one being removed
26099         while(index < items.length){
26100             var item = items[++index];
26101             if(item && !item.isHidden()){
26102                 return item;
26103             }
26104         }
26105         // if one isn't found select the previous tab (on the left)
26106         index = start;
26107         while(index >= 0){
26108             var item = items[--index];
26109             if(item && !item.isHidden()){
26110                 return item;
26111             }
26112         }
26113         return null;
26114     },
26115
26116     /**
26117      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
26118      * @param {String/Number} id The id or index of the TabPanelItem to disable.
26119      */
26120     disableTab : function(id){
26121         var tab = this.items[id];
26122         if(tab && this.active != tab){
26123             tab.disable();
26124         }
26125     },
26126
26127     /**
26128      * Enables a {@link Roo.TabPanelItem} that is disabled.
26129      * @param {String/Number} id The id or index of the TabPanelItem to enable.
26130      */
26131     enableTab : function(id){
26132         var tab = this.items[id];
26133         tab.enable();
26134     },
26135
26136     /**
26137      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
26138      * @param {String/Number} id The id or index of the TabPanelItem to activate.
26139      * @return {Roo.TabPanelItem} The TabPanelItem.
26140      */
26141     activate : function(id){
26142         var tab = this.items[id];
26143         if(!tab){
26144             return null;
26145         }
26146         if(tab == this.active || tab.disabled){
26147             return tab;
26148         }
26149         var e = {};
26150         this.fireEvent("beforetabchange", this, e, tab);
26151         if(e.cancel !== true && !tab.disabled){
26152             if(this.active){
26153                 this.active.hide();
26154             }
26155             this.active = this.items[id];
26156             this.active.show();
26157             this.fireEvent("tabchange", this, this.active);
26158         }
26159         return tab;
26160     },
26161
26162     /**
26163      * Gets the active {@link Roo.TabPanelItem}.
26164      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
26165      */
26166     getActiveTab : function(){
26167         return this.active;
26168     },
26169
26170     /**
26171      * Updates the tab body element to fit the height of the container element
26172      * for overflow scrolling
26173      * @param {Number} targetHeight (optional) Override the starting height from the elements height
26174      */
26175     syncHeight : function(targetHeight){
26176         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
26177         var bm = this.bodyEl.getMargins();
26178         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
26179         this.bodyEl.setHeight(newHeight);
26180         return newHeight;
26181     },
26182
26183     onResize : function(){
26184         if(this.monitorResize){
26185             this.autoSizeTabs();
26186         }
26187     },
26188
26189     /**
26190      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
26191      */
26192     beginUpdate : function(){
26193         this.updating = true;
26194     },
26195
26196     /**
26197      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
26198      */
26199     endUpdate : function(){
26200         this.updating = false;
26201         this.autoSizeTabs();
26202     },
26203
26204     /**
26205      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
26206      */
26207     autoSizeTabs : function(){
26208         var count = this.items.length;
26209         var vcount = count - this.hiddenCount;
26210         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) return;
26211         var w = Math.max(this.el.getWidth() - this.cpad, 10);
26212         var availWidth = Math.floor(w / vcount);
26213         var b = this.stripBody;
26214         if(b.getWidth() > w){
26215             var tabs = this.items;
26216             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
26217             if(availWidth < this.minTabWidth){
26218                 /*if(!this.sleft){    // incomplete scrolling code
26219                     this.createScrollButtons();
26220                 }
26221                 this.showScroll();
26222                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
26223             }
26224         }else{
26225             if(this.currentTabWidth < this.preferredTabWidth){
26226                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
26227             }
26228         }
26229     },
26230
26231     /**
26232      * Returns the number of tabs in this TabPanel.
26233      * @return {Number}
26234      */
26235      getCount : function(){
26236          return this.items.length;
26237      },
26238
26239     /**
26240      * Resizes all the tabs to the passed width
26241      * @param {Number} The new width
26242      */
26243     setTabWidth : function(width){
26244         this.currentTabWidth = width;
26245         for(var i = 0, len = this.items.length; i < len; i++) {
26246                 if(!this.items[i].isHidden())this.items[i].setWidth(width);
26247         }
26248     },
26249
26250     /**
26251      * Destroys this TabPanel
26252      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
26253      */
26254     destroy : function(removeEl){
26255         Roo.EventManager.removeResizeListener(this.onResize, this);
26256         for(var i = 0, len = this.items.length; i < len; i++){
26257             this.items[i].purgeListeners();
26258         }
26259         if(removeEl === true){
26260             this.el.update("");
26261             this.el.remove();
26262         }
26263     }
26264 });
26265
26266 /**
26267  * @class Roo.TabPanelItem
26268  * @extends Roo.util.Observable
26269  * Represents an individual item (tab plus body) in a TabPanel.
26270  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
26271  * @param {String} id The id of this TabPanelItem
26272  * @param {String} text The text for the tab of this TabPanelItem
26273  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
26274  */
26275 Roo.TabPanelItem = function(tabPanel, id, text, closable){
26276     /**
26277      * The {@link Roo.TabPanel} this TabPanelItem belongs to
26278      * @type Roo.TabPanel
26279      */
26280     this.tabPanel = tabPanel;
26281     /**
26282      * The id for this TabPanelItem
26283      * @type String
26284      */
26285     this.id = id;
26286     /** @private */
26287     this.disabled = false;
26288     /** @private */
26289     this.text = text;
26290     /** @private */
26291     this.loaded = false;
26292     this.closable = closable;
26293
26294     /**
26295      * The body element for this TabPanelItem.
26296      * @type Roo.Element
26297      */
26298     this.bodyEl = Roo.get(tabPanel.createItemBody(tabPanel.bodyEl.dom, id));
26299     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
26300     this.bodyEl.setStyle("display", "block");
26301     this.bodyEl.setStyle("zoom", "1");
26302     this.hideAction();
26303
26304     var els = tabPanel.createStripElements(tabPanel.stripEl.dom, text, closable);
26305     /** @private */
26306     this.el = Roo.get(els.el, true);
26307     this.inner = Roo.get(els.inner, true);
26308     this.textEl = Roo.get(this.el.dom.firstChild.firstChild.firstChild, true);
26309     this.pnode = Roo.get(els.el.parentNode, true);
26310     this.el.on("mousedown", this.onTabMouseDown, this);
26311     this.el.on("click", this.onTabClick, this);
26312     /** @private */
26313     if(closable){
26314         var c = Roo.get(els.close, true);
26315         c.dom.title = this.closeText;
26316         c.addClassOnOver("close-over");
26317         c.on("click", this.closeClick, this);
26318      }
26319
26320     this.addEvents({
26321          /**
26322          * @event activate
26323          * Fires when this tab becomes the active tab.
26324          * @param {Roo.TabPanel} tabPanel The parent TabPanel
26325          * @param {Roo.TabPanelItem} this
26326          */
26327         "activate": true,
26328         /**
26329          * @event beforeclose
26330          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
26331          * @param {Roo.TabPanelItem} this
26332          * @param {Object} e Set cancel to true on this object to cancel the close.
26333          */
26334         "beforeclose": true,
26335         /**
26336          * @event close
26337          * Fires when this tab is closed.
26338          * @param {Roo.TabPanelItem} this
26339          */
26340          "close": true,
26341         /**
26342          * @event deactivate
26343          * Fires when this tab is no longer the active tab.
26344          * @param {Roo.TabPanel} tabPanel The parent TabPanel
26345          * @param {Roo.TabPanelItem} this
26346          */
26347          "deactivate" : true
26348     });
26349     this.hidden = false;
26350
26351     Roo.TabPanelItem.superclass.constructor.call(this);
26352 };
26353
26354 Roo.extend(Roo.TabPanelItem, Roo.util.Observable, {
26355     purgeListeners : function(){
26356        Roo.util.Observable.prototype.purgeListeners.call(this);
26357        this.el.removeAllListeners();
26358     },
26359     /**
26360      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
26361      */
26362     show : function(){
26363         this.pnode.addClass("on");
26364         this.showAction();
26365         if(Roo.isOpera){
26366             this.tabPanel.stripWrap.repaint();
26367         }
26368         this.fireEvent("activate", this.tabPanel, this);
26369     },
26370
26371     /**
26372      * Returns true if this tab is the active tab.
26373      * @return {Boolean}
26374      */
26375     isActive : function(){
26376         return this.tabPanel.getActiveTab() == this;
26377     },
26378
26379     /**
26380      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
26381      */
26382     hide : function(){
26383         this.pnode.removeClass("on");
26384         this.hideAction();
26385         this.fireEvent("deactivate", this.tabPanel, this);
26386     },
26387
26388     hideAction : function(){
26389         this.bodyEl.hide();
26390         this.bodyEl.setStyle("position", "absolute");
26391         this.bodyEl.setLeft("-20000px");
26392         this.bodyEl.setTop("-20000px");
26393     },
26394
26395     showAction : function(){
26396         this.bodyEl.setStyle("position", "relative");
26397         this.bodyEl.setTop("");
26398         this.bodyEl.setLeft("");
26399         this.bodyEl.show();
26400     },
26401
26402     /**
26403      * Set the tooltip for the tab.
26404      * @param {String} tooltip The tab's tooltip
26405      */
26406     setTooltip : function(text){
26407         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
26408             this.textEl.dom.qtip = text;
26409             this.textEl.dom.removeAttribute('title');
26410         }else{
26411             this.textEl.dom.title = text;
26412         }
26413     },
26414
26415     onTabClick : function(e){
26416         e.preventDefault();
26417         this.tabPanel.activate(this.id);
26418     },
26419
26420     onTabMouseDown : function(e){
26421         e.preventDefault();
26422         this.tabPanel.activate(this.id);
26423     },
26424
26425     getWidth : function(){
26426         return this.inner.getWidth();
26427     },
26428
26429     setWidth : function(width){
26430         var iwidth = width - this.pnode.getPadding("lr");
26431         this.inner.setWidth(iwidth);
26432         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
26433         this.pnode.setWidth(width);
26434     },
26435
26436     /**
26437      * Show or hide the tab
26438      * @param {Boolean} hidden True to hide or false to show.
26439      */
26440     setHidden : function(hidden){
26441         this.hidden = hidden;
26442         this.pnode.setStyle("display", hidden ? "none" : "");
26443     },
26444
26445     /**
26446      * Returns true if this tab is "hidden"
26447      * @return {Boolean}
26448      */
26449     isHidden : function(){
26450         return this.hidden;
26451     },
26452
26453     /**
26454      * Returns the text for this tab
26455      * @return {String}
26456      */
26457     getText : function(){
26458         return this.text;
26459     },
26460
26461     autoSize : function(){
26462         //this.el.beginMeasure();
26463         this.textEl.setWidth(1);
26464         this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr"));
26465         //this.el.endMeasure();
26466     },
26467
26468     /**
26469      * Sets the text for the tab (Note: this also sets the tooltip text)
26470      * @param {String} text The tab's text and tooltip
26471      */
26472     setText : function(text){
26473         this.text = text;
26474         this.textEl.update(text);
26475         this.setTooltip(text);
26476         if(!this.tabPanel.resizeTabs){
26477             this.autoSize();
26478         }
26479     },
26480     /**
26481      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
26482      */
26483     activate : function(){
26484         this.tabPanel.activate(this.id);
26485     },
26486
26487     /**
26488      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
26489      */
26490     disable : function(){
26491         if(this.tabPanel.active != this){
26492             this.disabled = true;
26493             this.pnode.addClass("disabled");
26494         }
26495     },
26496
26497     /**
26498      * Enables this TabPanelItem if it was previously disabled.
26499      */
26500     enable : function(){
26501         this.disabled = false;
26502         this.pnode.removeClass("disabled");
26503     },
26504
26505     /**
26506      * Sets the content for this TabPanelItem.
26507      * @param {String} content The content
26508      * @param {Boolean} loadScripts true to look for and load scripts
26509      */
26510     setContent : function(content, loadScripts){
26511         this.bodyEl.update(content, loadScripts);
26512     },
26513
26514     /**
26515      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
26516      * @return {Roo.UpdateManager} The UpdateManager
26517      */
26518     getUpdateManager : function(){
26519         return this.bodyEl.getUpdateManager();
26520     },
26521
26522     /**
26523      * Set a URL to be used to load the content for this TabPanelItem.
26524      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
26525      * @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)
26526      * @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)
26527      * @return {Roo.UpdateManager} The UpdateManager
26528      */
26529     setUrl : function(url, params, loadOnce){
26530         if(this.refreshDelegate){
26531             this.un('activate', this.refreshDelegate);
26532         }
26533         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
26534         this.on("activate", this.refreshDelegate);
26535         return this.bodyEl.getUpdateManager();
26536     },
26537
26538     /** @private */
26539     _handleRefresh : function(url, params, loadOnce){
26540         if(!loadOnce || !this.loaded){
26541             var updater = this.bodyEl.getUpdateManager();
26542             updater.update(url, params, this._setLoaded.createDelegate(this));
26543         }
26544     },
26545
26546     /**
26547      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
26548      *   Will fail silently if the setUrl method has not been called.
26549      *   This does not activate the panel, just updates its content.
26550      */
26551     refresh : function(){
26552         if(this.refreshDelegate){
26553            this.loaded = false;
26554            this.refreshDelegate();
26555         }
26556     },
26557
26558     /** @private */
26559     _setLoaded : function(){
26560         this.loaded = true;
26561     },
26562
26563     /** @private */
26564     closeClick : function(e){
26565         var o = {};
26566         e.stopEvent();
26567         this.fireEvent("beforeclose", this, o);
26568         if(o.cancel !== true){
26569             this.tabPanel.removeTab(this.id);
26570         }
26571     },
26572     /**
26573      * The text displayed in the tooltip for the close icon.
26574      * @type String
26575      */
26576     closeText : "Close this tab"
26577 });
26578
26579 /** @private */
26580 Roo.TabPanel.prototype.createStrip = function(container){
26581     var strip = document.createElement("div");
26582     strip.className = "x-tabs-wrap";
26583     container.appendChild(strip);
26584     return strip;
26585 };
26586 /** @private */
26587 Roo.TabPanel.prototype.createStripList = function(strip){
26588     // div wrapper for retard IE
26589     // returns the "tr" element.
26590     strip.innerHTML = '<div class="x-tabs-strip-wrap">'+
26591         '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
26592         '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
26593     return strip.firstChild.firstChild.firstChild.firstChild;
26594 };
26595 /** @private */
26596 Roo.TabPanel.prototype.createBody = function(container){
26597     var body = document.createElement("div");
26598     Roo.id(body, "tab-body");
26599     Roo.fly(body).addClass("x-tabs-body");
26600     container.appendChild(body);
26601     return body;
26602 };
26603 /** @private */
26604 Roo.TabPanel.prototype.createItemBody = function(bodyEl, id){
26605     var body = Roo.getDom(id);
26606     if(!body){
26607         body = document.createElement("div");
26608         body.id = id;
26609     }
26610     Roo.fly(body).addClass("x-tabs-item-body");
26611     bodyEl.insertBefore(body, bodyEl.firstChild);
26612     return body;
26613 };
26614 /** @private */
26615 Roo.TabPanel.prototype.createStripElements = function(stripEl, text, closable){
26616     var td = document.createElement("td");
26617     stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
26618     //stripEl.appendChild(td);
26619     if(closable){
26620         td.className = "x-tabs-closable";
26621         if(!this.closeTpl){
26622             this.closeTpl = new Roo.Template(
26623                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
26624                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
26625                '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
26626             );
26627         }
26628         var el = this.closeTpl.overwrite(td, {"text": text});
26629         var close = el.getElementsByTagName("div")[0];
26630         var inner = el.getElementsByTagName("em")[0];
26631         return {"el": el, "close": close, "inner": inner};
26632     } else {
26633         if(!this.tabTpl){
26634             this.tabTpl = new Roo.Template(
26635                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
26636                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
26637             );
26638         }
26639         var el = this.tabTpl.overwrite(td, {"text": text});
26640         var inner = el.getElementsByTagName("em")[0];
26641         return {"el": el, "inner": inner};
26642     }
26643 };/*
26644  * Based on:
26645  * Ext JS Library 1.1.1
26646  * Copyright(c) 2006-2007, Ext JS, LLC.
26647  *
26648  * Originally Released Under LGPL - original licence link has changed is not relivant.
26649  *
26650  * Fork - LGPL
26651  * <script type="text/javascript">
26652  */
26653
26654 /**
26655  * @class Roo.Button
26656  * @extends Roo.util.Observable
26657  * Simple Button class
26658  * @cfg {String} text The button text
26659  * @cfg {String} icon The path to an image to display in the button (the image will be set as the background-image
26660  * CSS property of the button by default, so if you want a mixed icon/text button, set cls:"x-btn-text-icon")
26661  * @cfg {Function} handler A function called when the button is clicked (can be used instead of click event)
26662  * @cfg {Object} scope The scope of the handler
26663  * @cfg {Number} minWidth The minimum width for this button (used to give a set of buttons a common width)
26664  * @cfg {String/Object} tooltip The tooltip for the button - can be a string or QuickTips config object
26665  * @cfg {Boolean} hidden True to start hidden (defaults to false)
26666  * @cfg {Boolean} disabled True to start disabled (defaults to false)
26667  * @cfg {Boolean} pressed True to start pressed (only if enableToggle = true)
26668  * @cfg {String} toggleGroup The group this toggle button is a member of (only 1 per group can be pressed, only
26669    applies if enableToggle = true)
26670  * @cfg {String/HTMLElement/Element} renderTo The element to append the button to
26671  * @cfg {Boolean/Object} repeat True to repeat fire the click event while the mouse is down. This can also be
26672   an {@link Roo.util.ClickRepeater} config object (defaults to false).
26673  * @constructor
26674  * Create a new button
26675  * @param {Object} config The config object
26676  */
26677 Roo.Button = function(renderTo, config)
26678 {
26679     if (!config) {
26680         config = renderTo;
26681         renderTo = config.renderTo || false;
26682     }
26683     
26684     Roo.apply(this, config);
26685     this.addEvents({
26686         /**
26687              * @event click
26688              * Fires when this button is clicked
26689              * @param {Button} this
26690              * @param {EventObject} e The click event
26691              */
26692             "click" : true,
26693         /**
26694              * @event toggle
26695              * Fires when the "pressed" state of this button changes (only if enableToggle = true)
26696              * @param {Button} this
26697              * @param {Boolean} pressed
26698              */
26699             "toggle" : true,
26700         /**
26701              * @event mouseover
26702              * Fires when the mouse hovers over the button
26703              * @param {Button} this
26704              * @param {Event} e The event object
26705              */
26706         'mouseover' : true,
26707         /**
26708              * @event mouseout
26709              * Fires when the mouse exits the button
26710              * @param {Button} this
26711              * @param {Event} e The event object
26712              */
26713         'mouseout': true,
26714          /**
26715              * @event render
26716              * Fires when the button is rendered
26717              * @param {Button} this
26718              */
26719         'render': true
26720     });
26721     if(this.menu){
26722         this.menu = Roo.menu.MenuMgr.get(this.menu);
26723     }
26724     // register listeners first!!  - so render can be captured..
26725     Roo.util.Observable.call(this);
26726     if(renderTo){
26727         this.render(renderTo);
26728     }
26729     
26730   
26731 };
26732
26733 Roo.extend(Roo.Button, Roo.util.Observable, {
26734     /**
26735      * 
26736      */
26737     
26738     /**
26739      * Read-only. True if this button is hidden
26740      * @type Boolean
26741      */
26742     hidden : false,
26743     /**
26744      * Read-only. True if this button is disabled
26745      * @type Boolean
26746      */
26747     disabled : false,
26748     /**
26749      * Read-only. True if this button is pressed (only if enableToggle = true)
26750      * @type Boolean
26751      */
26752     pressed : false,
26753
26754     /**
26755      * @cfg {Number} tabIndex 
26756      * The DOM tabIndex for this button (defaults to undefined)
26757      */
26758     tabIndex : undefined,
26759
26760     /**
26761      * @cfg {Boolean} enableToggle
26762      * True to enable pressed/not pressed toggling (defaults to false)
26763      */
26764     enableToggle: false,
26765     /**
26766      * @cfg {Mixed} menu
26767      * Standard menu attribute consisting of a reference to a menu object, a menu id or a menu config blob (defaults to undefined).
26768      */
26769     menu : undefined,
26770     /**
26771      * @cfg {String} menuAlign
26772      * The position to align the menu to (see {@link Roo.Element#alignTo} for more details, defaults to 'tl-bl?').
26773      */
26774     menuAlign : "tl-bl?",
26775
26776     /**
26777      * @cfg {String} iconCls
26778      * A css class which sets a background image to be used as the icon for this button (defaults to undefined).
26779      */
26780     iconCls : undefined,
26781     /**
26782      * @cfg {String} type
26783      * The button's type, corresponding to the DOM input element type attribute.  Either "submit," "reset" or "button" (default).
26784      */
26785     type : 'button',
26786
26787     // private
26788     menuClassTarget: 'tr',
26789
26790     /**
26791      * @cfg {String} clickEvent
26792      * The type of event to map to the button's event handler (defaults to 'click')
26793      */
26794     clickEvent : 'click',
26795
26796     /**
26797      * @cfg {Boolean} handleMouseEvents
26798      * False to disable visual cues on mouseover, mouseout and mousedown (defaults to true)
26799      */
26800     handleMouseEvents : true,
26801
26802     /**
26803      * @cfg {String} tooltipType
26804      * The type of tooltip to use. Either "qtip" (default) for QuickTips or "title" for title attribute.
26805      */
26806     tooltipType : 'qtip',
26807
26808     /**
26809      * @cfg {String} cls
26810      * A CSS class to apply to the button's main element.
26811      */
26812     
26813     /**
26814      * @cfg {Roo.Template} template (Optional)
26815      * An {@link Roo.Template} with which to create the Button's main element. This Template must
26816      * contain numeric substitution parameter 0 if it is to display the tRoo property. Changing the template could
26817      * require code modifications if required elements (e.g. a button) aren't present.
26818      */
26819
26820     // private
26821     render : function(renderTo){
26822         var btn;
26823         if(this.hideParent){
26824             this.parentEl = Roo.get(renderTo);
26825         }
26826         if(!this.dhconfig){
26827             if(!this.template){
26828                 if(!Roo.Button.buttonTemplate){
26829                     // hideous table template
26830                     Roo.Button.buttonTemplate = new Roo.Template(
26831                         '<table border="0" cellpadding="0" cellspacing="0" class="x-btn-wrap"><tbody><tr>',
26832                         '<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>',
26833                         "</tr></tbody></table>");
26834                 }
26835                 this.template = Roo.Button.buttonTemplate;
26836             }
26837             btn = this.template.append(renderTo, [this.text || '&#160;', this.type], true);
26838             var btnEl = btn.child("button:first");
26839             btnEl.on('focus', this.onFocus, this);
26840             btnEl.on('blur', this.onBlur, this);
26841             if(this.cls){
26842                 btn.addClass(this.cls);
26843             }
26844             if(this.icon){
26845                 btnEl.setStyle('background-image', 'url(' +this.icon +')');
26846             }
26847             if(this.iconCls){
26848                 btnEl.addClass(this.iconCls);
26849                 if(!this.cls){
26850                     btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
26851                 }
26852             }
26853             if(this.tabIndex !== undefined){
26854                 btnEl.dom.tabIndex = this.tabIndex;
26855             }
26856             if(this.tooltip){
26857                 if(typeof this.tooltip == 'object'){
26858                     Roo.QuickTips.tips(Roo.apply({
26859                           target: btnEl.id
26860                     }, this.tooltip));
26861                 } else {
26862                     btnEl.dom[this.tooltipType] = this.tooltip;
26863                 }
26864             }
26865         }else{
26866             btn = Roo.DomHelper.append(Roo.get(renderTo).dom, this.dhconfig, true);
26867         }
26868         this.el = btn;
26869         if(this.id){
26870             this.el.dom.id = this.el.id = this.id;
26871         }
26872         if(this.menu){
26873             this.el.child(this.menuClassTarget).addClass("x-btn-with-menu");
26874             this.menu.on("show", this.onMenuShow, this);
26875             this.menu.on("hide", this.onMenuHide, this);
26876         }
26877         btn.addClass("x-btn");
26878         if(Roo.isIE && !Roo.isIE7){
26879             this.autoWidth.defer(1, this);
26880         }else{
26881             this.autoWidth();
26882         }
26883         if(this.handleMouseEvents){
26884             btn.on("mouseover", this.onMouseOver, this);
26885             btn.on("mouseout", this.onMouseOut, this);
26886             btn.on("mousedown", this.onMouseDown, this);
26887         }
26888         btn.on(this.clickEvent, this.onClick, this);
26889         //btn.on("mouseup", this.onMouseUp, this);
26890         if(this.hidden){
26891             this.hide();
26892         }
26893         if(this.disabled){
26894             this.disable();
26895         }
26896         Roo.ButtonToggleMgr.register(this);
26897         if(this.pressed){
26898             this.el.addClass("x-btn-pressed");
26899         }
26900         if(this.repeat){
26901             var repeater = new Roo.util.ClickRepeater(btn,
26902                 typeof this.repeat == "object" ? this.repeat : {}
26903             );
26904             repeater.on("click", this.onClick,  this);
26905         }
26906         
26907         this.fireEvent('render', this);
26908         
26909     },
26910     /**
26911      * Returns the button's underlying element
26912      * @return {Roo.Element} The element
26913      */
26914     getEl : function(){
26915         return this.el;  
26916     },
26917     
26918     /**
26919      * Destroys this Button and removes any listeners.
26920      */
26921     destroy : function(){
26922         Roo.ButtonToggleMgr.unregister(this);
26923         this.el.removeAllListeners();
26924         this.purgeListeners();
26925         this.el.remove();
26926     },
26927
26928     // private
26929     autoWidth : function(){
26930         if(this.el){
26931             this.el.setWidth("auto");
26932             if(Roo.isIE7 && Roo.isStrict){
26933                 var ib = this.el.child('button');
26934                 if(ib && ib.getWidth() > 20){
26935                     ib.clip();
26936                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
26937                 }
26938             }
26939             if(this.minWidth){
26940                 if(this.hidden){
26941                     this.el.beginMeasure();
26942                 }
26943                 if(this.el.getWidth() < this.minWidth){
26944                     this.el.setWidth(this.minWidth);
26945                 }
26946                 if(this.hidden){
26947                     this.el.endMeasure();
26948                 }
26949             }
26950         }
26951     },
26952
26953     /**
26954      * Assigns this button's click handler
26955      * @param {Function} handler The function to call when the button is clicked
26956      * @param {Object} scope (optional) Scope for the function passed in
26957      */
26958     setHandler : function(handler, scope){
26959         this.handler = handler;
26960         this.scope = scope;  
26961     },
26962     
26963     /**
26964      * Sets this button's text
26965      * @param {String} text The button text
26966      */
26967     setText : function(text){
26968         this.text = text;
26969         if(this.el){
26970             this.el.child("td.x-btn-center button.x-btn-text").update(text);
26971         }
26972         this.autoWidth();
26973     },
26974     
26975     /**
26976      * Gets the text for this button
26977      * @return {String} The button text
26978      */
26979     getText : function(){
26980         return this.text;  
26981     },
26982     
26983     /**
26984      * Show this button
26985      */
26986     show: function(){
26987         this.hidden = false;
26988         if(this.el){
26989             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "");
26990         }
26991     },
26992     
26993     /**
26994      * Hide this button
26995      */
26996     hide: function(){
26997         this.hidden = true;
26998         if(this.el){
26999             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "none");
27000         }
27001     },
27002     
27003     /**
27004      * Convenience function for boolean show/hide
27005      * @param {Boolean} visible True to show, false to hide
27006      */
27007     setVisible: function(visible){
27008         if(visible) {
27009             this.show();
27010         }else{
27011             this.hide();
27012         }
27013     },
27014     
27015     /**
27016      * If a state it passed, it becomes the pressed state otherwise the current state is toggled.
27017      * @param {Boolean} state (optional) Force a particular state
27018      */
27019     toggle : function(state){
27020         state = state === undefined ? !this.pressed : state;
27021         if(state != this.pressed){
27022             if(state){
27023                 this.el.addClass("x-btn-pressed");
27024                 this.pressed = true;
27025                 this.fireEvent("toggle", this, true);
27026             }else{
27027                 this.el.removeClass("x-btn-pressed");
27028                 this.pressed = false;
27029                 this.fireEvent("toggle", this, false);
27030             }
27031             if(this.toggleHandler){
27032                 this.toggleHandler.call(this.scope || this, this, state);
27033             }
27034         }
27035     },
27036     
27037     /**
27038      * Focus the button
27039      */
27040     focus : function(){
27041         this.el.child('button:first').focus();
27042     },
27043     
27044     /**
27045      * Disable this button
27046      */
27047     disable : function(){
27048         if(this.el){
27049             this.el.addClass("x-btn-disabled");
27050         }
27051         this.disabled = true;
27052     },
27053     
27054     /**
27055      * Enable this button
27056      */
27057     enable : function(){
27058         if(this.el){
27059             this.el.removeClass("x-btn-disabled");
27060         }
27061         this.disabled = false;
27062     },
27063
27064     /**
27065      * Convenience function for boolean enable/disable
27066      * @param {Boolean} enabled True to enable, false to disable
27067      */
27068     setDisabled : function(v){
27069         this[v !== true ? "enable" : "disable"]();
27070     },
27071
27072     // private
27073     onClick : function(e){
27074         if(e){
27075             e.preventDefault();
27076         }
27077         if(e.button != 0){
27078             return;
27079         }
27080         if(!this.disabled){
27081             if(this.enableToggle){
27082                 this.toggle();
27083             }
27084             if(this.menu && !this.menu.isVisible()){
27085                 this.menu.show(this.el, this.menuAlign);
27086             }
27087             this.fireEvent("click", this, e);
27088             if(this.handler){
27089                 this.el.removeClass("x-btn-over");
27090                 this.handler.call(this.scope || this, this, e);
27091             }
27092         }
27093     },
27094     // private
27095     onMouseOver : function(e){
27096         if(!this.disabled){
27097             this.el.addClass("x-btn-over");
27098             this.fireEvent('mouseover', this, e);
27099         }
27100     },
27101     // private
27102     onMouseOut : function(e){
27103         if(!e.within(this.el,  true)){
27104             this.el.removeClass("x-btn-over");
27105             this.fireEvent('mouseout', this, e);
27106         }
27107     },
27108     // private
27109     onFocus : function(e){
27110         if(!this.disabled){
27111             this.el.addClass("x-btn-focus");
27112         }
27113     },
27114     // private
27115     onBlur : function(e){
27116         this.el.removeClass("x-btn-focus");
27117     },
27118     // private
27119     onMouseDown : function(e){
27120         if(!this.disabled && e.button == 0){
27121             this.el.addClass("x-btn-click");
27122             Roo.get(document).on('mouseup', this.onMouseUp, this);
27123         }
27124     },
27125     // private
27126     onMouseUp : function(e){
27127         if(e.button == 0){
27128             this.el.removeClass("x-btn-click");
27129             Roo.get(document).un('mouseup', this.onMouseUp, this);
27130         }
27131     },
27132     // private
27133     onMenuShow : function(e){
27134         this.el.addClass("x-btn-menu-active");
27135     },
27136     // private
27137     onMenuHide : function(e){
27138         this.el.removeClass("x-btn-menu-active");
27139     }   
27140 });
27141
27142 // Private utility class used by Button
27143 Roo.ButtonToggleMgr = function(){
27144    var groups = {};
27145    
27146    function toggleGroup(btn, state){
27147        if(state){
27148            var g = groups[btn.toggleGroup];
27149            for(var i = 0, l = g.length; i < l; i++){
27150                if(g[i] != btn){
27151                    g[i].toggle(false);
27152                }
27153            }
27154        }
27155    }
27156    
27157    return {
27158        register : function(btn){
27159            if(!btn.toggleGroup){
27160                return;
27161            }
27162            var g = groups[btn.toggleGroup];
27163            if(!g){
27164                g = groups[btn.toggleGroup] = [];
27165            }
27166            g.push(btn);
27167            btn.on("toggle", toggleGroup);
27168        },
27169        
27170        unregister : function(btn){
27171            if(!btn.toggleGroup){
27172                return;
27173            }
27174            var g = groups[btn.toggleGroup];
27175            if(g){
27176                g.remove(btn);
27177                btn.un("toggle", toggleGroup);
27178            }
27179        }
27180    };
27181 }();/*
27182  * Based on:
27183  * Ext JS Library 1.1.1
27184  * Copyright(c) 2006-2007, Ext JS, LLC.
27185  *
27186  * Originally Released Under LGPL - original licence link has changed is not relivant.
27187  *
27188  * Fork - LGPL
27189  * <script type="text/javascript">
27190  */
27191  
27192 /**
27193  * @class Roo.SplitButton
27194  * @extends Roo.Button
27195  * A split button that provides a built-in dropdown arrow that can fire an event separately from the default
27196  * click event of the button.  Typically this would be used to display a dropdown menu that provides additional
27197  * options to the primary button action, but any custom handler can provide the arrowclick implementation.
27198  * @cfg {Function} arrowHandler A function called when the arrow button is clicked (can be used instead of click event)
27199  * @cfg {String} arrowTooltip The title attribute of the arrow
27200  * @constructor
27201  * Create a new menu button
27202  * @param {String/HTMLElement/Element} renderTo The element to append the button to
27203  * @param {Object} config The config object
27204  */
27205 Roo.SplitButton = function(renderTo, config){
27206     Roo.SplitButton.superclass.constructor.call(this, renderTo, config);
27207     /**
27208      * @event arrowclick
27209      * Fires when this button's arrow is clicked
27210      * @param {SplitButton} this
27211      * @param {EventObject} e The click event
27212      */
27213     this.addEvents({"arrowclick":true});
27214 };
27215
27216 Roo.extend(Roo.SplitButton, Roo.Button, {
27217     render : function(renderTo){
27218         // this is one sweet looking template!
27219         var tpl = new Roo.Template(
27220             '<table cellspacing="0" class="x-btn-menu-wrap x-btn"><tr><td>',
27221             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-text-wrap"><tbody>',
27222             '<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>',
27223             "</tbody></table></td><td>",
27224             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-arrow-wrap"><tbody>',
27225             '<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>',
27226             "</tbody></table></td></tr></table>"
27227         );
27228         var btn = tpl.append(renderTo, [this.text, this.type], true);
27229         var btnEl = btn.child("button");
27230         if(this.cls){
27231             btn.addClass(this.cls);
27232         }
27233         if(this.icon){
27234             btnEl.setStyle('background-image', 'url(' +this.icon +')');
27235         }
27236         if(this.iconCls){
27237             btnEl.addClass(this.iconCls);
27238             if(!this.cls){
27239                 btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
27240             }
27241         }
27242         this.el = btn;
27243         if(this.handleMouseEvents){
27244             btn.on("mouseover", this.onMouseOver, this);
27245             btn.on("mouseout", this.onMouseOut, this);
27246             btn.on("mousedown", this.onMouseDown, this);
27247             btn.on("mouseup", this.onMouseUp, this);
27248         }
27249         btn.on(this.clickEvent, this.onClick, this);
27250         if(this.tooltip){
27251             if(typeof this.tooltip == 'object'){
27252                 Roo.QuickTips.tips(Roo.apply({
27253                       target: btnEl.id
27254                 }, this.tooltip));
27255             } else {
27256                 btnEl.dom[this.tooltipType] = this.tooltip;
27257             }
27258         }
27259         if(this.arrowTooltip){
27260             btn.child("button:nth(2)").dom[this.tooltipType] = this.arrowTooltip;
27261         }
27262         if(this.hidden){
27263             this.hide();
27264         }
27265         if(this.disabled){
27266             this.disable();
27267         }
27268         if(this.pressed){
27269             this.el.addClass("x-btn-pressed");
27270         }
27271         if(Roo.isIE && !Roo.isIE7){
27272             this.autoWidth.defer(1, this);
27273         }else{
27274             this.autoWidth();
27275         }
27276         if(this.menu){
27277             this.menu.on("show", this.onMenuShow, this);
27278             this.menu.on("hide", this.onMenuHide, this);
27279         }
27280         this.fireEvent('render', this);
27281     },
27282
27283     // private
27284     autoWidth : function(){
27285         if(this.el){
27286             var tbl = this.el.child("table:first");
27287             var tbl2 = this.el.child("table:last");
27288             this.el.setWidth("auto");
27289             tbl.setWidth("auto");
27290             if(Roo.isIE7 && Roo.isStrict){
27291                 var ib = this.el.child('button:first');
27292                 if(ib && ib.getWidth() > 20){
27293                     ib.clip();
27294                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
27295                 }
27296             }
27297             if(this.minWidth){
27298                 if(this.hidden){
27299                     this.el.beginMeasure();
27300                 }
27301                 if((tbl.getWidth()+tbl2.getWidth()) < this.minWidth){
27302                     tbl.setWidth(this.minWidth-tbl2.getWidth());
27303                 }
27304                 if(this.hidden){
27305                     this.el.endMeasure();
27306                 }
27307             }
27308             this.el.setWidth(tbl.getWidth()+tbl2.getWidth());
27309         } 
27310     },
27311     /**
27312      * Sets this button's click handler
27313      * @param {Function} handler The function to call when the button is clicked
27314      * @param {Object} scope (optional) Scope for the function passed above
27315      */
27316     setHandler : function(handler, scope){
27317         this.handler = handler;
27318         this.scope = scope;  
27319     },
27320     
27321     /**
27322      * Sets this button's arrow click handler
27323      * @param {Function} handler The function to call when the arrow is clicked
27324      * @param {Object} scope (optional) Scope for the function passed above
27325      */
27326     setArrowHandler : function(handler, scope){
27327         this.arrowHandler = handler;
27328         this.scope = scope;  
27329     },
27330     
27331     /**
27332      * Focus the button
27333      */
27334     focus : function(){
27335         if(this.el){
27336             this.el.child("button:first").focus();
27337         }
27338     },
27339
27340     // private
27341     onClick : function(e){
27342         e.preventDefault();
27343         if(!this.disabled){
27344             if(e.getTarget(".x-btn-menu-arrow-wrap")){
27345                 if(this.menu && !this.menu.isVisible()){
27346                     this.menu.show(this.el, this.menuAlign);
27347                 }
27348                 this.fireEvent("arrowclick", this, e);
27349                 if(this.arrowHandler){
27350                     this.arrowHandler.call(this.scope || this, this, e);
27351                 }
27352             }else{
27353                 this.fireEvent("click", this, e);
27354                 if(this.handler){
27355                     this.handler.call(this.scope || this, this, e);
27356                 }
27357             }
27358         }
27359     },
27360     // private
27361     onMouseDown : function(e){
27362         if(!this.disabled){
27363             Roo.fly(e.getTarget("table")).addClass("x-btn-click");
27364         }
27365     },
27366     // private
27367     onMouseUp : function(e){
27368         Roo.fly(e.getTarget("table")).removeClass("x-btn-click");
27369     }   
27370 });
27371
27372
27373 // backwards compat
27374 Roo.MenuButton = Roo.SplitButton;/*
27375  * Based on:
27376  * Ext JS Library 1.1.1
27377  * Copyright(c) 2006-2007, Ext JS, LLC.
27378  *
27379  * Originally Released Under LGPL - original licence link has changed is not relivant.
27380  *
27381  * Fork - LGPL
27382  * <script type="text/javascript">
27383  */
27384
27385 /**
27386  * @class Roo.Toolbar
27387  * Basic Toolbar class.
27388  * @constructor
27389  * Creates a new Toolbar
27390  * @param {Object} container The config object
27391  */ 
27392 Roo.Toolbar = function(container, buttons, config)
27393 {
27394     /// old consturctor format still supported..
27395     if(container instanceof Array){ // omit the container for later rendering
27396         buttons = container;
27397         config = buttons;
27398         container = null;
27399     }
27400     if (typeof(container) == 'object' && container.xtype) {
27401         config = container;
27402         container = config.container;
27403         buttons = config.buttons || []; // not really - use items!!
27404     }
27405     var xitems = [];
27406     if (config && config.items) {
27407         xitems = config.items;
27408         delete config.items;
27409     }
27410     Roo.apply(this, config);
27411     this.buttons = buttons;
27412     
27413     if(container){
27414         this.render(container);
27415     }
27416     this.xitems = xitems;
27417     Roo.each(xitems, function(b) {
27418         this.add(b);
27419     }, this);
27420     
27421 };
27422
27423 Roo.Toolbar.prototype = {
27424     /**
27425      * @cfg {Array} items
27426      * array of button configs or elements to add (will be converted to a MixedCollection)
27427      */
27428     
27429     /**
27430      * @cfg {String/HTMLElement/Element} container
27431      * The id or element that will contain the toolbar
27432      */
27433     // private
27434     render : function(ct){
27435         this.el = Roo.get(ct);
27436         if(this.cls){
27437             this.el.addClass(this.cls);
27438         }
27439         // using a table allows for vertical alignment
27440         // 100% width is needed by Safari...
27441         this.el.update('<div class="x-toolbar x-small-editor"><table cellspacing="0"><tr></tr></table></div>');
27442         this.tr = this.el.child("tr", true);
27443         var autoId = 0;
27444         this.items = new Roo.util.MixedCollection(false, function(o){
27445             return o.id || ("item" + (++autoId));
27446         });
27447         if(this.buttons){
27448             this.add.apply(this, this.buttons);
27449             delete this.buttons;
27450         }
27451     },
27452
27453     /**
27454      * Adds element(s) to the toolbar -- this function takes a variable number of 
27455      * arguments of mixed type and adds them to the toolbar.
27456      * @param {Mixed} arg1 The following types of arguments are all valid:<br />
27457      * <ul>
27458      * <li>{@link Roo.Toolbar.Button} config: A valid button config object (equivalent to {@link #addButton})</li>
27459      * <li>HtmlElement: Any standard HTML element (equivalent to {@link #addElement})</li>
27460      * <li>Field: Any form field (equivalent to {@link #addField})</li>
27461      * <li>Item: Any subclass of {@link Roo.Toolbar.Item} (equivalent to {@link #addItem})</li>
27462      * <li>String: Any generic string (gets wrapped in a {@link Roo.Toolbar.TextItem}, equivalent to {@link #addText}).
27463      * Note that there are a few special strings that are treated differently as explained nRoo.</li>
27464      * <li>'separator' or '-': Creates a separator element (equivalent to {@link #addSeparator})</li>
27465      * <li>' ': Creates a spacer element (equivalent to {@link #addSpacer})</li>
27466      * <li>'->': Creates a fill element (equivalent to {@link #addFill})</li>
27467      * </ul>
27468      * @param {Mixed} arg2
27469      * @param {Mixed} etc.
27470      */
27471     add : function(){
27472         var a = arguments, l = a.length;
27473         for(var i = 0; i < l; i++){
27474             this._add(a[i]);
27475         }
27476     },
27477     // private..
27478     _add : function(el) {
27479         
27480         if (el.xtype) {
27481             el = Roo.factory(el, typeof(Roo.Toolbar[el.xtype]) == 'undefined' ? Roo.form : Roo.Toolbar);
27482         }
27483         
27484         if (el.applyTo){ // some kind of form field
27485             return this.addField(el);
27486         } 
27487         if (el.render){ // some kind of Toolbar.Item
27488             return this.addItem(el);
27489         }
27490         if (typeof el == "string"){ // string
27491             if(el == "separator" || el == "-"){
27492                 return this.addSeparator();
27493             }
27494             if (el == " "){
27495                 return this.addSpacer();
27496             }
27497             if(el == "->"){
27498                 return this.addFill();
27499             }
27500             return this.addText(el);
27501             
27502         }
27503         if(el.tagName){ // element
27504             return this.addElement(el);
27505         }
27506         if(typeof el == "object"){ // must be button config?
27507             return this.addButton(el);
27508         }
27509         // and now what?!?!
27510         return false;
27511         
27512     },
27513     
27514     /**
27515      * Add an Xtype element
27516      * @param {Object} xtype Xtype Object
27517      * @return {Object} created Object
27518      */
27519     addxtype : function(e){
27520         return this.add(e);  
27521     },
27522     
27523     /**
27524      * Returns the Element for this toolbar.
27525      * @return {Roo.Element}
27526      */
27527     getEl : function(){
27528         return this.el;  
27529     },
27530     
27531     /**
27532      * Adds a separator
27533      * @return {Roo.Toolbar.Item} The separator item
27534      */
27535     addSeparator : function(){
27536         return this.addItem(new Roo.Toolbar.Separator());
27537     },
27538
27539     /**
27540      * Adds a spacer element
27541      * @return {Roo.Toolbar.Spacer} The spacer item
27542      */
27543     addSpacer : function(){
27544         return this.addItem(new Roo.Toolbar.Spacer());
27545     },
27546
27547     /**
27548      * Adds a fill element that forces subsequent additions to the right side of the toolbar
27549      * @return {Roo.Toolbar.Fill} The fill item
27550      */
27551     addFill : function(){
27552         return this.addItem(new Roo.Toolbar.Fill());
27553     },
27554
27555     /**
27556      * Adds any standard HTML element to the toolbar
27557      * @param {String/HTMLElement/Element} el The element or id of the element to add
27558      * @return {Roo.Toolbar.Item} The element's item
27559      */
27560     addElement : function(el){
27561         return this.addItem(new Roo.Toolbar.Item(el));
27562     },
27563     /**
27564      * Collection of items on the toolbar.. (only Toolbar Items, so use fields to retrieve fields)
27565      * @type Roo.util.MixedCollection  
27566      */
27567     items : false,
27568      
27569     /**
27570      * Adds any Toolbar.Item or subclass
27571      * @param {Roo.Toolbar.Item} item
27572      * @return {Roo.Toolbar.Item} The item
27573      */
27574     addItem : function(item){
27575         var td = this.nextBlock();
27576         item.render(td);
27577         this.items.add(item);
27578         return item;
27579     },
27580     
27581     /**
27582      * Adds a button (or buttons). See {@link Roo.Toolbar.Button} for more info on the config.
27583      * @param {Object/Array} config A button config or array of configs
27584      * @return {Roo.Toolbar.Button/Array}
27585      */
27586     addButton : function(config){
27587         if(config instanceof Array){
27588             var buttons = [];
27589             for(var i = 0, len = config.length; i < len; i++) {
27590                 buttons.push(this.addButton(config[i]));
27591             }
27592             return buttons;
27593         }
27594         var b = config;
27595         if(!(config instanceof Roo.Toolbar.Button)){
27596             b = config.split ?
27597                 new Roo.Toolbar.SplitButton(config) :
27598                 new Roo.Toolbar.Button(config);
27599         }
27600         var td = this.nextBlock();
27601         b.render(td);
27602         this.items.add(b);
27603         return b;
27604     },
27605     
27606     /**
27607      * Adds text to the toolbar
27608      * @param {String} text The text to add
27609      * @return {Roo.Toolbar.Item} The element's item
27610      */
27611     addText : function(text){
27612         return this.addItem(new Roo.Toolbar.TextItem(text));
27613     },
27614     
27615     /**
27616      * Inserts any {@link Roo.Toolbar.Item}/{@link Roo.Toolbar.Button} at the specified index.
27617      * @param {Number} index The index where the item is to be inserted
27618      * @param {Object/Roo.Toolbar.Item/Roo.Toolbar.Button (may be Array)} item The button, or button config object to be inserted.
27619      * @return {Roo.Toolbar.Button/Item}
27620      */
27621     insertButton : function(index, item){
27622         if(item instanceof Array){
27623             var buttons = [];
27624             for(var i = 0, len = item.length; i < len; i++) {
27625                buttons.push(this.insertButton(index + i, item[i]));
27626             }
27627             return buttons;
27628         }
27629         if (!(item instanceof Roo.Toolbar.Button)){
27630            item = new Roo.Toolbar.Button(item);
27631         }
27632         var td = document.createElement("td");
27633         this.tr.insertBefore(td, this.tr.childNodes[index]);
27634         item.render(td);
27635         this.items.insert(index, item);
27636         return item;
27637     },
27638     
27639     /**
27640      * Adds a new element to the toolbar from the passed {@link Roo.DomHelper} config.
27641      * @param {Object} config
27642      * @return {Roo.Toolbar.Item} The element's item
27643      */
27644     addDom : function(config, returnEl){
27645         var td = this.nextBlock();
27646         Roo.DomHelper.overwrite(td, config);
27647         var ti = new Roo.Toolbar.Item(td.firstChild);
27648         ti.render(td);
27649         this.items.add(ti);
27650         return ti;
27651     },
27652
27653     /**
27654      * Collection of fields on the toolbar.. usefull for quering (value is false if there are no fields)
27655      * @type Roo.util.MixedCollection  
27656      */
27657     fields : false,
27658     
27659     /**
27660      * Adds a dynamically rendered Roo.form field (TextField, ComboBox, etc).
27661      * Note: the field should not have been rendered yet. For a field that has already been
27662      * rendered, use {@link #addElement}.
27663      * @param {Roo.form.Field} field
27664      * @return {Roo.ToolbarItem}
27665      */
27666      
27667       
27668     addField : function(field) {
27669         if (!this.fields) {
27670             var autoId = 0;
27671             this.fields = new Roo.util.MixedCollection(false, function(o){
27672                 return o.id || ("item" + (++autoId));
27673             });
27674
27675         }
27676         
27677         var td = this.nextBlock();
27678         field.render(td);
27679         var ti = new Roo.Toolbar.Item(td.firstChild);
27680         ti.render(td);
27681         this.items.add(ti);
27682         this.fields.add(field);
27683         return ti;
27684     },
27685     /**
27686      * Hide the toolbar
27687      * @method hide
27688      */
27689      
27690       
27691     hide : function()
27692     {
27693         this.el.child('div').setVisibilityMode(Roo.Element.DISPLAY);
27694         this.el.child('div').hide();
27695     },
27696     /**
27697      * Show the toolbar
27698      * @method show
27699      */
27700     show : function()
27701     {
27702         this.el.child('div').show();
27703     },
27704       
27705     // private
27706     nextBlock : function(){
27707         var td = document.createElement("td");
27708         this.tr.appendChild(td);
27709         return td;
27710     },
27711
27712     // private
27713     destroy : function(){
27714         if(this.items){ // rendered?
27715             Roo.destroy.apply(Roo, this.items.items);
27716         }
27717         if(this.fields){ // rendered?
27718             Roo.destroy.apply(Roo, this.fields.items);
27719         }
27720         Roo.Element.uncache(this.el, this.tr);
27721     }
27722 };
27723
27724 /**
27725  * @class Roo.Toolbar.Item
27726  * The base class that other classes should extend in order to get some basic common toolbar item functionality.
27727  * @constructor
27728  * Creates a new Item
27729  * @param {HTMLElement} el 
27730  */
27731 Roo.Toolbar.Item = function(el){
27732     this.el = Roo.getDom(el);
27733     this.id = Roo.id(this.el);
27734     this.hidden = false;
27735 };
27736
27737 Roo.Toolbar.Item.prototype = {
27738     
27739     /**
27740      * Get this item's HTML Element
27741      * @return {HTMLElement}
27742      */
27743     getEl : function(){
27744        return this.el;  
27745     },
27746
27747     // private
27748     render : function(td){
27749         this.td = td;
27750         td.appendChild(this.el);
27751     },
27752     
27753     /**
27754      * Removes and destroys this item.
27755      */
27756     destroy : function(){
27757         this.td.parentNode.removeChild(this.td);
27758     },
27759     
27760     /**
27761      * Shows this item.
27762      */
27763     show: function(){
27764         this.hidden = false;
27765         this.td.style.display = "";
27766     },
27767     
27768     /**
27769      * Hides this item.
27770      */
27771     hide: function(){
27772         this.hidden = true;
27773         this.td.style.display = "none";
27774     },
27775     
27776     /**
27777      * Convenience function for boolean show/hide.
27778      * @param {Boolean} visible true to show/false to hide
27779      */
27780     setVisible: function(visible){
27781         if(visible) {
27782             this.show();
27783         }else{
27784             this.hide();
27785         }
27786     },
27787     
27788     /**
27789      * Try to focus this item.
27790      */
27791     focus : function(){
27792         Roo.fly(this.el).focus();
27793     },
27794     
27795     /**
27796      * Disables this item.
27797      */
27798     disable : function(){
27799         Roo.fly(this.td).addClass("x-item-disabled");
27800         this.disabled = true;
27801         this.el.disabled = true;
27802     },
27803     
27804     /**
27805      * Enables this item.
27806      */
27807     enable : function(){
27808         Roo.fly(this.td).removeClass("x-item-disabled");
27809         this.disabled = false;
27810         this.el.disabled = false;
27811     }
27812 };
27813
27814
27815 /**
27816  * @class Roo.Toolbar.Separator
27817  * @extends Roo.Toolbar.Item
27818  * A simple toolbar separator class
27819  * @constructor
27820  * Creates a new Separator
27821  */
27822 Roo.Toolbar.Separator = function(){
27823     var s = document.createElement("span");
27824     s.className = "ytb-sep";
27825     Roo.Toolbar.Separator.superclass.constructor.call(this, s);
27826 };
27827 Roo.extend(Roo.Toolbar.Separator, Roo.Toolbar.Item, {
27828     enable:Roo.emptyFn,
27829     disable:Roo.emptyFn,
27830     focus:Roo.emptyFn
27831 });
27832
27833 /**
27834  * @class Roo.Toolbar.Spacer
27835  * @extends Roo.Toolbar.Item
27836  * A simple element that adds extra horizontal space to a toolbar.
27837  * @constructor
27838  * Creates a new Spacer
27839  */
27840 Roo.Toolbar.Spacer = function(){
27841     var s = document.createElement("div");
27842     s.className = "ytb-spacer";
27843     Roo.Toolbar.Spacer.superclass.constructor.call(this, s);
27844 };
27845 Roo.extend(Roo.Toolbar.Spacer, Roo.Toolbar.Item, {
27846     enable:Roo.emptyFn,
27847     disable:Roo.emptyFn,
27848     focus:Roo.emptyFn
27849 });
27850
27851 /**
27852  * @class Roo.Toolbar.Fill
27853  * @extends Roo.Toolbar.Spacer
27854  * A simple element that adds a greedy (100% width) horizontal space to a toolbar.
27855  * @constructor
27856  * Creates a new Spacer
27857  */
27858 Roo.Toolbar.Fill = Roo.extend(Roo.Toolbar.Spacer, {
27859     // private
27860     render : function(td){
27861         td.style.width = '100%';
27862         Roo.Toolbar.Fill.superclass.render.call(this, td);
27863     }
27864 });
27865
27866 /**
27867  * @class Roo.Toolbar.TextItem
27868  * @extends Roo.Toolbar.Item
27869  * A simple class that renders text directly into a toolbar.
27870  * @constructor
27871  * Creates a new TextItem
27872  * @param {String} text
27873  */
27874 Roo.Toolbar.TextItem = function(text){
27875     if (typeof(text) == 'object') {
27876         text = text.text;
27877     }
27878     var s = document.createElement("span");
27879     s.className = "ytb-text";
27880     s.innerHTML = text;
27881     Roo.Toolbar.TextItem.superclass.constructor.call(this, s);
27882 };
27883 Roo.extend(Roo.Toolbar.TextItem, Roo.Toolbar.Item, {
27884     enable:Roo.emptyFn,
27885     disable:Roo.emptyFn,
27886     focus:Roo.emptyFn
27887 });
27888
27889 /**
27890  * @class Roo.Toolbar.Button
27891  * @extends Roo.Button
27892  * A button that renders into a toolbar.
27893  * @constructor
27894  * Creates a new Button
27895  * @param {Object} config A standard {@link Roo.Button} config object
27896  */
27897 Roo.Toolbar.Button = function(config){
27898     Roo.Toolbar.Button.superclass.constructor.call(this, null, config);
27899 };
27900 Roo.extend(Roo.Toolbar.Button, Roo.Button, {
27901     render : function(td){
27902         this.td = td;
27903         Roo.Toolbar.Button.superclass.render.call(this, td);
27904     },
27905     
27906     /**
27907      * Removes and destroys this button
27908      */
27909     destroy : function(){
27910         Roo.Toolbar.Button.superclass.destroy.call(this);
27911         this.td.parentNode.removeChild(this.td);
27912     },
27913     
27914     /**
27915      * Shows this button
27916      */
27917     show: function(){
27918         this.hidden = false;
27919         this.td.style.display = "";
27920     },
27921     
27922     /**
27923      * Hides this button
27924      */
27925     hide: function(){
27926         this.hidden = true;
27927         this.td.style.display = "none";
27928     },
27929
27930     /**
27931      * Disables this item
27932      */
27933     disable : function(){
27934         Roo.fly(this.td).addClass("x-item-disabled");
27935         this.disabled = true;
27936     },
27937
27938     /**
27939      * Enables this item
27940      */
27941     enable : function(){
27942         Roo.fly(this.td).removeClass("x-item-disabled");
27943         this.disabled = false;
27944     }
27945 });
27946 // backwards compat
27947 Roo.ToolbarButton = Roo.Toolbar.Button;
27948
27949 /**
27950  * @class Roo.Toolbar.SplitButton
27951  * @extends Roo.SplitButton
27952  * A menu button that renders into a toolbar.
27953  * @constructor
27954  * Creates a new SplitButton
27955  * @param {Object} config A standard {@link Roo.SplitButton} config object
27956  */
27957 Roo.Toolbar.SplitButton = function(config){
27958     Roo.Toolbar.SplitButton.superclass.constructor.call(this, null, config);
27959 };
27960 Roo.extend(Roo.Toolbar.SplitButton, Roo.SplitButton, {
27961     render : function(td){
27962         this.td = td;
27963         Roo.Toolbar.SplitButton.superclass.render.call(this, td);
27964     },
27965     
27966     /**
27967      * Removes and destroys this button
27968      */
27969     destroy : function(){
27970         Roo.Toolbar.SplitButton.superclass.destroy.call(this);
27971         this.td.parentNode.removeChild(this.td);
27972     },
27973     
27974     /**
27975      * Shows this button
27976      */
27977     show: function(){
27978         this.hidden = false;
27979         this.td.style.display = "";
27980     },
27981     
27982     /**
27983      * Hides this button
27984      */
27985     hide: function(){
27986         this.hidden = true;
27987         this.td.style.display = "none";
27988     }
27989 });
27990
27991 // backwards compat
27992 Roo.Toolbar.MenuButton = Roo.Toolbar.SplitButton;/*
27993  * Based on:
27994  * Ext JS Library 1.1.1
27995  * Copyright(c) 2006-2007, Ext JS, LLC.
27996  *
27997  * Originally Released Under LGPL - original licence link has changed is not relivant.
27998  *
27999  * Fork - LGPL
28000  * <script type="text/javascript">
28001  */
28002  
28003 /**
28004  * @class Roo.PagingToolbar
28005  * @extends Roo.Toolbar
28006  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
28007  * @constructor
28008  * Create a new PagingToolbar
28009  * @param {Object} config The config object
28010  */
28011 Roo.PagingToolbar = function(el, ds, config)
28012 {
28013     // old args format still supported... - xtype is prefered..
28014     if (typeof(el) == 'object' && el.xtype) {
28015         // created from xtype...
28016         config = el;
28017         ds = el.dataSource;
28018         el = config.container;
28019     }
28020     var items = [];
28021     if (config.items) {
28022         items = config.items;
28023         config.items = [];
28024     }
28025     
28026     Roo.PagingToolbar.superclass.constructor.call(this, el, null, config);
28027     this.ds = ds;
28028     this.cursor = 0;
28029     this.renderButtons(this.el);
28030     this.bind(ds);
28031     
28032     // supprot items array.
28033    
28034     Roo.each(items, function(e) {
28035         this.add(Roo.factory(e));
28036     },this);
28037     
28038 };
28039
28040 Roo.extend(Roo.PagingToolbar, Roo.Toolbar, {
28041     /**
28042      * @cfg {Roo.data.Store} dataSource
28043      * The underlying data store providing the paged data
28044      */
28045     /**
28046      * @cfg {String/HTMLElement/Element} container
28047      * container The id or element that will contain the toolbar
28048      */
28049     /**
28050      * @cfg {Boolean} displayInfo
28051      * True to display the displayMsg (defaults to false)
28052      */
28053     /**
28054      * @cfg {Number} pageSize
28055      * The number of records to display per page (defaults to 20)
28056      */
28057     pageSize: 20,
28058     /**
28059      * @cfg {String} displayMsg
28060      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
28061      */
28062     displayMsg : 'Displaying {0} - {1} of {2}',
28063     /**
28064      * @cfg {String} emptyMsg
28065      * The message to display when no records are found (defaults to "No data to display")
28066      */
28067     emptyMsg : 'No data to display',
28068     /**
28069      * Customizable piece of the default paging text (defaults to "Page")
28070      * @type String
28071      */
28072     beforePageText : "Page",
28073     /**
28074      * Customizable piece of the default paging text (defaults to "of %0")
28075      * @type String
28076      */
28077     afterPageText : "of {0}",
28078     /**
28079      * Customizable piece of the default paging text (defaults to "First Page")
28080      * @type String
28081      */
28082     firstText : "First Page",
28083     /**
28084      * Customizable piece of the default paging text (defaults to "Previous Page")
28085      * @type String
28086      */
28087     prevText : "Previous Page",
28088     /**
28089      * Customizable piece of the default paging text (defaults to "Next Page")
28090      * @type String
28091      */
28092     nextText : "Next Page",
28093     /**
28094      * Customizable piece of the default paging text (defaults to "Last Page")
28095      * @type String
28096      */
28097     lastText : "Last Page",
28098     /**
28099      * Customizable piece of the default paging text (defaults to "Refresh")
28100      * @type String
28101      */
28102     refreshText : "Refresh",
28103
28104     // private
28105     renderButtons : function(el){
28106         Roo.PagingToolbar.superclass.render.call(this, el);
28107         this.first = this.addButton({
28108             tooltip: this.firstText,
28109             cls: "x-btn-icon x-grid-page-first",
28110             disabled: true,
28111             handler: this.onClick.createDelegate(this, ["first"])
28112         });
28113         this.prev = this.addButton({
28114             tooltip: this.prevText,
28115             cls: "x-btn-icon x-grid-page-prev",
28116             disabled: true,
28117             handler: this.onClick.createDelegate(this, ["prev"])
28118         });
28119         //this.addSeparator();
28120         this.add(this.beforePageText);
28121         this.field = Roo.get(this.addDom({
28122            tag: "input",
28123            type: "text",
28124            size: "3",
28125            value: "1",
28126            cls: "x-grid-page-number"
28127         }).el);
28128         this.field.on("keydown", this.onPagingKeydown, this);
28129         this.field.on("focus", function(){this.dom.select();});
28130         this.afterTextEl = this.addText(String.format(this.afterPageText, 1));
28131         this.field.setHeight(18);
28132         //this.addSeparator();
28133         this.next = this.addButton({
28134             tooltip: this.nextText,
28135             cls: "x-btn-icon x-grid-page-next",
28136             disabled: true,
28137             handler: this.onClick.createDelegate(this, ["next"])
28138         });
28139         this.last = this.addButton({
28140             tooltip: this.lastText,
28141             cls: "x-btn-icon x-grid-page-last",
28142             disabled: true,
28143             handler: this.onClick.createDelegate(this, ["last"])
28144         });
28145         //this.addSeparator();
28146         this.loading = this.addButton({
28147             tooltip: this.refreshText,
28148             cls: "x-btn-icon x-grid-loading",
28149             handler: this.onClick.createDelegate(this, ["refresh"])
28150         });
28151
28152         if(this.displayInfo){
28153             this.displayEl = Roo.fly(this.el.dom.firstChild).createChild({cls:'x-paging-info'});
28154         }
28155     },
28156
28157     // private
28158     updateInfo : function(){
28159         if(this.displayEl){
28160             var count = this.ds.getCount();
28161             var msg = count == 0 ?
28162                 this.emptyMsg :
28163                 String.format(
28164                     this.displayMsg,
28165                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
28166                 );
28167             this.displayEl.update(msg);
28168         }
28169     },
28170
28171     // private
28172     onLoad : function(ds, r, o){
28173        this.cursor = o.params ? o.params.start : 0;
28174        var d = this.getPageData(), ap = d.activePage, ps = d.pages;
28175
28176        this.afterTextEl.el.innerHTML = String.format(this.afterPageText, d.pages);
28177        this.field.dom.value = ap;
28178        this.first.setDisabled(ap == 1);
28179        this.prev.setDisabled(ap == 1);
28180        this.next.setDisabled(ap == ps);
28181        this.last.setDisabled(ap == ps);
28182        this.loading.enable();
28183        this.updateInfo();
28184     },
28185
28186     // private
28187     getPageData : function(){
28188         var total = this.ds.getTotalCount();
28189         return {
28190             total : total,
28191             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
28192             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
28193         };
28194     },
28195
28196     // private
28197     onLoadError : function(){
28198         this.loading.enable();
28199     },
28200
28201     // private
28202     onPagingKeydown : function(e){
28203         var k = e.getKey();
28204         var d = this.getPageData();
28205         if(k == e.RETURN){
28206             var v = this.field.dom.value, pageNum;
28207             if(!v || isNaN(pageNum = parseInt(v, 10))){
28208                 this.field.dom.value = d.activePage;
28209                 return;
28210             }
28211             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
28212             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
28213             e.stopEvent();
28214         }
28215         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))
28216         {
28217           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
28218           this.field.dom.value = pageNum;
28219           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
28220           e.stopEvent();
28221         }
28222         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
28223         {
28224           var v = this.field.dom.value, pageNum; 
28225           var increment = (e.shiftKey) ? 10 : 1;
28226           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
28227             increment *= -1;
28228           if(!v || isNaN(pageNum = parseInt(v, 10))) {
28229             this.field.dom.value = d.activePage;
28230             return;
28231           }
28232           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
28233           {
28234             this.field.dom.value = parseInt(v, 10) + increment;
28235             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
28236             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
28237           }
28238           e.stopEvent();
28239         }
28240     },
28241
28242     // private
28243     beforeLoad : function(){
28244         if(this.loading){
28245             this.loading.disable();
28246         }
28247     },
28248
28249     // private
28250     onClick : function(which){
28251         var ds = this.ds;
28252         switch(which){
28253             case "first":
28254                 ds.load({params:{start: 0, limit: this.pageSize}});
28255             break;
28256             case "prev":
28257                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
28258             break;
28259             case "next":
28260                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
28261             break;
28262             case "last":
28263                 var total = ds.getTotalCount();
28264                 var extra = total % this.pageSize;
28265                 var lastStart = extra ? (total - extra) : total-this.pageSize;
28266                 ds.load({params:{start: lastStart, limit: this.pageSize}});
28267             break;
28268             case "refresh":
28269                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
28270             break;
28271         }
28272     },
28273
28274     /**
28275      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
28276      * @param {Roo.data.Store} store The data store to unbind
28277      */
28278     unbind : function(ds){
28279         ds.un("beforeload", this.beforeLoad, this);
28280         ds.un("load", this.onLoad, this);
28281         ds.un("loadexception", this.onLoadError, this);
28282         ds.un("remove", this.updateInfo, this);
28283         ds.un("add", this.updateInfo, this);
28284         this.ds = undefined;
28285     },
28286
28287     /**
28288      * Binds the paging toolbar to the specified {@link Roo.data.Store}
28289      * @param {Roo.data.Store} store The data store to bind
28290      */
28291     bind : function(ds){
28292         ds.on("beforeload", this.beforeLoad, this);
28293         ds.on("load", this.onLoad, this);
28294         ds.on("loadexception", this.onLoadError, this);
28295         ds.on("remove", this.updateInfo, this);
28296         ds.on("add", this.updateInfo, this);
28297         this.ds = ds;
28298     }
28299 });/*
28300  * Based on:
28301  * Ext JS Library 1.1.1
28302  * Copyright(c) 2006-2007, Ext JS, LLC.
28303  *
28304  * Originally Released Under LGPL - original licence link has changed is not relivant.
28305  *
28306  * Fork - LGPL
28307  * <script type="text/javascript">
28308  */
28309
28310 /**
28311  * @class Roo.Resizable
28312  * @extends Roo.util.Observable
28313  * <p>Applies drag handles to an element to make it resizable. The drag handles are inserted into the element
28314  * and positioned absolute. Some elements, such as a textarea or image, don't support this. To overcome that, you can wrap
28315  * 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
28316  * the element will be wrapped for you automatically.</p>
28317  * <p>Here is the list of valid resize handles:</p>
28318  * <pre>
28319 Value   Description
28320 ------  -------------------
28321  'n'     north
28322  's'     south
28323  'e'     east
28324  'w'     west
28325  'nw'    northwest
28326  'sw'    southwest
28327  'se'    southeast
28328  'ne'    northeast
28329  'hd'    horizontal drag
28330  'all'   all
28331 </pre>
28332  * <p>Here's an example showing the creation of a typical Resizable:</p>
28333  * <pre><code>
28334 var resizer = new Roo.Resizable("element-id", {
28335     handles: 'all',
28336     minWidth: 200,
28337     minHeight: 100,
28338     maxWidth: 500,
28339     maxHeight: 400,
28340     pinned: true
28341 });
28342 resizer.on("resize", myHandler);
28343 </code></pre>
28344  * <p>To hide a particular handle, set its display to none in CSS, or through script:<br>
28345  * resizer.east.setDisplayed(false);</p>
28346  * @cfg {Boolean/String/Element} resizeChild True to resize the first child, or id/element to resize (defaults to false)
28347  * @cfg {Array/String} adjustments String "auto" or an array [width, height] with values to be <b>added</b> to the
28348  * resize operation's new size (defaults to [0, 0])
28349  * @cfg {Number} minWidth The minimum width for the element (defaults to 5)
28350  * @cfg {Number} minHeight The minimum height for the element (defaults to 5)
28351  * @cfg {Number} maxWidth The maximum width for the element (defaults to 10000)
28352  * @cfg {Number} maxHeight The maximum height for the element (defaults to 10000)
28353  * @cfg {Boolean} enabled False to disable resizing (defaults to true)
28354  * @cfg {Boolean} wrap True to wrap an element with a div if needed (required for textareas and images, defaults to false)
28355  * @cfg {Number} width The width of the element in pixels (defaults to null)
28356  * @cfg {Number} height The height of the element in pixels (defaults to null)
28357  * @cfg {Boolean} animate True to animate the resize (not compatible with dynamic sizing, defaults to false)
28358  * @cfg {Number} duration Animation duration if animate = true (defaults to .35)
28359  * @cfg {Boolean} dynamic True to resize the element while dragging instead of using a proxy (defaults to false)
28360  * @cfg {String} handles String consisting of the resize handles to display (defaults to undefined)
28361  * @cfg {Boolean} multiDirectional <b>Deprecated</b>.  The old style of adding multi-direction resize handles, deprecated
28362  * in favor of the handles config option (defaults to false)
28363  * @cfg {Boolean} disableTrackOver True to disable mouse tracking. This is only applied at config time. (defaults to false)
28364  * @cfg {String} easing Animation easing if animate = true (defaults to 'easingOutStrong')
28365  * @cfg {Number} widthIncrement The increment to snap the width resize in pixels (dynamic must be true, defaults to 0)
28366  * @cfg {Number} heightIncrement The increment to snap the height resize in pixels (dynamic must be true, defaults to 0)
28367  * @cfg {Boolean} pinned True to ensure that the resize handles are always visible, false to display them only when the
28368  * user mouses over the resizable borders. This is only applied at config time. (defaults to false)
28369  * @cfg {Boolean} preserveRatio True to preserve the original ratio between height and width during resize (defaults to false)
28370  * @cfg {Boolean} transparent True for transparent handles. This is only applied at config time. (defaults to false)
28371  * @cfg {Number} minX The minimum allowed page X for the element (only used for west resizing, defaults to 0)
28372  * @cfg {Number} minY The minimum allowed page Y for the element (only used for north resizing, defaults to 0)
28373  * @cfg {Boolean} draggable Convenience to initialize drag drop (defaults to false)
28374  * @constructor
28375  * Create a new resizable component
28376  * @param {String/HTMLElement/Roo.Element} el The id or element to resize
28377  * @param {Object} config configuration options
28378   */
28379 Roo.Resizable = function(el, config)
28380 {
28381     this.el = Roo.get(el);
28382
28383     if(config && config.wrap){
28384         config.resizeChild = this.el;
28385         this.el = this.el.wrap(typeof config.wrap == "object" ? config.wrap : {cls:"xresizable-wrap"});
28386         this.el.id = this.el.dom.id = config.resizeChild.id + "-rzwrap";
28387         this.el.setStyle("overflow", "hidden");
28388         this.el.setPositioning(config.resizeChild.getPositioning());
28389         config.resizeChild.clearPositioning();
28390         if(!config.width || !config.height){
28391             var csize = config.resizeChild.getSize();
28392             this.el.setSize(csize.width, csize.height);
28393         }
28394         if(config.pinned && !config.adjustments){
28395             config.adjustments = "auto";
28396         }
28397     }
28398
28399     this.proxy = this.el.createProxy({tag: "div", cls: "x-resizable-proxy", id: this.el.id + "-rzproxy"});
28400     this.proxy.unselectable();
28401     this.proxy.enableDisplayMode('block');
28402
28403     Roo.apply(this, config);
28404
28405     if(this.pinned){
28406         this.disableTrackOver = true;
28407         this.el.addClass("x-resizable-pinned");
28408     }
28409     // if the element isn't positioned, make it relative
28410     var position = this.el.getStyle("position");
28411     if(position != "absolute" && position != "fixed"){
28412         this.el.setStyle("position", "relative");
28413     }
28414     if(!this.handles){ // no handles passed, must be legacy style
28415         this.handles = 's,e,se';
28416         if(this.multiDirectional){
28417             this.handles += ',n,w';
28418         }
28419     }
28420     if(this.handles == "all"){
28421         this.handles = "n s e w ne nw se sw";
28422     }
28423     var hs = this.handles.split(/\s*?[,;]\s*?| /);
28424     var ps = Roo.Resizable.positions;
28425     for(var i = 0, len = hs.length; i < len; i++){
28426         if(hs[i] && ps[hs[i]]){
28427             var pos = ps[hs[i]];
28428             this[pos] = new Roo.Resizable.Handle(this, pos, this.disableTrackOver, this.transparent);
28429         }
28430     }
28431     // legacy
28432     this.corner = this.southeast;
28433     
28434     // updateBox = the box can move..
28435     if(this.handles.indexOf("n") != -1 || this.handles.indexOf("w") != -1 || this.handles.indexOf("hd") != -1) {
28436         this.updateBox = true;
28437     }
28438
28439     this.activeHandle = null;
28440
28441     if(this.resizeChild){
28442         if(typeof this.resizeChild == "boolean"){
28443             this.resizeChild = Roo.get(this.el.dom.firstChild, true);
28444         }else{
28445             this.resizeChild = Roo.get(this.resizeChild, true);
28446         }
28447     }
28448     
28449     if(this.adjustments == "auto"){
28450         var rc = this.resizeChild;
28451         var hw = this.west, he = this.east, hn = this.north, hs = this.south;
28452         if(rc && (hw || hn)){
28453             rc.position("relative");
28454             rc.setLeft(hw ? hw.el.getWidth() : 0);
28455             rc.setTop(hn ? hn.el.getHeight() : 0);
28456         }
28457         this.adjustments = [
28458             (he ? -he.el.getWidth() : 0) + (hw ? -hw.el.getWidth() : 0),
28459             (hn ? -hn.el.getHeight() : 0) + (hs ? -hs.el.getHeight() : 0) -1
28460         ];
28461     }
28462
28463     if(this.draggable){
28464         this.dd = this.dynamic ?
28465             this.el.initDD(null) : this.el.initDDProxy(null, {dragElId: this.proxy.id});
28466         this.dd.setHandleElId(this.resizeChild ? this.resizeChild.id : this.el.id);
28467     }
28468
28469     // public events
28470     this.addEvents({
28471         /**
28472          * @event beforeresize
28473          * Fired before resize is allowed. Set enabled to false to cancel resize.
28474          * @param {Roo.Resizable} this
28475          * @param {Roo.EventObject} e The mousedown event
28476          */
28477         "beforeresize" : true,
28478         /**
28479          * @event resize
28480          * Fired after a resize.
28481          * @param {Roo.Resizable} this
28482          * @param {Number} width The new width
28483          * @param {Number} height The new height
28484          * @param {Roo.EventObject} e The mouseup event
28485          */
28486         "resize" : true
28487     });
28488
28489     if(this.width !== null && this.height !== null){
28490         this.resizeTo(this.width, this.height);
28491     }else{
28492         this.updateChildSize();
28493     }
28494     if(Roo.isIE){
28495         this.el.dom.style.zoom = 1;
28496     }
28497     Roo.Resizable.superclass.constructor.call(this);
28498 };
28499
28500 Roo.extend(Roo.Resizable, Roo.util.Observable, {
28501         resizeChild : false,
28502         adjustments : [0, 0],
28503         minWidth : 5,
28504         minHeight : 5,
28505         maxWidth : 10000,
28506         maxHeight : 10000,
28507         enabled : true,
28508         animate : false,
28509         duration : .35,
28510         dynamic : false,
28511         handles : false,
28512         multiDirectional : false,
28513         disableTrackOver : false,
28514         easing : 'easeOutStrong',
28515         widthIncrement : 0,
28516         heightIncrement : 0,
28517         pinned : false,
28518         width : null,
28519         height : null,
28520         preserveRatio : false,
28521         transparent: false,
28522         minX: 0,
28523         minY: 0,
28524         draggable: false,
28525
28526         /**
28527          * @cfg {String/HTMLElement/Element} constrainTo Constrain the resize to a particular element
28528          */
28529         constrainTo: undefined,
28530         /**
28531          * @cfg {Roo.lib.Region} resizeRegion Constrain the resize to a particular region
28532          */
28533         resizeRegion: undefined,
28534
28535
28536     /**
28537      * Perform a manual resize
28538      * @param {Number} width
28539      * @param {Number} height
28540      */
28541     resizeTo : function(width, height){
28542         this.el.setSize(width, height);
28543         this.updateChildSize();
28544         this.fireEvent("resize", this, width, height, null);
28545     },
28546
28547     // private
28548     startSizing : function(e, handle){
28549         this.fireEvent("beforeresize", this, e);
28550         if(this.enabled){ // 2nd enabled check in case disabled before beforeresize handler
28551
28552             if(!this.overlay){
28553                 this.overlay = this.el.createProxy({tag: "div", cls: "x-resizable-overlay", html: "&#160;"});
28554                 this.overlay.unselectable();
28555                 this.overlay.enableDisplayMode("block");
28556                 this.overlay.on("mousemove", this.onMouseMove, this);
28557                 this.overlay.on("mouseup", this.onMouseUp, this);
28558             }
28559             this.overlay.setStyle("cursor", handle.el.getStyle("cursor"));
28560
28561             this.resizing = true;
28562             this.startBox = this.el.getBox();
28563             this.startPoint = e.getXY();
28564             this.offsets = [(this.startBox.x + this.startBox.width) - this.startPoint[0],
28565                             (this.startBox.y + this.startBox.height) - this.startPoint[1]];
28566
28567             this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
28568             this.overlay.show();
28569
28570             if(this.constrainTo) {
28571                 var ct = Roo.get(this.constrainTo);
28572                 this.resizeRegion = ct.getRegion().adjust(
28573                     ct.getFrameWidth('t'),
28574                     ct.getFrameWidth('l'),
28575                     -ct.getFrameWidth('b'),
28576                     -ct.getFrameWidth('r')
28577                 );
28578             }
28579
28580             this.proxy.setStyle('visibility', 'hidden'); // workaround display none
28581             this.proxy.show();
28582             this.proxy.setBox(this.startBox);
28583             if(!this.dynamic){
28584                 this.proxy.setStyle('visibility', 'visible');
28585             }
28586         }
28587     },
28588
28589     // private
28590     onMouseDown : function(handle, e){
28591         if(this.enabled){
28592             e.stopEvent();
28593             this.activeHandle = handle;
28594             this.startSizing(e, handle);
28595         }
28596     },
28597
28598     // private
28599     onMouseUp : function(e){
28600         var size = this.resizeElement();
28601         this.resizing = false;
28602         this.handleOut();
28603         this.overlay.hide();
28604         this.proxy.hide();
28605         this.fireEvent("resize", this, size.width, size.height, e);
28606     },
28607
28608     // private
28609     updateChildSize : function(){
28610         
28611         if(this.resizeChild){
28612             Roo.log('in?');
28613             var el = this.el;
28614             var child = this.resizeChild;
28615             var adj = this.adjustments;
28616             if(el.dom.offsetWidth){
28617                 var b = el.getSize(true);
28618                 child.setSize(b.width+adj[0], b.height+adj[1]);
28619             }
28620             // Second call here for IE
28621             // The first call enables instant resizing and
28622             // the second call corrects scroll bars if they
28623             // exist
28624             if(Roo.isIE){
28625                 setTimeout(function(){
28626                     if(el.dom.offsetWidth){
28627                         var b = el.getSize(true);
28628                         child.setSize(b.width+adj[0], b.height+adj[1]);
28629                     }
28630                 }, 10);
28631             }
28632         }
28633     },
28634
28635     // private
28636     snap : function(value, inc, min){
28637         if(!inc || !value) return value;
28638         var newValue = value;
28639         var m = value % inc;
28640         if(m > 0){
28641             if(m > (inc/2)){
28642                 newValue = value + (inc-m);
28643             }else{
28644                 newValue = value - m;
28645             }
28646         }
28647         return Math.max(min, newValue);
28648     },
28649
28650     // private
28651     resizeElement : function(){
28652         var box = this.proxy.getBox();
28653         if(this.updateBox){
28654             this.el.setBox(box, false, this.animate, this.duration, null, this.easing);
28655         }else{
28656             this.el.setSize(box.width, box.height, this.animate, this.duration, null, this.easing);
28657         }
28658         this.updateChildSize();
28659         if(!this.dynamic){
28660             this.proxy.hide();
28661         }
28662         return box;
28663     },
28664
28665     // private
28666     constrain : function(v, diff, m, mx){
28667         if(v - diff < m){
28668             diff = v - m;
28669         }else if(v - diff > mx){
28670             diff = mx - v;
28671         }
28672         return diff;
28673     },
28674
28675     // private
28676     onMouseMove : function(e){
28677         if(this.enabled){
28678             try{// try catch so if something goes wrong the user doesn't get hung
28679
28680             if(this.resizeRegion && !this.resizeRegion.contains(e.getPoint())) {
28681                 return;
28682             }
28683
28684             //var curXY = this.startPoint;
28685             var curSize = this.curSize || this.startBox;
28686             var x = this.startBox.x, y = this.startBox.y;
28687             var ox = x, oy = y;
28688             var w = curSize.width, h = curSize.height;
28689             var ow = w, oh = h;
28690             var mw = this.minWidth, mh = this.minHeight;
28691             var mxw = this.maxWidth, mxh = this.maxHeight;
28692             var wi = this.widthIncrement;
28693             var hi = this.heightIncrement;
28694
28695             var eventXY = e.getXY();
28696             var diffX = -(this.startPoint[0] - Math.max(this.minX, eventXY[0]));
28697             var diffY = -(this.startPoint[1] - Math.max(this.minY, eventXY[1]));
28698
28699             var pos = this.activeHandle.position;
28700
28701             switch(pos){
28702                 case "east":
28703                     w += diffX;
28704                     w = Math.min(Math.max(mw, w), mxw);
28705                     break;
28706              
28707                 case "south":
28708                     h += diffY;
28709                     h = Math.min(Math.max(mh, h), mxh);
28710                     break;
28711                 case "southeast":
28712                     w += diffX;
28713                     h += diffY;
28714                     w = Math.min(Math.max(mw, w), mxw);
28715                     h = Math.min(Math.max(mh, h), mxh);
28716                     break;
28717                 case "north":
28718                     diffY = this.constrain(h, diffY, mh, mxh);
28719                     y += diffY;
28720                     h -= diffY;
28721                     break;
28722                 case "hdrag":
28723                     
28724                     if (wi) {
28725                         var adiffX = Math.abs(diffX);
28726                         var sub = (adiffX % wi); // how much 
28727                         if (sub > (wi/2)) { // far enough to snap
28728                             diffX = (diffX > 0) ? diffX-sub + wi : diffX+sub - wi;
28729                         } else {
28730                             // remove difference.. 
28731                             diffX = (diffX > 0) ? diffX-sub : diffX+sub;
28732                         }
28733                     }
28734                     x += diffX;
28735                     x = Math.max(this.minX, x);
28736                     break;
28737                 case "west":
28738                     diffX = this.constrain(w, diffX, mw, mxw);
28739                     x += diffX;
28740                     w -= diffX;
28741                     break;
28742                 case "northeast":
28743                     w += diffX;
28744                     w = Math.min(Math.max(mw, w), mxw);
28745                     diffY = this.constrain(h, diffY, mh, mxh);
28746                     y += diffY;
28747                     h -= diffY;
28748                     break;
28749                 case "northwest":
28750                     diffX = this.constrain(w, diffX, mw, mxw);
28751                     diffY = this.constrain(h, diffY, mh, mxh);
28752                     y += diffY;
28753                     h -= diffY;
28754                     x += diffX;
28755                     w -= diffX;
28756                     break;
28757                case "southwest":
28758                     diffX = this.constrain(w, diffX, mw, mxw);
28759                     h += diffY;
28760                     h = Math.min(Math.max(mh, h), mxh);
28761                     x += diffX;
28762                     w -= diffX;
28763                     break;
28764             }
28765
28766             var sw = this.snap(w, wi, mw);
28767             var sh = this.snap(h, hi, mh);
28768             if(sw != w || sh != h){
28769                 switch(pos){
28770                     case "northeast":
28771                         y -= sh - h;
28772                     break;
28773                     case "north":
28774                         y -= sh - h;
28775                         break;
28776                     case "southwest":
28777                         x -= sw - w;
28778                     break;
28779                     case "west":
28780                         x -= sw - w;
28781                         break;
28782                     case "northwest":
28783                         x -= sw - w;
28784                         y -= sh - h;
28785                     break;
28786                 }
28787                 w = sw;
28788                 h = sh;
28789             }
28790
28791             if(this.preserveRatio){
28792                 switch(pos){
28793                     case "southeast":
28794                     case "east":
28795                         h = oh * (w/ow);
28796                         h = Math.min(Math.max(mh, h), mxh);
28797                         w = ow * (h/oh);
28798                        break;
28799                     case "south":
28800                         w = ow * (h/oh);
28801                         w = Math.min(Math.max(mw, w), mxw);
28802                         h = oh * (w/ow);
28803                         break;
28804                     case "northeast":
28805                         w = ow * (h/oh);
28806                         w = Math.min(Math.max(mw, w), mxw);
28807                         h = oh * (w/ow);
28808                     break;
28809                     case "north":
28810                         var tw = w;
28811                         w = ow * (h/oh);
28812                         w = Math.min(Math.max(mw, w), mxw);
28813                         h = oh * (w/ow);
28814                         x += (tw - w) / 2;
28815                         break;
28816                     case "southwest":
28817                         h = oh * (w/ow);
28818                         h = Math.min(Math.max(mh, h), mxh);
28819                         var tw = w;
28820                         w = ow * (h/oh);
28821                         x += tw - w;
28822                         break;
28823                     case "west":
28824                         var th = h;
28825                         h = oh * (w/ow);
28826                         h = Math.min(Math.max(mh, h), mxh);
28827                         y += (th - h) / 2;
28828                         var tw = w;
28829                         w = ow * (h/oh);
28830                         x += tw - w;
28831                        break;
28832                     case "northwest":
28833                         var tw = w;
28834                         var th = h;
28835                         h = oh * (w/ow);
28836                         h = Math.min(Math.max(mh, h), mxh);
28837                         w = ow * (h/oh);
28838                         y += th - h;
28839                         x += tw - w;
28840                        break;
28841
28842                 }
28843             }
28844             if (pos == 'hdrag') {
28845                 w = ow;
28846             }
28847             this.proxy.setBounds(x, y, w, h);
28848             if(this.dynamic){
28849                 this.resizeElement();
28850             }
28851             }catch(e){}
28852         }
28853     },
28854
28855     // private
28856     handleOver : function(){
28857         if(this.enabled){
28858             this.el.addClass("x-resizable-over");
28859         }
28860     },
28861
28862     // private
28863     handleOut : function(){
28864         if(!this.resizing){
28865             this.el.removeClass("x-resizable-over");
28866         }
28867     },
28868
28869     /**
28870      * Returns the element this component is bound to.
28871      * @return {Roo.Element}
28872      */
28873     getEl : function(){
28874         return this.el;
28875     },
28876
28877     /**
28878      * Returns the resizeChild element (or null).
28879      * @return {Roo.Element}
28880      */
28881     getResizeChild : function(){
28882         return this.resizeChild;
28883     },
28884
28885     /**
28886      * Destroys this resizable. If the element was wrapped and
28887      * removeEl is not true then the element remains.
28888      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
28889      */
28890     destroy : function(removeEl){
28891         this.proxy.remove();
28892         if(this.overlay){
28893             this.overlay.removeAllListeners();
28894             this.overlay.remove();
28895         }
28896         var ps = Roo.Resizable.positions;
28897         for(var k in ps){
28898             if(typeof ps[k] != "function" && this[ps[k]]){
28899                 var h = this[ps[k]];
28900                 h.el.removeAllListeners();
28901                 h.el.remove();
28902             }
28903         }
28904         if(removeEl){
28905             this.el.update("");
28906             this.el.remove();
28907         }
28908     }
28909 });
28910
28911 // private
28912 // hash to map config positions to true positions
28913 Roo.Resizable.positions = {
28914     n: "north", s: "south", e: "east", w: "west", se: "southeast", sw: "southwest", nw: "northwest", ne: "northeast", 
28915     hd: "hdrag"
28916 };
28917
28918 // private
28919 Roo.Resizable.Handle = function(rz, pos, disableTrackOver, transparent){
28920     if(!this.tpl){
28921         // only initialize the template if resizable is used
28922         var tpl = Roo.DomHelper.createTemplate(
28923             {tag: "div", cls: "x-resizable-handle x-resizable-handle-{0}"}
28924         );
28925         tpl.compile();
28926         Roo.Resizable.Handle.prototype.tpl = tpl;
28927     }
28928     this.position = pos;
28929     this.rz = rz;
28930     // show north drag fro topdra
28931     var handlepos = pos == 'hdrag' ? 'north' : pos;
28932     
28933     this.el = this.tpl.append(rz.el.dom, [handlepos], true);
28934     if (pos == 'hdrag') {
28935         this.el.setStyle('cursor', 'pointer');
28936     }
28937     this.el.unselectable();
28938     if(transparent){
28939         this.el.setOpacity(0);
28940     }
28941     this.el.on("mousedown", this.onMouseDown, this);
28942     if(!disableTrackOver){
28943         this.el.on("mouseover", this.onMouseOver, this);
28944         this.el.on("mouseout", this.onMouseOut, this);
28945     }
28946 };
28947
28948 // private
28949 Roo.Resizable.Handle.prototype = {
28950     afterResize : function(rz){
28951         // do nothing
28952     },
28953     // private
28954     onMouseDown : function(e){
28955         this.rz.onMouseDown(this, e);
28956     },
28957     // private
28958     onMouseOver : function(e){
28959         this.rz.handleOver(this, e);
28960     },
28961     // private
28962     onMouseOut : function(e){
28963         this.rz.handleOut(this, e);
28964     }
28965 };/*
28966  * Based on:
28967  * Ext JS Library 1.1.1
28968  * Copyright(c) 2006-2007, Ext JS, LLC.
28969  *
28970  * Originally Released Under LGPL - original licence link has changed is not relivant.
28971  *
28972  * Fork - LGPL
28973  * <script type="text/javascript">
28974  */
28975
28976 /**
28977  * @class Roo.Editor
28978  * @extends Roo.Component
28979  * A base editor field that handles displaying/hiding on demand and has some built-in sizing and event handling logic.
28980  * @constructor
28981  * Create a new Editor
28982  * @param {Roo.form.Field} field The Field object (or descendant)
28983  * @param {Object} config The config object
28984  */
28985 Roo.Editor = function(field, config){
28986     Roo.Editor.superclass.constructor.call(this, config);
28987     this.field = field;
28988     this.addEvents({
28989         /**
28990              * @event beforestartedit
28991              * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
28992              * false from the handler of this event.
28993              * @param {Editor} this
28994              * @param {Roo.Element} boundEl The underlying element bound to this editor
28995              * @param {Mixed} value The field value being set
28996              */
28997         "beforestartedit" : true,
28998         /**
28999              * @event startedit
29000              * Fires when this editor is displayed
29001              * @param {Roo.Element} boundEl The underlying element bound to this editor
29002              * @param {Mixed} value The starting field value
29003              */
29004         "startedit" : true,
29005         /**
29006              * @event beforecomplete
29007              * Fires after a change has been made to the field, but before the change is reflected in the underlying
29008              * field.  Saving the change to the field can be canceled by returning false from the handler of this event.
29009              * Note that if the value has not changed and ignoreNoChange = true, the editing will still end but this
29010              * event will not fire since no edit actually occurred.
29011              * @param {Editor} this
29012              * @param {Mixed} value The current field value
29013              * @param {Mixed} startValue The original field value
29014              */
29015         "beforecomplete" : true,
29016         /**
29017              * @event complete
29018              * Fires after editing is complete and any changed value has been written to the underlying field.
29019              * @param {Editor} this
29020              * @param {Mixed} value The current field value
29021              * @param {Mixed} startValue The original field value
29022              */
29023         "complete" : true,
29024         /**
29025          * @event specialkey
29026          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
29027          * {@link Roo.EventObject#getKey} to determine which key was pressed.
29028          * @param {Roo.form.Field} this
29029          * @param {Roo.EventObject} e The event object
29030          */
29031         "specialkey" : true
29032     });
29033 };
29034
29035 Roo.extend(Roo.Editor, Roo.Component, {
29036     /**
29037      * @cfg {Boolean/String} autosize
29038      * True for the editor to automatically adopt the size of the underlying field, "width" to adopt the width only,
29039      * or "height" to adopt the height only (defaults to false)
29040      */
29041     /**
29042      * @cfg {Boolean} revertInvalid
29043      * True to automatically revert the field value and cancel the edit when the user completes an edit and the field
29044      * validation fails (defaults to true)
29045      */
29046     /**
29047      * @cfg {Boolean} ignoreNoChange
29048      * True to skip the the edit completion process (no save, no events fired) if the user completes an edit and
29049      * the value has not changed (defaults to false).  Applies only to string values - edits for other data types
29050      * will never be ignored.
29051      */
29052     /**
29053      * @cfg {Boolean} hideEl
29054      * False to keep the bound element visible while the editor is displayed (defaults to true)
29055      */
29056     /**
29057      * @cfg {Mixed} value
29058      * The data value of the underlying field (defaults to "")
29059      */
29060     value : "",
29061     /**
29062      * @cfg {String} alignment
29063      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "c-c?").
29064      */
29065     alignment: "c-c?",
29066     /**
29067      * @cfg {Boolean/String} shadow "sides" for sides/bottom only, "frame" for 4-way shadow, and "drop"
29068      * for bottom-right shadow (defaults to "frame")
29069      */
29070     shadow : "frame",
29071     /**
29072      * @cfg {Boolean} constrain True to constrain the editor to the viewport
29073      */
29074     constrain : false,
29075     /**
29076      * @cfg {Boolean} completeOnEnter True to complete the edit when the enter key is pressed (defaults to false)
29077      */
29078     completeOnEnter : false,
29079     /**
29080      * @cfg {Boolean} cancelOnEsc True to cancel the edit when the escape key is pressed (defaults to false)
29081      */
29082     cancelOnEsc : false,
29083     /**
29084      * @cfg {Boolean} updateEl True to update the innerHTML of the bound element when the update completes (defaults to false)
29085      */
29086     updateEl : false,
29087
29088     // private
29089     onRender : function(ct, position){
29090         this.el = new Roo.Layer({
29091             shadow: this.shadow,
29092             cls: "x-editor",
29093             parentEl : ct,
29094             shim : this.shim,
29095             shadowOffset:4,
29096             id: this.id,
29097             constrain: this.constrain
29098         });
29099         this.el.setStyle("overflow", Roo.isGecko ? "auto" : "hidden");
29100         if(this.field.msgTarget != 'title'){
29101             this.field.msgTarget = 'qtip';
29102         }
29103         this.field.render(this.el);
29104         if(Roo.isGecko){
29105             this.field.el.dom.setAttribute('autocomplete', 'off');
29106         }
29107         this.field.on("specialkey", this.onSpecialKey, this);
29108         if(this.swallowKeys){
29109             this.field.el.swallowEvent(['keydown','keypress']);
29110         }
29111         this.field.show();
29112         this.field.on("blur", this.onBlur, this);
29113         if(this.field.grow){
29114             this.field.on("autosize", this.el.sync,  this.el, {delay:1});
29115         }
29116     },
29117
29118     onSpecialKey : function(field, e)
29119     {
29120         //Roo.log('editor onSpecialKey');
29121         if(this.completeOnEnter && e.getKey() == e.ENTER){
29122             e.stopEvent();
29123             this.completeEdit();
29124             return;
29125         }
29126         // do not fire special key otherwise it might hide close the editor...
29127         if(e.getKey() == e.ENTER){    
29128             return;
29129         }
29130         if(this.cancelOnEsc && e.getKey() == e.ESC){
29131             this.cancelEdit();
29132             return;
29133         } 
29134         this.fireEvent('specialkey', field, e);
29135     
29136     },
29137
29138     /**
29139      * Starts the editing process and shows the editor.
29140      * @param {String/HTMLElement/Element} el The element to edit
29141      * @param {String} value (optional) A value to initialize the editor with. If a value is not provided, it defaults
29142       * to the innerHTML of el.
29143      */
29144     startEdit : function(el, value){
29145         if(this.editing){
29146             this.completeEdit();
29147         }
29148         this.boundEl = Roo.get(el);
29149         var v = value !== undefined ? value : this.boundEl.dom.innerHTML;
29150         if(!this.rendered){
29151             this.render(this.parentEl || document.body);
29152         }
29153         if(this.fireEvent("beforestartedit", this, this.boundEl, v) === false){
29154             return;
29155         }
29156         this.startValue = v;
29157         this.field.setValue(v);
29158         if(this.autoSize){
29159             var sz = this.boundEl.getSize();
29160             switch(this.autoSize){
29161                 case "width":
29162                 this.setSize(sz.width,  "");
29163                 break;
29164                 case "height":
29165                 this.setSize("",  sz.height);
29166                 break;
29167                 default:
29168                 this.setSize(sz.width,  sz.height);
29169             }
29170         }
29171         this.el.alignTo(this.boundEl, this.alignment);
29172         this.editing = true;
29173         if(Roo.QuickTips){
29174             Roo.QuickTips.disable();
29175         }
29176         this.show();
29177     },
29178
29179     /**
29180      * Sets the height and width of this editor.
29181      * @param {Number} width The new width
29182      * @param {Number} height The new height
29183      */
29184     setSize : function(w, h){
29185         this.field.setSize(w, h);
29186         if(this.el){
29187             this.el.sync();
29188         }
29189     },
29190
29191     /**
29192      * Realigns the editor to the bound field based on the current alignment config value.
29193      */
29194     realign : function(){
29195         this.el.alignTo(this.boundEl, this.alignment);
29196     },
29197
29198     /**
29199      * Ends the editing process, persists the changed value to the underlying field, and hides the editor.
29200      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after edit (defaults to false)
29201      */
29202     completeEdit : function(remainVisible){
29203         if(!this.editing){
29204             return;
29205         }
29206         var v = this.getValue();
29207         if(this.revertInvalid !== false && !this.field.isValid()){
29208             v = this.startValue;
29209             this.cancelEdit(true);
29210         }
29211         if(String(v) === String(this.startValue) && this.ignoreNoChange){
29212             this.editing = false;
29213             this.hide();
29214             return;
29215         }
29216         if(this.fireEvent("beforecomplete", this, v, this.startValue) !== false){
29217             this.editing = false;
29218             if(this.updateEl && this.boundEl){
29219                 this.boundEl.update(v);
29220             }
29221             if(remainVisible !== true){
29222                 this.hide();
29223             }
29224             this.fireEvent("complete", this, v, this.startValue);
29225         }
29226     },
29227
29228     // private
29229     onShow : function(){
29230         this.el.show();
29231         if(this.hideEl !== false){
29232             this.boundEl.hide();
29233         }
29234         this.field.show();
29235         if(Roo.isIE && !this.fixIEFocus){ // IE has problems with focusing the first time
29236             this.fixIEFocus = true;
29237             this.deferredFocus.defer(50, this);
29238         }else{
29239             this.field.focus();
29240         }
29241         this.fireEvent("startedit", this.boundEl, this.startValue);
29242     },
29243
29244     deferredFocus : function(){
29245         if(this.editing){
29246             this.field.focus();
29247         }
29248     },
29249
29250     /**
29251      * Cancels the editing process and hides the editor without persisting any changes.  The field value will be
29252      * reverted to the original starting value.
29253      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after
29254      * cancel (defaults to false)
29255      */
29256     cancelEdit : function(remainVisible){
29257         if(this.editing){
29258             this.setValue(this.startValue);
29259             if(remainVisible !== true){
29260                 this.hide();
29261             }
29262         }
29263     },
29264
29265     // private
29266     onBlur : function(){
29267         if(this.allowBlur !== true && this.editing){
29268             this.completeEdit();
29269         }
29270     },
29271
29272     // private
29273     onHide : function(){
29274         if(this.editing){
29275             this.completeEdit();
29276             return;
29277         }
29278         this.field.blur();
29279         if(this.field.collapse){
29280             this.field.collapse();
29281         }
29282         this.el.hide();
29283         if(this.hideEl !== false){
29284             this.boundEl.show();
29285         }
29286         if(Roo.QuickTips){
29287             Roo.QuickTips.enable();
29288         }
29289     },
29290
29291     /**
29292      * Sets the data value of the editor
29293      * @param {Mixed} value Any valid value supported by the underlying field
29294      */
29295     setValue : function(v){
29296         this.field.setValue(v);
29297     },
29298
29299     /**
29300      * Gets the data value of the editor
29301      * @return {Mixed} The data value
29302      */
29303     getValue : function(){
29304         return this.field.getValue();
29305     }
29306 });/*
29307  * Based on:
29308  * Ext JS Library 1.1.1
29309  * Copyright(c) 2006-2007, Ext JS, LLC.
29310  *
29311  * Originally Released Under LGPL - original licence link has changed is not relivant.
29312  *
29313  * Fork - LGPL
29314  * <script type="text/javascript">
29315  */
29316  
29317 /**
29318  * @class Roo.BasicDialog
29319  * @extends Roo.util.Observable
29320  * Lightweight Dialog Class.  The code below shows the creation of a typical dialog using existing HTML markup:
29321  * <pre><code>
29322 var dlg = new Roo.BasicDialog("my-dlg", {
29323     height: 200,
29324     width: 300,
29325     minHeight: 100,
29326     minWidth: 150,
29327     modal: true,
29328     proxyDrag: true,
29329     shadow: true
29330 });
29331 dlg.addKeyListener(27, dlg.hide, dlg); // ESC can also close the dialog
29332 dlg.addButton('OK', dlg.hide, dlg);    // Could call a save function instead of hiding
29333 dlg.addButton('Cancel', dlg.hide, dlg);
29334 dlg.show();
29335 </code></pre>
29336   <b>A Dialog should always be a direct child of the body element.</b>
29337  * @cfg {Boolean/DomHelper} autoCreate True to auto create from scratch, or using a DomHelper Object (defaults to false)
29338  * @cfg {String} title Default text to display in the title bar (defaults to null)
29339  * @cfg {Number} width Width of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
29340  * @cfg {Number} height Height of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
29341  * @cfg {Number} x The default left page coordinate of the dialog (defaults to center screen)
29342  * @cfg {Number} y The default top page coordinate of the dialog (defaults to center screen)
29343  * @cfg {String/Element} animateTarget Id or element from which the dialog should animate while opening
29344  * (defaults to null with no animation)
29345  * @cfg {Boolean} resizable False to disable manual dialog resizing (defaults to true)
29346  * @cfg {String} resizeHandles Which resize handles to display - see the {@link Roo.Resizable} handles config
29347  * property for valid values (defaults to 'all')
29348  * @cfg {Number} minHeight The minimum allowable height for a resizable dialog (defaults to 80)
29349  * @cfg {Number} minWidth The minimum allowable width for a resizable dialog (defaults to 200)
29350  * @cfg {Boolean} modal True to show the dialog modally, preventing user interaction with the rest of the page (defaults to false)
29351  * @cfg {Boolean} autoScroll True to allow the dialog body contents to overflow and display scrollbars (defaults to false)
29352  * @cfg {Boolean} closable False to remove the built-in top-right corner close button (defaults to true)
29353  * @cfg {Boolean} collapsible False to remove the built-in top-right corner collapse button (defaults to true)
29354  * @cfg {Boolean} constraintoviewport True to keep the dialog constrained within the visible viewport boundaries (defaults to true)
29355  * @cfg {Boolean} syncHeightBeforeShow True to cause the dimensions to be recalculated before the dialog is shown (defaults to false)
29356  * @cfg {Boolean} draggable False to disable dragging of the dialog within the viewport (defaults to true)
29357  * @cfg {Boolean} autoTabs If true, all elements with class 'x-dlg-tab' will get automatically converted to tabs (defaults to false)
29358  * @cfg {String} tabTag The tag name of tab elements, used when autoTabs = true (defaults to 'div')
29359  * @cfg {Boolean} proxyDrag True to drag a lightweight proxy element rather than the dialog itself, used when
29360  * draggable = true (defaults to false)
29361  * @cfg {Boolean} fixedcenter True to ensure that anytime the dialog is shown or resized it gets centered (defaults to false)
29362  * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
29363  * shadow (defaults to false)
29364  * @cfg {Number} shadowOffset The number of pixels to offset the shadow if displayed (defaults to 5)
29365  * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "right")
29366  * @cfg {Number} minButtonWidth Minimum width of all dialog buttons (defaults to 75)
29367  * @cfg {Array} buttons Array of buttons
29368  * @cfg {Boolean} shim True to create an iframe shim that prevents selects from showing through (defaults to false)
29369  * @constructor
29370  * Create a new BasicDialog.
29371  * @param {String/HTMLElement/Roo.Element} el The container element or DOM node, or its id
29372  * @param {Object} config Configuration options
29373  */
29374 Roo.BasicDialog = function(el, config){
29375     this.el = Roo.get(el);
29376     var dh = Roo.DomHelper;
29377     if(!this.el && config && config.autoCreate){
29378         if(typeof config.autoCreate == "object"){
29379             if(!config.autoCreate.id){
29380                 config.autoCreate.id = el;
29381             }
29382             this.el = dh.append(document.body,
29383                         config.autoCreate, true);
29384         }else{
29385             this.el = dh.append(document.body,
29386                         {tag: "div", id: el, style:'visibility:hidden;'}, true);
29387         }
29388     }
29389     el = this.el;
29390     el.setDisplayed(true);
29391     el.hide = this.hideAction;
29392     this.id = el.id;
29393     el.addClass("x-dlg");
29394
29395     Roo.apply(this, config);
29396
29397     this.proxy = el.createProxy("x-dlg-proxy");
29398     this.proxy.hide = this.hideAction;
29399     this.proxy.setOpacity(.5);
29400     this.proxy.hide();
29401
29402     if(config.width){
29403         el.setWidth(config.width);
29404     }
29405     if(config.height){
29406         el.setHeight(config.height);
29407     }
29408     this.size = el.getSize();
29409     if(typeof config.x != "undefined" && typeof config.y != "undefined"){
29410         this.xy = [config.x,config.y];
29411     }else{
29412         this.xy = el.getCenterXY(true);
29413     }
29414     /** The header element @type Roo.Element */
29415     this.header = el.child("> .x-dlg-hd");
29416     /** The body element @type Roo.Element */
29417     this.body = el.child("> .x-dlg-bd");
29418     /** The footer element @type Roo.Element */
29419     this.footer = el.child("> .x-dlg-ft");
29420
29421     if(!this.header){
29422         this.header = el.createChild({tag: "div", cls:"x-dlg-hd", html: "&#160;"}, this.body ? this.body.dom : null);
29423     }
29424     if(!this.body){
29425         this.body = el.createChild({tag: "div", cls:"x-dlg-bd"});
29426     }
29427
29428     this.header.unselectable();
29429     if(this.title){
29430         this.header.update(this.title);
29431     }
29432     // this element allows the dialog to be focused for keyboard event
29433     this.focusEl = el.createChild({tag: "a", href:"#", cls:"x-dlg-focus", tabIndex:"-1"});
29434     this.focusEl.swallowEvent("click", true);
29435
29436     this.header.wrap({cls:"x-dlg-hd-right"}).wrap({cls:"x-dlg-hd-left"}, true);
29437
29438     // wrap the body and footer for special rendering
29439     this.bwrap = this.body.wrap({tag: "div", cls:"x-dlg-dlg-body"});
29440     if(this.footer){
29441         this.bwrap.dom.appendChild(this.footer.dom);
29442     }
29443
29444     this.bg = this.el.createChild({
29445         tag: "div", cls:"x-dlg-bg",
29446         html: '<div class="x-dlg-bg-left"><div class="x-dlg-bg-right"><div class="x-dlg-bg-center">&#160;</div></div></div>'
29447     });
29448     this.centerBg = this.bg.child("div.x-dlg-bg-center");
29449
29450
29451     if(this.autoScroll !== false && !this.autoTabs){
29452         this.body.setStyle("overflow", "auto");
29453     }
29454
29455     this.toolbox = this.el.createChild({cls: "x-dlg-toolbox"});
29456
29457     if(this.closable !== false){
29458         this.el.addClass("x-dlg-closable");
29459         this.close = this.toolbox.createChild({cls:"x-dlg-close"});
29460         this.close.on("click", this.closeClick, this);
29461         this.close.addClassOnOver("x-dlg-close-over");
29462     }
29463     if(this.collapsible !== false){
29464         this.collapseBtn = this.toolbox.createChild({cls:"x-dlg-collapse"});
29465         this.collapseBtn.on("click", this.collapseClick, this);
29466         this.collapseBtn.addClassOnOver("x-dlg-collapse-over");
29467         this.header.on("dblclick", this.collapseClick, this);
29468     }
29469     if(this.resizable !== false){
29470         this.el.addClass("x-dlg-resizable");
29471         this.resizer = new Roo.Resizable(el, {
29472             minWidth: this.minWidth || 80,
29473             minHeight:this.minHeight || 80,
29474             handles: this.resizeHandles || "all",
29475             pinned: true
29476         });
29477         this.resizer.on("beforeresize", this.beforeResize, this);
29478         this.resizer.on("resize", this.onResize, this);
29479     }
29480     if(this.draggable !== false){
29481         el.addClass("x-dlg-draggable");
29482         if (!this.proxyDrag) {
29483             var dd = new Roo.dd.DD(el.dom.id, "WindowDrag");
29484         }
29485         else {
29486             var dd = new Roo.dd.DDProxy(el.dom.id, "WindowDrag", {dragElId: this.proxy.id});
29487         }
29488         dd.setHandleElId(this.header.id);
29489         dd.endDrag = this.endMove.createDelegate(this);
29490         dd.startDrag = this.startMove.createDelegate(this);
29491         dd.onDrag = this.onDrag.createDelegate(this);
29492         dd.scroll = false;
29493         this.dd = dd;
29494     }
29495     if(this.modal){
29496         this.mask = dh.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
29497         this.mask.enableDisplayMode("block");
29498         this.mask.hide();
29499         this.el.addClass("x-dlg-modal");
29500     }
29501     if(this.shadow){
29502         this.shadow = new Roo.Shadow({
29503             mode : typeof this.shadow == "string" ? this.shadow : "sides",
29504             offset : this.shadowOffset
29505         });
29506     }else{
29507         this.shadowOffset = 0;
29508     }
29509     if(Roo.useShims && this.shim !== false){
29510         this.shim = this.el.createShim();
29511         this.shim.hide = this.hideAction;
29512         this.shim.hide();
29513     }else{
29514         this.shim = false;
29515     }
29516     if(this.autoTabs){
29517         this.initTabs();
29518     }
29519     if (this.buttons) { 
29520         var bts= this.buttons;
29521         this.buttons = [];
29522         Roo.each(bts, function(b) {
29523             this.addButton(b);
29524         }, this);
29525     }
29526     
29527     
29528     this.addEvents({
29529         /**
29530          * @event keydown
29531          * Fires when a key is pressed
29532          * @param {Roo.BasicDialog} this
29533          * @param {Roo.EventObject} e
29534          */
29535         "keydown" : true,
29536         /**
29537          * @event move
29538          * Fires when this dialog is moved by the user.
29539          * @param {Roo.BasicDialog} this
29540          * @param {Number} x The new page X
29541          * @param {Number} y The new page Y
29542          */
29543         "move" : true,
29544         /**
29545          * @event resize
29546          * Fires when this dialog is resized by the user.
29547          * @param {Roo.BasicDialog} this
29548          * @param {Number} width The new width
29549          * @param {Number} height The new height
29550          */
29551         "resize" : true,
29552         /**
29553          * @event beforehide
29554          * Fires before this dialog is hidden.
29555          * @param {Roo.BasicDialog} this
29556          */
29557         "beforehide" : true,
29558         /**
29559          * @event hide
29560          * Fires when this dialog is hidden.
29561          * @param {Roo.BasicDialog} this
29562          */
29563         "hide" : true,
29564         /**
29565          * @event beforeshow
29566          * Fires before this dialog is shown.
29567          * @param {Roo.BasicDialog} this
29568          */
29569         "beforeshow" : true,
29570         /**
29571          * @event show
29572          * Fires when this dialog is shown.
29573          * @param {Roo.BasicDialog} this
29574          */
29575         "show" : true
29576     });
29577     el.on("keydown", this.onKeyDown, this);
29578     el.on("mousedown", this.toFront, this);
29579     Roo.EventManager.onWindowResize(this.adjustViewport, this, true);
29580     this.el.hide();
29581     Roo.DialogManager.register(this);
29582     Roo.BasicDialog.superclass.constructor.call(this);
29583 };
29584
29585 Roo.extend(Roo.BasicDialog, Roo.util.Observable, {
29586     shadowOffset: Roo.isIE ? 6 : 5,
29587     minHeight: 80,
29588     minWidth: 200,
29589     minButtonWidth: 75,
29590     defaultButton: null,
29591     buttonAlign: "right",
29592     tabTag: 'div',
29593     firstShow: true,
29594
29595     /**
29596      * Sets the dialog title text
29597      * @param {String} text The title text to display
29598      * @return {Roo.BasicDialog} this
29599      */
29600     setTitle : function(text){
29601         this.header.update(text);
29602         return this;
29603     },
29604
29605     // private
29606     closeClick : function(){
29607         this.hide();
29608     },
29609
29610     // private
29611     collapseClick : function(){
29612         this[this.collapsed ? "expand" : "collapse"]();
29613     },
29614
29615     /**
29616      * Collapses the dialog to its minimized state (only the title bar is visible).
29617      * Equivalent to the user clicking the collapse dialog button.
29618      */
29619     collapse : function(){
29620         if(!this.collapsed){
29621             this.collapsed = true;
29622             this.el.addClass("x-dlg-collapsed");
29623             this.restoreHeight = this.el.getHeight();
29624             this.resizeTo(this.el.getWidth(), this.header.getHeight());
29625         }
29626     },
29627
29628     /**
29629      * Expands a collapsed dialog back to its normal state.  Equivalent to the user
29630      * clicking the expand dialog button.
29631      */
29632     expand : function(){
29633         if(this.collapsed){
29634             this.collapsed = false;
29635             this.el.removeClass("x-dlg-collapsed");
29636             this.resizeTo(this.el.getWidth(), this.restoreHeight);
29637         }
29638     },
29639
29640     /**
29641      * Reinitializes the tabs component, clearing out old tabs and finding new ones.
29642      * @return {Roo.TabPanel} The tabs component
29643      */
29644     initTabs : function(){
29645         var tabs = this.getTabs();
29646         while(tabs.getTab(0)){
29647             tabs.removeTab(0);
29648         }
29649         this.el.select(this.tabTag+'.x-dlg-tab').each(function(el){
29650             var dom = el.dom;
29651             tabs.addTab(Roo.id(dom), dom.title);
29652             dom.title = "";
29653         });
29654         tabs.activate(0);
29655         return tabs;
29656     },
29657
29658     // private
29659     beforeResize : function(){
29660         this.resizer.minHeight = Math.max(this.minHeight, this.getHeaderFooterHeight(true)+40);
29661     },
29662
29663     // private
29664     onResize : function(){
29665         this.refreshSize();
29666         this.syncBodyHeight();
29667         this.adjustAssets();
29668         this.focus();
29669         this.fireEvent("resize", this, this.size.width, this.size.height);
29670     },
29671
29672     // private
29673     onKeyDown : function(e){
29674         if(this.isVisible()){
29675             this.fireEvent("keydown", this, e);
29676         }
29677     },
29678
29679     /**
29680      * Resizes the dialog.
29681      * @param {Number} width
29682      * @param {Number} height
29683      * @return {Roo.BasicDialog} this
29684      */
29685     resizeTo : function(width, height){
29686         this.el.setSize(width, height);
29687         this.size = {width: width, height: height};
29688         this.syncBodyHeight();
29689         if(this.fixedcenter){
29690             this.center();
29691         }
29692         if(this.isVisible()){
29693             this.constrainXY();
29694             this.adjustAssets();
29695         }
29696         this.fireEvent("resize", this, width, height);
29697         return this;
29698     },
29699
29700
29701     /**
29702      * Resizes the dialog to fit the specified content size.
29703      * @param {Number} width
29704      * @param {Number} height
29705      * @return {Roo.BasicDialog} this
29706      */
29707     setContentSize : function(w, h){
29708         h += this.getHeaderFooterHeight() + this.body.getMargins("tb");
29709         w += this.body.getMargins("lr") + this.bwrap.getMargins("lr") + this.centerBg.getPadding("lr");
29710         //if(!this.el.isBorderBox()){
29711             h +=  this.body.getPadding("tb") + this.bwrap.getBorderWidth("tb") + this.body.getBorderWidth("tb") + this.el.getBorderWidth("tb");
29712             w += this.body.getPadding("lr") + this.bwrap.getBorderWidth("lr") + this.body.getBorderWidth("lr") + this.bwrap.getPadding("lr") + this.el.getBorderWidth("lr");
29713         //}
29714         if(this.tabs){
29715             h += this.tabs.stripWrap.getHeight() + this.tabs.bodyEl.getMargins("tb") + this.tabs.bodyEl.getPadding("tb");
29716             w += this.tabs.bodyEl.getMargins("lr") + this.tabs.bodyEl.getPadding("lr");
29717         }
29718         this.resizeTo(w, h);
29719         return this;
29720     },
29721
29722     /**
29723      * Adds a key listener for when this dialog is displayed.  This allows you to hook in a function that will be
29724      * executed in response to a particular key being pressed while the dialog is active.
29725      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the following options:
29726      *                                  {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
29727      * @param {Function} fn The function to call
29728      * @param {Object} scope (optional) The scope of the function
29729      * @return {Roo.BasicDialog} this
29730      */
29731     addKeyListener : function(key, fn, scope){
29732         var keyCode, shift, ctrl, alt;
29733         if(typeof key == "object" && !(key instanceof Array)){
29734             keyCode = key["key"];
29735             shift = key["shift"];
29736             ctrl = key["ctrl"];
29737             alt = key["alt"];
29738         }else{
29739             keyCode = key;
29740         }
29741         var handler = function(dlg, e){
29742             if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) &&  (!alt || e.altKey)){
29743                 var k = e.getKey();
29744                 if(keyCode instanceof Array){
29745                     for(var i = 0, len = keyCode.length; i < len; i++){
29746                         if(keyCode[i] == k){
29747                           fn.call(scope || window, dlg, k, e);
29748                           return;
29749                         }
29750                     }
29751                 }else{
29752                     if(k == keyCode){
29753                         fn.call(scope || window, dlg, k, e);
29754                     }
29755                 }
29756             }
29757         };
29758         this.on("keydown", handler);
29759         return this;
29760     },
29761
29762     /**
29763      * Returns the TabPanel component (creates it if it doesn't exist).
29764      * Note: If you wish to simply check for the existence of tabs without creating them,
29765      * check for a null 'tabs' property.
29766      * @return {Roo.TabPanel} The tabs component
29767      */
29768     getTabs : function(){
29769         if(!this.tabs){
29770             this.el.addClass("x-dlg-auto-tabs");
29771             this.body.addClass(this.tabPosition == "bottom" ? "x-tabs-bottom" : "x-tabs-top");
29772             this.tabs = new Roo.TabPanel(this.body.dom, this.tabPosition == "bottom");
29773         }
29774         return this.tabs;
29775     },
29776
29777     /**
29778      * Adds a button to the footer section of the dialog.
29779      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
29780      * object or a valid Roo.DomHelper element config
29781      * @param {Function} handler The function called when the button is clicked
29782      * @param {Object} scope (optional) The scope of the handler function (accepts position as a property)
29783      * @return {Roo.Button} The new button
29784      */
29785     addButton : function(config, handler, scope){
29786         var dh = Roo.DomHelper;
29787         if(!this.footer){
29788             this.footer = dh.append(this.bwrap, {tag: "div", cls:"x-dlg-ft"}, true);
29789         }
29790         if(!this.btnContainer){
29791             var tb = this.footer.createChild({
29792
29793                 cls:"x-dlg-btns x-dlg-btns-"+this.buttonAlign,
29794                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
29795             }, null, true);
29796             this.btnContainer = tb.firstChild.firstChild.firstChild;
29797         }
29798         var bconfig = {
29799             handler: handler,
29800             scope: scope,
29801             minWidth: this.minButtonWidth,
29802             hideParent:true
29803         };
29804         if(typeof config == "string"){
29805             bconfig.text = config;
29806         }else{
29807             if(config.tag){
29808                 bconfig.dhconfig = config;
29809             }else{
29810                 Roo.apply(bconfig, config);
29811             }
29812         }
29813         var fc = false;
29814         if ((typeof(bconfig.position) != 'undefined') && bconfig.position < this.btnContainer.childNodes.length-1) {
29815             bconfig.position = Math.max(0, bconfig.position);
29816             fc = this.btnContainer.childNodes[bconfig.position];
29817         }
29818          
29819         var btn = new Roo.Button(
29820             fc ? 
29821                 this.btnContainer.insertBefore(document.createElement("td"),fc)
29822                 : this.btnContainer.appendChild(document.createElement("td")),
29823             //Roo.get(this.btnContainer).createChild( { tag: 'td'},  fc ),
29824             bconfig
29825         );
29826         this.syncBodyHeight();
29827         if(!this.buttons){
29828             /**
29829              * Array of all the buttons that have been added to this dialog via addButton
29830              * @type Array
29831              */
29832             this.buttons = [];
29833         }
29834         this.buttons.push(btn);
29835         return btn;
29836     },
29837
29838     /**
29839      * Sets the default button to be focused when the dialog is displayed.
29840      * @param {Roo.BasicDialog.Button} btn The button object returned by {@link #addButton}
29841      * @return {Roo.BasicDialog} this
29842      */
29843     setDefaultButton : function(btn){
29844         this.defaultButton = btn;
29845         return this;
29846     },
29847
29848     // private
29849     getHeaderFooterHeight : function(safe){
29850         var height = 0;
29851         if(this.header){
29852            height += this.header.getHeight();
29853         }
29854         if(this.footer){
29855            var fm = this.footer.getMargins();
29856             height += (this.footer.getHeight()+fm.top+fm.bottom);
29857         }
29858         height += this.bwrap.getPadding("tb")+this.bwrap.getBorderWidth("tb");
29859         height += this.centerBg.getPadding("tb");
29860         return height;
29861     },
29862
29863     // private
29864     syncBodyHeight : function()
29865     {
29866         var bd = this.body, // the text
29867             cb = this.centerBg, // wrapper around bottom.. but does not seem to be used..
29868             bw = this.bwrap;
29869         var height = this.size.height - this.getHeaderFooterHeight(false);
29870         bd.setHeight(height-bd.getMargins("tb"));
29871         var hh = this.header.getHeight();
29872         var h = this.size.height-hh;
29873         cb.setHeight(h);
29874         
29875         bw.setLeftTop(cb.getPadding("l"), hh+cb.getPadding("t"));
29876         bw.setHeight(h-cb.getPadding("tb"));
29877         
29878         bw.setWidth(this.el.getWidth(true)-cb.getPadding("lr"));
29879         bd.setWidth(bw.getWidth(true));
29880         if(this.tabs){
29881             this.tabs.syncHeight();
29882             if(Roo.isIE){
29883                 this.tabs.el.repaint();
29884             }
29885         }
29886     },
29887
29888     /**
29889      * Restores the previous state of the dialog if Roo.state is configured.
29890      * @return {Roo.BasicDialog} this
29891      */
29892     restoreState : function(){
29893         var box = Roo.state.Manager.get(this.stateId || (this.el.id + "-state"));
29894         if(box && box.width){
29895             this.xy = [box.x, box.y];
29896             this.resizeTo(box.width, box.height);
29897         }
29898         return this;
29899     },
29900
29901     // private
29902     beforeShow : function(){
29903         this.expand();
29904         if(this.fixedcenter){
29905             this.xy = this.el.getCenterXY(true);
29906         }
29907         if(this.modal){
29908             Roo.get(document.body).addClass("x-body-masked");
29909             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
29910             this.mask.show();
29911         }
29912         this.constrainXY();
29913     },
29914
29915     // private
29916     animShow : function(){
29917         var b = Roo.get(this.animateTarget).getBox();
29918         this.proxy.setSize(b.width, b.height);
29919         this.proxy.setLocation(b.x, b.y);
29920         this.proxy.show();
29921         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height,
29922                     true, .35, this.showEl.createDelegate(this));
29923     },
29924
29925     /**
29926      * Shows the dialog.
29927      * @param {String/HTMLElement/Roo.Element} animateTarget (optional) Reset the animation target
29928      * @return {Roo.BasicDialog} this
29929      */
29930     show : function(animateTarget){
29931         if (this.fireEvent("beforeshow", this) === false){
29932             return;
29933         }
29934         if(this.syncHeightBeforeShow){
29935             this.syncBodyHeight();
29936         }else if(this.firstShow){
29937             this.firstShow = false;
29938             this.syncBodyHeight(); // sync the height on the first show instead of in the constructor
29939         }
29940         this.animateTarget = animateTarget || this.animateTarget;
29941         if(!this.el.isVisible()){
29942             this.beforeShow();
29943             if(this.animateTarget && Roo.get(this.animateTarget)){
29944                 this.animShow();
29945             }else{
29946                 this.showEl();
29947             }
29948         }
29949         return this;
29950     },
29951
29952     // private
29953     showEl : function(){
29954         this.proxy.hide();
29955         this.el.setXY(this.xy);
29956         this.el.show();
29957         this.adjustAssets(true);
29958         this.toFront();
29959         this.focus();
29960         // IE peekaboo bug - fix found by Dave Fenwick
29961         if(Roo.isIE){
29962             this.el.repaint();
29963         }
29964         this.fireEvent("show", this);
29965     },
29966
29967     /**
29968      * Focuses the dialog.  If a defaultButton is set, it will receive focus, otherwise the
29969      * dialog itself will receive focus.
29970      */
29971     focus : function(){
29972         if(this.defaultButton){
29973             this.defaultButton.focus();
29974         }else{
29975             this.focusEl.focus();
29976         }
29977     },
29978
29979     // private
29980     constrainXY : function(){
29981         if(this.constraintoviewport !== false){
29982             if(!this.viewSize){
29983                 if(this.container){
29984                     var s = this.container.getSize();
29985                     this.viewSize = [s.width, s.height];
29986                 }else{
29987                     this.viewSize = [Roo.lib.Dom.getViewWidth(),Roo.lib.Dom.getViewHeight()];
29988                 }
29989             }
29990             var s = Roo.get(this.container||document).getScroll();
29991
29992             var x = this.xy[0], y = this.xy[1];
29993             var w = this.size.width, h = this.size.height;
29994             var vw = this.viewSize[0], vh = this.viewSize[1];
29995             // only move it if it needs it
29996             var moved = false;
29997             // first validate right/bottom
29998             if(x + w > vw+s.left){
29999                 x = vw - w;
30000                 moved = true;
30001             }
30002             if(y + h > vh+s.top){
30003                 y = vh - h;
30004                 moved = true;
30005             }
30006             // then make sure top/left isn't negative
30007             if(x < s.left){
30008                 x = s.left;
30009                 moved = true;
30010             }
30011             if(y < s.top){
30012                 y = s.top;
30013                 moved = true;
30014             }
30015             if(moved){
30016                 // cache xy
30017                 this.xy = [x, y];
30018                 if(this.isVisible()){
30019                     this.el.setLocation(x, y);
30020                     this.adjustAssets();
30021                 }
30022             }
30023         }
30024     },
30025
30026     // private
30027     onDrag : function(){
30028         if(!this.proxyDrag){
30029             this.xy = this.el.getXY();
30030             this.adjustAssets();
30031         }
30032     },
30033
30034     // private
30035     adjustAssets : function(doShow){
30036         var x = this.xy[0], y = this.xy[1];
30037         var w = this.size.width, h = this.size.height;
30038         if(doShow === true){
30039             if(this.shadow){
30040                 this.shadow.show(this.el);
30041             }
30042             if(this.shim){
30043                 this.shim.show();
30044             }
30045         }
30046         if(this.shadow && this.shadow.isVisible()){
30047             this.shadow.show(this.el);
30048         }
30049         if(this.shim && this.shim.isVisible()){
30050             this.shim.setBounds(x, y, w, h);
30051         }
30052     },
30053
30054     // private
30055     adjustViewport : function(w, h){
30056         if(!w || !h){
30057             w = Roo.lib.Dom.getViewWidth();
30058             h = Roo.lib.Dom.getViewHeight();
30059         }
30060         // cache the size
30061         this.viewSize = [w, h];
30062         if(this.modal && this.mask.isVisible()){
30063             this.mask.setSize(w, h); // first make sure the mask isn't causing overflow
30064             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
30065         }
30066         if(this.isVisible()){
30067             this.constrainXY();
30068         }
30069     },
30070
30071     /**
30072      * Destroys this dialog and all its supporting elements (including any tabs, shim,
30073      * shadow, proxy, mask, etc.)  Also removes all event listeners.
30074      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
30075      */
30076     destroy : function(removeEl){
30077         if(this.isVisible()){
30078             this.animateTarget = null;
30079             this.hide();
30080         }
30081         Roo.EventManager.removeResizeListener(this.adjustViewport, this);
30082         if(this.tabs){
30083             this.tabs.destroy(removeEl);
30084         }
30085         Roo.destroy(
30086              this.shim,
30087              this.proxy,
30088              this.resizer,
30089              this.close,
30090              this.mask
30091         );
30092         if(this.dd){
30093             this.dd.unreg();
30094         }
30095         if(this.buttons){
30096            for(var i = 0, len = this.buttons.length; i < len; i++){
30097                this.buttons[i].destroy();
30098            }
30099         }
30100         this.el.removeAllListeners();
30101         if(removeEl === true){
30102             this.el.update("");
30103             this.el.remove();
30104         }
30105         Roo.DialogManager.unregister(this);
30106     },
30107
30108     // private
30109     startMove : function(){
30110         if(this.proxyDrag){
30111             this.proxy.show();
30112         }
30113         if(this.constraintoviewport !== false){
30114             this.dd.constrainTo(document.body, {right: this.shadowOffset, bottom: this.shadowOffset});
30115         }
30116     },
30117
30118     // private
30119     endMove : function(){
30120         if(!this.proxyDrag){
30121             Roo.dd.DD.prototype.endDrag.apply(this.dd, arguments);
30122         }else{
30123             Roo.dd.DDProxy.prototype.endDrag.apply(this.dd, arguments);
30124             this.proxy.hide();
30125         }
30126         this.refreshSize();
30127         this.adjustAssets();
30128         this.focus();
30129         this.fireEvent("move", this, this.xy[0], this.xy[1]);
30130     },
30131
30132     /**
30133      * Brings this dialog to the front of any other visible dialogs
30134      * @return {Roo.BasicDialog} this
30135      */
30136     toFront : function(){
30137         Roo.DialogManager.bringToFront(this);
30138         return this;
30139     },
30140
30141     /**
30142      * Sends this dialog to the back (under) of any other visible dialogs
30143      * @return {Roo.BasicDialog} this
30144      */
30145     toBack : function(){
30146         Roo.DialogManager.sendToBack(this);
30147         return this;
30148     },
30149
30150     /**
30151      * Centers this dialog in the viewport
30152      * @return {Roo.BasicDialog} this
30153      */
30154     center : function(){
30155         var xy = this.el.getCenterXY(true);
30156         this.moveTo(xy[0], xy[1]);
30157         return this;
30158     },
30159
30160     /**
30161      * Moves the dialog's top-left corner to the specified point
30162      * @param {Number} x
30163      * @param {Number} y
30164      * @return {Roo.BasicDialog} this
30165      */
30166     moveTo : function(x, y){
30167         this.xy = [x,y];
30168         if(this.isVisible()){
30169             this.el.setXY(this.xy);
30170             this.adjustAssets();
30171         }
30172         return this;
30173     },
30174
30175     /**
30176      * Aligns the dialog to the specified element
30177      * @param {String/HTMLElement/Roo.Element} element The element to align to.
30178      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details).
30179      * @param {Array} offsets (optional) Offset the positioning by [x, y]
30180      * @return {Roo.BasicDialog} this
30181      */
30182     alignTo : function(element, position, offsets){
30183         this.xy = this.el.getAlignToXY(element, position, offsets);
30184         if(this.isVisible()){
30185             this.el.setXY(this.xy);
30186             this.adjustAssets();
30187         }
30188         return this;
30189     },
30190
30191     /**
30192      * Anchors an element to another element and realigns it when the window is resized.
30193      * @param {String/HTMLElement/Roo.Element} element The element to align to.
30194      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details)
30195      * @param {Array} offsets (optional) Offset the positioning by [x, y]
30196      * @param {Boolean/Number} monitorScroll (optional) true to monitor body scroll and reposition. If this parameter
30197      * is a number, it is used as the buffer delay (defaults to 50ms).
30198      * @return {Roo.BasicDialog} this
30199      */
30200     anchorTo : function(el, alignment, offsets, monitorScroll){
30201         var action = function(){
30202             this.alignTo(el, alignment, offsets);
30203         };
30204         Roo.EventManager.onWindowResize(action, this);
30205         var tm = typeof monitorScroll;
30206         if(tm != 'undefined'){
30207             Roo.EventManager.on(window, 'scroll', action, this,
30208                 {buffer: tm == 'number' ? monitorScroll : 50});
30209         }
30210         action.call(this);
30211         return this;
30212     },
30213
30214     /**
30215      * Returns true if the dialog is visible
30216      * @return {Boolean}
30217      */
30218     isVisible : function(){
30219         return this.el.isVisible();
30220     },
30221
30222     // private
30223     animHide : function(callback){
30224         var b = Roo.get(this.animateTarget).getBox();
30225         this.proxy.show();
30226         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height);
30227         this.el.hide();
30228         this.proxy.setBounds(b.x, b.y, b.width, b.height, true, .35,
30229                     this.hideEl.createDelegate(this, [callback]));
30230     },
30231
30232     /**
30233      * Hides the dialog.
30234      * @param {Function} callback (optional) Function to call when the dialog is hidden
30235      * @return {Roo.BasicDialog} this
30236      */
30237     hide : function(callback){
30238         if (this.fireEvent("beforehide", this) === false){
30239             return;
30240         }
30241         if(this.shadow){
30242             this.shadow.hide();
30243         }
30244         if(this.shim) {
30245           this.shim.hide();
30246         }
30247         // sometimes animateTarget seems to get set.. causing problems...
30248         // this just double checks..
30249         if(this.animateTarget && Roo.get(this.animateTarget)) {
30250            this.animHide(callback);
30251         }else{
30252             this.el.hide();
30253             this.hideEl(callback);
30254         }
30255         return this;
30256     },
30257
30258     // private
30259     hideEl : function(callback){
30260         this.proxy.hide();
30261         if(this.modal){
30262             this.mask.hide();
30263             Roo.get(document.body).removeClass("x-body-masked");
30264         }
30265         this.fireEvent("hide", this);
30266         if(typeof callback == "function"){
30267             callback();
30268         }
30269     },
30270
30271     // private
30272     hideAction : function(){
30273         this.setLeft("-10000px");
30274         this.setTop("-10000px");
30275         this.setStyle("visibility", "hidden");
30276     },
30277
30278     // private
30279     refreshSize : function(){
30280         this.size = this.el.getSize();
30281         this.xy = this.el.getXY();
30282         Roo.state.Manager.set(this.stateId || this.el.id + "-state", this.el.getBox());
30283     },
30284
30285     // private
30286     // z-index is managed by the DialogManager and may be overwritten at any time
30287     setZIndex : function(index){
30288         if(this.modal){
30289             this.mask.setStyle("z-index", index);
30290         }
30291         if(this.shim){
30292             this.shim.setStyle("z-index", ++index);
30293         }
30294         if(this.shadow){
30295             this.shadow.setZIndex(++index);
30296         }
30297         this.el.setStyle("z-index", ++index);
30298         if(this.proxy){
30299             this.proxy.setStyle("z-index", ++index);
30300         }
30301         if(this.resizer){
30302             this.resizer.proxy.setStyle("z-index", ++index);
30303         }
30304
30305         this.lastZIndex = index;
30306     },
30307
30308     /**
30309      * Returns the element for this dialog
30310      * @return {Roo.Element} The underlying dialog Element
30311      */
30312     getEl : function(){
30313         return this.el;
30314     }
30315 });
30316
30317 /**
30318  * @class Roo.DialogManager
30319  * Provides global access to BasicDialogs that have been created and
30320  * support for z-indexing (layering) multiple open dialogs.
30321  */
30322 Roo.DialogManager = function(){
30323     var list = {};
30324     var accessList = [];
30325     var front = null;
30326
30327     // private
30328     var sortDialogs = function(d1, d2){
30329         return (!d1._lastAccess || d1._lastAccess < d2._lastAccess) ? -1 : 1;
30330     };
30331
30332     // private
30333     var orderDialogs = function(){
30334         accessList.sort(sortDialogs);
30335         var seed = Roo.DialogManager.zseed;
30336         for(var i = 0, len = accessList.length; i < len; i++){
30337             var dlg = accessList[i];
30338             if(dlg){
30339                 dlg.setZIndex(seed + (i*10));
30340             }
30341         }
30342     };
30343
30344     return {
30345         /**
30346          * The starting z-index for BasicDialogs (defaults to 9000)
30347          * @type Number The z-index value
30348          */
30349         zseed : 9000,
30350
30351         // private
30352         register : function(dlg){
30353             list[dlg.id] = dlg;
30354             accessList.push(dlg);
30355         },
30356
30357         // private
30358         unregister : function(dlg){
30359             delete list[dlg.id];
30360             var i=0;
30361             var len=0;
30362             if(!accessList.indexOf){
30363                 for(  i = 0, len = accessList.length; i < len; i++){
30364                     if(accessList[i] == dlg){
30365                         accessList.splice(i, 1);
30366                         return;
30367                     }
30368                 }
30369             }else{
30370                  i = accessList.indexOf(dlg);
30371                 if(i != -1){
30372                     accessList.splice(i, 1);
30373                 }
30374             }
30375         },
30376
30377         /**
30378          * Gets a registered dialog by id
30379          * @param {String/Object} id The id of the dialog or a dialog
30380          * @return {Roo.BasicDialog} this
30381          */
30382         get : function(id){
30383             return typeof id == "object" ? id : list[id];
30384         },
30385
30386         /**
30387          * Brings the specified dialog to the front
30388          * @param {String/Object} dlg The id of the dialog or a dialog
30389          * @return {Roo.BasicDialog} this
30390          */
30391         bringToFront : function(dlg){
30392             dlg = this.get(dlg);
30393             if(dlg != front){
30394                 front = dlg;
30395                 dlg._lastAccess = new Date().getTime();
30396                 orderDialogs();
30397             }
30398             return dlg;
30399         },
30400
30401         /**
30402          * Sends the specified dialog to the back
30403          * @param {String/Object} dlg The id of the dialog or a dialog
30404          * @return {Roo.BasicDialog} this
30405          */
30406         sendToBack : function(dlg){
30407             dlg = this.get(dlg);
30408             dlg._lastAccess = -(new Date().getTime());
30409             orderDialogs();
30410             return dlg;
30411         },
30412
30413         /**
30414          * Hides all dialogs
30415          */
30416         hideAll : function(){
30417             for(var id in list){
30418                 if(list[id] && typeof list[id] != "function" && list[id].isVisible()){
30419                     list[id].hide();
30420                 }
30421             }
30422         }
30423     };
30424 }();
30425
30426 /**
30427  * @class Roo.LayoutDialog
30428  * @extends Roo.BasicDialog
30429  * Dialog which provides adjustments for working with a layout in a Dialog.
30430  * Add your necessary layout config options to the dialog's config.<br>
30431  * Example usage (including a nested layout):
30432  * <pre><code>
30433 if(!dialog){
30434     dialog = new Roo.LayoutDialog("download-dlg", {
30435         modal: true,
30436         width:600,
30437         height:450,
30438         shadow:true,
30439         minWidth:500,
30440         minHeight:350,
30441         autoTabs:true,
30442         proxyDrag:true,
30443         // layout config merges with the dialog config
30444         center:{
30445             tabPosition: "top",
30446             alwaysShowTabs: true
30447         }
30448     });
30449     dialog.addKeyListener(27, dialog.hide, dialog);
30450     dialog.setDefaultButton(dialog.addButton("Close", dialog.hide, dialog));
30451     dialog.addButton("Build It!", this.getDownload, this);
30452
30453     // we can even add nested layouts
30454     var innerLayout = new Roo.BorderLayout("dl-inner", {
30455         east: {
30456             initialSize: 200,
30457             autoScroll:true,
30458             split:true
30459         },
30460         center: {
30461             autoScroll:true
30462         }
30463     });
30464     innerLayout.beginUpdate();
30465     innerLayout.add("east", new Roo.ContentPanel("dl-details"));
30466     innerLayout.add("center", new Roo.ContentPanel("selection-panel"));
30467     innerLayout.endUpdate(true);
30468
30469     var layout = dialog.getLayout();
30470     layout.beginUpdate();
30471     layout.add("center", new Roo.ContentPanel("standard-panel",
30472                         {title: "Download the Source", fitToFrame:true}));
30473     layout.add("center", new Roo.NestedLayoutPanel(innerLayout,
30474                {title: "Build your own roo.js"}));
30475     layout.getRegion("center").showPanel(sp);
30476     layout.endUpdate();
30477 }
30478 </code></pre>
30479     * @constructor
30480     * @param {String/HTMLElement/Roo.Element} el The id of or container element, or config
30481     * @param {Object} config configuration options
30482   */
30483 Roo.LayoutDialog = function(el, cfg){
30484     
30485     var config=  cfg;
30486     if (typeof(cfg) == 'undefined') {
30487         config = Roo.apply({}, el);
30488         // not sure why we use documentElement here.. - it should always be body.
30489         // IE7 borks horribly if we use documentElement.
30490         // webkit also does not like documentElement - it creates a body element...
30491         el = Roo.get( document.body || document.documentElement ).createChild();
30492         //config.autoCreate = true;
30493     }
30494     
30495     
30496     config.autoTabs = false;
30497     Roo.LayoutDialog.superclass.constructor.call(this, el, config);
30498     this.body.setStyle({overflow:"hidden", position:"relative"});
30499     this.layout = new Roo.BorderLayout(this.body.dom, config);
30500     this.layout.monitorWindowResize = false;
30501     this.el.addClass("x-dlg-auto-layout");
30502     // fix case when center region overwrites center function
30503     this.center = Roo.BasicDialog.prototype.center;
30504     this.on("show", this.layout.layout, this.layout, true);
30505     if (config.items) {
30506         var xitems = config.items;
30507         delete config.items;
30508         Roo.each(xitems, this.addxtype, this);
30509     }
30510     
30511     
30512 };
30513 Roo.extend(Roo.LayoutDialog, Roo.BasicDialog, {
30514     /**
30515      * Ends update of the layout <strike>and resets display to none</strike>. Use standard beginUpdate/endUpdate on the layout.
30516      * @deprecated
30517      */
30518     endUpdate : function(){
30519         this.layout.endUpdate();
30520     },
30521
30522     /**
30523      * Begins an update of the layout <strike>and sets display to block and visibility to hidden</strike>. Use standard beginUpdate/endUpdate on the layout.
30524      *  @deprecated
30525      */
30526     beginUpdate : function(){
30527         this.layout.beginUpdate();
30528     },
30529
30530     /**
30531      * Get the BorderLayout for this dialog
30532      * @return {Roo.BorderLayout}
30533      */
30534     getLayout : function(){
30535         return this.layout;
30536     },
30537
30538     showEl : function(){
30539         Roo.LayoutDialog.superclass.showEl.apply(this, arguments);
30540         if(Roo.isIE7){
30541             this.layout.layout();
30542         }
30543     },
30544
30545     // private
30546     // Use the syncHeightBeforeShow config option to control this automatically
30547     syncBodyHeight : function(){
30548         Roo.LayoutDialog.superclass.syncBodyHeight.call(this);
30549         if(this.layout){this.layout.layout();}
30550     },
30551     
30552       /**
30553      * Add an xtype element (actually adds to the layout.)
30554      * @return {Object} xdata xtype object data.
30555      */
30556     
30557     addxtype : function(c) {
30558         return this.layout.addxtype(c);
30559     }
30560 });/*
30561  * Based on:
30562  * Ext JS Library 1.1.1
30563  * Copyright(c) 2006-2007, Ext JS, LLC.
30564  *
30565  * Originally Released Under LGPL - original licence link has changed is not relivant.
30566  *
30567  * Fork - LGPL
30568  * <script type="text/javascript">
30569  */
30570  
30571 /**
30572  * @class Roo.MessageBox
30573  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
30574  * Example usage:
30575  *<pre><code>
30576 // Basic alert:
30577 Roo.Msg.alert('Status', 'Changes saved successfully.');
30578
30579 // Prompt for user data:
30580 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
30581     if (btn == 'ok'){
30582         // process text value...
30583     }
30584 });
30585
30586 // Show a dialog using config options:
30587 Roo.Msg.show({
30588    title:'Save Changes?',
30589    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
30590    buttons: Roo.Msg.YESNOCANCEL,
30591    fn: processResult,
30592    animEl: 'elId'
30593 });
30594 </code></pre>
30595  * @singleton
30596  */
30597 Roo.MessageBox = function(){
30598     var dlg, opt, mask, waitTimer;
30599     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
30600     var buttons, activeTextEl, bwidth;
30601
30602     // private
30603     var handleButton = function(button){
30604         dlg.hide();
30605         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
30606     };
30607
30608     // private
30609     var handleHide = function(){
30610         if(opt && opt.cls){
30611             dlg.el.removeClass(opt.cls);
30612         }
30613         if(waitTimer){
30614             Roo.TaskMgr.stop(waitTimer);
30615             waitTimer = null;
30616         }
30617     };
30618
30619     // private
30620     var updateButtons = function(b){
30621         var width = 0;
30622         if(!b){
30623             buttons["ok"].hide();
30624             buttons["cancel"].hide();
30625             buttons["yes"].hide();
30626             buttons["no"].hide();
30627             dlg.footer.dom.style.display = 'none';
30628             return width;
30629         }
30630         dlg.footer.dom.style.display = '';
30631         for(var k in buttons){
30632             if(typeof buttons[k] != "function"){
30633                 if(b[k]){
30634                     buttons[k].show();
30635                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.MessageBox.buttonText[k]);
30636                     width += buttons[k].el.getWidth()+15;
30637                 }else{
30638                     buttons[k].hide();
30639                 }
30640             }
30641         }
30642         return width;
30643     };
30644
30645     // private
30646     var handleEsc = function(d, k, e){
30647         if(opt && opt.closable !== false){
30648             dlg.hide();
30649         }
30650         if(e){
30651             e.stopEvent();
30652         }
30653     };
30654
30655     return {
30656         /**
30657          * Returns a reference to the underlying {@link Roo.BasicDialog} element
30658          * @return {Roo.BasicDialog} The BasicDialog element
30659          */
30660         getDialog : function(){
30661            if(!dlg){
30662                 dlg = new Roo.BasicDialog("x-msg-box", {
30663                     autoCreate : true,
30664                     shadow: true,
30665                     draggable: true,
30666                     resizable:false,
30667                     constraintoviewport:false,
30668                     fixedcenter:true,
30669                     collapsible : false,
30670                     shim:true,
30671                     modal: true,
30672                     width:400, height:100,
30673                     buttonAlign:"center",
30674                     closeClick : function(){
30675                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
30676                             handleButton("no");
30677                         }else{
30678                             handleButton("cancel");
30679                         }
30680                     }
30681                 });
30682                 dlg.on("hide", handleHide);
30683                 mask = dlg.mask;
30684                 dlg.addKeyListener(27, handleEsc);
30685                 buttons = {};
30686                 var bt = this.buttonText;
30687                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
30688                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
30689                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
30690                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
30691                 bodyEl = dlg.body.createChild({
30692
30693                     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>'
30694                 });
30695                 msgEl = bodyEl.dom.firstChild;
30696                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
30697                 textboxEl.enableDisplayMode();
30698                 textboxEl.addKeyListener([10,13], function(){
30699                     if(dlg.isVisible() && opt && opt.buttons){
30700                         if(opt.buttons.ok){
30701                             handleButton("ok");
30702                         }else if(opt.buttons.yes){
30703                             handleButton("yes");
30704                         }
30705                     }
30706                 });
30707                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
30708                 textareaEl.enableDisplayMode();
30709                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
30710                 progressEl.enableDisplayMode();
30711                 var pf = progressEl.dom.firstChild;
30712                 if (pf) {
30713                     pp = Roo.get(pf.firstChild);
30714                     pp.setHeight(pf.offsetHeight);
30715                 }
30716                 
30717             }
30718             return dlg;
30719         },
30720
30721         /**
30722          * Updates the message box body text
30723          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
30724          * the XHTML-compliant non-breaking space character '&amp;#160;')
30725          * @return {Roo.MessageBox} This message box
30726          */
30727         updateText : function(text){
30728             if(!dlg.isVisible() && !opt.width){
30729                 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
30730             }
30731             msgEl.innerHTML = text || '&#160;';
30732       
30733             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
30734             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
30735             var w = Math.max(
30736                     Math.min(opt.width || cw , this.maxWidth), 
30737                     Math.max(opt.minWidth || this.minWidth, bwidth)
30738             );
30739             if(opt.prompt){
30740                 activeTextEl.setWidth(w);
30741             }
30742             if(dlg.isVisible()){
30743                 dlg.fixedcenter = false;
30744             }
30745             // to big, make it scroll. = But as usual stupid IE does not support
30746             // !important..
30747             
30748             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
30749                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
30750                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
30751             } else {
30752                 bodyEl.dom.style.height = '';
30753                 bodyEl.dom.style.overflowY = '';
30754             }
30755             if (cw > w) {
30756                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
30757             } else {
30758                 bodyEl.dom.style.overflowX = '';
30759             }
30760             
30761             dlg.setContentSize(w, bodyEl.getHeight());
30762             if(dlg.isVisible()){
30763                 dlg.fixedcenter = true;
30764             }
30765             return this;
30766         },
30767
30768         /**
30769          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
30770          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
30771          * @param {Number} value Any number between 0 and 1 (e.g., .5)
30772          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
30773          * @return {Roo.MessageBox} This message box
30774          */
30775         updateProgress : function(value, text){
30776             if(text){
30777                 this.updateText(text);
30778             }
30779             if (pp) { // weird bug on my firefox - for some reason this is not defined
30780                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
30781             }
30782             return this;
30783         },        
30784
30785         /**
30786          * Returns true if the message box is currently displayed
30787          * @return {Boolean} True if the message box is visible, else false
30788          */
30789         isVisible : function(){
30790             return dlg && dlg.isVisible();  
30791         },
30792
30793         /**
30794          * Hides the message box if it is displayed
30795          */
30796         hide : function(){
30797             if(this.isVisible()){
30798                 dlg.hide();
30799             }  
30800         },
30801
30802         /**
30803          * Displays a new message box, or reinitializes an existing message box, based on the config options
30804          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
30805          * The following config object properties are supported:
30806          * <pre>
30807 Property    Type             Description
30808 ----------  ---------------  ------------------------------------------------------------------------------------
30809 animEl            String/Element   An id or Element from which the message box should animate as it opens and
30810                                    closes (defaults to undefined)
30811 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
30812                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
30813 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
30814                                    progress and wait dialogs will ignore this property and always hide the
30815                                    close button as they can only be closed programmatically.
30816 cls               String           A custom CSS class to apply to the message box element
30817 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
30818                                    displayed (defaults to 75)
30819 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
30820                                    function will be btn (the name of the button that was clicked, if applicable,
30821                                    e.g. "ok"), and text (the value of the active text field, if applicable).
30822                                    Progress and wait dialogs will ignore this option since they do not respond to
30823                                    user actions and can only be closed programmatically, so any required function
30824                                    should be called by the same code after it closes the dialog.
30825 icon              String           A CSS class that provides a background image to be used as an icon for
30826                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
30827 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
30828 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
30829 modal             Boolean          False to allow user interaction with the page while the message box is
30830                                    displayed (defaults to true)
30831 msg               String           A string that will replace the existing message box body text (defaults
30832                                    to the XHTML-compliant non-breaking space character '&#160;')
30833 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
30834 progress          Boolean          True to display a progress bar (defaults to false)
30835 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
30836 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
30837 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
30838 title             String           The title text
30839 value             String           The string value to set into the active textbox element if displayed
30840 wait              Boolean          True to display a progress bar (defaults to false)
30841 width             Number           The width of the dialog in pixels
30842 </pre>
30843          *
30844          * Example usage:
30845          * <pre><code>
30846 Roo.Msg.show({
30847    title: 'Address',
30848    msg: 'Please enter your address:',
30849    width: 300,
30850    buttons: Roo.MessageBox.OKCANCEL,
30851    multiline: true,
30852    fn: saveAddress,
30853    animEl: 'addAddressBtn'
30854 });
30855 </code></pre>
30856          * @param {Object} config Configuration options
30857          * @return {Roo.MessageBox} This message box
30858          */
30859         show : function(options)
30860         {
30861             
30862             // this causes nightmares if you show one dialog after another
30863             // especially on callbacks..
30864              
30865             if(this.isVisible()){
30866                 
30867                 this.hide();
30868                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
30869                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
30870                 Roo.log("New Dialog Message:" +  options.msg )
30871                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
30872                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
30873                 
30874             }
30875             var d = this.getDialog();
30876             opt = options;
30877             d.setTitle(opt.title || "&#160;");
30878             d.close.setDisplayed(opt.closable !== false);
30879             activeTextEl = textboxEl;
30880             opt.prompt = opt.prompt || (opt.multiline ? true : false);
30881             if(opt.prompt){
30882                 if(opt.multiline){
30883                     textboxEl.hide();
30884                     textareaEl.show();
30885                     textareaEl.setHeight(typeof opt.multiline == "number" ?
30886                         opt.multiline : this.defaultTextHeight);
30887                     activeTextEl = textareaEl;
30888                 }else{
30889                     textboxEl.show();
30890                     textareaEl.hide();
30891                 }
30892             }else{
30893                 textboxEl.hide();
30894                 textareaEl.hide();
30895             }
30896             progressEl.setDisplayed(opt.progress === true);
30897             this.updateProgress(0);
30898             activeTextEl.dom.value = opt.value || "";
30899             if(opt.prompt){
30900                 dlg.setDefaultButton(activeTextEl);
30901             }else{
30902                 var bs = opt.buttons;
30903                 var db = null;
30904                 if(bs && bs.ok){
30905                     db = buttons["ok"];
30906                 }else if(bs && bs.yes){
30907                     db = buttons["yes"];
30908                 }
30909                 dlg.setDefaultButton(db);
30910             }
30911             bwidth = updateButtons(opt.buttons);
30912             this.updateText(opt.msg);
30913             if(opt.cls){
30914                 d.el.addClass(opt.cls);
30915             }
30916             d.proxyDrag = opt.proxyDrag === true;
30917             d.modal = opt.modal !== false;
30918             d.mask = opt.modal !== false ? mask : false;
30919             if(!d.isVisible()){
30920                 // force it to the end of the z-index stack so it gets a cursor in FF
30921                 document.body.appendChild(dlg.el.dom);
30922                 d.animateTarget = null;
30923                 d.show(options.animEl);
30924             }
30925             return this;
30926         },
30927
30928         /**
30929          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
30930          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
30931          * and closing the message box when the process is complete.
30932          * @param {String} title The title bar text
30933          * @param {String} msg The message box body text
30934          * @return {Roo.MessageBox} This message box
30935          */
30936         progress : function(title, msg){
30937             this.show({
30938                 title : title,
30939                 msg : msg,
30940                 buttons: false,
30941                 progress:true,
30942                 closable:false,
30943                 minWidth: this.minProgressWidth,
30944                 modal : true
30945             });
30946             return this;
30947         },
30948
30949         /**
30950          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
30951          * If a callback function is passed it will be called after the user clicks the button, and the
30952          * id of the button that was clicked will be passed as the only parameter to the callback
30953          * (could also be the top-right close button).
30954          * @param {String} title The title bar text
30955          * @param {String} msg The message box body text
30956          * @param {Function} fn (optional) The callback function invoked after the message box is closed
30957          * @param {Object} scope (optional) The scope of the callback function
30958          * @return {Roo.MessageBox} This message box
30959          */
30960         alert : function(title, msg, fn, scope){
30961             this.show({
30962                 title : title,
30963                 msg : msg,
30964                 buttons: this.OK,
30965                 fn: fn,
30966                 scope : scope,
30967                 modal : true
30968             });
30969             return this;
30970         },
30971
30972         /**
30973          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
30974          * interaction while waiting for a long-running process to complete that does not have defined intervals.
30975          * You are responsible for closing the message box when the process is complete.
30976          * @param {String} msg The message box body text
30977          * @param {String} title (optional) The title bar text
30978          * @return {Roo.MessageBox} This message box
30979          */
30980         wait : function(msg, title){
30981             this.show({
30982                 title : title,
30983                 msg : msg,
30984                 buttons: false,
30985                 closable:false,
30986                 progress:true,
30987                 modal:true,
30988                 width:300,
30989                 wait:true
30990             });
30991             waitTimer = Roo.TaskMgr.start({
30992                 run: function(i){
30993                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
30994                 },
30995                 interval: 1000
30996             });
30997             return this;
30998         },
30999
31000         /**
31001          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
31002          * If a callback function is passed it will be called after the user clicks either button, and the id of the
31003          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
31004          * @param {String} title The title bar text
31005          * @param {String} msg The message box body text
31006          * @param {Function} fn (optional) The callback function invoked after the message box is closed
31007          * @param {Object} scope (optional) The scope of the callback function
31008          * @return {Roo.MessageBox} This message box
31009          */
31010         confirm : function(title, msg, fn, scope){
31011             this.show({
31012                 title : title,
31013                 msg : msg,
31014                 buttons: this.YESNO,
31015                 fn: fn,
31016                 scope : scope,
31017                 modal : true
31018             });
31019             return this;
31020         },
31021
31022         /**
31023          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
31024          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
31025          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
31026          * (could also be the top-right close button) and the text that was entered will be passed as the two
31027          * parameters to the callback.
31028          * @param {String} title The title bar text
31029          * @param {String} msg The message box body text
31030          * @param {Function} fn (optional) The callback function invoked after the message box is closed
31031          * @param {Object} scope (optional) The scope of the callback function
31032          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
31033          * property, or the height in pixels to create the textbox (defaults to false / single-line)
31034          * @return {Roo.MessageBox} This message box
31035          */
31036         prompt : function(title, msg, fn, scope, multiline){
31037             this.show({
31038                 title : title,
31039                 msg : msg,
31040                 buttons: this.OKCANCEL,
31041                 fn: fn,
31042                 minWidth:250,
31043                 scope : scope,
31044                 prompt:true,
31045                 multiline: multiline,
31046                 modal : true
31047             });
31048             return this;
31049         },
31050
31051         /**
31052          * Button config that displays a single OK button
31053          * @type Object
31054          */
31055         OK : {ok:true},
31056         /**
31057          * Button config that displays Yes and No buttons
31058          * @type Object
31059          */
31060         YESNO : {yes:true, no:true},
31061         /**
31062          * Button config that displays OK and Cancel buttons
31063          * @type Object
31064          */
31065         OKCANCEL : {ok:true, cancel:true},
31066         /**
31067          * Button config that displays Yes, No and Cancel buttons
31068          * @type Object
31069          */
31070         YESNOCANCEL : {yes:true, no:true, cancel:true},
31071
31072         /**
31073          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
31074          * @type Number
31075          */
31076         defaultTextHeight : 75,
31077         /**
31078          * The maximum width in pixels of the message box (defaults to 600)
31079          * @type Number
31080          */
31081         maxWidth : 600,
31082         /**
31083          * The minimum width in pixels of the message box (defaults to 100)
31084          * @type Number
31085          */
31086         minWidth : 100,
31087         /**
31088          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
31089          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
31090          * @type Number
31091          */
31092         minProgressWidth : 250,
31093         /**
31094          * An object containing the default button text strings that can be overriden for localized language support.
31095          * Supported properties are: ok, cancel, yes and no.
31096          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
31097          * @type Object
31098          */
31099         buttonText : {
31100             ok : "OK",
31101             cancel : "Cancel",
31102             yes : "Yes",
31103             no : "No"
31104         }
31105     };
31106 }();
31107
31108 /**
31109  * Shorthand for {@link Roo.MessageBox}
31110  */
31111 Roo.Msg = Roo.MessageBox;/*
31112  * Based on:
31113  * Ext JS Library 1.1.1
31114  * Copyright(c) 2006-2007, Ext JS, LLC.
31115  *
31116  * Originally Released Under LGPL - original licence link has changed is not relivant.
31117  *
31118  * Fork - LGPL
31119  * <script type="text/javascript">
31120  */
31121 /**
31122  * @class Roo.QuickTips
31123  * Provides attractive and customizable tooltips for any element.
31124  * @singleton
31125  */
31126 Roo.QuickTips = function(){
31127     var el, tipBody, tipBodyText, tipTitle, tm, cfg, close, tagEls = {}, esc, removeCls = null, bdLeft, bdRight;
31128     var ce, bd, xy, dd;
31129     var visible = false, disabled = true, inited = false;
31130     var showProc = 1, hideProc = 1, dismissProc = 1, locks = [];
31131     
31132     var onOver = function(e){
31133         if(disabled){
31134             return;
31135         }
31136         var t = e.getTarget();
31137         if(!t || t.nodeType !== 1 || t == document || t == document.body){
31138             return;
31139         }
31140         if(ce && t == ce.el){
31141             clearTimeout(hideProc);
31142             return;
31143         }
31144         if(t && tagEls[t.id]){
31145             tagEls[t.id].el = t;
31146             showProc = show.defer(tm.showDelay, tm, [tagEls[t.id]]);
31147             return;
31148         }
31149         var ttp, et = Roo.fly(t);
31150         var ns = cfg.namespace;
31151         if(tm.interceptTitles && t.title){
31152             ttp = t.title;
31153             t.qtip = ttp;
31154             t.removeAttribute("title");
31155             e.preventDefault();
31156         }else{
31157             ttp = t.qtip || et.getAttributeNS(ns, cfg.attribute);
31158         }
31159         if(ttp){
31160             showProc = show.defer(tm.showDelay, tm, [{
31161                 el: t, 
31162                 text: ttp, 
31163                 width: et.getAttributeNS(ns, cfg.width),
31164                 autoHide: et.getAttributeNS(ns, cfg.hide) != "user",
31165                 title: et.getAttributeNS(ns, cfg.title),
31166                     cls: et.getAttributeNS(ns, cfg.cls)
31167             }]);
31168         }
31169     };
31170     
31171     var onOut = function(e){
31172         clearTimeout(showProc);
31173         var t = e.getTarget();
31174         if(t && ce && ce.el == t && (tm.autoHide && ce.autoHide !== false)){
31175             hideProc = setTimeout(hide, tm.hideDelay);
31176         }
31177     };
31178     
31179     var onMove = function(e){
31180         if(disabled){
31181             return;
31182         }
31183         xy = e.getXY();
31184         xy[1] += 18;
31185         if(tm.trackMouse && ce){
31186             el.setXY(xy);
31187         }
31188     };
31189     
31190     var onDown = function(e){
31191         clearTimeout(showProc);
31192         clearTimeout(hideProc);
31193         if(!e.within(el)){
31194             if(tm.hideOnClick){
31195                 hide();
31196                 tm.disable();
31197                 tm.enable.defer(100, tm);
31198             }
31199         }
31200     };
31201     
31202     var getPad = function(){
31203         return 2;//bdLeft.getPadding('l')+bdRight.getPadding('r');
31204     };
31205
31206     var show = function(o){
31207         if(disabled){
31208             return;
31209         }
31210         clearTimeout(dismissProc);
31211         ce = o;
31212         if(removeCls){ // in case manually hidden
31213             el.removeClass(removeCls);
31214             removeCls = null;
31215         }
31216         if(ce.cls){
31217             el.addClass(ce.cls);
31218             removeCls = ce.cls;
31219         }
31220         if(ce.title){
31221             tipTitle.update(ce.title);
31222             tipTitle.show();
31223         }else{
31224             tipTitle.update('');
31225             tipTitle.hide();
31226         }
31227         el.dom.style.width  = tm.maxWidth+'px';
31228         //tipBody.dom.style.width = '';
31229         tipBodyText.update(o.text);
31230         var p = getPad(), w = ce.width;
31231         if(!w){
31232             var td = tipBodyText.dom;
31233             var aw = Math.max(td.offsetWidth, td.clientWidth, td.scrollWidth);
31234             if(aw > tm.maxWidth){
31235                 w = tm.maxWidth;
31236             }else if(aw < tm.minWidth){
31237                 w = tm.minWidth;
31238             }else{
31239                 w = aw;
31240             }
31241         }
31242         //tipBody.setWidth(w);
31243         el.setWidth(parseInt(w, 10) + p);
31244         if(ce.autoHide === false){
31245             close.setDisplayed(true);
31246             if(dd){
31247                 dd.unlock();
31248             }
31249         }else{
31250             close.setDisplayed(false);
31251             if(dd){
31252                 dd.lock();
31253             }
31254         }
31255         if(xy){
31256             el.avoidY = xy[1]-18;
31257             el.setXY(xy);
31258         }
31259         if(tm.animate){
31260             el.setOpacity(.1);
31261             el.setStyle("visibility", "visible");
31262             el.fadeIn({callback: afterShow});
31263         }else{
31264             afterShow();
31265         }
31266     };
31267     
31268     var afterShow = function(){
31269         if(ce){
31270             el.show();
31271             esc.enable();
31272             if(tm.autoDismiss && ce.autoHide !== false){
31273                 dismissProc = setTimeout(hide, tm.autoDismissDelay);
31274             }
31275         }
31276     };
31277     
31278     var hide = function(noanim){
31279         clearTimeout(dismissProc);
31280         clearTimeout(hideProc);
31281         ce = null;
31282         if(el.isVisible()){
31283             esc.disable();
31284             if(noanim !== true && tm.animate){
31285                 el.fadeOut({callback: afterHide});
31286             }else{
31287                 afterHide();
31288             } 
31289         }
31290     };
31291     
31292     var afterHide = function(){
31293         el.hide();
31294         if(removeCls){
31295             el.removeClass(removeCls);
31296             removeCls = null;
31297         }
31298     };
31299     
31300     return {
31301         /**
31302         * @cfg {Number} minWidth
31303         * The minimum width of the quick tip (defaults to 40)
31304         */
31305        minWidth : 40,
31306         /**
31307         * @cfg {Number} maxWidth
31308         * The maximum width of the quick tip (defaults to 300)
31309         */
31310        maxWidth : 300,
31311         /**
31312         * @cfg {Boolean} interceptTitles
31313         * True to automatically use the element's DOM title value if available (defaults to false)
31314         */
31315        interceptTitles : false,
31316         /**
31317         * @cfg {Boolean} trackMouse
31318         * True to have the quick tip follow the mouse as it moves over the target element (defaults to false)
31319         */
31320        trackMouse : false,
31321         /**
31322         * @cfg {Boolean} hideOnClick
31323         * True to hide the quick tip if the user clicks anywhere in the document (defaults to true)
31324         */
31325        hideOnClick : true,
31326         /**
31327         * @cfg {Number} showDelay
31328         * Delay in milliseconds before the quick tip displays after the mouse enters the target element (defaults to 500)
31329         */
31330        showDelay : 500,
31331         /**
31332         * @cfg {Number} hideDelay
31333         * Delay in milliseconds before the quick tip hides when autoHide = true (defaults to 200)
31334         */
31335        hideDelay : 200,
31336         /**
31337         * @cfg {Boolean} autoHide
31338         * True to automatically hide the quick tip after the mouse exits the target element (defaults to true).
31339         * Used in conjunction with hideDelay.
31340         */
31341        autoHide : true,
31342         /**
31343         * @cfg {Boolean}
31344         * True to automatically hide the quick tip after a set period of time, regardless of the user's actions
31345         * (defaults to true).  Used in conjunction with autoDismissDelay.
31346         */
31347        autoDismiss : true,
31348         /**
31349         * @cfg {Number}
31350         * Delay in milliseconds before the quick tip hides when autoDismiss = true (defaults to 5000)
31351         */
31352        autoDismissDelay : 5000,
31353        /**
31354         * @cfg {Boolean} animate
31355         * True to turn on fade animation. Defaults to false (ClearType/scrollbar flicker issues in IE7).
31356         */
31357        animate : false,
31358
31359        /**
31360         * @cfg {String} title
31361         * Title text to display (defaults to '').  This can be any valid HTML markup.
31362         */
31363         title: '',
31364        /**
31365         * @cfg {String} text
31366         * Body text to display (defaults to '').  This can be any valid HTML markup.
31367         */
31368         text : '',
31369        /**
31370         * @cfg {String} cls
31371         * A CSS class to apply to the base quick tip element (defaults to '').
31372         */
31373         cls : '',
31374        /**
31375         * @cfg {Number} width
31376         * Width in pixels of the quick tip (defaults to auto).  Width will be ignored if it exceeds the bounds of
31377         * minWidth or maxWidth.
31378         */
31379         width : null,
31380
31381     /**
31382      * Initialize and enable QuickTips for first use.  This should be called once before the first attempt to access
31383      * or display QuickTips in a page.
31384      */
31385        init : function(){
31386           tm = Roo.QuickTips;
31387           cfg = tm.tagConfig;
31388           if(!inited){
31389               if(!Roo.isReady){ // allow calling of init() before onReady
31390                   Roo.onReady(Roo.QuickTips.init, Roo.QuickTips);
31391                   return;
31392               }
31393               el = new Roo.Layer({cls:"x-tip", shadow:"drop", shim: true, constrain:true, shadowOffset:4});
31394               el.fxDefaults = {stopFx: true};
31395               // maximum custom styling
31396               //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>');
31397               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>');              
31398               tipTitle = el.child('h3');
31399               tipTitle.enableDisplayMode("block");
31400               tipBody = el.child('div.x-tip-bd');
31401               tipBodyText = el.child('div.x-tip-bd-inner');
31402               //bdLeft = el.child('div.x-tip-bd-left');
31403               //bdRight = el.child('div.x-tip-bd-right');
31404               close = el.child('div.x-tip-close');
31405               close.enableDisplayMode("block");
31406               close.on("click", hide);
31407               var d = Roo.get(document);
31408               d.on("mousedown", onDown);
31409               d.on("mouseover", onOver);
31410               d.on("mouseout", onOut);
31411               d.on("mousemove", onMove);
31412               esc = d.addKeyListener(27, hide);
31413               esc.disable();
31414               if(Roo.dd.DD){
31415                   dd = el.initDD("default", null, {
31416                       onDrag : function(){
31417                           el.sync();  
31418                       }
31419                   });
31420                   dd.setHandleElId(tipTitle.id);
31421                   dd.lock();
31422               }
31423               inited = true;
31424           }
31425           this.enable(); 
31426        },
31427
31428     /**
31429      * Configures a new quick tip instance and assigns it to a target element.  The following config options
31430      * are supported:
31431      * <pre>
31432 Property    Type                   Description
31433 ----------  ---------------------  ------------------------------------------------------------------------
31434 target      Element/String/Array   An Element, id or array of ids that this quick tip should be tied to
31435      * </ul>
31436      * @param {Object} config The config object
31437      */
31438        register : function(config){
31439            var cs = config instanceof Array ? config : arguments;
31440            for(var i = 0, len = cs.length; i < len; i++) {
31441                var c = cs[i];
31442                var target = c.target;
31443                if(target){
31444                    if(target instanceof Array){
31445                        for(var j = 0, jlen = target.length; j < jlen; j++){
31446                            tagEls[target[j]] = c;
31447                        }
31448                    }else{
31449                        tagEls[typeof target == 'string' ? target : Roo.id(target)] = c;
31450                    }
31451                }
31452            }
31453        },
31454
31455     /**
31456      * Removes this quick tip from its element and destroys it.
31457      * @param {String/HTMLElement/Element} el The element from which the quick tip is to be removed.
31458      */
31459        unregister : function(el){
31460            delete tagEls[Roo.id(el)];
31461        },
31462
31463     /**
31464      * Enable this quick tip.
31465      */
31466        enable : function(){
31467            if(inited && disabled){
31468                locks.pop();
31469                if(locks.length < 1){
31470                    disabled = false;
31471                }
31472            }
31473        },
31474
31475     /**
31476      * Disable this quick tip.
31477      */
31478        disable : function(){
31479           disabled = true;
31480           clearTimeout(showProc);
31481           clearTimeout(hideProc);
31482           clearTimeout(dismissProc);
31483           if(ce){
31484               hide(true);
31485           }
31486           locks.push(1);
31487        },
31488
31489     /**
31490      * Returns true if the quick tip is enabled, else false.
31491      */
31492        isEnabled : function(){
31493             return !disabled;
31494        },
31495
31496         // private
31497        tagConfig : {
31498            namespace : "ext",
31499            attribute : "qtip",
31500            width : "width",
31501            target : "target",
31502            title : "qtitle",
31503            hide : "hide",
31504            cls : "qclass"
31505        }
31506    };
31507 }();
31508
31509 // backwards compat
31510 Roo.QuickTips.tips = Roo.QuickTips.register;/*
31511  * Based on:
31512  * Ext JS Library 1.1.1
31513  * Copyright(c) 2006-2007, Ext JS, LLC.
31514  *
31515  * Originally Released Under LGPL - original licence link has changed is not relivant.
31516  *
31517  * Fork - LGPL
31518  * <script type="text/javascript">
31519  */
31520  
31521
31522 /**
31523  * @class Roo.tree.TreePanel
31524  * @extends Roo.data.Tree
31525
31526  * @cfg {Boolean} rootVisible false to hide the root node (defaults to true)
31527  * @cfg {Boolean} lines false to disable tree lines (defaults to true)
31528  * @cfg {Boolean} enableDD true to enable drag and drop
31529  * @cfg {Boolean} enableDrag true to enable just drag
31530  * @cfg {Boolean} enableDrop true to enable just drop
31531  * @cfg {Object} dragConfig Custom config to pass to the {@link Roo.tree.TreeDragZone} instance
31532  * @cfg {Object} dropConfig Custom config to pass to the {@link Roo.tree.TreeDropZone} instance
31533  * @cfg {String} ddGroup The DD group this TreePanel belongs to
31534  * @cfg {String} ddAppendOnly True if the tree should only allow append drops (use for trees which are sorted)
31535  * @cfg {Boolean} ddScroll true to enable YUI body scrolling
31536  * @cfg {Boolean} containerScroll true to register this container with ScrollManager
31537  * @cfg {Boolean} hlDrop false to disable node highlight on drop (defaults to the value of Roo.enableFx)
31538  * @cfg {String} hlColor The color of the node highlight (defaults to C3DAF9)
31539  * @cfg {Boolean} animate true to enable animated expand/collapse (defaults to the value of Roo.enableFx)
31540  * @cfg {Boolean} singleExpand true if only 1 node per branch may be expanded
31541  * @cfg {Boolean} selModel A tree selection model to use with this TreePanel (defaults to a {@link Roo.tree.DefaultSelectionModel})
31542  * @cfg {Boolean} loader A TreeLoader for use with this TreePanel
31543  * @cfg {Object|Roo.tree.TreeEditor} editor The TreeEditor or xtype data to display when clicked.
31544  * @cfg {String} pathSeparator The token used to separate sub-paths in path strings (defaults to '/')
31545  * @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>
31546  * @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>
31547  * 
31548  * @constructor
31549  * @param {String/HTMLElement/Element} el The container element
31550  * @param {Object} config
31551  */
31552 Roo.tree.TreePanel = function(el, config){
31553     var root = false;
31554     var loader = false;
31555     if (config.root) {
31556         root = config.root;
31557         delete config.root;
31558     }
31559     if (config.loader) {
31560         loader = config.loader;
31561         delete config.loader;
31562     }
31563     
31564     Roo.apply(this, config);
31565     Roo.tree.TreePanel.superclass.constructor.call(this);
31566     this.el = Roo.get(el);
31567     this.el.addClass('x-tree');
31568     //console.log(root);
31569     if (root) {
31570         this.setRootNode( Roo.factory(root, Roo.tree));
31571     }
31572     if (loader) {
31573         this.loader = Roo.factory(loader, Roo.tree);
31574     }
31575    /**
31576     * Read-only. The id of the container element becomes this TreePanel's id.
31577     */
31578     this.id = this.el.id;
31579     this.addEvents({
31580         /**
31581         * @event beforeload
31582         * Fires before a node is loaded, return false to cancel
31583         * @param {Node} node The node being loaded
31584         */
31585         "beforeload" : true,
31586         /**
31587         * @event load
31588         * Fires when a node is loaded
31589         * @param {Node} node The node that was loaded
31590         */
31591         "load" : true,
31592         /**
31593         * @event textchange
31594         * Fires when the text for a node is changed
31595         * @param {Node} node The node
31596         * @param {String} text The new text
31597         * @param {String} oldText The old text
31598         */
31599         "textchange" : true,
31600         /**
31601         * @event beforeexpand
31602         * Fires before a node is expanded, return false to cancel.
31603         * @param {Node} node The node
31604         * @param {Boolean} deep
31605         * @param {Boolean} anim
31606         */
31607         "beforeexpand" : true,
31608         /**
31609         * @event beforecollapse
31610         * Fires before a node is collapsed, return false to cancel.
31611         * @param {Node} node The node
31612         * @param {Boolean} deep
31613         * @param {Boolean} anim
31614         */
31615         "beforecollapse" : true,
31616         /**
31617         * @event expand
31618         * Fires when a node is expanded
31619         * @param {Node} node The node
31620         */
31621         "expand" : true,
31622         /**
31623         * @event disabledchange
31624         * Fires when the disabled status of a node changes
31625         * @param {Node} node The node
31626         * @param {Boolean} disabled
31627         */
31628         "disabledchange" : true,
31629         /**
31630         * @event collapse
31631         * Fires when a node is collapsed
31632         * @param {Node} node The node
31633         */
31634         "collapse" : true,
31635         /**
31636         * @event beforeclick
31637         * Fires before click processing on a node. Return false to cancel the default action.
31638         * @param {Node} node The node
31639         * @param {Roo.EventObject} e The event object
31640         */
31641         "beforeclick":true,
31642         /**
31643         * @event checkchange
31644         * Fires when a node with a checkbox's checked property changes
31645         * @param {Node} this This node
31646         * @param {Boolean} checked
31647         */
31648         "checkchange":true,
31649         /**
31650         * @event click
31651         * Fires when a node is clicked
31652         * @param {Node} node The node
31653         * @param {Roo.EventObject} e The event object
31654         */
31655         "click":true,
31656         /**
31657         * @event dblclick
31658         * Fires when a node is double clicked
31659         * @param {Node} node The node
31660         * @param {Roo.EventObject} e The event object
31661         */
31662         "dblclick":true,
31663         /**
31664         * @event contextmenu
31665         * Fires when a node is right clicked
31666         * @param {Node} node The node
31667         * @param {Roo.EventObject} e The event object
31668         */
31669         "contextmenu":true,
31670         /**
31671         * @event beforechildrenrendered
31672         * Fires right before the child nodes for a node are rendered
31673         * @param {Node} node The node
31674         */
31675         "beforechildrenrendered":true,
31676         /**
31677         * @event startdrag
31678         * Fires when a node starts being dragged
31679         * @param {Roo.tree.TreePanel} this
31680         * @param {Roo.tree.TreeNode} node
31681         * @param {event} e The raw browser event
31682         */ 
31683        "startdrag" : true,
31684        /**
31685         * @event enddrag
31686         * Fires when a drag operation is complete
31687         * @param {Roo.tree.TreePanel} this
31688         * @param {Roo.tree.TreeNode} node
31689         * @param {event} e The raw browser event
31690         */
31691        "enddrag" : true,
31692        /**
31693         * @event dragdrop
31694         * Fires when a dragged node is dropped on a valid DD target
31695         * @param {Roo.tree.TreePanel} this
31696         * @param {Roo.tree.TreeNode} node
31697         * @param {DD} dd The dd it was dropped on
31698         * @param {event} e The raw browser event
31699         */
31700        "dragdrop" : true,
31701        /**
31702         * @event beforenodedrop
31703         * Fires when a DD object is dropped on a node in this tree for preprocessing. Return false to cancel the drop. The dropEvent
31704         * passed to handlers has the following properties:<br />
31705         * <ul style="padding:5px;padding-left:16px;">
31706         * <li>tree - The TreePanel</li>
31707         * <li>target - The node being targeted for the drop</li>
31708         * <li>data - The drag data from the drag source</li>
31709         * <li>point - The point of the drop - append, above or below</li>
31710         * <li>source - The drag source</li>
31711         * <li>rawEvent - Raw mouse event</li>
31712         * <li>dropNode - Drop node(s) provided by the source <b>OR</b> you can supply node(s)
31713         * to be inserted by setting them on this object.</li>
31714         * <li>cancel - Set this to true to cancel the drop.</li>
31715         * </ul>
31716         * @param {Object} dropEvent
31717         */
31718        "beforenodedrop" : true,
31719        /**
31720         * @event nodedrop
31721         * Fires after a DD object is dropped on a node in this tree. The dropEvent
31722         * passed to handlers has the following properties:<br />
31723         * <ul style="padding:5px;padding-left:16px;">
31724         * <li>tree - The TreePanel</li>
31725         * <li>target - The node being targeted for the drop</li>
31726         * <li>data - The drag data from the drag source</li>
31727         * <li>point - The point of the drop - append, above or below</li>
31728         * <li>source - The drag source</li>
31729         * <li>rawEvent - Raw mouse event</li>
31730         * <li>dropNode - Dropped node(s).</li>
31731         * </ul>
31732         * @param {Object} dropEvent
31733         */
31734        "nodedrop" : true,
31735         /**
31736         * @event nodedragover
31737         * Fires when a tree node is being targeted for a drag drop, return false to signal drop not allowed. The dragOverEvent
31738         * passed to handlers has the following properties:<br />
31739         * <ul style="padding:5px;padding-left:16px;">
31740         * <li>tree - The TreePanel</li>
31741         * <li>target - The node being targeted for the drop</li>
31742         * <li>data - The drag data from the drag source</li>
31743         * <li>point - The point of the drop - append, above or below</li>
31744         * <li>source - The drag source</li>
31745         * <li>rawEvent - Raw mouse event</li>
31746         * <li>dropNode - Drop node(s) provided by the source.</li>
31747         * <li>cancel - Set this to true to signal drop not allowed.</li>
31748         * </ul>
31749         * @param {Object} dragOverEvent
31750         */
31751        "nodedragover" : true
31752         
31753     });
31754     if(this.singleExpand){
31755        this.on("beforeexpand", this.restrictExpand, this);
31756     }
31757     if (this.editor) {
31758         this.editor.tree = this;
31759         this.editor = Roo.factory(this.editor, Roo.tree);
31760     }
31761     
31762     if (this.selModel) {
31763         this.selModel = Roo.factory(this.selModel, Roo.tree);
31764     }
31765    
31766 };
31767 Roo.extend(Roo.tree.TreePanel, Roo.data.Tree, {
31768     rootVisible : true,
31769     animate: Roo.enableFx,
31770     lines : true,
31771     enableDD : false,
31772     hlDrop : Roo.enableFx,
31773   
31774     renderer: false,
31775     
31776     rendererTip: false,
31777     // private
31778     restrictExpand : function(node){
31779         var p = node.parentNode;
31780         if(p){
31781             if(p.expandedChild && p.expandedChild.parentNode == p){
31782                 p.expandedChild.collapse();
31783             }
31784             p.expandedChild = node;
31785         }
31786     },
31787
31788     // private override
31789     setRootNode : function(node){
31790         Roo.tree.TreePanel.superclass.setRootNode.call(this, node);
31791         if(!this.rootVisible){
31792             node.ui = new Roo.tree.RootTreeNodeUI(node);
31793         }
31794         return node;
31795     },
31796
31797     /**
31798      * Returns the container element for this TreePanel
31799      */
31800     getEl : function(){
31801         return this.el;
31802     },
31803
31804     /**
31805      * Returns the default TreeLoader for this TreePanel
31806      */
31807     getLoader : function(){
31808         return this.loader;
31809     },
31810
31811     /**
31812      * Expand all nodes
31813      */
31814     expandAll : function(){
31815         this.root.expand(true);
31816     },
31817
31818     /**
31819      * Collapse all nodes
31820      */
31821     collapseAll : function(){
31822         this.root.collapse(true);
31823     },
31824
31825     /**
31826      * Returns the selection model used by this TreePanel
31827      */
31828     getSelectionModel : function(){
31829         if(!this.selModel){
31830             this.selModel = new Roo.tree.DefaultSelectionModel();
31831         }
31832         return this.selModel;
31833     },
31834
31835     /**
31836      * Retrieve an array of checked nodes, or an array of a specific attribute of checked nodes (e.g. "id")
31837      * @param {String} attribute (optional) Defaults to null (return the actual nodes)
31838      * @param {TreeNode} startNode (optional) The node to start from, defaults to the root
31839      * @return {Array}
31840      */
31841     getChecked : function(a, startNode){
31842         startNode = startNode || this.root;
31843         var r = [];
31844         var f = function(){
31845             if(this.attributes.checked){
31846                 r.push(!a ? this : (a == 'id' ? this.id : this.attributes[a]));
31847             }
31848         }
31849         startNode.cascade(f);
31850         return r;
31851     },
31852
31853     /**
31854      * Expands a specified path in this TreePanel. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
31855      * @param {String} path
31856      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
31857      * @param {Function} callback (optional) The callback to call when the expand is complete. The callback will be called with
31858      * (bSuccess, oLastNode) where bSuccess is if the expand was successful and oLastNode is the last node that was expanded.
31859      */
31860     expandPath : function(path, attr, callback){
31861         attr = attr || "id";
31862         var keys = path.split(this.pathSeparator);
31863         var curNode = this.root;
31864         if(curNode.attributes[attr] != keys[1]){ // invalid root
31865             if(callback){
31866                 callback(false, null);
31867             }
31868             return;
31869         }
31870         var index = 1;
31871         var f = function(){
31872             if(++index == keys.length){
31873                 if(callback){
31874                     callback(true, curNode);
31875                 }
31876                 return;
31877             }
31878             var c = curNode.findChild(attr, keys[index]);
31879             if(!c){
31880                 if(callback){
31881                     callback(false, curNode);
31882                 }
31883                 return;
31884             }
31885             curNode = c;
31886             c.expand(false, false, f);
31887         };
31888         curNode.expand(false, false, f);
31889     },
31890
31891     /**
31892      * Selects the node in this tree at the specified path. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
31893      * @param {String} path
31894      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
31895      * @param {Function} callback (optional) The callback to call when the selection is complete. The callback will be called with
31896      * (bSuccess, oSelNode) where bSuccess is if the selection was successful and oSelNode is the selected node.
31897      */
31898     selectPath : function(path, attr, callback){
31899         attr = attr || "id";
31900         var keys = path.split(this.pathSeparator);
31901         var v = keys.pop();
31902         if(keys.length > 0){
31903             var f = function(success, node){
31904                 if(success && node){
31905                     var n = node.findChild(attr, v);
31906                     if(n){
31907                         n.select();
31908                         if(callback){
31909                             callback(true, n);
31910                         }
31911                     }else if(callback){
31912                         callback(false, n);
31913                     }
31914                 }else{
31915                     if(callback){
31916                         callback(false, n);
31917                     }
31918                 }
31919             };
31920             this.expandPath(keys.join(this.pathSeparator), attr, f);
31921         }else{
31922             this.root.select();
31923             if(callback){
31924                 callback(true, this.root);
31925             }
31926         }
31927     },
31928
31929     getTreeEl : function(){
31930         return this.el;
31931     },
31932
31933     /**
31934      * Trigger rendering of this TreePanel
31935      */
31936     render : function(){
31937         if (this.innerCt) {
31938             return this; // stop it rendering more than once!!
31939         }
31940         
31941         this.innerCt = this.el.createChild({tag:"ul",
31942                cls:"x-tree-root-ct " +
31943                (this.lines ? "x-tree-lines" : "x-tree-no-lines")});
31944
31945         if(this.containerScroll){
31946             Roo.dd.ScrollManager.register(this.el);
31947         }
31948         if((this.enableDD || this.enableDrop) && !this.dropZone){
31949            /**
31950             * The dropZone used by this tree if drop is enabled
31951             * @type Roo.tree.TreeDropZone
31952             */
31953              this.dropZone = new Roo.tree.TreeDropZone(this, this.dropConfig || {
31954                ddGroup: this.ddGroup || "TreeDD", appendOnly: this.ddAppendOnly === true
31955            });
31956         }
31957         if((this.enableDD || this.enableDrag) && !this.dragZone){
31958            /**
31959             * The dragZone used by this tree if drag is enabled
31960             * @type Roo.tree.TreeDragZone
31961             */
31962             this.dragZone = new Roo.tree.TreeDragZone(this, this.dragConfig || {
31963                ddGroup: this.ddGroup || "TreeDD",
31964                scroll: this.ddScroll
31965            });
31966         }
31967         this.getSelectionModel().init(this);
31968         if (!this.root) {
31969             Roo.log("ROOT not set in tree");
31970             return this;
31971         }
31972         this.root.render();
31973         if(!this.rootVisible){
31974             this.root.renderChildren();
31975         }
31976         return this;
31977     }
31978 });/*
31979  * Based on:
31980  * Ext JS Library 1.1.1
31981  * Copyright(c) 2006-2007, Ext JS, LLC.
31982  *
31983  * Originally Released Under LGPL - original licence link has changed is not relivant.
31984  *
31985  * Fork - LGPL
31986  * <script type="text/javascript">
31987  */
31988  
31989
31990 /**
31991  * @class Roo.tree.DefaultSelectionModel
31992  * @extends Roo.util.Observable
31993  * The default single selection for a TreePanel.
31994  * @param {Object} cfg Configuration
31995  */
31996 Roo.tree.DefaultSelectionModel = function(cfg){
31997    this.selNode = null;
31998    
31999    
32000    
32001    this.addEvents({
32002        /**
32003         * @event selectionchange
32004         * Fires when the selected node changes
32005         * @param {DefaultSelectionModel} this
32006         * @param {TreeNode} node the new selection
32007         */
32008        "selectionchange" : true,
32009
32010        /**
32011         * @event beforeselect
32012         * Fires before the selected node changes, return false to cancel the change
32013         * @param {DefaultSelectionModel} this
32014         * @param {TreeNode} node the new selection
32015         * @param {TreeNode} node the old selection
32016         */
32017        "beforeselect" : true
32018    });
32019    
32020     Roo.tree.DefaultSelectionModel.superclass.constructor.call(this,cfg);
32021 };
32022
32023 Roo.extend(Roo.tree.DefaultSelectionModel, Roo.util.Observable, {
32024     init : function(tree){
32025         this.tree = tree;
32026         tree.getTreeEl().on("keydown", this.onKeyDown, this);
32027         tree.on("click", this.onNodeClick, this);
32028     },
32029     
32030     onNodeClick : function(node, e){
32031         if (e.ctrlKey && this.selNode == node)  {
32032             this.unselect(node);
32033             return;
32034         }
32035         this.select(node);
32036     },
32037     
32038     /**
32039      * Select a node.
32040      * @param {TreeNode} node The node to select
32041      * @return {TreeNode} The selected node
32042      */
32043     select : function(node){
32044         var last = this.selNode;
32045         if(last != node && this.fireEvent('beforeselect', this, node, last) !== false){
32046             if(last){
32047                 last.ui.onSelectedChange(false);
32048             }
32049             this.selNode = node;
32050             node.ui.onSelectedChange(true);
32051             this.fireEvent("selectionchange", this, node, last);
32052         }
32053         return node;
32054     },
32055     
32056     /**
32057      * Deselect a node.
32058      * @param {TreeNode} node The node to unselect
32059      */
32060     unselect : function(node){
32061         if(this.selNode == node){
32062             this.clearSelections();
32063         }    
32064     },
32065     
32066     /**
32067      * Clear all selections
32068      */
32069     clearSelections : function(){
32070         var n = this.selNode;
32071         if(n){
32072             n.ui.onSelectedChange(false);
32073             this.selNode = null;
32074             this.fireEvent("selectionchange", this, null);
32075         }
32076         return n;
32077     },
32078     
32079     /**
32080      * Get the selected node
32081      * @return {TreeNode} The selected node
32082      */
32083     getSelectedNode : function(){
32084         return this.selNode;    
32085     },
32086     
32087     /**
32088      * Returns true if the node is selected
32089      * @param {TreeNode} node The node to check
32090      * @return {Boolean}
32091      */
32092     isSelected : function(node){
32093         return this.selNode == node;  
32094     },
32095
32096     /**
32097      * Selects the node above the selected node in the tree, intelligently walking the nodes
32098      * @return TreeNode The new selection
32099      */
32100     selectPrevious : function(){
32101         var s = this.selNode || this.lastSelNode;
32102         if(!s){
32103             return null;
32104         }
32105         var ps = s.previousSibling;
32106         if(ps){
32107             if(!ps.isExpanded() || ps.childNodes.length < 1){
32108                 return this.select(ps);
32109             } else{
32110                 var lc = ps.lastChild;
32111                 while(lc && lc.isExpanded() && lc.childNodes.length > 0){
32112                     lc = lc.lastChild;
32113                 }
32114                 return this.select(lc);
32115             }
32116         } else if(s.parentNode && (this.tree.rootVisible || !s.parentNode.isRoot)){
32117             return this.select(s.parentNode);
32118         }
32119         return null;
32120     },
32121
32122     /**
32123      * Selects the node above the selected node in the tree, intelligently walking the nodes
32124      * @return TreeNode The new selection
32125      */
32126     selectNext : function(){
32127         var s = this.selNode || this.lastSelNode;
32128         if(!s){
32129             return null;
32130         }
32131         if(s.firstChild && s.isExpanded()){
32132              return this.select(s.firstChild);
32133          }else if(s.nextSibling){
32134              return this.select(s.nextSibling);
32135          }else if(s.parentNode){
32136             var newS = null;
32137             s.parentNode.bubble(function(){
32138                 if(this.nextSibling){
32139                     newS = this.getOwnerTree().selModel.select(this.nextSibling);
32140                     return false;
32141                 }
32142             });
32143             return newS;
32144          }
32145         return null;
32146     },
32147
32148     onKeyDown : function(e){
32149         var s = this.selNode || this.lastSelNode;
32150         // undesirable, but required
32151         var sm = this;
32152         if(!s){
32153             return;
32154         }
32155         var k = e.getKey();
32156         switch(k){
32157              case e.DOWN:
32158                  e.stopEvent();
32159                  this.selectNext();
32160              break;
32161              case e.UP:
32162                  e.stopEvent();
32163                  this.selectPrevious();
32164              break;
32165              case e.RIGHT:
32166                  e.preventDefault();
32167                  if(s.hasChildNodes()){
32168                      if(!s.isExpanded()){
32169                          s.expand();
32170                      }else if(s.firstChild){
32171                          this.select(s.firstChild, e);
32172                      }
32173                  }
32174              break;
32175              case e.LEFT:
32176                  e.preventDefault();
32177                  if(s.hasChildNodes() && s.isExpanded()){
32178                      s.collapse();
32179                  }else if(s.parentNode && (this.tree.rootVisible || s.parentNode != this.tree.getRootNode())){
32180                      this.select(s.parentNode, e);
32181                  }
32182              break;
32183         };
32184     }
32185 });
32186
32187 /**
32188  * @class Roo.tree.MultiSelectionModel
32189  * @extends Roo.util.Observable
32190  * Multi selection for a TreePanel.
32191  * @param {Object} cfg Configuration
32192  */
32193 Roo.tree.MultiSelectionModel = function(){
32194    this.selNodes = [];
32195    this.selMap = {};
32196    this.addEvents({
32197        /**
32198         * @event selectionchange
32199         * Fires when the selected nodes change
32200         * @param {MultiSelectionModel} this
32201         * @param {Array} nodes Array of the selected nodes
32202         */
32203        "selectionchange" : true
32204    });
32205    Roo.tree.MultiSelectionModel.superclass.constructor.call(this,cfg);
32206    
32207 };
32208
32209 Roo.extend(Roo.tree.MultiSelectionModel, Roo.util.Observable, {
32210     init : function(tree){
32211         this.tree = tree;
32212         tree.getTreeEl().on("keydown", this.onKeyDown, this);
32213         tree.on("click", this.onNodeClick, this);
32214     },
32215     
32216     onNodeClick : function(node, e){
32217         this.select(node, e, e.ctrlKey);
32218     },
32219     
32220     /**
32221      * Select a node.
32222      * @param {TreeNode} node The node to select
32223      * @param {EventObject} e (optional) An event associated with the selection
32224      * @param {Boolean} keepExisting True to retain existing selections
32225      * @return {TreeNode} The selected node
32226      */
32227     select : function(node, e, keepExisting){
32228         if(keepExisting !== true){
32229             this.clearSelections(true);
32230         }
32231         if(this.isSelected(node)){
32232             this.lastSelNode = node;
32233             return node;
32234         }
32235         this.selNodes.push(node);
32236         this.selMap[node.id] = node;
32237         this.lastSelNode = node;
32238         node.ui.onSelectedChange(true);
32239         this.fireEvent("selectionchange", this, this.selNodes);
32240         return node;
32241     },
32242     
32243     /**
32244      * Deselect a node.
32245      * @param {TreeNode} node The node to unselect
32246      */
32247     unselect : function(node){
32248         if(this.selMap[node.id]){
32249             node.ui.onSelectedChange(false);
32250             var sn = this.selNodes;
32251             var index = -1;
32252             if(sn.indexOf){
32253                 index = sn.indexOf(node);
32254             }else{
32255                 for(var i = 0, len = sn.length; i < len; i++){
32256                     if(sn[i] == node){
32257                         index = i;
32258                         break;
32259                     }
32260                 }
32261             }
32262             if(index != -1){
32263                 this.selNodes.splice(index, 1);
32264             }
32265             delete this.selMap[node.id];
32266             this.fireEvent("selectionchange", this, this.selNodes);
32267         }
32268     },
32269     
32270     /**
32271      * Clear all selections
32272      */
32273     clearSelections : function(suppressEvent){
32274         var sn = this.selNodes;
32275         if(sn.length > 0){
32276             for(var i = 0, len = sn.length; i < len; i++){
32277                 sn[i].ui.onSelectedChange(false);
32278             }
32279             this.selNodes = [];
32280             this.selMap = {};
32281             if(suppressEvent !== true){
32282                 this.fireEvent("selectionchange", this, this.selNodes);
32283             }
32284         }
32285     },
32286     
32287     /**
32288      * Returns true if the node is selected
32289      * @param {TreeNode} node The node to check
32290      * @return {Boolean}
32291      */
32292     isSelected : function(node){
32293         return this.selMap[node.id] ? true : false;  
32294     },
32295     
32296     /**
32297      * Returns an array of the selected nodes
32298      * @return {Array}
32299      */
32300     getSelectedNodes : function(){
32301         return this.selNodes;    
32302     },
32303
32304     onKeyDown : Roo.tree.DefaultSelectionModel.prototype.onKeyDown,
32305
32306     selectNext : Roo.tree.DefaultSelectionModel.prototype.selectNext,
32307
32308     selectPrevious : Roo.tree.DefaultSelectionModel.prototype.selectPrevious
32309 });/*
32310  * Based on:
32311  * Ext JS Library 1.1.1
32312  * Copyright(c) 2006-2007, Ext JS, LLC.
32313  *
32314  * Originally Released Under LGPL - original licence link has changed is not relivant.
32315  *
32316  * Fork - LGPL
32317  * <script type="text/javascript">
32318  */
32319  
32320 /**
32321  * @class Roo.tree.TreeNode
32322  * @extends Roo.data.Node
32323  * @cfg {String} text The text for this node
32324  * @cfg {Boolean} expanded true to start the node expanded
32325  * @cfg {Boolean} allowDrag false to make this node undraggable if DD is on (defaults to true)
32326  * @cfg {Boolean} allowDrop false if this node cannot be drop on
32327  * @cfg {Boolean} disabled true to start the node disabled
32328  * @cfg {String} icon The path to an icon for the node. The preferred way to do this
32329  * is to use the cls or iconCls attributes and add the icon via a CSS background image.
32330  * @cfg {String} cls A css class to be added to the node
32331  * @cfg {String} iconCls A css class to be added to the nodes icon element for applying css background images
32332  * @cfg {String} href URL of the link used for the node (defaults to #)
32333  * @cfg {String} hrefTarget target frame for the link
32334  * @cfg {String} qtip An Ext QuickTip for the node
32335  * @cfg {String} qtipCfg An Ext QuickTip config for the node (used instead of qtip)
32336  * @cfg {Boolean} singleClickExpand True for single click expand on this node
32337  * @cfg {Function} uiProvider A UI <b>class</b> to use for this node (defaults to Roo.tree.TreeNodeUI)
32338  * @cfg {Boolean} checked True to render a checked checkbox for this node, false to render an unchecked checkbox
32339  * (defaults to undefined with no checkbox rendered)
32340  * @constructor
32341  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node
32342  */
32343 Roo.tree.TreeNode = function(attributes){
32344     attributes = attributes || {};
32345     if(typeof attributes == "string"){
32346         attributes = {text: attributes};
32347     }
32348     this.childrenRendered = false;
32349     this.rendered = false;
32350     Roo.tree.TreeNode.superclass.constructor.call(this, attributes);
32351     this.expanded = attributes.expanded === true;
32352     this.isTarget = attributes.isTarget !== false;
32353     this.draggable = attributes.draggable !== false && attributes.allowDrag !== false;
32354     this.allowChildren = attributes.allowChildren !== false && attributes.allowDrop !== false;
32355
32356     /**
32357      * Read-only. The text for this node. To change it use setText().
32358      * @type String
32359      */
32360     this.text = attributes.text;
32361     /**
32362      * True if this node is disabled.
32363      * @type Boolean
32364      */
32365     this.disabled = attributes.disabled === true;
32366
32367     this.addEvents({
32368         /**
32369         * @event textchange
32370         * Fires when the text for this node is changed
32371         * @param {Node} this This node
32372         * @param {String} text The new text
32373         * @param {String} oldText The old text
32374         */
32375         "textchange" : true,
32376         /**
32377         * @event beforeexpand
32378         * Fires before this node is expanded, return false to cancel.
32379         * @param {Node} this This node
32380         * @param {Boolean} deep
32381         * @param {Boolean} anim
32382         */
32383         "beforeexpand" : true,
32384         /**
32385         * @event beforecollapse
32386         * Fires before this node is collapsed, return false to cancel.
32387         * @param {Node} this This node
32388         * @param {Boolean} deep
32389         * @param {Boolean} anim
32390         */
32391         "beforecollapse" : true,
32392         /**
32393         * @event expand
32394         * Fires when this node is expanded
32395         * @param {Node} this This node
32396         */
32397         "expand" : true,
32398         /**
32399         * @event disabledchange
32400         * Fires when the disabled status of this node changes
32401         * @param {Node} this This node
32402         * @param {Boolean} disabled
32403         */
32404         "disabledchange" : true,
32405         /**
32406         * @event collapse
32407         * Fires when this node is collapsed
32408         * @param {Node} this This node
32409         */
32410         "collapse" : true,
32411         /**
32412         * @event beforeclick
32413         * Fires before click processing. Return false to cancel the default action.
32414         * @param {Node} this This node
32415         * @param {Roo.EventObject} e The event object
32416         */
32417         "beforeclick":true,
32418         /**
32419         * @event checkchange
32420         * Fires when a node with a checkbox's checked property changes
32421         * @param {Node} this This node
32422         * @param {Boolean} checked
32423         */
32424         "checkchange":true,
32425         /**
32426         * @event click
32427         * Fires when this node is clicked
32428         * @param {Node} this This node
32429         * @param {Roo.EventObject} e The event object
32430         */
32431         "click":true,
32432         /**
32433         * @event dblclick
32434         * Fires when this node is double clicked
32435         * @param {Node} this This node
32436         * @param {Roo.EventObject} e The event object
32437         */
32438         "dblclick":true,
32439         /**
32440         * @event contextmenu
32441         * Fires when this node is right clicked
32442         * @param {Node} this This node
32443         * @param {Roo.EventObject} e The event object
32444         */
32445         "contextmenu":true,
32446         /**
32447         * @event beforechildrenrendered
32448         * Fires right before the child nodes for this node are rendered
32449         * @param {Node} this This node
32450         */
32451         "beforechildrenrendered":true
32452     });
32453
32454     var uiClass = this.attributes.uiProvider || Roo.tree.TreeNodeUI;
32455
32456     /**
32457      * Read-only. The UI for this node
32458      * @type TreeNodeUI
32459      */
32460     this.ui = new uiClass(this);
32461     
32462     // finally support items[]
32463     if (typeof(this.attributes.items) == 'undefined' || !this.attributes.items) {
32464         return;
32465     }
32466     
32467     
32468     Roo.each(this.attributes.items, function(c) {
32469         this.appendChild(Roo.factory(c,Roo.Tree));
32470     }, this);
32471     delete this.attributes.items;
32472     
32473     
32474     
32475 };
32476 Roo.extend(Roo.tree.TreeNode, Roo.data.Node, {
32477     preventHScroll: true,
32478     /**
32479      * Returns true if this node is expanded
32480      * @return {Boolean}
32481      */
32482     isExpanded : function(){
32483         return this.expanded;
32484     },
32485
32486     /**
32487      * Returns the UI object for this node
32488      * @return {TreeNodeUI}
32489      */
32490     getUI : function(){
32491         return this.ui;
32492     },
32493
32494     // private override
32495     setFirstChild : function(node){
32496         var of = this.firstChild;
32497         Roo.tree.TreeNode.superclass.setFirstChild.call(this, node);
32498         if(this.childrenRendered && of && node != of){
32499             of.renderIndent(true, true);
32500         }
32501         if(this.rendered){
32502             this.renderIndent(true, true);
32503         }
32504     },
32505
32506     // private override
32507     setLastChild : function(node){
32508         var ol = this.lastChild;
32509         Roo.tree.TreeNode.superclass.setLastChild.call(this, node);
32510         if(this.childrenRendered && ol && node != ol){
32511             ol.renderIndent(true, true);
32512         }
32513         if(this.rendered){
32514             this.renderIndent(true, true);
32515         }
32516     },
32517
32518     // these methods are overridden to provide lazy rendering support
32519     // private override
32520     appendChild : function()
32521     {
32522         var node = Roo.tree.TreeNode.superclass.appendChild.apply(this, arguments);
32523         if(node && this.childrenRendered){
32524             node.render();
32525         }
32526         this.ui.updateExpandIcon();
32527         return node;
32528     },
32529
32530     // private override
32531     removeChild : function(node){
32532         this.ownerTree.getSelectionModel().unselect(node);
32533         Roo.tree.TreeNode.superclass.removeChild.apply(this, arguments);
32534         // if it's been rendered remove dom node
32535         if(this.childrenRendered){
32536             node.ui.remove();
32537         }
32538         if(this.childNodes.length < 1){
32539             this.collapse(false, false);
32540         }else{
32541             this.ui.updateExpandIcon();
32542         }
32543         if(!this.firstChild) {
32544             this.childrenRendered = false;
32545         }
32546         return node;
32547     },
32548
32549     // private override
32550     insertBefore : function(node, refNode){
32551         var newNode = Roo.tree.TreeNode.superclass.insertBefore.apply(this, arguments);
32552         if(newNode && refNode && this.childrenRendered){
32553             node.render();
32554         }
32555         this.ui.updateExpandIcon();
32556         return newNode;
32557     },
32558
32559     /**
32560      * Sets the text for this node
32561      * @param {String} text
32562      */
32563     setText : function(text){
32564         var oldText = this.text;
32565         this.text = text;
32566         this.attributes.text = text;
32567         if(this.rendered){ // event without subscribing
32568             this.ui.onTextChange(this, text, oldText);
32569         }
32570         this.fireEvent("textchange", this, text, oldText);
32571     },
32572
32573     /**
32574      * Triggers selection of this node
32575      */
32576     select : function(){
32577         this.getOwnerTree().getSelectionModel().select(this);
32578     },
32579
32580     /**
32581      * Triggers deselection of this node
32582      */
32583     unselect : function(){
32584         this.getOwnerTree().getSelectionModel().unselect(this);
32585     },
32586
32587     /**
32588      * Returns true if this node is selected
32589      * @return {Boolean}
32590      */
32591     isSelected : function(){
32592         return this.getOwnerTree().getSelectionModel().isSelected(this);
32593     },
32594
32595     /**
32596      * Expand this node.
32597      * @param {Boolean} deep (optional) True to expand all children as well
32598      * @param {Boolean} anim (optional) false to cancel the default animation
32599      * @param {Function} callback (optional) A callback to be called when
32600      * expanding this node completes (does not wait for deep expand to complete).
32601      * Called with 1 parameter, this node.
32602      */
32603     expand : function(deep, anim, callback){
32604         if(!this.expanded){
32605             if(this.fireEvent("beforeexpand", this, deep, anim) === false){
32606                 return;
32607             }
32608             if(!this.childrenRendered){
32609                 this.renderChildren();
32610             }
32611             this.expanded = true;
32612             if(!this.isHiddenRoot() && (this.getOwnerTree().animate && anim !== false) || anim){
32613                 this.ui.animExpand(function(){
32614                     this.fireEvent("expand", this);
32615                     if(typeof callback == "function"){
32616                         callback(this);
32617                     }
32618                     if(deep === true){
32619                         this.expandChildNodes(true);
32620                     }
32621                 }.createDelegate(this));
32622                 return;
32623             }else{
32624                 this.ui.expand();
32625                 this.fireEvent("expand", this);
32626                 if(typeof callback == "function"){
32627                     callback(this);
32628                 }
32629             }
32630         }else{
32631            if(typeof callback == "function"){
32632                callback(this);
32633            }
32634         }
32635         if(deep === true){
32636             this.expandChildNodes(true);
32637         }
32638     },
32639
32640     isHiddenRoot : function(){
32641         return this.isRoot && !this.getOwnerTree().rootVisible;
32642     },
32643
32644     /**
32645      * Collapse this node.
32646      * @param {Boolean} deep (optional) True to collapse all children as well
32647      * @param {Boolean} anim (optional) false to cancel the default animation
32648      */
32649     collapse : function(deep, anim){
32650         if(this.expanded && !this.isHiddenRoot()){
32651             if(this.fireEvent("beforecollapse", this, deep, anim) === false){
32652                 return;
32653             }
32654             this.expanded = false;
32655             if((this.getOwnerTree().animate && anim !== false) || anim){
32656                 this.ui.animCollapse(function(){
32657                     this.fireEvent("collapse", this);
32658                     if(deep === true){
32659                         this.collapseChildNodes(true);
32660                     }
32661                 }.createDelegate(this));
32662                 return;
32663             }else{
32664                 this.ui.collapse();
32665                 this.fireEvent("collapse", this);
32666             }
32667         }
32668         if(deep === true){
32669             var cs = this.childNodes;
32670             for(var i = 0, len = cs.length; i < len; i++) {
32671                 cs[i].collapse(true, false);
32672             }
32673         }
32674     },
32675
32676     // private
32677     delayedExpand : function(delay){
32678         if(!this.expandProcId){
32679             this.expandProcId = this.expand.defer(delay, this);
32680         }
32681     },
32682
32683     // private
32684     cancelExpand : function(){
32685         if(this.expandProcId){
32686             clearTimeout(this.expandProcId);
32687         }
32688         this.expandProcId = false;
32689     },
32690
32691     /**
32692      * Toggles expanded/collapsed state of the node
32693      */
32694     toggle : function(){
32695         if(this.expanded){
32696             this.collapse();
32697         }else{
32698             this.expand();
32699         }
32700     },
32701
32702     /**
32703      * Ensures all parent nodes are expanded
32704      */
32705     ensureVisible : function(callback){
32706         var tree = this.getOwnerTree();
32707         tree.expandPath(this.parentNode.getPath(), false, function(){
32708             tree.getTreeEl().scrollChildIntoView(this.ui.anchor);
32709             Roo.callback(callback);
32710         }.createDelegate(this));
32711     },
32712
32713     /**
32714      * Expand all child nodes
32715      * @param {Boolean} deep (optional) true if the child nodes should also expand their child nodes
32716      */
32717     expandChildNodes : function(deep){
32718         var cs = this.childNodes;
32719         for(var i = 0, len = cs.length; i < len; i++) {
32720                 cs[i].expand(deep);
32721         }
32722     },
32723
32724     /**
32725      * Collapse all child nodes
32726      * @param {Boolean} deep (optional) true if the child nodes should also collapse their child nodes
32727      */
32728     collapseChildNodes : function(deep){
32729         var cs = this.childNodes;
32730         for(var i = 0, len = cs.length; i < len; i++) {
32731                 cs[i].collapse(deep);
32732         }
32733     },
32734
32735     /**
32736      * Disables this node
32737      */
32738     disable : function(){
32739         this.disabled = true;
32740         this.unselect();
32741         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
32742             this.ui.onDisableChange(this, true);
32743         }
32744         this.fireEvent("disabledchange", this, true);
32745     },
32746
32747     /**
32748      * Enables this node
32749      */
32750     enable : function(){
32751         this.disabled = false;
32752         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
32753             this.ui.onDisableChange(this, false);
32754         }
32755         this.fireEvent("disabledchange", this, false);
32756     },
32757
32758     // private
32759     renderChildren : function(suppressEvent){
32760         if(suppressEvent !== false){
32761             this.fireEvent("beforechildrenrendered", this);
32762         }
32763         var cs = this.childNodes;
32764         for(var i = 0, len = cs.length; i < len; i++){
32765             cs[i].render(true);
32766         }
32767         this.childrenRendered = true;
32768     },
32769
32770     // private
32771     sort : function(fn, scope){
32772         Roo.tree.TreeNode.superclass.sort.apply(this, arguments);
32773         if(this.childrenRendered){
32774             var cs = this.childNodes;
32775             for(var i = 0, len = cs.length; i < len; i++){
32776                 cs[i].render(true);
32777             }
32778         }
32779     },
32780
32781     // private
32782     render : function(bulkRender){
32783         this.ui.render(bulkRender);
32784         if(!this.rendered){
32785             this.rendered = true;
32786             if(this.expanded){
32787                 this.expanded = false;
32788                 this.expand(false, false);
32789             }
32790         }
32791     },
32792
32793     // private
32794     renderIndent : function(deep, refresh){
32795         if(refresh){
32796             this.ui.childIndent = null;
32797         }
32798         this.ui.renderIndent();
32799         if(deep === true && this.childrenRendered){
32800             var cs = this.childNodes;
32801             for(var i = 0, len = cs.length; i < len; i++){
32802                 cs[i].renderIndent(true, refresh);
32803             }
32804         }
32805     }
32806 });/*
32807  * Based on:
32808  * Ext JS Library 1.1.1
32809  * Copyright(c) 2006-2007, Ext JS, LLC.
32810  *
32811  * Originally Released Under LGPL - original licence link has changed is not relivant.
32812  *
32813  * Fork - LGPL
32814  * <script type="text/javascript">
32815  */
32816  
32817 /**
32818  * @class Roo.tree.AsyncTreeNode
32819  * @extends Roo.tree.TreeNode
32820  * @cfg {TreeLoader} loader A TreeLoader to be used by this node (defaults to the loader defined on the tree)
32821  * @constructor
32822  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node 
32823  */
32824  Roo.tree.AsyncTreeNode = function(config){
32825     this.loaded = false;
32826     this.loading = false;
32827     Roo.tree.AsyncTreeNode.superclass.constructor.apply(this, arguments);
32828     /**
32829     * @event beforeload
32830     * Fires before this node is loaded, return false to cancel
32831     * @param {Node} this This node
32832     */
32833     this.addEvents({'beforeload':true, 'load': true});
32834     /**
32835     * @event load
32836     * Fires when this node is loaded
32837     * @param {Node} this This node
32838     */
32839     /**
32840      * The loader used by this node (defaults to using the tree's defined loader)
32841      * @type TreeLoader
32842      * @property loader
32843      */
32844 };
32845 Roo.extend(Roo.tree.AsyncTreeNode, Roo.tree.TreeNode, {
32846     expand : function(deep, anim, callback){
32847         if(this.loading){ // if an async load is already running, waiting til it's done
32848             var timer;
32849             var f = function(){
32850                 if(!this.loading){ // done loading
32851                     clearInterval(timer);
32852                     this.expand(deep, anim, callback);
32853                 }
32854             }.createDelegate(this);
32855             timer = setInterval(f, 200);
32856             return;
32857         }
32858         if(!this.loaded){
32859             if(this.fireEvent("beforeload", this) === false){
32860                 return;
32861             }
32862             this.loading = true;
32863             this.ui.beforeLoad(this);
32864             var loader = this.loader || this.attributes.loader || this.getOwnerTree().getLoader();
32865             if(loader){
32866                 loader.load(this, this.loadComplete.createDelegate(this, [deep, anim, callback]));
32867                 return;
32868             }
32869         }
32870         Roo.tree.AsyncTreeNode.superclass.expand.call(this, deep, anim, callback);
32871     },
32872     
32873     /**
32874      * Returns true if this node is currently loading
32875      * @return {Boolean}
32876      */
32877     isLoading : function(){
32878         return this.loading;  
32879     },
32880     
32881     loadComplete : function(deep, anim, callback){
32882         this.loading = false;
32883         this.loaded = true;
32884         this.ui.afterLoad(this);
32885         this.fireEvent("load", this);
32886         this.expand(deep, anim, callback);
32887     },
32888     
32889     /**
32890      * Returns true if this node has been loaded
32891      * @return {Boolean}
32892      */
32893     isLoaded : function(){
32894         return this.loaded;
32895     },
32896     
32897     hasChildNodes : function(){
32898         if(!this.isLeaf() && !this.loaded){
32899             return true;
32900         }else{
32901             return Roo.tree.AsyncTreeNode.superclass.hasChildNodes.call(this);
32902         }
32903     },
32904
32905     /**
32906      * Trigger a reload for this node
32907      * @param {Function} callback
32908      */
32909     reload : function(callback){
32910         this.collapse(false, false);
32911         while(this.firstChild){
32912             this.removeChild(this.firstChild);
32913         }
32914         this.childrenRendered = false;
32915         this.loaded = false;
32916         if(this.isHiddenRoot()){
32917             this.expanded = false;
32918         }
32919         this.expand(false, false, callback);
32920     }
32921 });/*
32922  * Based on:
32923  * Ext JS Library 1.1.1
32924  * Copyright(c) 2006-2007, Ext JS, LLC.
32925  *
32926  * Originally Released Under LGPL - original licence link has changed is not relivant.
32927  *
32928  * Fork - LGPL
32929  * <script type="text/javascript">
32930  */
32931  
32932 /**
32933  * @class Roo.tree.TreeNodeUI
32934  * @constructor
32935  * @param {Object} node The node to render
32936  * The TreeNode UI implementation is separate from the
32937  * tree implementation. Unless you are customizing the tree UI,
32938  * you should never have to use this directly.
32939  */
32940 Roo.tree.TreeNodeUI = function(node){
32941     this.node = node;
32942     this.rendered = false;
32943     this.animating = false;
32944     this.emptyIcon = Roo.BLANK_IMAGE_URL;
32945 };
32946
32947 Roo.tree.TreeNodeUI.prototype = {
32948     removeChild : function(node){
32949         if(this.rendered){
32950             this.ctNode.removeChild(node.ui.getEl());
32951         }
32952     },
32953
32954     beforeLoad : function(){
32955          this.addClass("x-tree-node-loading");
32956     },
32957
32958     afterLoad : function(){
32959          this.removeClass("x-tree-node-loading");
32960     },
32961
32962     onTextChange : function(node, text, oldText){
32963         if(this.rendered){
32964             this.textNode.innerHTML = text;
32965         }
32966     },
32967
32968     onDisableChange : function(node, state){
32969         this.disabled = state;
32970         if(state){
32971             this.addClass("x-tree-node-disabled");
32972         }else{
32973             this.removeClass("x-tree-node-disabled");
32974         }
32975     },
32976
32977     onSelectedChange : function(state){
32978         if(state){
32979             this.focus();
32980             this.addClass("x-tree-selected");
32981         }else{
32982             //this.blur();
32983             this.removeClass("x-tree-selected");
32984         }
32985     },
32986
32987     onMove : function(tree, node, oldParent, newParent, index, refNode){
32988         this.childIndent = null;
32989         if(this.rendered){
32990             var targetNode = newParent.ui.getContainer();
32991             if(!targetNode){//target not rendered
32992                 this.holder = document.createElement("div");
32993                 this.holder.appendChild(this.wrap);
32994                 return;
32995             }
32996             var insertBefore = refNode ? refNode.ui.getEl() : null;
32997             if(insertBefore){
32998                 targetNode.insertBefore(this.wrap, insertBefore);
32999             }else{
33000                 targetNode.appendChild(this.wrap);
33001             }
33002             this.node.renderIndent(true);
33003         }
33004     },
33005
33006     addClass : function(cls){
33007         if(this.elNode){
33008             Roo.fly(this.elNode).addClass(cls);
33009         }
33010     },
33011
33012     removeClass : function(cls){
33013         if(this.elNode){
33014             Roo.fly(this.elNode).removeClass(cls);
33015         }
33016     },
33017
33018     remove : function(){
33019         if(this.rendered){
33020             this.holder = document.createElement("div");
33021             this.holder.appendChild(this.wrap);
33022         }
33023     },
33024
33025     fireEvent : function(){
33026         return this.node.fireEvent.apply(this.node, arguments);
33027     },
33028
33029     initEvents : function(){
33030         this.node.on("move", this.onMove, this);
33031         var E = Roo.EventManager;
33032         var a = this.anchor;
33033
33034         var el = Roo.fly(a, '_treeui');
33035
33036         if(Roo.isOpera){ // opera render bug ignores the CSS
33037             el.setStyle("text-decoration", "none");
33038         }
33039
33040         el.on("click", this.onClick, this);
33041         el.on("dblclick", this.onDblClick, this);
33042
33043         if(this.checkbox){
33044             Roo.EventManager.on(this.checkbox,
33045                     Roo.isIE ? 'click' : 'change', this.onCheckChange, this);
33046         }
33047
33048         el.on("contextmenu", this.onContextMenu, this);
33049
33050         var icon = Roo.fly(this.iconNode);
33051         icon.on("click", this.onClick, this);
33052         icon.on("dblclick", this.onDblClick, this);
33053         icon.on("contextmenu", this.onContextMenu, this);
33054         E.on(this.ecNode, "click", this.ecClick, this, true);
33055
33056         if(this.node.disabled){
33057             this.addClass("x-tree-node-disabled");
33058         }
33059         if(this.node.hidden){
33060             this.addClass("x-tree-node-disabled");
33061         }
33062         var ot = this.node.getOwnerTree();
33063         var dd = ot.enableDD || ot.enableDrag || ot.enableDrop;
33064         if(dd && (!this.node.isRoot || ot.rootVisible)){
33065             Roo.dd.Registry.register(this.elNode, {
33066                 node: this.node,
33067                 handles: this.getDDHandles(),
33068                 isHandle: false
33069             });
33070         }
33071     },
33072
33073     getDDHandles : function(){
33074         return [this.iconNode, this.textNode];
33075     },
33076
33077     hide : function(){
33078         if(this.rendered){
33079             this.wrap.style.display = "none";
33080         }
33081     },
33082
33083     show : function(){
33084         if(this.rendered){
33085             this.wrap.style.display = "";
33086         }
33087     },
33088
33089     onContextMenu : function(e){
33090         if (this.node.hasListener("contextmenu") || this.node.getOwnerTree().hasListener("contextmenu")) {
33091             e.preventDefault();
33092             this.focus();
33093             this.fireEvent("contextmenu", this.node, e);
33094         }
33095     },
33096
33097     onClick : function(e){
33098         if(this.dropping){
33099             e.stopEvent();
33100             return;
33101         }
33102         if(this.fireEvent("beforeclick", this.node, e) !== false){
33103             if(!this.disabled && this.node.attributes.href){
33104                 this.fireEvent("click", this.node, e);
33105                 return;
33106             }
33107             e.preventDefault();
33108             if(this.disabled){
33109                 return;
33110             }
33111
33112             if(this.node.attributes.singleClickExpand && !this.animating && this.node.hasChildNodes()){
33113                 this.node.toggle();
33114             }
33115
33116             this.fireEvent("click", this.node, e);
33117         }else{
33118             e.stopEvent();
33119         }
33120     },
33121
33122     onDblClick : function(e){
33123         e.preventDefault();
33124         if(this.disabled){
33125             return;
33126         }
33127         if(this.checkbox){
33128             this.toggleCheck();
33129         }
33130         if(!this.animating && this.node.hasChildNodes()){
33131             this.node.toggle();
33132         }
33133         this.fireEvent("dblclick", this.node, e);
33134     },
33135
33136     onCheckChange : function(){
33137         var checked = this.checkbox.checked;
33138         this.node.attributes.checked = checked;
33139         this.fireEvent('checkchange', this.node, checked);
33140     },
33141
33142     ecClick : function(e){
33143         if(!this.animating && this.node.hasChildNodes()){
33144             this.node.toggle();
33145         }
33146     },
33147
33148     startDrop : function(){
33149         this.dropping = true;
33150     },
33151
33152     // delayed drop so the click event doesn't get fired on a drop
33153     endDrop : function(){
33154        setTimeout(function(){
33155            this.dropping = false;
33156        }.createDelegate(this), 50);
33157     },
33158
33159     expand : function(){
33160         this.updateExpandIcon();
33161         this.ctNode.style.display = "";
33162     },
33163
33164     focus : function(){
33165         if(!this.node.preventHScroll){
33166             try{this.anchor.focus();
33167             }catch(e){}
33168         }else if(!Roo.isIE){
33169             try{
33170                 var noscroll = this.node.getOwnerTree().getTreeEl().dom;
33171                 var l = noscroll.scrollLeft;
33172                 this.anchor.focus();
33173                 noscroll.scrollLeft = l;
33174             }catch(e){}
33175         }
33176     },
33177
33178     toggleCheck : function(value){
33179         var cb = this.checkbox;
33180         if(cb){
33181             cb.checked = (value === undefined ? !cb.checked : value);
33182         }
33183     },
33184
33185     blur : function(){
33186         try{
33187             this.anchor.blur();
33188         }catch(e){}
33189     },
33190
33191     animExpand : function(callback){
33192         var ct = Roo.get(this.ctNode);
33193         ct.stopFx();
33194         if(!this.node.hasChildNodes()){
33195             this.updateExpandIcon();
33196             this.ctNode.style.display = "";
33197             Roo.callback(callback);
33198             return;
33199         }
33200         this.animating = true;
33201         this.updateExpandIcon();
33202
33203         ct.slideIn('t', {
33204            callback : function(){
33205                this.animating = false;
33206                Roo.callback(callback);
33207             },
33208             scope: this,
33209             duration: this.node.ownerTree.duration || .25
33210         });
33211     },
33212
33213     highlight : function(){
33214         var tree = this.node.getOwnerTree();
33215         Roo.fly(this.wrap).highlight(
33216             tree.hlColor || "C3DAF9",
33217             {endColor: tree.hlBaseColor}
33218         );
33219     },
33220
33221     collapse : function(){
33222         this.updateExpandIcon();
33223         this.ctNode.style.display = "none";
33224     },
33225
33226     animCollapse : function(callback){
33227         var ct = Roo.get(this.ctNode);
33228         ct.enableDisplayMode('block');
33229         ct.stopFx();
33230
33231         this.animating = true;
33232         this.updateExpandIcon();
33233
33234         ct.slideOut('t', {
33235             callback : function(){
33236                this.animating = false;
33237                Roo.callback(callback);
33238             },
33239             scope: this,
33240             duration: this.node.ownerTree.duration || .25
33241         });
33242     },
33243
33244     getContainer : function(){
33245         return this.ctNode;
33246     },
33247
33248     getEl : function(){
33249         return this.wrap;
33250     },
33251
33252     appendDDGhost : function(ghostNode){
33253         ghostNode.appendChild(this.elNode.cloneNode(true));
33254     },
33255
33256     getDDRepairXY : function(){
33257         return Roo.lib.Dom.getXY(this.iconNode);
33258     },
33259
33260     onRender : function(){
33261         this.render();
33262     },
33263
33264     render : function(bulkRender){
33265         var n = this.node, a = n.attributes;
33266         var targetNode = n.parentNode ?
33267               n.parentNode.ui.getContainer() : n.ownerTree.innerCt.dom;
33268
33269         if(!this.rendered){
33270             this.rendered = true;
33271
33272             this.renderElements(n, a, targetNode, bulkRender);
33273
33274             if(a.qtip){
33275                if(this.textNode.setAttributeNS){
33276                    this.textNode.setAttributeNS("ext", "qtip", a.qtip);
33277                    if(a.qtipTitle){
33278                        this.textNode.setAttributeNS("ext", "qtitle", a.qtipTitle);
33279                    }
33280                }else{
33281                    this.textNode.setAttribute("ext:qtip", a.qtip);
33282                    if(a.qtipTitle){
33283                        this.textNode.setAttribute("ext:qtitle", a.qtipTitle);
33284                    }
33285                }
33286             }else if(a.qtipCfg){
33287                 a.qtipCfg.target = Roo.id(this.textNode);
33288                 Roo.QuickTips.register(a.qtipCfg);
33289             }
33290             this.initEvents();
33291             if(!this.node.expanded){
33292                 this.updateExpandIcon();
33293             }
33294         }else{
33295             if(bulkRender === true) {
33296                 targetNode.appendChild(this.wrap);
33297             }
33298         }
33299     },
33300
33301     renderElements : function(n, a, targetNode, bulkRender)
33302     {
33303         // add some indent caching, this helps performance when rendering a large tree
33304         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
33305         var t = n.getOwnerTree();
33306         var txt = t.renderer ? t.renderer(n.attributes) : Roo.util.Format.htmlEncode(n.text);
33307         if (typeof(n.attributes.html) != 'undefined') {
33308             txt = n.attributes.html;
33309         }
33310         var tip = t.rendererTip ? t.rendererTip(n.attributes) : txt;
33311         var cb = typeof a.checked == 'boolean';
33312         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
33313         var buf = ['<li class="x-tree-node"><div class="x-tree-node-el ', a.cls,'">',
33314             '<span class="x-tree-node-indent">',this.indentMarkup,"</span>",
33315             '<img src="', this.emptyIcon, '" class="x-tree-ec-icon" />',
33316             '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',(a.icon ? " x-tree-node-inline-icon" : ""),(a.iconCls ? " "+a.iconCls : ""),'" unselectable="on" />',
33317             cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + (a.checked ? 'checked="checked" />' : ' />')) : '',
33318             '<a hidefocus="on" href="',href,'" tabIndex="1" ',
33319              a.hrefTarget ? ' target="'+a.hrefTarget+'"' : "", 
33320                 '><span unselectable="on" qtip="' , tip ,'">',txt,"</span></a></div>",
33321             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
33322             "</li>"];
33323
33324         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
33325             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
33326                                 n.nextSibling.ui.getEl(), buf.join(""));
33327         }else{
33328             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
33329         }
33330
33331         this.elNode = this.wrap.childNodes[0];
33332         this.ctNode = this.wrap.childNodes[1];
33333         var cs = this.elNode.childNodes;
33334         this.indentNode = cs[0];
33335         this.ecNode = cs[1];
33336         this.iconNode = cs[2];
33337         var index = 3;
33338         if(cb){
33339             this.checkbox = cs[3];
33340             index++;
33341         }
33342         this.anchor = cs[index];
33343         this.textNode = cs[index].firstChild;
33344     },
33345
33346     getAnchor : function(){
33347         return this.anchor;
33348     },
33349
33350     getTextEl : function(){
33351         return this.textNode;
33352     },
33353
33354     getIconEl : function(){
33355         return this.iconNode;
33356     },
33357
33358     isChecked : function(){
33359         return this.checkbox ? this.checkbox.checked : false;
33360     },
33361
33362     updateExpandIcon : function(){
33363         if(this.rendered){
33364             var n = this.node, c1, c2;
33365             var cls = n.isLast() ? "x-tree-elbow-end" : "x-tree-elbow";
33366             var hasChild = n.hasChildNodes();
33367             if(hasChild){
33368                 if(n.expanded){
33369                     cls += "-minus";
33370                     c1 = "x-tree-node-collapsed";
33371                     c2 = "x-tree-node-expanded";
33372                 }else{
33373                     cls += "-plus";
33374                     c1 = "x-tree-node-expanded";
33375                     c2 = "x-tree-node-collapsed";
33376                 }
33377                 if(this.wasLeaf){
33378                     this.removeClass("x-tree-node-leaf");
33379                     this.wasLeaf = false;
33380                 }
33381                 if(this.c1 != c1 || this.c2 != c2){
33382                     Roo.fly(this.elNode).replaceClass(c1, c2);
33383                     this.c1 = c1; this.c2 = c2;
33384                 }
33385             }else{
33386                 // this changes non-leafs into leafs if they have no children.
33387                 // it's not very rational behaviour..
33388                 
33389                 if(!this.wasLeaf && this.node.leaf){
33390                     Roo.fly(this.elNode).replaceClass("x-tree-node-expanded", "x-tree-node-leaf");
33391                     delete this.c1;
33392                     delete this.c2;
33393                     this.wasLeaf = true;
33394                 }
33395             }
33396             var ecc = "x-tree-ec-icon "+cls;
33397             if(this.ecc != ecc){
33398                 this.ecNode.className = ecc;
33399                 this.ecc = ecc;
33400             }
33401         }
33402     },
33403
33404     getChildIndent : function(){
33405         if(!this.childIndent){
33406             var buf = [];
33407             var p = this.node;
33408             while(p){
33409                 if(!p.isRoot || (p.isRoot && p.ownerTree.rootVisible)){
33410                     if(!p.isLast()) {
33411                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-elbow-line" />');
33412                     } else {
33413                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-icon" />');
33414                     }
33415                 }
33416                 p = p.parentNode;
33417             }
33418             this.childIndent = buf.join("");
33419         }
33420         return this.childIndent;
33421     },
33422
33423     renderIndent : function(){
33424         if(this.rendered){
33425             var indent = "";
33426             var p = this.node.parentNode;
33427             if(p){
33428                 indent = p.ui.getChildIndent();
33429             }
33430             if(this.indentMarkup != indent){ // don't rerender if not required
33431                 this.indentNode.innerHTML = indent;
33432                 this.indentMarkup = indent;
33433             }
33434             this.updateExpandIcon();
33435         }
33436     }
33437 };
33438
33439 Roo.tree.RootTreeNodeUI = function(){
33440     Roo.tree.RootTreeNodeUI.superclass.constructor.apply(this, arguments);
33441 };
33442 Roo.extend(Roo.tree.RootTreeNodeUI, Roo.tree.TreeNodeUI, {
33443     render : function(){
33444         if(!this.rendered){
33445             var targetNode = this.node.ownerTree.innerCt.dom;
33446             this.node.expanded = true;
33447             targetNode.innerHTML = '<div class="x-tree-root-node"></div>';
33448             this.wrap = this.ctNode = targetNode.firstChild;
33449         }
33450     },
33451     collapse : function(){
33452     },
33453     expand : function(){
33454     }
33455 });/*
33456  * Based on:
33457  * Ext JS Library 1.1.1
33458  * Copyright(c) 2006-2007, Ext JS, LLC.
33459  *
33460  * Originally Released Under LGPL - original licence link has changed is not relivant.
33461  *
33462  * Fork - LGPL
33463  * <script type="text/javascript">
33464  */
33465 /**
33466  * @class Roo.tree.TreeLoader
33467  * @extends Roo.util.Observable
33468  * A TreeLoader provides for lazy loading of an {@link Roo.tree.TreeNode}'s child
33469  * nodes from a specified URL. The response must be a javascript Array definition
33470  * who's elements are node definition objects. eg:
33471  * <pre><code>
33472 {  success : true,
33473    data :      [
33474    
33475     { 'id': 1, 'text': 'A folder Node', 'leaf': false },
33476     { 'id': 2, 'text': 'A leaf Node', 'leaf': true }
33477     ]
33478 }
33479
33480
33481 </code></pre>
33482  * <br><br>
33483  * The old style respose with just an array is still supported, but not recommended.
33484  * <br><br>
33485  *
33486  * A server request is sent, and child nodes are loaded only when a node is expanded.
33487  * The loading node's id is passed to the server under the parameter name "node" to
33488  * enable the server to produce the correct child nodes.
33489  * <br><br>
33490  * To pass extra parameters, an event handler may be attached to the "beforeload"
33491  * event, and the parameters specified in the TreeLoader's baseParams property:
33492  * <pre><code>
33493     myTreeLoader.on("beforeload", function(treeLoader, node) {
33494         this.baseParams.category = node.attributes.category;
33495     }, this);
33496 </code></pre><
33497  * This would pass an HTTP parameter called "category" to the server containing
33498  * the value of the Node's "category" attribute.
33499  * @constructor
33500  * Creates a new Treeloader.
33501  * @param {Object} config A config object containing config properties.
33502  */
33503 Roo.tree.TreeLoader = function(config){
33504     this.baseParams = {};
33505     this.requestMethod = "POST";
33506     Roo.apply(this, config);
33507
33508     this.addEvents({
33509     
33510         /**
33511          * @event beforeload
33512          * Fires before a network request is made to retrieve the Json text which specifies a node's children.
33513          * @param {Object} This TreeLoader object.
33514          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
33515          * @param {Object} callback The callback function specified in the {@link #load} call.
33516          */
33517         beforeload : true,
33518         /**
33519          * @event load
33520          * Fires when the node has been successfuly loaded.
33521          * @param {Object} This TreeLoader object.
33522          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
33523          * @param {Object} response The response object containing the data from the server.
33524          */
33525         load : true,
33526         /**
33527          * @event loadexception
33528          * Fires if the network request failed.
33529          * @param {Object} This TreeLoader object.
33530          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
33531          * @param {Object} response The response object containing the data from the server.
33532          */
33533         loadexception : true,
33534         /**
33535          * @event create
33536          * Fires before a node is created, enabling you to return custom Node types 
33537          * @param {Object} This TreeLoader object.
33538          * @param {Object} attr - the data returned from the AJAX call (modify it to suit)
33539          */
33540         create : true
33541     });
33542
33543     Roo.tree.TreeLoader.superclass.constructor.call(this);
33544 };
33545
33546 Roo.extend(Roo.tree.TreeLoader, Roo.util.Observable, {
33547     /**
33548     * @cfg {String} dataUrl The URL from which to request a Json string which
33549     * specifies an array of node definition object representing the child nodes
33550     * to be loaded.
33551     */
33552     /**
33553     * @cfg {String} requestMethod either GET or POST
33554     * defaults to POST (due to BC)
33555     * to be loaded.
33556     */
33557     /**
33558     * @cfg {Object} baseParams (optional) An object containing properties which
33559     * specify HTTP parameters to be passed to each request for child nodes.
33560     */
33561     /**
33562     * @cfg {Object} baseAttrs (optional) An object containing attributes to be added to all nodes
33563     * created by this loader. If the attributes sent by the server have an attribute in this object,
33564     * they take priority.
33565     */
33566     /**
33567     * @cfg {Object} uiProviders (optional) An object containing properties which
33568     * 
33569     * DEPRECATED - use 'create' event handler to modify attributes - which affect creation.
33570     * specify custom {@link Roo.tree.TreeNodeUI} implementations. If the optional
33571     * <i>uiProvider</i> attribute of a returned child node is a string rather
33572     * than a reference to a TreeNodeUI implementation, this that string value
33573     * is used as a property name in the uiProviders object. You can define the provider named
33574     * 'default' , and this will be used for all nodes (if no uiProvider is delivered by the node data)
33575     */
33576     uiProviders : {},
33577
33578     /**
33579     * @cfg {Boolean} clearOnLoad (optional) Default to true. Remove previously existing
33580     * child nodes before loading.
33581     */
33582     clearOnLoad : true,
33583
33584     /**
33585     * @cfg {String} root (optional) Default to false. Use this to read data from an object 
33586     * property on loading, rather than expecting an array. (eg. more compatible to a standard
33587     * Grid query { data : [ .....] }
33588     */
33589     
33590     root : false,
33591      /**
33592     * @cfg {String} queryParam (optional) 
33593     * Name of the query as it will be passed on the querystring (defaults to 'node')
33594     * eg. the request will be ?node=[id]
33595     */
33596     
33597     
33598     queryParam: false,
33599     
33600     /**
33601      * Load an {@link Roo.tree.TreeNode} from the URL specified in the constructor.
33602      * This is called automatically when a node is expanded, but may be used to reload
33603      * a node (or append new children if the {@link #clearOnLoad} option is false.)
33604      * @param {Roo.tree.TreeNode} node
33605      * @param {Function} callback
33606      */
33607     load : function(node, callback){
33608         if(this.clearOnLoad){
33609             while(node.firstChild){
33610                 node.removeChild(node.firstChild);
33611             }
33612         }
33613         if(node.attributes.children){ // preloaded json children
33614             var cs = node.attributes.children;
33615             for(var i = 0, len = cs.length; i < len; i++){
33616                 node.appendChild(this.createNode(cs[i]));
33617             }
33618             if(typeof callback == "function"){
33619                 callback();
33620             }
33621         }else if(this.dataUrl){
33622             this.requestData(node, callback);
33623         }
33624     },
33625
33626     getParams: function(node){
33627         var buf = [], bp = this.baseParams;
33628         for(var key in bp){
33629             if(typeof bp[key] != "function"){
33630                 buf.push(encodeURIComponent(key), "=", encodeURIComponent(bp[key]), "&");
33631             }
33632         }
33633         var n = this.queryParam === false ? 'node' : this.queryParam;
33634         buf.push(n + "=", encodeURIComponent(node.id));
33635         return buf.join("");
33636     },
33637
33638     requestData : function(node, callback){
33639         if(this.fireEvent("beforeload", this, node, callback) !== false){
33640             this.transId = Roo.Ajax.request({
33641                 method:this.requestMethod,
33642                 url: this.dataUrl||this.url,
33643                 success: this.handleResponse,
33644                 failure: this.handleFailure,
33645                 scope: this,
33646                 argument: {callback: callback, node: node},
33647                 params: this.getParams(node)
33648             });
33649         }else{
33650             // if the load is cancelled, make sure we notify
33651             // the node that we are done
33652             if(typeof callback == "function"){
33653                 callback();
33654             }
33655         }
33656     },
33657
33658     isLoading : function(){
33659         return this.transId ? true : false;
33660     },
33661
33662     abort : function(){
33663         if(this.isLoading()){
33664             Roo.Ajax.abort(this.transId);
33665         }
33666     },
33667
33668     // private
33669     createNode : function(attr)
33670     {
33671         // apply baseAttrs, nice idea Corey!
33672         if(this.baseAttrs){
33673             Roo.applyIf(attr, this.baseAttrs);
33674         }
33675         if(this.applyLoader !== false){
33676             attr.loader = this;
33677         }
33678         // uiProvider = depreciated..
33679         
33680         if(typeof(attr.uiProvider) == 'string'){
33681            attr.uiProvider = this.uiProviders[attr.uiProvider] || 
33682                 /**  eval:var:attr */ eval(attr.uiProvider);
33683         }
33684         if(typeof(this.uiProviders['default']) != 'undefined') {
33685             attr.uiProvider = this.uiProviders['default'];
33686         }
33687         
33688         this.fireEvent('create', this, attr);
33689         
33690         attr.leaf  = typeof(attr.leaf) == 'string' ? attr.leaf * 1 : attr.leaf;
33691         return(attr.leaf ?
33692                         new Roo.tree.TreeNode(attr) :
33693                         new Roo.tree.AsyncTreeNode(attr));
33694     },
33695
33696     processResponse : function(response, node, callback)
33697     {
33698         var json = response.responseText;
33699         try {
33700             
33701             var o = Roo.decode(json);
33702             
33703             if (this.root === false && typeof(o.success) != undefined) {
33704                 this.root = 'data'; // the default behaviour for list like data..
33705                 }
33706                 
33707             if (this.root !== false &&  !o.success) {
33708                 // it's a failure condition.
33709                 var a = response.argument;
33710                 this.fireEvent("loadexception", this, a.node, response);
33711                 Roo.log("Load failed - should have a handler really");
33712                 return;
33713             }
33714             
33715             
33716             
33717             if (this.root !== false) {
33718                  o = o[this.root];
33719             }
33720             
33721             for(var i = 0, len = o.length; i < len; i++){
33722                 var n = this.createNode(o[i]);
33723                 if(n){
33724                     node.appendChild(n);
33725                 }
33726             }
33727             if(typeof callback == "function"){
33728                 callback(this, node);
33729             }
33730         }catch(e){
33731             this.handleFailure(response);
33732         }
33733     },
33734
33735     handleResponse : function(response){
33736         this.transId = false;
33737         var a = response.argument;
33738         this.processResponse(response, a.node, a.callback);
33739         this.fireEvent("load", this, a.node, response);
33740     },
33741
33742     handleFailure : function(response)
33743     {
33744         // should handle failure better..
33745         this.transId = false;
33746         var a = response.argument;
33747         this.fireEvent("loadexception", this, a.node, response);
33748         if(typeof a.callback == "function"){
33749             a.callback(this, a.node);
33750         }
33751     }
33752 });/*
33753  * Based on:
33754  * Ext JS Library 1.1.1
33755  * Copyright(c) 2006-2007, Ext JS, LLC.
33756  *
33757  * Originally Released Under LGPL - original licence link has changed is not relivant.
33758  *
33759  * Fork - LGPL
33760  * <script type="text/javascript">
33761  */
33762
33763 /**
33764 * @class Roo.tree.TreeFilter
33765 * Note this class is experimental and doesn't update the indent (lines) or expand collapse icons of the nodes
33766 * @param {TreePanel} tree
33767 * @param {Object} config (optional)
33768  */
33769 Roo.tree.TreeFilter = function(tree, config){
33770     this.tree = tree;
33771     this.filtered = {};
33772     Roo.apply(this, config);
33773 };
33774
33775 Roo.tree.TreeFilter.prototype = {
33776     clearBlank:false,
33777     reverse:false,
33778     autoClear:false,
33779     remove:false,
33780
33781      /**
33782      * Filter the data by a specific attribute.
33783      * @param {String/RegExp} value Either string that the attribute value
33784      * should start with or a RegExp to test against the attribute
33785      * @param {String} attr (optional) The attribute passed in your node's attributes collection. Defaults to "text".
33786      * @param {TreeNode} startNode (optional) The node to start the filter at.
33787      */
33788     filter : function(value, attr, startNode){
33789         attr = attr || "text";
33790         var f;
33791         if(typeof value == "string"){
33792             var vlen = value.length;
33793             // auto clear empty filter
33794             if(vlen == 0 && this.clearBlank){
33795                 this.clear();
33796                 return;
33797             }
33798             value = value.toLowerCase();
33799             f = function(n){
33800                 return n.attributes[attr].substr(0, vlen).toLowerCase() == value;
33801             };
33802         }else if(value.exec){ // regex?
33803             f = function(n){
33804                 return value.test(n.attributes[attr]);
33805             };
33806         }else{
33807             throw 'Illegal filter type, must be string or regex';
33808         }
33809         this.filterBy(f, null, startNode);
33810         },
33811
33812     /**
33813      * Filter by a function. The passed function will be called with each
33814      * node in the tree (or from the startNode). If the function returns true, the node is kept
33815      * otherwise it is filtered. If a node is filtered, its children are also filtered.
33816      * @param {Function} fn The filter function
33817      * @param {Object} scope (optional) The scope of the function (defaults to the current node)
33818      */
33819     filterBy : function(fn, scope, startNode){
33820         startNode = startNode || this.tree.root;
33821         if(this.autoClear){
33822             this.clear();
33823         }
33824         var af = this.filtered, rv = this.reverse;
33825         var f = function(n){
33826             if(n == startNode){
33827                 return true;
33828             }
33829             if(af[n.id]){
33830                 return false;
33831             }
33832             var m = fn.call(scope || n, n);
33833             if(!m || rv){
33834                 af[n.id] = n;
33835                 n.ui.hide();
33836                 return false;
33837             }
33838             return true;
33839         };
33840         startNode.cascade(f);
33841         if(this.remove){
33842            for(var id in af){
33843                if(typeof id != "function"){
33844                    var n = af[id];
33845                    if(n && n.parentNode){
33846                        n.parentNode.removeChild(n);
33847                    }
33848                }
33849            }
33850         }
33851     },
33852
33853     /**
33854      * Clears the current filter. Note: with the "remove" option
33855      * set a filter cannot be cleared.
33856      */
33857     clear : function(){
33858         var t = this.tree;
33859         var af = this.filtered;
33860         for(var id in af){
33861             if(typeof id != "function"){
33862                 var n = af[id];
33863                 if(n){
33864                     n.ui.show();
33865                 }
33866             }
33867         }
33868         this.filtered = {};
33869     }
33870 };
33871 /*
33872  * Based on:
33873  * Ext JS Library 1.1.1
33874  * Copyright(c) 2006-2007, Ext JS, LLC.
33875  *
33876  * Originally Released Under LGPL - original licence link has changed is not relivant.
33877  *
33878  * Fork - LGPL
33879  * <script type="text/javascript">
33880  */
33881  
33882
33883 /**
33884  * @class Roo.tree.TreeSorter
33885  * Provides sorting of nodes in a TreePanel
33886  * 
33887  * @cfg {Boolean} folderSort True to sort leaf nodes under non leaf nodes
33888  * @cfg {String} property The named attribute on the node to sort by (defaults to text)
33889  * @cfg {String} dir The direction to sort (asc or desc) (defaults to asc)
33890  * @cfg {String} leafAttr The attribute used to determine leaf nodes in folder sort (defaults to "leaf")
33891  * @cfg {Boolean} caseSensitive true for case sensitive sort (defaults to false)
33892  * @cfg {Function} sortType A custom "casting" function used to convert node values before sorting
33893  * @constructor
33894  * @param {TreePanel} tree
33895  * @param {Object} config
33896  */
33897 Roo.tree.TreeSorter = function(tree, config){
33898     Roo.apply(this, config);
33899     tree.on("beforechildrenrendered", this.doSort, this);
33900     tree.on("append", this.updateSort, this);
33901     tree.on("insert", this.updateSort, this);
33902     
33903     var dsc = this.dir && this.dir.toLowerCase() == "desc";
33904     var p = this.property || "text";
33905     var sortType = this.sortType;
33906     var fs = this.folderSort;
33907     var cs = this.caseSensitive === true;
33908     var leafAttr = this.leafAttr || 'leaf';
33909
33910     this.sortFn = function(n1, n2){
33911         if(fs){
33912             if(n1.attributes[leafAttr] && !n2.attributes[leafAttr]){
33913                 return 1;
33914             }
33915             if(!n1.attributes[leafAttr] && n2.attributes[leafAttr]){
33916                 return -1;
33917             }
33918         }
33919         var v1 = sortType ? sortType(n1) : (cs ? n1.attributes[p] : n1.attributes[p].toUpperCase());
33920         var v2 = sortType ? sortType(n2) : (cs ? n2.attributes[p] : n2.attributes[p].toUpperCase());
33921         if(v1 < v2){
33922                         return dsc ? +1 : -1;
33923                 }else if(v1 > v2){
33924                         return dsc ? -1 : +1;
33925         }else{
33926                 return 0;
33927         }
33928     };
33929 };
33930
33931 Roo.tree.TreeSorter.prototype = {
33932     doSort : function(node){
33933         node.sort(this.sortFn);
33934     },
33935     
33936     compareNodes : function(n1, n2){
33937         return (n1.text.toUpperCase() > n2.text.toUpperCase() ? 1 : -1);
33938     },
33939     
33940     updateSort : function(tree, node){
33941         if(node.childrenRendered){
33942             this.doSort.defer(1, this, [node]);
33943         }
33944     }
33945 };/*
33946  * Based on:
33947  * Ext JS Library 1.1.1
33948  * Copyright(c) 2006-2007, Ext JS, LLC.
33949  *
33950  * Originally Released Under LGPL - original licence link has changed is not relivant.
33951  *
33952  * Fork - LGPL
33953  * <script type="text/javascript">
33954  */
33955
33956 if(Roo.dd.DropZone){
33957     
33958 Roo.tree.TreeDropZone = function(tree, config){
33959     this.allowParentInsert = false;
33960     this.allowContainerDrop = false;
33961     this.appendOnly = false;
33962     Roo.tree.TreeDropZone.superclass.constructor.call(this, tree.innerCt, config);
33963     this.tree = tree;
33964     this.lastInsertClass = "x-tree-no-status";
33965     this.dragOverData = {};
33966 };
33967
33968 Roo.extend(Roo.tree.TreeDropZone, Roo.dd.DropZone, {
33969     ddGroup : "TreeDD",
33970     scroll:  true,
33971     
33972     expandDelay : 1000,
33973     
33974     expandNode : function(node){
33975         if(node.hasChildNodes() && !node.isExpanded()){
33976             node.expand(false, null, this.triggerCacheRefresh.createDelegate(this));
33977         }
33978     },
33979     
33980     queueExpand : function(node){
33981         this.expandProcId = this.expandNode.defer(this.expandDelay, this, [node]);
33982     },
33983     
33984     cancelExpand : function(){
33985         if(this.expandProcId){
33986             clearTimeout(this.expandProcId);
33987             this.expandProcId = false;
33988         }
33989     },
33990     
33991     isValidDropPoint : function(n, pt, dd, e, data){
33992         if(!n || !data){ return false; }
33993         var targetNode = n.node;
33994         var dropNode = data.node;
33995         // default drop rules
33996         if(!(targetNode && targetNode.isTarget && pt)){
33997             return false;
33998         }
33999         if(pt == "append" && targetNode.allowChildren === false){
34000             return false;
34001         }
34002         if((pt == "above" || pt == "below") && (targetNode.parentNode && targetNode.parentNode.allowChildren === false)){
34003             return false;
34004         }
34005         if(dropNode && (targetNode == dropNode || dropNode.contains(targetNode))){
34006             return false;
34007         }
34008         // reuse the object
34009         var overEvent = this.dragOverData;
34010         overEvent.tree = this.tree;
34011         overEvent.target = targetNode;
34012         overEvent.data = data;
34013         overEvent.point = pt;
34014         overEvent.source = dd;
34015         overEvent.rawEvent = e;
34016         overEvent.dropNode = dropNode;
34017         overEvent.cancel = false;  
34018         var result = this.tree.fireEvent("nodedragover", overEvent);
34019         return overEvent.cancel === false && result !== false;
34020     },
34021     
34022     getDropPoint : function(e, n, dd)
34023     {
34024         var tn = n.node;
34025         if(tn.isRoot){
34026             return tn.allowChildren !== false ? "append" : false; // always append for root
34027         }
34028         var dragEl = n.ddel;
34029         var t = Roo.lib.Dom.getY(dragEl), b = t + dragEl.offsetHeight;
34030         var y = Roo.lib.Event.getPageY(e);
34031         //var noAppend = tn.allowChildren === false || tn.isLeaf();
34032         
34033         // we may drop nodes anywhere, as long as allowChildren has not been set to false..
34034         var noAppend = tn.allowChildren === false;
34035         if(this.appendOnly || tn.parentNode.allowChildren === false){
34036             return noAppend ? false : "append";
34037         }
34038         var noBelow = false;
34039         if(!this.allowParentInsert){
34040             noBelow = tn.hasChildNodes() && tn.isExpanded();
34041         }
34042         var q = (b - t) / (noAppend ? 2 : 3);
34043         if(y >= t && y < (t + q)){
34044             return "above";
34045         }else if(!noBelow && (noAppend || y >= b-q && y <= b)){
34046             return "below";
34047         }else{
34048             return "append";
34049         }
34050     },
34051     
34052     onNodeEnter : function(n, dd, e, data)
34053     {
34054         this.cancelExpand();
34055     },
34056     
34057     onNodeOver : function(n, dd, e, data)
34058     {
34059        
34060         var pt = this.getDropPoint(e, n, dd);
34061         var node = n.node;
34062         
34063         // auto node expand check
34064         if(!this.expandProcId && pt == "append" && node.hasChildNodes() && !n.node.isExpanded()){
34065             this.queueExpand(node);
34066         }else if(pt != "append"){
34067             this.cancelExpand();
34068         }
34069         
34070         // set the insert point style on the target node
34071         var returnCls = this.dropNotAllowed;
34072         if(this.isValidDropPoint(n, pt, dd, e, data)){
34073            if(pt){
34074                var el = n.ddel;
34075                var cls;
34076                if(pt == "above"){
34077                    returnCls = n.node.isFirst() ? "x-tree-drop-ok-above" : "x-tree-drop-ok-between";
34078                    cls = "x-tree-drag-insert-above";
34079                }else if(pt == "below"){
34080                    returnCls = n.node.isLast() ? "x-tree-drop-ok-below" : "x-tree-drop-ok-between";
34081                    cls = "x-tree-drag-insert-below";
34082                }else{
34083                    returnCls = "x-tree-drop-ok-append";
34084                    cls = "x-tree-drag-append";
34085                }
34086                if(this.lastInsertClass != cls){
34087                    Roo.fly(el).replaceClass(this.lastInsertClass, cls);
34088                    this.lastInsertClass = cls;
34089                }
34090            }
34091        }
34092        return returnCls;
34093     },
34094     
34095     onNodeOut : function(n, dd, e, data){
34096         
34097         this.cancelExpand();
34098         this.removeDropIndicators(n);
34099     },
34100     
34101     onNodeDrop : function(n, dd, e, data){
34102         var point = this.getDropPoint(e, n, dd);
34103         var targetNode = n.node;
34104         targetNode.ui.startDrop();
34105         if(!this.isValidDropPoint(n, point, dd, e, data)){
34106             targetNode.ui.endDrop();
34107             return false;
34108         }
34109         // first try to find the drop node
34110         var dropNode = data.node || (dd.getTreeNode ? dd.getTreeNode(data, targetNode, point, e) : null);
34111         var dropEvent = {
34112             tree : this.tree,
34113             target: targetNode,
34114             data: data,
34115             point: point,
34116             source: dd,
34117             rawEvent: e,
34118             dropNode: dropNode,
34119             cancel: !dropNode   
34120         };
34121         var retval = this.tree.fireEvent("beforenodedrop", dropEvent);
34122         if(retval === false || dropEvent.cancel === true || !dropEvent.dropNode){
34123             targetNode.ui.endDrop();
34124             return false;
34125         }
34126         // allow target changing
34127         targetNode = dropEvent.target;
34128         if(point == "append" && !targetNode.isExpanded()){
34129             targetNode.expand(false, null, function(){
34130                 this.completeDrop(dropEvent);
34131             }.createDelegate(this));
34132         }else{
34133             this.completeDrop(dropEvent);
34134         }
34135         return true;
34136     },
34137     
34138     completeDrop : function(de){
34139         var ns = de.dropNode, p = de.point, t = de.target;
34140         if(!(ns instanceof Array)){
34141             ns = [ns];
34142         }
34143         var n;
34144         for(var i = 0, len = ns.length; i < len; i++){
34145             n = ns[i];
34146             if(p == "above"){
34147                 t.parentNode.insertBefore(n, t);
34148             }else if(p == "below"){
34149                 t.parentNode.insertBefore(n, t.nextSibling);
34150             }else{
34151                 t.appendChild(n);
34152             }
34153         }
34154         n.ui.focus();
34155         if(this.tree.hlDrop){
34156             n.ui.highlight();
34157         }
34158         t.ui.endDrop();
34159         this.tree.fireEvent("nodedrop", de);
34160     },
34161     
34162     afterNodeMoved : function(dd, data, e, targetNode, dropNode){
34163         if(this.tree.hlDrop){
34164             dropNode.ui.focus();
34165             dropNode.ui.highlight();
34166         }
34167         this.tree.fireEvent("nodedrop", this.tree, targetNode, data, dd, e);
34168     },
34169     
34170     getTree : function(){
34171         return this.tree;
34172     },
34173     
34174     removeDropIndicators : function(n){
34175         if(n && n.ddel){
34176             var el = n.ddel;
34177             Roo.fly(el).removeClass([
34178                     "x-tree-drag-insert-above",
34179                     "x-tree-drag-insert-below",
34180                     "x-tree-drag-append"]);
34181             this.lastInsertClass = "_noclass";
34182         }
34183     },
34184     
34185     beforeDragDrop : function(target, e, id){
34186         this.cancelExpand();
34187         return true;
34188     },
34189     
34190     afterRepair : function(data){
34191         if(data && Roo.enableFx){
34192             data.node.ui.highlight();
34193         }
34194         this.hideProxy();
34195     } 
34196     
34197 });
34198
34199 }
34200 /*
34201  * Based on:
34202  * Ext JS Library 1.1.1
34203  * Copyright(c) 2006-2007, Ext JS, LLC.
34204  *
34205  * Originally Released Under LGPL - original licence link has changed is not relivant.
34206  *
34207  * Fork - LGPL
34208  * <script type="text/javascript">
34209  */
34210  
34211
34212 if(Roo.dd.DragZone){
34213 Roo.tree.TreeDragZone = function(tree, config){
34214     Roo.tree.TreeDragZone.superclass.constructor.call(this, tree.getTreeEl(), config);
34215     this.tree = tree;
34216 };
34217
34218 Roo.extend(Roo.tree.TreeDragZone, Roo.dd.DragZone, {
34219     ddGroup : "TreeDD",
34220    
34221     onBeforeDrag : function(data, e){
34222         var n = data.node;
34223         return n && n.draggable && !n.disabled;
34224     },
34225      
34226     
34227     onInitDrag : function(e){
34228         var data = this.dragData;
34229         this.tree.getSelectionModel().select(data.node);
34230         this.proxy.update("");
34231         data.node.ui.appendDDGhost(this.proxy.ghost.dom);
34232         this.tree.fireEvent("startdrag", this.tree, data.node, e);
34233     },
34234     
34235     getRepairXY : function(e, data){
34236         return data.node.ui.getDDRepairXY();
34237     },
34238     
34239     onEndDrag : function(data, e){
34240         this.tree.fireEvent("enddrag", this.tree, data.node, e);
34241         
34242         
34243     },
34244     
34245     onValidDrop : function(dd, e, id){
34246         this.tree.fireEvent("dragdrop", this.tree, this.dragData.node, dd, e);
34247         this.hideProxy();
34248     },
34249     
34250     beforeInvalidDrop : function(e, id){
34251         // this scrolls the original position back into view
34252         var sm = this.tree.getSelectionModel();
34253         sm.clearSelections();
34254         sm.select(this.dragData.node);
34255     }
34256 });
34257 }/*
34258  * Based on:
34259  * Ext JS Library 1.1.1
34260  * Copyright(c) 2006-2007, Ext JS, LLC.
34261  *
34262  * Originally Released Under LGPL - original licence link has changed is not relivant.
34263  *
34264  * Fork - LGPL
34265  * <script type="text/javascript">
34266  */
34267 /**
34268  * @class Roo.tree.TreeEditor
34269  * @extends Roo.Editor
34270  * Provides editor functionality for inline tree node editing.  Any valid {@link Roo.form.Field} can be used
34271  * as the editor field.
34272  * @constructor
34273  * @param {Object} config (used to be the tree panel.)
34274  * @param {Object} oldconfig DEPRECIATED Either a prebuilt {@link Roo.form.Field} instance or a Field config object
34275  * 
34276  * @cfg {Roo.tree.TreePanel} tree The tree to bind to.
34277  * @cfg {Roo.form.TextField|Object} field The field configuration
34278  *
34279  * 
34280  */
34281 Roo.tree.TreeEditor = function(config, oldconfig) { // was -- (tree, config){
34282     var tree = config;
34283     var field;
34284     if (oldconfig) { // old style..
34285         field = oldconfig.events ? oldconfig : new Roo.form.TextField(oldconfig);
34286     } else {
34287         // new style..
34288         tree = config.tree;
34289         config.field = config.field  || {};
34290         config.field.xtype = 'TextField';
34291         field = Roo.factory(config.field, Roo.form);
34292     }
34293     config = config || {};
34294     
34295     
34296     this.addEvents({
34297         /**
34298          * @event beforenodeedit
34299          * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
34300          * false from the handler of this event.
34301          * @param {Editor} this
34302          * @param {Roo.tree.Node} node 
34303          */
34304         "beforenodeedit" : true
34305     });
34306     
34307     //Roo.log(config);
34308     Roo.tree.TreeEditor.superclass.constructor.call(this, field, config);
34309
34310     this.tree = tree;
34311
34312     tree.on('beforeclick', this.beforeNodeClick, this);
34313     tree.getTreeEl().on('mousedown', this.hide, this);
34314     this.on('complete', this.updateNode, this);
34315     this.on('beforestartedit', this.fitToTree, this);
34316     this.on('startedit', this.bindScroll, this, {delay:10});
34317     this.on('specialkey', this.onSpecialKey, this);
34318 };
34319
34320 Roo.extend(Roo.tree.TreeEditor, Roo.Editor, {
34321     /**
34322      * @cfg {String} alignment
34323      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "l-l").
34324      */
34325     alignment: "l-l",
34326     // inherit
34327     autoSize: false,
34328     /**
34329      * @cfg {Boolean} hideEl
34330      * True to hide the bound element while the editor is displayed (defaults to false)
34331      */
34332     hideEl : false,
34333     /**
34334      * @cfg {String} cls
34335      * CSS class to apply to the editor (defaults to "x-small-editor x-tree-editor")
34336      */
34337     cls: "x-small-editor x-tree-editor",
34338     /**
34339      * @cfg {Boolean} shim
34340      * True to shim the editor if selects/iframes could be displayed beneath it (defaults to false)
34341      */
34342     shim:false,
34343     // inherit
34344     shadow:"frame",
34345     /**
34346      * @cfg {Number} maxWidth
34347      * The maximum width in pixels of the editor field (defaults to 250).  Note that if the maxWidth would exceed
34348      * the containing tree element's size, it will be automatically limited for you to the container width, taking
34349      * scroll and client offsets into account prior to each edit.
34350      */
34351     maxWidth: 250,
34352
34353     editDelay : 350,
34354
34355     // private
34356     fitToTree : function(ed, el){
34357         var td = this.tree.getTreeEl().dom, nd = el.dom;
34358         if(td.scrollLeft >  nd.offsetLeft){ // ensure the node left point is visible
34359             td.scrollLeft = nd.offsetLeft;
34360         }
34361         var w = Math.min(
34362                 this.maxWidth,
34363                 (td.clientWidth > 20 ? td.clientWidth : td.offsetWidth) - Math.max(0, nd.offsetLeft-td.scrollLeft) - /*cushion*/5);
34364         this.setSize(w, '');
34365         
34366         return this.fireEvent('beforenodeedit', this, this.editNode);
34367         
34368     },
34369
34370     // private
34371     triggerEdit : function(node){
34372         this.completeEdit();
34373         this.editNode = node;
34374         this.startEdit(node.ui.textNode, node.text);
34375     },
34376
34377     // private
34378     bindScroll : function(){
34379         this.tree.getTreeEl().on('scroll', this.cancelEdit, this);
34380     },
34381
34382     // private
34383     beforeNodeClick : function(node, e){
34384         var sinceLast = (this.lastClick ? this.lastClick.getElapsed() : 0);
34385         this.lastClick = new Date();
34386         if(sinceLast > this.editDelay && this.tree.getSelectionModel().isSelected(node)){
34387             e.stopEvent();
34388             this.triggerEdit(node);
34389             return false;
34390         }
34391         return true;
34392     },
34393
34394     // private
34395     updateNode : function(ed, value){
34396         this.tree.getTreeEl().un('scroll', this.cancelEdit, this);
34397         this.editNode.setText(value);
34398     },
34399
34400     // private
34401     onHide : function(){
34402         Roo.tree.TreeEditor.superclass.onHide.call(this);
34403         if(this.editNode){
34404             this.editNode.ui.focus();
34405         }
34406     },
34407
34408     // private
34409     onSpecialKey : function(field, e){
34410         var k = e.getKey();
34411         if(k == e.ESC){
34412             e.stopEvent();
34413             this.cancelEdit();
34414         }else if(k == e.ENTER && !e.hasModifier()){
34415             e.stopEvent();
34416             this.completeEdit();
34417         }
34418     }
34419 });//<Script type="text/javascript">
34420 /*
34421  * Based on:
34422  * Ext JS Library 1.1.1
34423  * Copyright(c) 2006-2007, Ext JS, LLC.
34424  *
34425  * Originally Released Under LGPL - original licence link has changed is not relivant.
34426  *
34427  * Fork - LGPL
34428  * <script type="text/javascript">
34429  */
34430  
34431 /**
34432  * Not documented??? - probably should be...
34433  */
34434
34435 Roo.tree.ColumnNodeUI = Roo.extend(Roo.tree.TreeNodeUI, {
34436     //focus: Roo.emptyFn, // prevent odd scrolling behavior
34437     
34438     renderElements : function(n, a, targetNode, bulkRender){
34439         //consel.log("renderElements?");
34440         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
34441
34442         var t = n.getOwnerTree();
34443         var tid = Pman.Tab.Document_TypesTree.tree.el.id;
34444         
34445         var cols = t.columns;
34446         var bw = t.borderWidth;
34447         var c = cols[0];
34448         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
34449          var cb = typeof a.checked == "boolean";
34450         var tx = String.format('{0}',n.text || (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
34451         var colcls = 'x-t-' + tid + '-c0';
34452         var buf = [
34453             '<li class="x-tree-node">',
34454             
34455                 
34456                 '<div class="x-tree-node-el ', a.cls,'">',
34457                     // extran...
34458                     '<div class="x-tree-col ', colcls, '" style="width:', c.width-bw, 'px;">',
34459                 
34460                 
34461                         '<span class="x-tree-node-indent">',this.indentMarkup,'</span>',
34462                         '<img src="', this.emptyIcon, '" class="x-tree-ec-icon  " />',
34463                         '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',
34464                            (a.icon ? ' x-tree-node-inline-icon' : ''),
34465                            (a.iconCls ? ' '+a.iconCls : ''),
34466                            '" unselectable="on" />',
34467                         (cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + 
34468                              (a.checked ? 'checked="checked" />' : ' />')) : ''),
34469                              
34470                         '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
34471                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>',
34472                             '<span unselectable="on" qtip="' + tx + '">',
34473                              tx,
34474                              '</span></a>' ,
34475                     '</div>',
34476                      '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
34477                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>'
34478                  ];
34479         for(var i = 1, len = cols.length; i < len; i++){
34480             c = cols[i];
34481             colcls = 'x-t-' + tid + '-c' +i;
34482             tx = String.format('{0}', (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
34483             buf.push('<div class="x-tree-col ', colcls, ' ' ,(c.cls?c.cls:''),'" style="width:',c.width-bw,'px;">',
34484                         '<div class="x-tree-col-text" qtip="' + tx +'">',tx,"</div>",
34485                       "</div>");
34486          }
34487          
34488          buf.push(
34489             '</a>',
34490             '<div class="x-clear"></div></div>',
34491             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
34492             "</li>");
34493         
34494         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
34495             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
34496                                 n.nextSibling.ui.getEl(), buf.join(""));
34497         }else{
34498             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
34499         }
34500         var el = this.wrap.firstChild;
34501         this.elRow = el;
34502         this.elNode = el.firstChild;
34503         this.ranchor = el.childNodes[1];
34504         this.ctNode = this.wrap.childNodes[1];
34505         var cs = el.firstChild.childNodes;
34506         this.indentNode = cs[0];
34507         this.ecNode = cs[1];
34508         this.iconNode = cs[2];
34509         var index = 3;
34510         if(cb){
34511             this.checkbox = cs[3];
34512             index++;
34513         }
34514         this.anchor = cs[index];
34515         
34516         this.textNode = cs[index].firstChild;
34517         
34518         //el.on("click", this.onClick, this);
34519         //el.on("dblclick", this.onDblClick, this);
34520         
34521         
34522        // console.log(this);
34523     },
34524     initEvents : function(){
34525         Roo.tree.ColumnNodeUI.superclass.initEvents.call(this);
34526         
34527             
34528         var a = this.ranchor;
34529
34530         var el = Roo.get(a);
34531
34532         if(Roo.isOpera){ // opera render bug ignores the CSS
34533             el.setStyle("text-decoration", "none");
34534         }
34535
34536         el.on("click", this.onClick, this);
34537         el.on("dblclick", this.onDblClick, this);
34538         el.on("contextmenu", this.onContextMenu, this);
34539         
34540     },
34541     
34542     /*onSelectedChange : function(state){
34543         if(state){
34544             this.focus();
34545             this.addClass("x-tree-selected");
34546         }else{
34547             //this.blur();
34548             this.removeClass("x-tree-selected");
34549         }
34550     },*/
34551     addClass : function(cls){
34552         if(this.elRow){
34553             Roo.fly(this.elRow).addClass(cls);
34554         }
34555         
34556     },
34557     
34558     
34559     removeClass : function(cls){
34560         if(this.elRow){
34561             Roo.fly(this.elRow).removeClass(cls);
34562         }
34563     }
34564
34565     
34566     
34567 });//<Script type="text/javascript">
34568
34569 /*
34570  * Based on:
34571  * Ext JS Library 1.1.1
34572  * Copyright(c) 2006-2007, Ext JS, LLC.
34573  *
34574  * Originally Released Under LGPL - original licence link has changed is not relivant.
34575  *
34576  * Fork - LGPL
34577  * <script type="text/javascript">
34578  */
34579  
34580
34581 /**
34582  * @class Roo.tree.ColumnTree
34583  * @extends Roo.data.TreePanel
34584  * @cfg {Object} columns  Including width, header, renderer, cls, dataIndex 
34585  * @cfg {int} borderWidth  compined right/left border allowance
34586  * @constructor
34587  * @param {String/HTMLElement/Element} el The container element
34588  * @param {Object} config
34589  */
34590 Roo.tree.ColumnTree =  function(el, config)
34591 {
34592    Roo.tree.ColumnTree.superclass.constructor.call(this, el , config);
34593    this.addEvents({
34594         /**
34595         * @event resize
34596         * Fire this event on a container when it resizes
34597         * @param {int} w Width
34598         * @param {int} h Height
34599         */
34600        "resize" : true
34601     });
34602     this.on('resize', this.onResize, this);
34603 };
34604
34605 Roo.extend(Roo.tree.ColumnTree, Roo.tree.TreePanel, {
34606     //lines:false,
34607     
34608     
34609     borderWidth: Roo.isBorderBox ? 0 : 2, 
34610     headEls : false,
34611     
34612     render : function(){
34613         // add the header.....
34614        
34615         Roo.tree.ColumnTree.superclass.render.apply(this);
34616         
34617         this.el.addClass('x-column-tree');
34618         
34619         this.headers = this.el.createChild(
34620             {cls:'x-tree-headers'},this.innerCt.dom);
34621    
34622         var cols = this.columns, c;
34623         var totalWidth = 0;
34624         this.headEls = [];
34625         var  len = cols.length;
34626         for(var i = 0; i < len; i++){
34627              c = cols[i];
34628              totalWidth += c.width;
34629             this.headEls.push(this.headers.createChild({
34630                  cls:'x-tree-hd ' + (c.cls?c.cls+'-hd':''),
34631                  cn: {
34632                      cls:'x-tree-hd-text',
34633                      html: c.header
34634                  },
34635                  style:'width:'+(c.width-this.borderWidth)+'px;'
34636              }));
34637         }
34638         this.headers.createChild({cls:'x-clear'});
34639         // prevent floats from wrapping when clipped
34640         this.headers.setWidth(totalWidth);
34641         //this.innerCt.setWidth(totalWidth);
34642         this.innerCt.setStyle({ overflow: 'auto' });
34643         this.onResize(this.width, this.height);
34644              
34645         
34646     },
34647     onResize : function(w,h)
34648     {
34649         this.height = h;
34650         this.width = w;
34651         // resize cols..
34652         this.innerCt.setWidth(this.width);
34653         this.innerCt.setHeight(this.height-20);
34654         
34655         // headers...
34656         var cols = this.columns, c;
34657         var totalWidth = 0;
34658         var expEl = false;
34659         var len = cols.length;
34660         for(var i = 0; i < len; i++){
34661             c = cols[i];
34662             if (this.autoExpandColumn !== false && c.dataIndex == this.autoExpandColumn) {
34663                 // it's the expander..
34664                 expEl  = this.headEls[i];
34665                 continue;
34666             }
34667             totalWidth += c.width;
34668             
34669         }
34670         if (expEl) {
34671             expEl.setWidth(  ((w - totalWidth)-this.borderWidth - 20));
34672         }
34673         this.headers.setWidth(w-20);
34674
34675         
34676         
34677         
34678     }
34679 });
34680 /*
34681  * Based on:
34682  * Ext JS Library 1.1.1
34683  * Copyright(c) 2006-2007, Ext JS, LLC.
34684  *
34685  * Originally Released Under LGPL - original licence link has changed is not relivant.
34686  *
34687  * Fork - LGPL
34688  * <script type="text/javascript">
34689  */
34690  
34691 /**
34692  * @class Roo.menu.Menu
34693  * @extends Roo.util.Observable
34694  * A menu object.  This is the container to which you add all other menu items.  Menu can also serve a as a base class
34695  * when you want a specialzed menu based off of another component (like {@link Roo.menu.DateMenu} for example).
34696  * @constructor
34697  * Creates a new Menu
34698  * @param {Object} config Configuration options
34699  */
34700 Roo.menu.Menu = function(config){
34701     Roo.apply(this, config);
34702     this.id = this.id || Roo.id();
34703     this.addEvents({
34704         /**
34705          * @event beforeshow
34706          * Fires before this menu is displayed
34707          * @param {Roo.menu.Menu} this
34708          */
34709         beforeshow : true,
34710         /**
34711          * @event beforehide
34712          * Fires before this menu is hidden
34713          * @param {Roo.menu.Menu} this
34714          */
34715         beforehide : true,
34716         /**
34717          * @event show
34718          * Fires after this menu is displayed
34719          * @param {Roo.menu.Menu} this
34720          */
34721         show : true,
34722         /**
34723          * @event hide
34724          * Fires after this menu is hidden
34725          * @param {Roo.menu.Menu} this
34726          */
34727         hide : true,
34728         /**
34729          * @event click
34730          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
34731          * @param {Roo.menu.Menu} this
34732          * @param {Roo.menu.Item} menuItem The menu item that was clicked
34733          * @param {Roo.EventObject} e
34734          */
34735         click : true,
34736         /**
34737          * @event mouseover
34738          * Fires when the mouse is hovering over this menu
34739          * @param {Roo.menu.Menu} this
34740          * @param {Roo.EventObject} e
34741          * @param {Roo.menu.Item} menuItem The menu item that was clicked
34742          */
34743         mouseover : true,
34744         /**
34745          * @event mouseout
34746          * Fires when the mouse exits this menu
34747          * @param {Roo.menu.Menu} this
34748          * @param {Roo.EventObject} e
34749          * @param {Roo.menu.Item} menuItem The menu item that was clicked
34750          */
34751         mouseout : true,
34752         /**
34753          * @event itemclick
34754          * Fires when a menu item contained in this menu is clicked
34755          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
34756          * @param {Roo.EventObject} e
34757          */
34758         itemclick: true
34759     });
34760     if (this.registerMenu) {
34761         Roo.menu.MenuMgr.register(this);
34762     }
34763     
34764     var mis = this.items;
34765     this.items = new Roo.util.MixedCollection();
34766     if(mis){
34767         this.add.apply(this, mis);
34768     }
34769 };
34770
34771 Roo.extend(Roo.menu.Menu, Roo.util.Observable, {
34772     /**
34773      * @cfg {Number} minWidth The minimum width of the menu in pixels (defaults to 120)
34774      */
34775     minWidth : 120,
34776     /**
34777      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop"
34778      * for bottom-right shadow (defaults to "sides")
34779      */
34780     shadow : "sides",
34781     /**
34782      * @cfg {String} subMenuAlign The {@link Roo.Element#alignTo} anchor position value to use for submenus of
34783      * this menu (defaults to "tl-tr?")
34784      */
34785     subMenuAlign : "tl-tr?",
34786     /**
34787      * @cfg {String} defaultAlign The default {@link Roo.Element#alignTo) anchor position value for this menu
34788      * relative to its element of origin (defaults to "tl-bl?")
34789      */
34790     defaultAlign : "tl-bl?",
34791     /**
34792      * @cfg {Boolean} allowOtherMenus True to allow multiple menus to be displayed at the same time (defaults to false)
34793      */
34794     allowOtherMenus : false,
34795     /**
34796      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
34797      */
34798     registerMenu : true,
34799
34800     hidden:true,
34801
34802     // private
34803     render : function(){
34804         if(this.el){
34805             return;
34806         }
34807         var el = this.el = new Roo.Layer({
34808             cls: "x-menu",
34809             shadow:this.shadow,
34810             constrain: false,
34811             parentEl: this.parentEl || document.body,
34812             zindex:15000
34813         });
34814
34815         this.keyNav = new Roo.menu.MenuNav(this);
34816
34817         if(this.plain){
34818             el.addClass("x-menu-plain");
34819         }
34820         if(this.cls){
34821             el.addClass(this.cls);
34822         }
34823         // generic focus element
34824         this.focusEl = el.createChild({
34825             tag: "a", cls: "x-menu-focus", href: "#", onclick: "return false;", tabIndex:"-1"
34826         });
34827         var ul = el.createChild({tag: "ul", cls: "x-menu-list"});
34828         ul.on("click", this.onClick, this);
34829         ul.on("mouseover", this.onMouseOver, this);
34830         ul.on("mouseout", this.onMouseOut, this);
34831         this.items.each(function(item){
34832             if (item.hidden) {
34833                 return;
34834             }
34835             
34836             var li = document.createElement("li");
34837             li.className = "x-menu-list-item";
34838             ul.dom.appendChild(li);
34839             item.render(li, this);
34840         }, this);
34841         this.ul = ul;
34842         this.autoWidth();
34843     },
34844
34845     // private
34846     autoWidth : function(){
34847         var el = this.el, ul = this.ul;
34848         if(!el){
34849             return;
34850         }
34851         var w = this.width;
34852         if(w){
34853             el.setWidth(w);
34854         }else if(Roo.isIE){
34855             el.setWidth(this.minWidth);
34856             var t = el.dom.offsetWidth; // force recalc
34857             el.setWidth(ul.getWidth()+el.getFrameWidth("lr"));
34858         }
34859     },
34860
34861     // private
34862     delayAutoWidth : function(){
34863         if(this.rendered){
34864             if(!this.awTask){
34865                 this.awTask = new Roo.util.DelayedTask(this.autoWidth, this);
34866             }
34867             this.awTask.delay(20);
34868         }
34869     },
34870
34871     // private
34872     findTargetItem : function(e){
34873         var t = e.getTarget(".x-menu-list-item", this.ul,  true);
34874         if(t && t.menuItemId){
34875             return this.items.get(t.menuItemId);
34876         }
34877     },
34878
34879     // private
34880     onClick : function(e){
34881         var t;
34882         if(t = this.findTargetItem(e)){
34883             t.onClick(e);
34884             this.fireEvent("click", this, t, e);
34885         }
34886     },
34887
34888     // private
34889     setActiveItem : function(item, autoExpand){
34890         if(item != this.activeItem){
34891             if(this.activeItem){
34892                 this.activeItem.deactivate();
34893             }
34894             this.activeItem = item;
34895             item.activate(autoExpand);
34896         }else if(autoExpand){
34897             item.expandMenu();
34898         }
34899     },
34900
34901     // private
34902     tryActivate : function(start, step){
34903         var items = this.items;
34904         for(var i = start, len = items.length; i >= 0 && i < len; i+= step){
34905             var item = items.get(i);
34906             if(!item.disabled && item.canActivate){
34907                 this.setActiveItem(item, false);
34908                 return item;
34909             }
34910         }
34911         return false;
34912     },
34913
34914     // private
34915     onMouseOver : function(e){
34916         var t;
34917         if(t = this.findTargetItem(e)){
34918             if(t.canActivate && !t.disabled){
34919                 this.setActiveItem(t, true);
34920             }
34921         }
34922         this.fireEvent("mouseover", this, e, t);
34923     },
34924
34925     // private
34926     onMouseOut : function(e){
34927         var t;
34928         if(t = this.findTargetItem(e)){
34929             if(t == this.activeItem && t.shouldDeactivate(e)){
34930                 this.activeItem.deactivate();
34931                 delete this.activeItem;
34932             }
34933         }
34934         this.fireEvent("mouseout", this, e, t);
34935     },
34936
34937     /**
34938      * Read-only.  Returns true if the menu is currently displayed, else false.
34939      * @type Boolean
34940      */
34941     isVisible : function(){
34942         return this.el && !this.hidden;
34943     },
34944
34945     /**
34946      * Displays this menu relative to another element
34947      * @param {String/HTMLElement/Roo.Element} element The element to align to
34948      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
34949      * the element (defaults to this.defaultAlign)
34950      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
34951      */
34952     show : function(el, pos, parentMenu){
34953         this.parentMenu = parentMenu;
34954         if(!this.el){
34955             this.render();
34956         }
34957         this.fireEvent("beforeshow", this);
34958         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
34959     },
34960
34961     /**
34962      * Displays this menu at a specific xy position
34963      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
34964      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
34965      */
34966     showAt : function(xy, parentMenu, /* private: */_e){
34967         this.parentMenu = parentMenu;
34968         if(!this.el){
34969             this.render();
34970         }
34971         if(_e !== false){
34972             this.fireEvent("beforeshow", this);
34973             xy = this.el.adjustForConstraints(xy);
34974         }
34975         this.el.setXY(xy);
34976         this.el.show();
34977         this.hidden = false;
34978         this.focus();
34979         this.fireEvent("show", this);
34980     },
34981
34982     focus : function(){
34983         if(!this.hidden){
34984             this.doFocus.defer(50, this);
34985         }
34986     },
34987
34988     doFocus : function(){
34989         if(!this.hidden){
34990             this.focusEl.focus();
34991         }
34992     },
34993
34994     /**
34995      * Hides this menu and optionally all parent menus
34996      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
34997      */
34998     hide : function(deep){
34999         if(this.el && this.isVisible()){
35000             this.fireEvent("beforehide", this);
35001             if(this.activeItem){
35002                 this.activeItem.deactivate();
35003                 this.activeItem = null;
35004             }
35005             this.el.hide();
35006             this.hidden = true;
35007             this.fireEvent("hide", this);
35008         }
35009         if(deep === true && this.parentMenu){
35010             this.parentMenu.hide(true);
35011         }
35012     },
35013
35014     /**
35015      * Addds one or more items of any type supported by the Menu class, or that can be converted into menu items.
35016      * Any of the following are valid:
35017      * <ul>
35018      * <li>Any menu item object based on {@link Roo.menu.Item}</li>
35019      * <li>An HTMLElement object which will be converted to a menu item</li>
35020      * <li>A menu item config object that will be created as a new menu item</li>
35021      * <li>A string, which can either be '-' or 'separator' to add a menu separator, otherwise
35022      * it will be converted into a {@link Roo.menu.TextItem} and added</li>
35023      * </ul>
35024      * Usage:
35025      * <pre><code>
35026 // Create the menu
35027 var menu = new Roo.menu.Menu();
35028
35029 // Create a menu item to add by reference
35030 var menuItem = new Roo.menu.Item({ text: 'New Item!' });
35031
35032 // Add a bunch of items at once using different methods.
35033 // Only the last item added will be returned.
35034 var item = menu.add(
35035     menuItem,                // add existing item by ref
35036     'Dynamic Item',          // new TextItem
35037     '-',                     // new separator
35038     { text: 'Config Item' }  // new item by config
35039 );
35040 </code></pre>
35041      * @param {Mixed} args One or more menu items, menu item configs or other objects that can be converted to menu items
35042      * @return {Roo.menu.Item} The menu item that was added, or the last one if multiple items were added
35043      */
35044     add : function(){
35045         var a = arguments, l = a.length, item;
35046         for(var i = 0; i < l; i++){
35047             var el = a[i];
35048             if ((typeof(el) == "object") && el.xtype && el.xns) {
35049                 el = Roo.factory(el, Roo.menu);
35050             }
35051             
35052             if(el.render){ // some kind of Item
35053                 item = this.addItem(el);
35054             }else if(typeof el == "string"){ // string
35055                 if(el == "separator" || el == "-"){
35056                     item = this.addSeparator();
35057                 }else{
35058                     item = this.addText(el);
35059                 }
35060             }else if(el.tagName || el.el){ // element
35061                 item = this.addElement(el);
35062             }else if(typeof el == "object"){ // must be menu item config?
35063                 item = this.addMenuItem(el);
35064             }
35065         }
35066         return item;
35067     },
35068
35069     /**
35070      * Returns this menu's underlying {@link Roo.Element} object
35071      * @return {Roo.Element} The element
35072      */
35073     getEl : function(){
35074         if(!this.el){
35075             this.render();
35076         }
35077         return this.el;
35078     },
35079
35080     /**
35081      * Adds a separator bar to the menu
35082      * @return {Roo.menu.Item} The menu item that was added
35083      */
35084     addSeparator : function(){
35085         return this.addItem(new Roo.menu.Separator());
35086     },
35087
35088     /**
35089      * Adds an {@link Roo.Element} object to the menu
35090      * @param {String/HTMLElement/Roo.Element} el The element or DOM node to add, or its id
35091      * @return {Roo.menu.Item} The menu item that was added
35092      */
35093     addElement : function(el){
35094         return this.addItem(new Roo.menu.BaseItem(el));
35095     },
35096
35097     /**
35098      * Adds an existing object based on {@link Roo.menu.Item} to the menu
35099      * @param {Roo.menu.Item} item The menu item to add
35100      * @return {Roo.menu.Item} The menu item that was added
35101      */
35102     addItem : function(item){
35103         this.items.add(item);
35104         if(this.ul){
35105             var li = document.createElement("li");
35106             li.className = "x-menu-list-item";
35107             this.ul.dom.appendChild(li);
35108             item.render(li, this);
35109             this.delayAutoWidth();
35110         }
35111         return item;
35112     },
35113
35114     /**
35115      * Creates a new {@link Roo.menu.Item} based an the supplied config object and adds it to the menu
35116      * @param {Object} config A MenuItem config object
35117      * @return {Roo.menu.Item} The menu item that was added
35118      */
35119     addMenuItem : function(config){
35120         if(!(config instanceof Roo.menu.Item)){
35121             if(typeof config.checked == "boolean"){ // must be check menu item config?
35122                 config = new Roo.menu.CheckItem(config);
35123             }else{
35124                 config = new Roo.menu.Item(config);
35125             }
35126         }
35127         return this.addItem(config);
35128     },
35129
35130     /**
35131      * Creates a new {@link Roo.menu.TextItem} with the supplied text and adds it to the menu
35132      * @param {String} text The text to display in the menu item
35133      * @return {Roo.menu.Item} The menu item that was added
35134      */
35135     addText : function(text){
35136         return this.addItem(new Roo.menu.TextItem({ text : text }));
35137     },
35138
35139     /**
35140      * Inserts an existing object based on {@link Roo.menu.Item} to the menu at a specified index
35141      * @param {Number} index The index in the menu's list of current items where the new item should be inserted
35142      * @param {Roo.menu.Item} item The menu item to add
35143      * @return {Roo.menu.Item} The menu item that was added
35144      */
35145     insert : function(index, item){
35146         this.items.insert(index, item);
35147         if(this.ul){
35148             var li = document.createElement("li");
35149             li.className = "x-menu-list-item";
35150             this.ul.dom.insertBefore(li, this.ul.dom.childNodes[index]);
35151             item.render(li, this);
35152             this.delayAutoWidth();
35153         }
35154         return item;
35155     },
35156
35157     /**
35158      * Removes an {@link Roo.menu.Item} from the menu and destroys the object
35159      * @param {Roo.menu.Item} item The menu item to remove
35160      */
35161     remove : function(item){
35162         this.items.removeKey(item.id);
35163         item.destroy();
35164     },
35165
35166     /**
35167      * Removes and destroys all items in the menu
35168      */
35169     removeAll : function(){
35170         var f;
35171         while(f = this.items.first()){
35172             this.remove(f);
35173         }
35174     }
35175 });
35176
35177 // MenuNav is a private utility class used internally by the Menu
35178 Roo.menu.MenuNav = function(menu){
35179     Roo.menu.MenuNav.superclass.constructor.call(this, menu.el);
35180     this.scope = this.menu = menu;
35181 };
35182
35183 Roo.extend(Roo.menu.MenuNav, Roo.KeyNav, {
35184     doRelay : function(e, h){
35185         var k = e.getKey();
35186         if(!this.menu.activeItem && e.isNavKeyPress() && k != e.SPACE && k != e.RETURN){
35187             this.menu.tryActivate(0, 1);
35188             return false;
35189         }
35190         return h.call(this.scope || this, e, this.menu);
35191     },
35192
35193     up : function(e, m){
35194         if(!m.tryActivate(m.items.indexOf(m.activeItem)-1, -1)){
35195             m.tryActivate(m.items.length-1, -1);
35196         }
35197     },
35198
35199     down : function(e, m){
35200         if(!m.tryActivate(m.items.indexOf(m.activeItem)+1, 1)){
35201             m.tryActivate(0, 1);
35202         }
35203     },
35204
35205     right : function(e, m){
35206         if(m.activeItem){
35207             m.activeItem.expandMenu(true);
35208         }
35209     },
35210
35211     left : function(e, m){
35212         m.hide();
35213         if(m.parentMenu && m.parentMenu.activeItem){
35214             m.parentMenu.activeItem.activate();
35215         }
35216     },
35217
35218     enter : function(e, m){
35219         if(m.activeItem){
35220             e.stopPropagation();
35221             m.activeItem.onClick(e);
35222             m.fireEvent("click", this, m.activeItem);
35223             return true;
35224         }
35225     }
35226 });/*
35227  * Based on:
35228  * Ext JS Library 1.1.1
35229  * Copyright(c) 2006-2007, Ext JS, LLC.
35230  *
35231  * Originally Released Under LGPL - original licence link has changed is not relivant.
35232  *
35233  * Fork - LGPL
35234  * <script type="text/javascript">
35235  */
35236  
35237 /**
35238  * @class Roo.menu.MenuMgr
35239  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
35240  * @singleton
35241  */
35242 Roo.menu.MenuMgr = function(){
35243    var menus, active, groups = {}, attached = false, lastShow = new Date();
35244
35245    // private - called when first menu is created
35246    function init(){
35247        menus = {};
35248        active = new Roo.util.MixedCollection();
35249        Roo.get(document).addKeyListener(27, function(){
35250            if(active.length > 0){
35251                hideAll();
35252            }
35253        });
35254    }
35255
35256    // private
35257    function hideAll(){
35258        if(active && active.length > 0){
35259            var c = active.clone();
35260            c.each(function(m){
35261                m.hide();
35262            });
35263        }
35264    }
35265
35266    // private
35267    function onHide(m){
35268        active.remove(m);
35269        if(active.length < 1){
35270            Roo.get(document).un("mousedown", onMouseDown);
35271            attached = false;
35272        }
35273    }
35274
35275    // private
35276    function onShow(m){
35277        var last = active.last();
35278        lastShow = new Date();
35279        active.add(m);
35280        if(!attached){
35281            Roo.get(document).on("mousedown", onMouseDown);
35282            attached = true;
35283        }
35284        if(m.parentMenu){
35285           m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
35286           m.parentMenu.activeChild = m;
35287        }else if(last && last.isVisible()){
35288           m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
35289        }
35290    }
35291
35292    // private
35293    function onBeforeHide(m){
35294        if(m.activeChild){
35295            m.activeChild.hide();
35296        }
35297        if(m.autoHideTimer){
35298            clearTimeout(m.autoHideTimer);
35299            delete m.autoHideTimer;
35300        }
35301    }
35302
35303    // private
35304    function onBeforeShow(m){
35305        var pm = m.parentMenu;
35306        if(!pm && !m.allowOtherMenus){
35307            hideAll();
35308        }else if(pm && pm.activeChild && active != m){
35309            pm.activeChild.hide();
35310        }
35311    }
35312
35313    // private
35314    function onMouseDown(e){
35315        if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".x-menu")){
35316            hideAll();
35317        }
35318    }
35319
35320    // private
35321    function onBeforeCheck(mi, state){
35322        if(state){
35323            var g = groups[mi.group];
35324            for(var i = 0, l = g.length; i < l; i++){
35325                if(g[i] != mi){
35326                    g[i].setChecked(false);
35327                }
35328            }
35329        }
35330    }
35331
35332    return {
35333
35334        /**
35335         * Hides all menus that are currently visible
35336         */
35337        hideAll : function(){
35338             hideAll();  
35339        },
35340
35341        // private
35342        register : function(menu){
35343            if(!menus){
35344                init();
35345            }
35346            menus[menu.id] = menu;
35347            menu.on("beforehide", onBeforeHide);
35348            menu.on("hide", onHide);
35349            menu.on("beforeshow", onBeforeShow);
35350            menu.on("show", onShow);
35351            var g = menu.group;
35352            if(g && menu.events["checkchange"]){
35353                if(!groups[g]){
35354                    groups[g] = [];
35355                }
35356                groups[g].push(menu);
35357                menu.on("checkchange", onCheck);
35358            }
35359        },
35360
35361         /**
35362          * Returns a {@link Roo.menu.Menu} object
35363          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
35364          * be used to generate and return a new Menu instance.
35365          */
35366        get : function(menu){
35367            if(typeof menu == "string"){ // menu id
35368                return menus[menu];
35369            }else if(menu.events){  // menu instance
35370                return menu;
35371            }else if(typeof menu.length == 'number'){ // array of menu items?
35372                return new Roo.menu.Menu({items:menu});
35373            }else{ // otherwise, must be a config
35374                return new Roo.menu.Menu(menu);
35375            }
35376        },
35377
35378        // private
35379        unregister : function(menu){
35380            delete menus[menu.id];
35381            menu.un("beforehide", onBeforeHide);
35382            menu.un("hide", onHide);
35383            menu.un("beforeshow", onBeforeShow);
35384            menu.un("show", onShow);
35385            var g = menu.group;
35386            if(g && menu.events["checkchange"]){
35387                groups[g].remove(menu);
35388                menu.un("checkchange", onCheck);
35389            }
35390        },
35391
35392        // private
35393        registerCheckable : function(menuItem){
35394            var g = menuItem.group;
35395            if(g){
35396                if(!groups[g]){
35397                    groups[g] = [];
35398                }
35399                groups[g].push(menuItem);
35400                menuItem.on("beforecheckchange", onBeforeCheck);
35401            }
35402        },
35403
35404        // private
35405        unregisterCheckable : function(menuItem){
35406            var g = menuItem.group;
35407            if(g){
35408                groups[g].remove(menuItem);
35409                menuItem.un("beforecheckchange", onBeforeCheck);
35410            }
35411        }
35412    };
35413 }();/*
35414  * Based on:
35415  * Ext JS Library 1.1.1
35416  * Copyright(c) 2006-2007, Ext JS, LLC.
35417  *
35418  * Originally Released Under LGPL - original licence link has changed is not relivant.
35419  *
35420  * Fork - LGPL
35421  * <script type="text/javascript">
35422  */
35423  
35424
35425 /**
35426  * @class Roo.menu.BaseItem
35427  * @extends Roo.Component
35428  * The base class for all items that render into menus.  BaseItem provides default rendering, activated state
35429  * management and base configuration options shared by all menu components.
35430  * @constructor
35431  * Creates a new BaseItem
35432  * @param {Object} config Configuration options
35433  */
35434 Roo.menu.BaseItem = function(config){
35435     Roo.menu.BaseItem.superclass.constructor.call(this, config);
35436
35437     this.addEvents({
35438         /**
35439          * @event click
35440          * Fires when this item is clicked
35441          * @param {Roo.menu.BaseItem} this
35442          * @param {Roo.EventObject} e
35443          */
35444         click: true,
35445         /**
35446          * @event activate
35447          * Fires when this item is activated
35448          * @param {Roo.menu.BaseItem} this
35449          */
35450         activate : true,
35451         /**
35452          * @event deactivate
35453          * Fires when this item is deactivated
35454          * @param {Roo.menu.BaseItem} this
35455          */
35456         deactivate : true
35457     });
35458
35459     if(this.handler){
35460         this.on("click", this.handler, this.scope, true);
35461     }
35462 };
35463
35464 Roo.extend(Roo.menu.BaseItem, Roo.Component, {
35465     /**
35466      * @cfg {Function} handler
35467      * A function that will handle the click event of this menu item (defaults to undefined)
35468      */
35469     /**
35470      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to false)
35471      */
35472     canActivate : false,
35473     
35474      /**
35475      * @cfg {Boolean} hidden True to prevent creation of this menu item (defaults to false)
35476      */
35477     hidden: false,
35478     
35479     /**
35480      * @cfg {String} activeClass The CSS class to use when the item becomes activated (defaults to "x-menu-item-active")
35481      */
35482     activeClass : "x-menu-item-active",
35483     /**
35484      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to true)
35485      */
35486     hideOnClick : true,
35487     /**
35488      * @cfg {Number} hideDelay Length of time in milliseconds to wait before hiding after a click (defaults to 100)
35489      */
35490     hideDelay : 100,
35491
35492     // private
35493     ctype: "Roo.menu.BaseItem",
35494
35495     // private
35496     actionMode : "container",
35497
35498     // private
35499     render : function(container, parentMenu){
35500         this.parentMenu = parentMenu;
35501         Roo.menu.BaseItem.superclass.render.call(this, container);
35502         this.container.menuItemId = this.id;
35503     },
35504
35505     // private
35506     onRender : function(container, position){
35507         this.el = Roo.get(this.el);
35508         container.dom.appendChild(this.el.dom);
35509     },
35510
35511     // private
35512     onClick : function(e){
35513         if(!this.disabled && this.fireEvent("click", this, e) !== false
35514                 && this.parentMenu.fireEvent("itemclick", this, e) !== false){
35515             this.handleClick(e);
35516         }else{
35517             e.stopEvent();
35518         }
35519     },
35520
35521     // private
35522     activate : function(){
35523         if(this.disabled){
35524             return false;
35525         }
35526         var li = this.container;
35527         li.addClass(this.activeClass);
35528         this.region = li.getRegion().adjust(2, 2, -2, -2);
35529         this.fireEvent("activate", this);
35530         return true;
35531     },
35532
35533     // private
35534     deactivate : function(){
35535         this.container.removeClass(this.activeClass);
35536         this.fireEvent("deactivate", this);
35537     },
35538
35539     // private
35540     shouldDeactivate : function(e){
35541         return !this.region || !this.region.contains(e.getPoint());
35542     },
35543
35544     // private
35545     handleClick : function(e){
35546         if(this.hideOnClick){
35547             this.parentMenu.hide.defer(this.hideDelay, this.parentMenu, [true]);
35548         }
35549     },
35550
35551     // private
35552     expandMenu : function(autoActivate){
35553         // do nothing
35554     },
35555
35556     // private
35557     hideMenu : function(){
35558         // do nothing
35559     }
35560 });/*
35561  * Based on:
35562  * Ext JS Library 1.1.1
35563  * Copyright(c) 2006-2007, Ext JS, LLC.
35564  *
35565  * Originally Released Under LGPL - original licence link has changed is not relivant.
35566  *
35567  * Fork - LGPL
35568  * <script type="text/javascript">
35569  */
35570  
35571 /**
35572  * @class Roo.menu.Adapter
35573  * @extends Roo.menu.BaseItem
35574  * 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.
35575  * It provides basic rendering, activation management and enable/disable logic required to work in menus.
35576  * @constructor
35577  * Creates a new Adapter
35578  * @param {Object} config Configuration options
35579  */
35580 Roo.menu.Adapter = function(component, config){
35581     Roo.menu.Adapter.superclass.constructor.call(this, config);
35582     this.component = component;
35583 };
35584 Roo.extend(Roo.menu.Adapter, Roo.menu.BaseItem, {
35585     // private
35586     canActivate : true,
35587
35588     // private
35589     onRender : function(container, position){
35590         this.component.render(container);
35591         this.el = this.component.getEl();
35592     },
35593
35594     // private
35595     activate : function(){
35596         if(this.disabled){
35597             return false;
35598         }
35599         this.component.focus();
35600         this.fireEvent("activate", this);
35601         return true;
35602     },
35603
35604     // private
35605     deactivate : function(){
35606         this.fireEvent("deactivate", this);
35607     },
35608
35609     // private
35610     disable : function(){
35611         this.component.disable();
35612         Roo.menu.Adapter.superclass.disable.call(this);
35613     },
35614
35615     // private
35616     enable : function(){
35617         this.component.enable();
35618         Roo.menu.Adapter.superclass.enable.call(this);
35619     }
35620 });/*
35621  * Based on:
35622  * Ext JS Library 1.1.1
35623  * Copyright(c) 2006-2007, Ext JS, LLC.
35624  *
35625  * Originally Released Under LGPL - original licence link has changed is not relivant.
35626  *
35627  * Fork - LGPL
35628  * <script type="text/javascript">
35629  */
35630
35631 /**
35632  * @class Roo.menu.TextItem
35633  * @extends Roo.menu.BaseItem
35634  * Adds a static text string to a menu, usually used as either a heading or group separator.
35635  * Note: old style constructor with text is still supported.
35636  * 
35637  * @constructor
35638  * Creates a new TextItem
35639  * @param {Object} cfg Configuration
35640  */
35641 Roo.menu.TextItem = function(cfg){
35642     if (typeof(cfg) == 'string') {
35643         this.text = cfg;
35644     } else {
35645         Roo.apply(this,cfg);
35646     }
35647     
35648     Roo.menu.TextItem.superclass.constructor.call(this);
35649 };
35650
35651 Roo.extend(Roo.menu.TextItem, Roo.menu.BaseItem, {
35652     /**
35653      * @cfg {Boolean} text Text to show on item.
35654      */
35655     text : '',
35656     
35657     /**
35658      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
35659      */
35660     hideOnClick : false,
35661     /**
35662      * @cfg {String} itemCls The default CSS class to use for text items (defaults to "x-menu-text")
35663      */
35664     itemCls : "x-menu-text",
35665
35666     // private
35667     onRender : function(){
35668         var s = document.createElement("span");
35669         s.className = this.itemCls;
35670         s.innerHTML = this.text;
35671         this.el = s;
35672         Roo.menu.TextItem.superclass.onRender.apply(this, arguments);
35673     }
35674 });/*
35675  * Based on:
35676  * Ext JS Library 1.1.1
35677  * Copyright(c) 2006-2007, Ext JS, LLC.
35678  *
35679  * Originally Released Under LGPL - original licence link has changed is not relivant.
35680  *
35681  * Fork - LGPL
35682  * <script type="text/javascript">
35683  */
35684
35685 /**
35686  * @class Roo.menu.Separator
35687  * @extends Roo.menu.BaseItem
35688  * Adds a separator bar to a menu, used to divide logical groups of menu items. Generally you will
35689  * add one of these by using "-" in you call to add() or in your items config rather than creating one directly.
35690  * @constructor
35691  * @param {Object} config Configuration options
35692  */
35693 Roo.menu.Separator = function(config){
35694     Roo.menu.Separator.superclass.constructor.call(this, config);
35695 };
35696
35697 Roo.extend(Roo.menu.Separator, Roo.menu.BaseItem, {
35698     /**
35699      * @cfg {String} itemCls The default CSS class to use for separators (defaults to "x-menu-sep")
35700      */
35701     itemCls : "x-menu-sep",
35702     /**
35703      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
35704      */
35705     hideOnClick : false,
35706
35707     // private
35708     onRender : function(li){
35709         var s = document.createElement("span");
35710         s.className = this.itemCls;
35711         s.innerHTML = "&#160;";
35712         this.el = s;
35713         li.addClass("x-menu-sep-li");
35714         Roo.menu.Separator.superclass.onRender.apply(this, arguments);
35715     }
35716 });/*
35717  * Based on:
35718  * Ext JS Library 1.1.1
35719  * Copyright(c) 2006-2007, Ext JS, LLC.
35720  *
35721  * Originally Released Under LGPL - original licence link has changed is not relivant.
35722  *
35723  * Fork - LGPL
35724  * <script type="text/javascript">
35725  */
35726 /**
35727  * @class Roo.menu.Item
35728  * @extends Roo.menu.BaseItem
35729  * A base class for all menu items that require menu-related functionality (like sub-menus) and are not static
35730  * display items.  Item extends the base functionality of {@link Roo.menu.BaseItem} by adding menu-specific
35731  * activation and click handling.
35732  * @constructor
35733  * Creates a new Item
35734  * @param {Object} config Configuration options
35735  */
35736 Roo.menu.Item = function(config){
35737     Roo.menu.Item.superclass.constructor.call(this, config);
35738     if(this.menu){
35739         this.menu = Roo.menu.MenuMgr.get(this.menu);
35740     }
35741 };
35742 Roo.extend(Roo.menu.Item, Roo.menu.BaseItem, {
35743     
35744     /**
35745      * @cfg {String} text
35746      * The text to show on the menu item.
35747      */
35748     text: '',
35749      /**
35750      * @cfg {String} HTML to render in menu
35751      * The text to show on the menu item (HTML version).
35752      */
35753     html: '',
35754     /**
35755      * @cfg {String} icon
35756      * The path to an icon to display in this menu item (defaults to Roo.BLANK_IMAGE_URL)
35757      */
35758     icon: undefined,
35759     /**
35760      * @cfg {String} itemCls The default CSS class to use for menu items (defaults to "x-menu-item")
35761      */
35762     itemCls : "x-menu-item",
35763     /**
35764      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to true)
35765      */
35766     canActivate : true,
35767     /**
35768      * @cfg {Number} showDelay Length of time in milliseconds to wait before showing this item (defaults to 200)
35769      */
35770     showDelay: 200,
35771     // doc'd in BaseItem
35772     hideDelay: 200,
35773
35774     // private
35775     ctype: "Roo.menu.Item",
35776     
35777     // private
35778     onRender : function(container, position){
35779         var el = document.createElement("a");
35780         el.hideFocus = true;
35781         el.unselectable = "on";
35782         el.href = this.href || "#";
35783         if(this.hrefTarget){
35784             el.target = this.hrefTarget;
35785         }
35786         el.className = this.itemCls + (this.menu ?  " x-menu-item-arrow" : "") + (this.cls ?  " " + this.cls : "");
35787         
35788         var html = this.html.length ? this.html  : String.format('{0}',this.text);
35789         
35790         el.innerHTML = String.format(
35791                 '<img src="{0}" class="x-menu-item-icon {1}" />' + html,
35792                 this.icon || Roo.BLANK_IMAGE_URL, this.iconCls || '');
35793         this.el = el;
35794         Roo.menu.Item.superclass.onRender.call(this, container, position);
35795     },
35796
35797     /**
35798      * Sets the text to display in this menu item
35799      * @param {String} text The text to display
35800      * @param {Boolean} isHTML true to indicate text is pure html.
35801      */
35802     setText : function(text, isHTML){
35803         if (isHTML) {
35804             this.html = text;
35805         } else {
35806             this.text = text;
35807             this.html = '';
35808         }
35809         if(this.rendered){
35810             var html = this.html.length ? this.html  : String.format('{0}',this.text);
35811      
35812             this.el.update(String.format(
35813                 '<img src="{0}" class="x-menu-item-icon {2}">' + html,
35814                 this.icon || Roo.BLANK_IMAGE_URL, this.text, this.iconCls || ''));
35815             this.parentMenu.autoWidth();
35816         }
35817     },
35818
35819     // private
35820     handleClick : function(e){
35821         if(!this.href){ // if no link defined, stop the event automatically
35822             e.stopEvent();
35823         }
35824         Roo.menu.Item.superclass.handleClick.apply(this, arguments);
35825     },
35826
35827     // private
35828     activate : function(autoExpand){
35829         if(Roo.menu.Item.superclass.activate.apply(this, arguments)){
35830             this.focus();
35831             if(autoExpand){
35832                 this.expandMenu();
35833             }
35834         }
35835         return true;
35836     },
35837
35838     // private
35839     shouldDeactivate : function(e){
35840         if(Roo.menu.Item.superclass.shouldDeactivate.call(this, e)){
35841             if(this.menu && this.menu.isVisible()){
35842                 return !this.menu.getEl().getRegion().contains(e.getPoint());
35843             }
35844             return true;
35845         }
35846         return false;
35847     },
35848
35849     // private
35850     deactivate : function(){
35851         Roo.menu.Item.superclass.deactivate.apply(this, arguments);
35852         this.hideMenu();
35853     },
35854
35855     // private
35856     expandMenu : function(autoActivate){
35857         if(!this.disabled && this.menu){
35858             clearTimeout(this.hideTimer);
35859             delete this.hideTimer;
35860             if(!this.menu.isVisible() && !this.showTimer){
35861                 this.showTimer = this.deferExpand.defer(this.showDelay, this, [autoActivate]);
35862             }else if (this.menu.isVisible() && autoActivate){
35863                 this.menu.tryActivate(0, 1);
35864             }
35865         }
35866     },
35867
35868     // private
35869     deferExpand : function(autoActivate){
35870         delete this.showTimer;
35871         this.menu.show(this.container, this.parentMenu.subMenuAlign || "tl-tr?", this.parentMenu);
35872         if(autoActivate){
35873             this.menu.tryActivate(0, 1);
35874         }
35875     },
35876
35877     // private
35878     hideMenu : function(){
35879         clearTimeout(this.showTimer);
35880         delete this.showTimer;
35881         if(!this.hideTimer && this.menu && this.menu.isVisible()){
35882             this.hideTimer = this.deferHide.defer(this.hideDelay, this);
35883         }
35884     },
35885
35886     // private
35887     deferHide : function(){
35888         delete this.hideTimer;
35889         this.menu.hide();
35890     }
35891 });/*
35892  * Based on:
35893  * Ext JS Library 1.1.1
35894  * Copyright(c) 2006-2007, Ext JS, LLC.
35895  *
35896  * Originally Released Under LGPL - original licence link has changed is not relivant.
35897  *
35898  * Fork - LGPL
35899  * <script type="text/javascript">
35900  */
35901  
35902 /**
35903  * @class Roo.menu.CheckItem
35904  * @extends Roo.menu.Item
35905  * Adds a menu item that contains a checkbox by default, but can also be part of a radio group.
35906  * @constructor
35907  * Creates a new CheckItem
35908  * @param {Object} config Configuration options
35909  */
35910 Roo.menu.CheckItem = function(config){
35911     Roo.menu.CheckItem.superclass.constructor.call(this, config);
35912     this.addEvents({
35913         /**
35914          * @event beforecheckchange
35915          * Fires before the checked value is set, providing an opportunity to cancel if needed
35916          * @param {Roo.menu.CheckItem} this
35917          * @param {Boolean} checked The new checked value that will be set
35918          */
35919         "beforecheckchange" : true,
35920         /**
35921          * @event checkchange
35922          * Fires after the checked value has been set
35923          * @param {Roo.menu.CheckItem} this
35924          * @param {Boolean} checked The checked value that was set
35925          */
35926         "checkchange" : true
35927     });
35928     if(this.checkHandler){
35929         this.on('checkchange', this.checkHandler, this.scope);
35930     }
35931 };
35932 Roo.extend(Roo.menu.CheckItem, Roo.menu.Item, {
35933     /**
35934      * @cfg {String} group
35935      * All check items with the same group name will automatically be grouped into a single-select
35936      * radio button group (defaults to '')
35937      */
35938     /**
35939      * @cfg {String} itemCls The default CSS class to use for check items (defaults to "x-menu-item x-menu-check-item")
35940      */
35941     itemCls : "x-menu-item x-menu-check-item",
35942     /**
35943      * @cfg {String} groupClass The default CSS class to use for radio group check items (defaults to "x-menu-group-item")
35944      */
35945     groupClass : "x-menu-group-item",
35946
35947     /**
35948      * @cfg {Boolean} checked True to initialize this checkbox as checked (defaults to false).  Note that
35949      * if this checkbox is part of a radio group (group = true) only the last item in the group that is
35950      * initialized with checked = true will be rendered as checked.
35951      */
35952     checked: false,
35953
35954     // private
35955     ctype: "Roo.menu.CheckItem",
35956
35957     // private
35958     onRender : function(c){
35959         Roo.menu.CheckItem.superclass.onRender.apply(this, arguments);
35960         if(this.group){
35961             this.el.addClass(this.groupClass);
35962         }
35963         Roo.menu.MenuMgr.registerCheckable(this);
35964         if(this.checked){
35965             this.checked = false;
35966             this.setChecked(true, true);
35967         }
35968     },
35969
35970     // private
35971     destroy : function(){
35972         if(this.rendered){
35973             Roo.menu.MenuMgr.unregisterCheckable(this);
35974         }
35975         Roo.menu.CheckItem.superclass.destroy.apply(this, arguments);
35976     },
35977
35978     /**
35979      * Set the checked state of this item
35980      * @param {Boolean} checked The new checked value
35981      * @param {Boolean} suppressEvent (optional) True to prevent the checkchange event from firing (defaults to false)
35982      */
35983     setChecked : function(state, suppressEvent){
35984         if(this.checked != state && this.fireEvent("beforecheckchange", this, state) !== false){
35985             if(this.container){
35986                 this.container[state ? "addClass" : "removeClass"]("x-menu-item-checked");
35987             }
35988             this.checked = state;
35989             if(suppressEvent !== true){
35990                 this.fireEvent("checkchange", this, state);
35991             }
35992         }
35993     },
35994
35995     // private
35996     handleClick : function(e){
35997        if(!this.disabled && !(this.checked && this.group)){// disable unselect on radio item
35998            this.setChecked(!this.checked);
35999        }
36000        Roo.menu.CheckItem.superclass.handleClick.apply(this, arguments);
36001     }
36002 });/*
36003  * Based on:
36004  * Ext JS Library 1.1.1
36005  * Copyright(c) 2006-2007, Ext JS, LLC.
36006  *
36007  * Originally Released Under LGPL - original licence link has changed is not relivant.
36008  *
36009  * Fork - LGPL
36010  * <script type="text/javascript">
36011  */
36012  
36013 /**
36014  * @class Roo.menu.DateItem
36015  * @extends Roo.menu.Adapter
36016  * A menu item that wraps the {@link Roo.DatPicker} component.
36017  * @constructor
36018  * Creates a new DateItem
36019  * @param {Object} config Configuration options
36020  */
36021 Roo.menu.DateItem = function(config){
36022     Roo.menu.DateItem.superclass.constructor.call(this, new Roo.DatePicker(config), config);
36023     /** The Roo.DatePicker object @type Roo.DatePicker */
36024     this.picker = this.component;
36025     this.addEvents({select: true});
36026     
36027     this.picker.on("render", function(picker){
36028         picker.getEl().swallowEvent("click");
36029         picker.container.addClass("x-menu-date-item");
36030     });
36031
36032     this.picker.on("select", this.onSelect, this);
36033 };
36034
36035 Roo.extend(Roo.menu.DateItem, Roo.menu.Adapter, {
36036     // private
36037     onSelect : function(picker, date){
36038         this.fireEvent("select", this, date, picker);
36039         Roo.menu.DateItem.superclass.handleClick.call(this);
36040     }
36041 });/*
36042  * Based on:
36043  * Ext JS Library 1.1.1
36044  * Copyright(c) 2006-2007, Ext JS, LLC.
36045  *
36046  * Originally Released Under LGPL - original licence link has changed is not relivant.
36047  *
36048  * Fork - LGPL
36049  * <script type="text/javascript">
36050  */
36051  
36052 /**
36053  * @class Roo.menu.ColorItem
36054  * @extends Roo.menu.Adapter
36055  * A menu item that wraps the {@link Roo.ColorPalette} component.
36056  * @constructor
36057  * Creates a new ColorItem
36058  * @param {Object} config Configuration options
36059  */
36060 Roo.menu.ColorItem = function(config){
36061     Roo.menu.ColorItem.superclass.constructor.call(this, new Roo.ColorPalette(config), config);
36062     /** The Roo.ColorPalette object @type Roo.ColorPalette */
36063     this.palette = this.component;
36064     this.relayEvents(this.palette, ["select"]);
36065     if(this.selectHandler){
36066         this.on('select', this.selectHandler, this.scope);
36067     }
36068 };
36069 Roo.extend(Roo.menu.ColorItem, Roo.menu.Adapter);/*
36070  * Based on:
36071  * Ext JS Library 1.1.1
36072  * Copyright(c) 2006-2007, Ext JS, LLC.
36073  *
36074  * Originally Released Under LGPL - original licence link has changed is not relivant.
36075  *
36076  * Fork - LGPL
36077  * <script type="text/javascript">
36078  */
36079  
36080
36081 /**
36082  * @class Roo.menu.DateMenu
36083  * @extends Roo.menu.Menu
36084  * A menu containing a {@link Roo.menu.DateItem} component (which provides a date picker).
36085  * @constructor
36086  * Creates a new DateMenu
36087  * @param {Object} config Configuration options
36088  */
36089 Roo.menu.DateMenu = function(config){
36090     Roo.menu.DateMenu.superclass.constructor.call(this, config);
36091     this.plain = true;
36092     var di = new Roo.menu.DateItem(config);
36093     this.add(di);
36094     /**
36095      * The {@link Roo.DatePicker} instance for this DateMenu
36096      * @type DatePicker
36097      */
36098     this.picker = di.picker;
36099     /**
36100      * @event select
36101      * @param {DatePicker} picker
36102      * @param {Date} date
36103      */
36104     this.relayEvents(di, ["select"]);
36105     this.on('beforeshow', function(){
36106         if(this.picker){
36107             this.picker.hideMonthPicker(false);
36108         }
36109     }, this);
36110 };
36111 Roo.extend(Roo.menu.DateMenu, Roo.menu.Menu, {
36112     cls:'x-date-menu'
36113 });/*
36114  * Based on:
36115  * Ext JS Library 1.1.1
36116  * Copyright(c) 2006-2007, Ext JS, LLC.
36117  *
36118  * Originally Released Under LGPL - original licence link has changed is not relivant.
36119  *
36120  * Fork - LGPL
36121  * <script type="text/javascript">
36122  */
36123  
36124
36125 /**
36126  * @class Roo.menu.ColorMenu
36127  * @extends Roo.menu.Menu
36128  * A menu containing a {@link Roo.menu.ColorItem} component (which provides a basic color picker).
36129  * @constructor
36130  * Creates a new ColorMenu
36131  * @param {Object} config Configuration options
36132  */
36133 Roo.menu.ColorMenu = function(config){
36134     Roo.menu.ColorMenu.superclass.constructor.call(this, config);
36135     this.plain = true;
36136     var ci = new Roo.menu.ColorItem(config);
36137     this.add(ci);
36138     /**
36139      * The {@link Roo.ColorPalette} instance for this ColorMenu
36140      * @type ColorPalette
36141      */
36142     this.palette = ci.palette;
36143     /**
36144      * @event select
36145      * @param {ColorPalette} palette
36146      * @param {String} color
36147      */
36148     this.relayEvents(ci, ["select"]);
36149 };
36150 Roo.extend(Roo.menu.ColorMenu, Roo.menu.Menu);/*
36151  * Based on:
36152  * Ext JS Library 1.1.1
36153  * Copyright(c) 2006-2007, Ext JS, LLC.
36154  *
36155  * Originally Released Under LGPL - original licence link has changed is not relivant.
36156  *
36157  * Fork - LGPL
36158  * <script type="text/javascript">
36159  */
36160  
36161 /**
36162  * @class Roo.form.Field
36163  * @extends Roo.BoxComponent
36164  * Base class for form fields that provides default event handling, sizing, value handling and other functionality.
36165  * @constructor
36166  * Creates a new Field
36167  * @param {Object} config Configuration options
36168  */
36169 Roo.form.Field = function(config){
36170     Roo.form.Field.superclass.constructor.call(this, config);
36171 };
36172
36173 Roo.extend(Roo.form.Field, Roo.BoxComponent,  {
36174     /**
36175      * @cfg {String} fieldLabel Label to use when rendering a form.
36176      */
36177        /**
36178      * @cfg {String} qtip Mouse over tip
36179      */
36180      
36181     /**
36182      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
36183      */
36184     invalidClass : "x-form-invalid",
36185     /**
36186      * @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")
36187      */
36188     invalidText : "The value in this field is invalid",
36189     /**
36190      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
36191      */
36192     focusClass : "x-form-focus",
36193     /**
36194      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
36195       automatic validation (defaults to "keyup").
36196      */
36197     validationEvent : "keyup",
36198     /**
36199      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
36200      */
36201     validateOnBlur : true,
36202     /**
36203      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
36204      */
36205     validationDelay : 250,
36206     /**
36207      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
36208      * {tag: "input", type: "text", size: "20", autocomplete: "off"})
36209      */
36210     defaultAutoCreate : {tag: "input", type: "text", size: "20", autocomplete: "off"},
36211     /**
36212      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field")
36213      */
36214     fieldClass : "x-form-field",
36215     /**
36216      * @cfg {String} msgTarget The location where error text should display.  Should be one of the following values (defaults to 'qtip'):
36217      *<pre>
36218 Value         Description
36219 -----------   ----------------------------------------------------------------------
36220 qtip          Display a quick tip when the user hovers over the field
36221 title         Display a default browser title attribute popup
36222 under         Add a block div beneath the field containing the error text
36223 side          Add an error icon to the right of the field with a popup on hover
36224 [element id]  Add the error text directly to the innerHTML of the specified element
36225 </pre>
36226      */
36227     msgTarget : 'qtip',
36228     /**
36229      * @cfg {String} msgFx <b>Experimental</b> The effect used when displaying a validation message under the field (defaults to 'normal').
36230      */
36231     msgFx : 'normal',
36232
36233     /**
36234      * @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.
36235      */
36236     readOnly : false,
36237
36238     /**
36239      * @cfg {Boolean} disabled True to disable the field (defaults to false).
36240      */
36241     disabled : false,
36242
36243     /**
36244      * @cfg {String} inputType The type attribute for input fields -- e.g. radio, text, password (defaults to "text").
36245      */
36246     inputType : undefined,
36247     
36248     /**
36249      * @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).
36250          */
36251         tabIndex : undefined,
36252         
36253     // private
36254     isFormField : true,
36255
36256     // private
36257     hasFocus : false,
36258     /**
36259      * @property {Roo.Element} fieldEl
36260      * Element Containing the rendered Field (with label etc.)
36261      */
36262     /**
36263      * @cfg {Mixed} value A value to initialize this field with.
36264      */
36265     value : undefined,
36266
36267     /**
36268      * @cfg {String} name The field's HTML name attribute.
36269      */
36270     /**
36271      * @cfg {String} cls A CSS class to apply to the field's underlying element.
36272      */
36273
36274         // private ??
36275         initComponent : function(){
36276         Roo.form.Field.superclass.initComponent.call(this);
36277         this.addEvents({
36278             /**
36279              * @event focus
36280              * Fires when this field receives input focus.
36281              * @param {Roo.form.Field} this
36282              */
36283             focus : true,
36284             /**
36285              * @event blur
36286              * Fires when this field loses input focus.
36287              * @param {Roo.form.Field} this
36288              */
36289             blur : true,
36290             /**
36291              * @event specialkey
36292              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
36293              * {@link Roo.EventObject#getKey} to determine which key was pressed.
36294              * @param {Roo.form.Field} this
36295              * @param {Roo.EventObject} e The event object
36296              */
36297             specialkey : true,
36298             /**
36299              * @event change
36300              * Fires just before the field blurs if the field value has changed.
36301              * @param {Roo.form.Field} this
36302              * @param {Mixed} newValue The new value
36303              * @param {Mixed} oldValue The original value
36304              */
36305             change : true,
36306             /**
36307              * @event invalid
36308              * Fires after the field has been marked as invalid.
36309              * @param {Roo.form.Field} this
36310              * @param {String} msg The validation message
36311              */
36312             invalid : true,
36313             /**
36314              * @event valid
36315              * Fires after the field has been validated with no errors.
36316              * @param {Roo.form.Field} this
36317              */
36318             valid : true,
36319              /**
36320              * @event keyup
36321              * Fires after the key up
36322              * @param {Roo.form.Field} this
36323              * @param {Roo.EventObject}  e The event Object
36324              */
36325             keyup : true
36326         });
36327     },
36328
36329     /**
36330      * Returns the name attribute of the field if available
36331      * @return {String} name The field name
36332      */
36333     getName: function(){
36334          return this.rendered && this.el.dom.name ? this.el.dom.name : (this.hiddenName || '');
36335     },
36336
36337     // private
36338     onRender : function(ct, position){
36339         Roo.form.Field.superclass.onRender.call(this, ct, position);
36340         if(!this.el){
36341             var cfg = this.getAutoCreate();
36342             if(!cfg.name){
36343                 cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
36344             }
36345             if (!cfg.name.length) {
36346                 delete cfg.name;
36347             }
36348             if(this.inputType){
36349                 cfg.type = this.inputType;
36350             }
36351             this.el = ct.createChild(cfg, position);
36352         }
36353         var type = this.el.dom.type;
36354         if(type){
36355             if(type == 'password'){
36356                 type = 'text';
36357             }
36358             this.el.addClass('x-form-'+type);
36359         }
36360         if(this.readOnly){
36361             this.el.dom.readOnly = true;
36362         }
36363         if(this.tabIndex !== undefined){
36364             this.el.dom.setAttribute('tabIndex', this.tabIndex);
36365         }
36366
36367         this.el.addClass([this.fieldClass, this.cls]);
36368         this.initValue();
36369     },
36370
36371     /**
36372      * Apply the behaviors of this component to an existing element. <b>This is used instead of render().</b>
36373      * @param {String/HTMLElement/Element} el The id of the node, a DOM node or an existing Element
36374      * @return {Roo.form.Field} this
36375      */
36376     applyTo : function(target){
36377         this.allowDomMove = false;
36378         this.el = Roo.get(target);
36379         this.render(this.el.dom.parentNode);
36380         return this;
36381     },
36382
36383     // private
36384     initValue : function(){
36385         if(this.value !== undefined){
36386             this.setValue(this.value);
36387         }else if(this.el.dom.value.length > 0){
36388             this.setValue(this.el.dom.value);
36389         }
36390     },
36391
36392     /**
36393      * Returns true if this field has been changed since it was originally loaded and is not disabled.
36394      */
36395     isDirty : function() {
36396         if(this.disabled) {
36397             return false;
36398         }
36399         return String(this.getValue()) !== String(this.originalValue);
36400     },
36401
36402     // private
36403     afterRender : function(){
36404         Roo.form.Field.superclass.afterRender.call(this);
36405         this.initEvents();
36406     },
36407
36408     // private
36409     fireKey : function(e){
36410         //Roo.log('field ' + e.getKey());
36411         if(e.isNavKeyPress()){
36412             this.fireEvent("specialkey", this, e);
36413         }
36414     },
36415
36416     /**
36417      * Resets the current field value to the originally loaded value and clears any validation messages
36418      */
36419     reset : function(){
36420         this.setValue(this.originalValue);
36421         this.clearInvalid();
36422     },
36423
36424     // private
36425     initEvents : function(){
36426         // safari killled keypress - so keydown is now used..
36427         this.el.on("keydown" , this.fireKey,  this);
36428         this.el.on("focus", this.onFocus,  this);
36429         this.el.on("blur", this.onBlur,  this);
36430         this.el.relayEvent('keyup', this);
36431
36432         // reference to original value for reset
36433         this.originalValue = this.getValue();
36434     },
36435
36436     // private
36437     onFocus : function(){
36438         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
36439             this.el.addClass(this.focusClass);
36440         }
36441         if(!this.hasFocus){
36442             this.hasFocus = true;
36443             this.startValue = this.getValue();
36444             this.fireEvent("focus", this);
36445         }
36446     },
36447
36448     beforeBlur : Roo.emptyFn,
36449
36450     // private
36451     onBlur : function(){
36452         this.beforeBlur();
36453         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
36454             this.el.removeClass(this.focusClass);
36455         }
36456         this.hasFocus = false;
36457         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
36458             this.validate();
36459         }
36460         var v = this.getValue();
36461         if(String(v) !== String(this.startValue)){
36462             this.fireEvent('change', this, v, this.startValue);
36463         }
36464         this.fireEvent("blur", this);
36465     },
36466
36467     /**
36468      * Returns whether or not the field value is currently valid
36469      * @param {Boolean} preventMark True to disable marking the field invalid
36470      * @return {Boolean} True if the value is valid, else false
36471      */
36472     isValid : function(preventMark){
36473         if(this.disabled){
36474             return true;
36475         }
36476         var restore = this.preventMark;
36477         this.preventMark = preventMark === true;
36478         var v = this.validateValue(this.processValue(this.getRawValue()));
36479         this.preventMark = restore;
36480         return v;
36481     },
36482
36483     /**
36484      * Validates the field value
36485      * @return {Boolean} True if the value is valid, else false
36486      */
36487     validate : function(){
36488         if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
36489             this.clearInvalid();
36490             return true;
36491         }
36492         return false;
36493     },
36494
36495     processValue : function(value){
36496         return value;
36497     },
36498
36499     // private
36500     // Subclasses should provide the validation implementation by overriding this
36501     validateValue : function(value){
36502         return true;
36503     },
36504
36505     /**
36506      * Mark this field as invalid
36507      * @param {String} msg The validation message
36508      */
36509     markInvalid : function(msg){
36510         if(!this.rendered || this.preventMark){ // not rendered
36511             return;
36512         }
36513         this.el.addClass(this.invalidClass);
36514         msg = msg || this.invalidText;
36515         switch(this.msgTarget){
36516             case 'qtip':
36517                 this.el.dom.qtip = msg;
36518                 this.el.dom.qclass = 'x-form-invalid-tip';
36519                 if(Roo.QuickTips){ // fix for floating editors interacting with DND
36520                     Roo.QuickTips.enable();
36521                 }
36522                 break;
36523             case 'title':
36524                 this.el.dom.title = msg;
36525                 break;
36526             case 'under':
36527                 if(!this.errorEl){
36528                     var elp = this.el.findParent('.x-form-element', 5, true);
36529                     this.errorEl = elp.createChild({cls:'x-form-invalid-msg'});
36530                     this.errorEl.setWidth(elp.getWidth(true)-20);
36531                 }
36532                 this.errorEl.update(msg);
36533                 Roo.form.Field.msgFx[this.msgFx].show(this.errorEl, this);
36534                 break;
36535             case 'side':
36536                 if(!this.errorIcon){
36537                     var elp = this.el.findParent('.x-form-element', 5, true);
36538                     this.errorIcon = elp.createChild({cls:'x-form-invalid-icon'});
36539                 }
36540                 this.alignErrorIcon();
36541                 this.errorIcon.dom.qtip = msg;
36542                 this.errorIcon.dom.qclass = 'x-form-invalid-tip';
36543                 this.errorIcon.show();
36544                 this.on('resize', this.alignErrorIcon, this);
36545                 break;
36546             default:
36547                 var t = Roo.getDom(this.msgTarget);
36548                 t.innerHTML = msg;
36549                 t.style.display = this.msgDisplay;
36550                 break;
36551         }
36552         this.fireEvent('invalid', this, msg);
36553     },
36554
36555     // private
36556     alignErrorIcon : function(){
36557         this.errorIcon.alignTo(this.el, 'tl-tr', [2, 0]);
36558     },
36559
36560     /**
36561      * Clear any invalid styles/messages for this field
36562      */
36563     clearInvalid : function(){
36564         if(!this.rendered || this.preventMark){ // not rendered
36565             return;
36566         }
36567         this.el.removeClass(this.invalidClass);
36568         switch(this.msgTarget){
36569             case 'qtip':
36570                 this.el.dom.qtip = '';
36571                 break;
36572             case 'title':
36573                 this.el.dom.title = '';
36574                 break;
36575             case 'under':
36576                 if(this.errorEl){
36577                     Roo.form.Field.msgFx[this.msgFx].hide(this.errorEl, this);
36578                 }
36579                 break;
36580             case 'side':
36581                 if(this.errorIcon){
36582                     this.errorIcon.dom.qtip = '';
36583                     this.errorIcon.hide();
36584                     this.un('resize', this.alignErrorIcon, this);
36585                 }
36586                 break;
36587             default:
36588                 var t = Roo.getDom(this.msgTarget);
36589                 t.innerHTML = '';
36590                 t.style.display = 'none';
36591                 break;
36592         }
36593         this.fireEvent('valid', this);
36594     },
36595
36596     /**
36597      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
36598      * @return {Mixed} value The field value
36599      */
36600     getRawValue : function(){
36601         var v = this.el.getValue();
36602         
36603         return v;
36604     },
36605
36606     /**
36607      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
36608      * @return {Mixed} value The field value
36609      */
36610     getValue : function(){
36611         var v = this.el.getValue();
36612          
36613         return v;
36614     },
36615
36616     /**
36617      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
36618      * @param {Mixed} value The value to set
36619      */
36620     setRawValue : function(v){
36621         return this.el.dom.value = (v === null || v === undefined ? '' : v);
36622     },
36623
36624     /**
36625      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
36626      * @param {Mixed} value The value to set
36627      */
36628     setValue : function(v){
36629         this.value = v;
36630         if(this.rendered){
36631             this.el.dom.value = (v === null || v === undefined ? '' : v);
36632              this.validate();
36633         }
36634     },
36635
36636     adjustSize : function(w, h){
36637         var s = Roo.form.Field.superclass.adjustSize.call(this, w, h);
36638         s.width = this.adjustWidth(this.el.dom.tagName, s.width);
36639         return s;
36640     },
36641
36642     adjustWidth : function(tag, w){
36643         tag = tag.toLowerCase();
36644         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
36645             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
36646                 if(tag == 'input'){
36647                     return w + 2;
36648                 }
36649                 if(tag == 'textarea'){
36650                     return w-2;
36651                 }
36652             }else if(Roo.isOpera){
36653                 if(tag == 'input'){
36654                     return w + 2;
36655                 }
36656                 if(tag == 'textarea'){
36657                     return w-2;
36658                 }
36659             }
36660         }
36661         return w;
36662     }
36663 });
36664
36665
36666 // anything other than normal should be considered experimental
36667 Roo.form.Field.msgFx = {
36668     normal : {
36669         show: function(msgEl, f){
36670             msgEl.setDisplayed('block');
36671         },
36672
36673         hide : function(msgEl, f){
36674             msgEl.setDisplayed(false).update('');
36675         }
36676     },
36677
36678     slide : {
36679         show: function(msgEl, f){
36680             msgEl.slideIn('t', {stopFx:true});
36681         },
36682
36683         hide : function(msgEl, f){
36684             msgEl.slideOut('t', {stopFx:true,useDisplay:true});
36685         }
36686     },
36687
36688     slideRight : {
36689         show: function(msgEl, f){
36690             msgEl.fixDisplay();
36691             msgEl.alignTo(f.el, 'tl-tr');
36692             msgEl.slideIn('l', {stopFx:true});
36693         },
36694
36695         hide : function(msgEl, f){
36696             msgEl.slideOut('l', {stopFx:true,useDisplay:true});
36697         }
36698     }
36699 };/*
36700  * Based on:
36701  * Ext JS Library 1.1.1
36702  * Copyright(c) 2006-2007, Ext JS, LLC.
36703  *
36704  * Originally Released Under LGPL - original licence link has changed is not relivant.
36705  *
36706  * Fork - LGPL
36707  * <script type="text/javascript">
36708  */
36709  
36710
36711 /**
36712  * @class Roo.form.TextField
36713  * @extends Roo.form.Field
36714  * Basic text field.  Can be used as a direct replacement for traditional text inputs, or as the base
36715  * class for more sophisticated input controls (like {@link Roo.form.TextArea} and {@link Roo.form.ComboBox}).
36716  * @constructor
36717  * Creates a new TextField
36718  * @param {Object} config Configuration options
36719  */
36720 Roo.form.TextField = function(config){
36721     Roo.form.TextField.superclass.constructor.call(this, config);
36722     this.addEvents({
36723         /**
36724          * @event autosize
36725          * Fires when the autosize function is triggered.  The field may or may not have actually changed size
36726          * according to the default logic, but this event provides a hook for the developer to apply additional
36727          * logic at runtime to resize the field if needed.
36728              * @param {Roo.form.Field} this This text field
36729              * @param {Number} width The new field width
36730              */
36731         autosize : true
36732     });
36733 };
36734
36735 Roo.extend(Roo.form.TextField, Roo.form.Field,  {
36736     /**
36737      * @cfg {Boolean} grow True if this field should automatically grow and shrink to its content
36738      */
36739     grow : false,
36740     /**
36741      * @cfg {Number} growMin The minimum width to allow when grow = true (defaults to 30)
36742      */
36743     growMin : 30,
36744     /**
36745      * @cfg {Number} growMax The maximum width to allow when grow = true (defaults to 800)
36746      */
36747     growMax : 800,
36748     /**
36749      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
36750      */
36751     vtype : null,
36752     /**
36753      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
36754      */
36755     maskRe : null,
36756     /**
36757      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
36758      */
36759     disableKeyFilter : false,
36760     /**
36761      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
36762      */
36763     allowBlank : true,
36764     /**
36765      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
36766      */
36767     minLength : 0,
36768     /**
36769      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
36770      */
36771     maxLength : Number.MAX_VALUE,
36772     /**
36773      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
36774      */
36775     minLengthText : "The minimum length for this field is {0}",
36776     /**
36777      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
36778      */
36779     maxLengthText : "The maximum length for this field is {0}",
36780     /**
36781      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
36782      */
36783     selectOnFocus : false,
36784     /**
36785      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
36786      */
36787     blankText : "This field is required",
36788     /**
36789      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
36790      * If available, this function will be called only after the basic validators all return true, and will be passed the
36791      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
36792      */
36793     validator : null,
36794     /**
36795      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
36796      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
36797      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
36798      */
36799     regex : null,
36800     /**
36801      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
36802      */
36803     regexText : "",
36804     /**
36805      * @cfg {String} emptyText The default text to display in an empty field - placeholder... (defaults to null).
36806      */
36807     emptyText : null,
36808    
36809
36810     // private
36811     initEvents : function()
36812     {
36813         if (this.emptyText) {
36814             this.el.attr('placeholder', this.emptyText);
36815         }
36816         
36817         Roo.form.TextField.superclass.initEvents.call(this);
36818         if(this.validationEvent == 'keyup'){
36819             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
36820             this.el.on('keyup', this.filterValidation, this);
36821         }
36822         else if(this.validationEvent !== false){
36823             this.el.on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
36824         }
36825         
36826         if(this.selectOnFocus){
36827             this.on("focus", this.preFocus, this);
36828             
36829         }
36830         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
36831             this.el.on("keypress", this.filterKeys, this);
36832         }
36833         if(this.grow){
36834             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
36835             this.el.on("click", this.autoSize,  this);
36836         }
36837         if(this.el.is('input[type=password]') && Roo.isSafari){
36838             this.el.on('keydown', this.SafariOnKeyDown, this);
36839         }
36840     },
36841
36842     processValue : function(value){
36843         if(this.stripCharsRe){
36844             var newValue = value.replace(this.stripCharsRe, '');
36845             if(newValue !== value){
36846                 this.setRawValue(newValue);
36847                 return newValue;
36848             }
36849         }
36850         return value;
36851     },
36852
36853     filterValidation : function(e){
36854         if(!e.isNavKeyPress()){
36855             this.validationTask.delay(this.validationDelay);
36856         }
36857     },
36858
36859     // private
36860     onKeyUp : function(e){
36861         if(!e.isNavKeyPress()){
36862             this.autoSize();
36863         }
36864     },
36865
36866     /**
36867      * Resets the current field value to the originally-loaded value and clears any validation messages.
36868      *  
36869      */
36870     reset : function(){
36871         Roo.form.TextField.superclass.reset.call(this);
36872        
36873     },
36874
36875     
36876     // private
36877     preFocus : function(){
36878         
36879         if(this.selectOnFocus){
36880             this.el.dom.select();
36881         }
36882     },
36883
36884     
36885     // private
36886     filterKeys : function(e){
36887         var k = e.getKey();
36888         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
36889             return;
36890         }
36891         var c = e.getCharCode(), cc = String.fromCharCode(c);
36892         if(Roo.isIE && (e.isSpecialKey() || !cc)){
36893             return;
36894         }
36895         if(!this.maskRe.test(cc)){
36896             e.stopEvent();
36897         }
36898     },
36899
36900     setValue : function(v){
36901         
36902         Roo.form.TextField.superclass.setValue.apply(this, arguments);
36903         
36904         this.autoSize();
36905     },
36906
36907     /**
36908      * Validates a value according to the field's validation rules and marks the field as invalid
36909      * if the validation fails
36910      * @param {Mixed} value The value to validate
36911      * @return {Boolean} True if the value is valid, else false
36912      */
36913     validateValue : function(value){
36914         if(value.length < 1)  { // if it's blank
36915              if(this.allowBlank){
36916                 this.clearInvalid();
36917                 return true;
36918              }else{
36919                 this.markInvalid(this.blankText);
36920                 return false;
36921              }
36922         }
36923         if(value.length < this.minLength){
36924             this.markInvalid(String.format(this.minLengthText, this.minLength));
36925             return false;
36926         }
36927         if(value.length > this.maxLength){
36928             this.markInvalid(String.format(this.maxLengthText, this.maxLength));
36929             return false;
36930         }
36931         if(this.vtype){
36932             var vt = Roo.form.VTypes;
36933             if(!vt[this.vtype](value, this)){
36934                 this.markInvalid(this.vtypeText || vt[this.vtype +'Text']);
36935                 return false;
36936             }
36937         }
36938         if(typeof this.validator == "function"){
36939             var msg = this.validator(value);
36940             if(msg !== true){
36941                 this.markInvalid(msg);
36942                 return false;
36943             }
36944         }
36945         if(this.regex && !this.regex.test(value)){
36946             this.markInvalid(this.regexText);
36947             return false;
36948         }
36949         return true;
36950     },
36951
36952     /**
36953      * Selects text in this field
36954      * @param {Number} start (optional) The index where the selection should start (defaults to 0)
36955      * @param {Number} end (optional) The index where the selection should end (defaults to the text length)
36956      */
36957     selectText : function(start, end){
36958         var v = this.getRawValue();
36959         if(v.length > 0){
36960             start = start === undefined ? 0 : start;
36961             end = end === undefined ? v.length : end;
36962             var d = this.el.dom;
36963             if(d.setSelectionRange){
36964                 d.setSelectionRange(start, end);
36965             }else if(d.createTextRange){
36966                 var range = d.createTextRange();
36967                 range.moveStart("character", start);
36968                 range.moveEnd("character", v.length-end);
36969                 range.select();
36970             }
36971         }
36972     },
36973
36974     /**
36975      * Automatically grows the field to accomodate the width of the text up to the maximum field width allowed.
36976      * This only takes effect if grow = true, and fires the autosize event.
36977      */
36978     autoSize : function(){
36979         if(!this.grow || !this.rendered){
36980             return;
36981         }
36982         if(!this.metrics){
36983             this.metrics = Roo.util.TextMetrics.createInstance(this.el);
36984         }
36985         var el = this.el;
36986         var v = el.dom.value;
36987         var d = document.createElement('div');
36988         d.appendChild(document.createTextNode(v));
36989         v = d.innerHTML;
36990         d = null;
36991         v += "&#160;";
36992         var w = Math.min(this.growMax, Math.max(this.metrics.getWidth(v) + /* add extra padding */ 10, this.growMin));
36993         this.el.setWidth(w);
36994         this.fireEvent("autosize", this, w);
36995     },
36996     
36997     // private
36998     SafariOnKeyDown : function(event)
36999     {
37000         // this is a workaround for a password hang bug on chrome/ webkit.
37001         
37002         var isSelectAll = false;
37003         
37004         if(this.el.dom.selectionEnd > 0){
37005             isSelectAll = (this.el.dom.selectionEnd - this.el.dom.selectionStart - this.getValue().length == 0) ? true : false;
37006         }
37007         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
37008             event.preventDefault();
37009             this.setValue('');
37010             return;
37011         }
37012         
37013         if(isSelectAll){ // backspace and delete key
37014             
37015             event.preventDefault();
37016             // this is very hacky as keydown always get's upper case.
37017             //
37018             var cc = String.fromCharCode(event.getCharCode());
37019             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
37020             
37021         }
37022         
37023         
37024     }
37025 });/*
37026  * Based on:
37027  * Ext JS Library 1.1.1
37028  * Copyright(c) 2006-2007, Ext JS, LLC.
37029  *
37030  * Originally Released Under LGPL - original licence link has changed is not relivant.
37031  *
37032  * Fork - LGPL
37033  * <script type="text/javascript">
37034  */
37035  
37036 /**
37037  * @class Roo.form.Hidden
37038  * @extends Roo.form.TextField
37039  * Simple Hidden element used on forms 
37040  * 
37041  * usage: form.add(new Roo.form.HiddenField({ 'name' : 'test1' }));
37042  * 
37043  * @constructor
37044  * Creates a new Hidden form element.
37045  * @param {Object} config Configuration options
37046  */
37047
37048
37049
37050 // easy hidden field...
37051 Roo.form.Hidden = function(config){
37052     Roo.form.Hidden.superclass.constructor.call(this, config);
37053 };
37054   
37055 Roo.extend(Roo.form.Hidden, Roo.form.TextField, {
37056     fieldLabel:      '',
37057     inputType:      'hidden',
37058     width:          50,
37059     allowBlank:     true,
37060     labelSeparator: '',
37061     hidden:         true,
37062     itemCls :       'x-form-item-display-none'
37063
37064
37065 });
37066
37067
37068 /*
37069  * Based on:
37070  * Ext JS Library 1.1.1
37071  * Copyright(c) 2006-2007, Ext JS, LLC.
37072  *
37073  * Originally Released Under LGPL - original licence link has changed is not relivant.
37074  *
37075  * Fork - LGPL
37076  * <script type="text/javascript">
37077  */
37078  
37079 /**
37080  * @class Roo.form.TriggerField
37081  * @extends Roo.form.TextField
37082  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
37083  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
37084  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
37085  * for which you can provide a custom implementation.  For example:
37086  * <pre><code>
37087 var trigger = new Roo.form.TriggerField();
37088 trigger.onTriggerClick = myTriggerFn;
37089 trigger.applyTo('my-field');
37090 </code></pre>
37091  *
37092  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
37093  * {@link Roo.form.DateField} and {@link Roo.form.ComboBox} are perfect examples of this.
37094  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
37095  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
37096  * @constructor
37097  * Create a new TriggerField.
37098  * @param {Object} config Configuration options (valid {@Roo.form.TextField} config options will also be applied
37099  * to the base TextField)
37100  */
37101 Roo.form.TriggerField = function(config){
37102     this.mimicing = false;
37103     Roo.form.TriggerField.superclass.constructor.call(this, config);
37104 };
37105
37106 Roo.extend(Roo.form.TriggerField, Roo.form.TextField,  {
37107     /**
37108      * @cfg {String} triggerClass A CSS class to apply to the trigger
37109      */
37110     /**
37111      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
37112      * {tag: "input", type: "text", size: "16", autocomplete: "off"})
37113      */
37114     defaultAutoCreate : {tag: "input", type: "text", size: "16", autocomplete: "off"},
37115     /**
37116      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
37117      */
37118     hideTrigger:false,
37119
37120     /** @cfg {Boolean} grow @hide */
37121     /** @cfg {Number} growMin @hide */
37122     /** @cfg {Number} growMax @hide */
37123
37124     /**
37125      * @hide 
37126      * @method
37127      */
37128     autoSize: Roo.emptyFn,
37129     // private
37130     monitorTab : true,
37131     // private
37132     deferHeight : true,
37133
37134     
37135     actionMode : 'wrap',
37136     // private
37137     onResize : function(w, h){
37138         Roo.form.TriggerField.superclass.onResize.apply(this, arguments);
37139         if(typeof w == 'number'){
37140             var x = w - this.trigger.getWidth();
37141             this.el.setWidth(this.adjustWidth('input', x));
37142             this.trigger.setStyle('left', x+'px');
37143         }
37144     },
37145
37146     // private
37147     adjustSize : Roo.BoxComponent.prototype.adjustSize,
37148
37149     // private
37150     getResizeEl : function(){
37151         return this.wrap;
37152     },
37153
37154     // private
37155     getPositionEl : function(){
37156         return this.wrap;
37157     },
37158
37159     // private
37160     alignErrorIcon : function(){
37161         this.errorIcon.alignTo(this.wrap, 'tl-tr', [2, 0]);
37162     },
37163
37164     // private
37165     onRender : function(ct, position){
37166         Roo.form.TriggerField.superclass.onRender.call(this, ct, position);
37167         this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
37168         this.trigger = this.wrap.createChild(this.triggerConfig ||
37169                 {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.triggerClass});
37170         if(this.hideTrigger){
37171             this.trigger.setDisplayed(false);
37172         }
37173         this.initTrigger();
37174         if(!this.width){
37175             this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
37176         }
37177     },
37178
37179     // private
37180     initTrigger : function(){
37181         this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
37182         this.trigger.addClassOnOver('x-form-trigger-over');
37183         this.trigger.addClassOnClick('x-form-trigger-click');
37184     },
37185
37186     // private
37187     onDestroy : function(){
37188         if(this.trigger){
37189             this.trigger.removeAllListeners();
37190             this.trigger.remove();
37191         }
37192         if(this.wrap){
37193             this.wrap.remove();
37194         }
37195         Roo.form.TriggerField.superclass.onDestroy.call(this);
37196     },
37197
37198     // private
37199     onFocus : function(){
37200         Roo.form.TriggerField.superclass.onFocus.call(this);
37201         if(!this.mimicing){
37202             this.wrap.addClass('x-trigger-wrap-focus');
37203             this.mimicing = true;
37204             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
37205             if(this.monitorTab){
37206                 this.el.on("keydown", this.checkTab, this);
37207             }
37208         }
37209     },
37210
37211     // private
37212     checkTab : function(e){
37213         if(e.getKey() == e.TAB){
37214             this.triggerBlur();
37215         }
37216     },
37217
37218     // private
37219     onBlur : function(){
37220         // do nothing
37221     },
37222
37223     // private
37224     mimicBlur : function(e, t){
37225         if(!this.wrap.contains(t) && this.validateBlur()){
37226             this.triggerBlur();
37227         }
37228     },
37229
37230     // private
37231     triggerBlur : function(){
37232         this.mimicing = false;
37233         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
37234         if(this.monitorTab){
37235             this.el.un("keydown", this.checkTab, this);
37236         }
37237         this.wrap.removeClass('x-trigger-wrap-focus');
37238         Roo.form.TriggerField.superclass.onBlur.call(this);
37239     },
37240
37241     // private
37242     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
37243     validateBlur : function(e, t){
37244         return true;
37245     },
37246
37247     // private
37248     onDisable : function(){
37249         Roo.form.TriggerField.superclass.onDisable.call(this);
37250         if(this.wrap){
37251             this.wrap.addClass('x-item-disabled');
37252         }
37253     },
37254
37255     // private
37256     onEnable : function(){
37257         Roo.form.TriggerField.superclass.onEnable.call(this);
37258         if(this.wrap){
37259             this.wrap.removeClass('x-item-disabled');
37260         }
37261     },
37262
37263     // private
37264     onShow : function(){
37265         var ae = this.getActionEl();
37266         
37267         if(ae){
37268             ae.dom.style.display = '';
37269             ae.dom.style.visibility = 'visible';
37270         }
37271     },
37272
37273     // private
37274     
37275     onHide : function(){
37276         var ae = this.getActionEl();
37277         ae.dom.style.display = 'none';
37278     },
37279
37280     /**
37281      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
37282      * by an implementing function.
37283      * @method
37284      * @param {EventObject} e
37285      */
37286     onTriggerClick : Roo.emptyFn
37287 });
37288
37289 // TwinTriggerField is not a public class to be used directly.  It is meant as an abstract base class
37290 // to be extended by an implementing class.  For an example of implementing this class, see the custom
37291 // SearchField implementation here: http://extjs.com/deploy/ext/examples/form/custom.html
37292 Roo.form.TwinTriggerField = Roo.extend(Roo.form.TriggerField, {
37293     initComponent : function(){
37294         Roo.form.TwinTriggerField.superclass.initComponent.call(this);
37295
37296         this.triggerConfig = {
37297             tag:'span', cls:'x-form-twin-triggers', cn:[
37298             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger1Class},
37299             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger2Class}
37300         ]};
37301     },
37302
37303     getTrigger : function(index){
37304         return this.triggers[index];
37305     },
37306
37307     initTrigger : function(){
37308         var ts = this.trigger.select('.x-form-trigger', true);
37309         this.wrap.setStyle('overflow', 'hidden');
37310         var triggerField = this;
37311         ts.each(function(t, all, index){
37312             t.hide = function(){
37313                 var w = triggerField.wrap.getWidth();
37314                 this.dom.style.display = 'none';
37315                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
37316             };
37317             t.show = function(){
37318                 var w = triggerField.wrap.getWidth();
37319                 this.dom.style.display = '';
37320                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
37321             };
37322             var triggerIndex = 'Trigger'+(index+1);
37323
37324             if(this['hide'+triggerIndex]){
37325                 t.dom.style.display = 'none';
37326             }
37327             t.on("click", this['on'+triggerIndex+'Click'], this, {preventDefault:true});
37328             t.addClassOnOver('x-form-trigger-over');
37329             t.addClassOnClick('x-form-trigger-click');
37330         }, this);
37331         this.triggers = ts.elements;
37332     },
37333
37334     onTrigger1Click : Roo.emptyFn,
37335     onTrigger2Click : Roo.emptyFn
37336 });/*
37337  * Based on:
37338  * Ext JS Library 1.1.1
37339  * Copyright(c) 2006-2007, Ext JS, LLC.
37340  *
37341  * Originally Released Under LGPL - original licence link has changed is not relivant.
37342  *
37343  * Fork - LGPL
37344  * <script type="text/javascript">
37345  */
37346  
37347 /**
37348  * @class Roo.form.TextArea
37349  * @extends Roo.form.TextField
37350  * Multiline text field.  Can be used as a direct replacement for traditional textarea fields, plus adds
37351  * support for auto-sizing.
37352  * @constructor
37353  * Creates a new TextArea
37354  * @param {Object} config Configuration options
37355  */
37356 Roo.form.TextArea = function(config){
37357     Roo.form.TextArea.superclass.constructor.call(this, config);
37358     // these are provided exchanges for backwards compat
37359     // minHeight/maxHeight were replaced by growMin/growMax to be
37360     // compatible with TextField growing config values
37361     if(this.minHeight !== undefined){
37362         this.growMin = this.minHeight;
37363     }
37364     if(this.maxHeight !== undefined){
37365         this.growMax = this.maxHeight;
37366     }
37367 };
37368
37369 Roo.extend(Roo.form.TextArea, Roo.form.TextField,  {
37370     /**
37371      * @cfg {Number} growMin The minimum height to allow when grow = true (defaults to 60)
37372      */
37373     growMin : 60,
37374     /**
37375      * @cfg {Number} growMax The maximum height to allow when grow = true (defaults to 1000)
37376      */
37377     growMax: 1000,
37378     /**
37379      * @cfg {Boolean} preventScrollbars True to prevent scrollbars from appearing regardless of how much text is
37380      * in the field (equivalent to setting overflow: hidden, defaults to false)
37381      */
37382     preventScrollbars: false,
37383     /**
37384      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
37385      * {tag: "textarea", style: "width:300px;height:60px;", autocomplete: "off"})
37386      */
37387
37388     // private
37389     onRender : function(ct, position){
37390         if(!this.el){
37391             this.defaultAutoCreate = {
37392                 tag: "textarea",
37393                 style:"width:300px;height:60px;",
37394                 autocomplete: "off"
37395             };
37396         }
37397         Roo.form.TextArea.superclass.onRender.call(this, ct, position);
37398         if(this.grow){
37399             this.textSizeEl = Roo.DomHelper.append(document.body, {
37400                 tag: "pre", cls: "x-form-grow-sizer"
37401             });
37402             if(this.preventScrollbars){
37403                 this.el.setStyle("overflow", "hidden");
37404             }
37405             this.el.setHeight(this.growMin);
37406         }
37407     },
37408
37409     onDestroy : function(){
37410         if(this.textSizeEl){
37411             this.textSizeEl.parentNode.removeChild(this.textSizeEl);
37412         }
37413         Roo.form.TextArea.superclass.onDestroy.call(this);
37414     },
37415
37416     // private
37417     onKeyUp : function(e){
37418         if(!e.isNavKeyPress() || e.getKey() == e.ENTER){
37419             this.autoSize();
37420         }
37421     },
37422
37423     /**
37424      * Automatically grows the field to accomodate the height of the text up to the maximum field height allowed.
37425      * This only takes effect if grow = true, and fires the autosize event if the height changes.
37426      */
37427     autoSize : function(){
37428         if(!this.grow || !this.textSizeEl){
37429             return;
37430         }
37431         var el = this.el;
37432         var v = el.dom.value;
37433         var ts = this.textSizeEl;
37434
37435         ts.innerHTML = '';
37436         ts.appendChild(document.createTextNode(v));
37437         v = ts.innerHTML;
37438
37439         Roo.fly(ts).setWidth(this.el.getWidth());
37440         if(v.length < 1){
37441             v = "&#160;&#160;";
37442         }else{
37443             if(Roo.isIE){
37444                 v = v.replace(/\n/g, '<p>&#160;</p>');
37445             }
37446             v += "&#160;\n&#160;";
37447         }
37448         ts.innerHTML = v;
37449         var h = Math.min(this.growMax, Math.max(ts.offsetHeight, this.growMin));
37450         if(h != this.lastHeight){
37451             this.lastHeight = h;
37452             this.el.setHeight(h);
37453             this.fireEvent("autosize", this, h);
37454         }
37455     }
37456 });/*
37457  * Based on:
37458  * Ext JS Library 1.1.1
37459  * Copyright(c) 2006-2007, Ext JS, LLC.
37460  *
37461  * Originally Released Under LGPL - original licence link has changed is not relivant.
37462  *
37463  * Fork - LGPL
37464  * <script type="text/javascript">
37465  */
37466  
37467
37468 /**
37469  * @class Roo.form.NumberField
37470  * @extends Roo.form.TextField
37471  * Numeric text field that provides automatic keystroke filtering and numeric validation.
37472  * @constructor
37473  * Creates a new NumberField
37474  * @param {Object} config Configuration options
37475  */
37476 Roo.form.NumberField = function(config){
37477     Roo.form.NumberField.superclass.constructor.call(this, config);
37478 };
37479
37480 Roo.extend(Roo.form.NumberField, Roo.form.TextField,  {
37481     /**
37482      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field x-form-num-field")
37483      */
37484     fieldClass: "x-form-field x-form-num-field",
37485     /**
37486      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
37487      */
37488     allowDecimals : true,
37489     /**
37490      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
37491      */
37492     decimalSeparator : ".",
37493     /**
37494      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
37495      */
37496     decimalPrecision : 2,
37497     /**
37498      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
37499      */
37500     allowNegative : true,
37501     /**
37502      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
37503      */
37504     minValue : Number.NEGATIVE_INFINITY,
37505     /**
37506      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
37507      */
37508     maxValue : Number.MAX_VALUE,
37509     /**
37510      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
37511      */
37512     minText : "The minimum value for this field is {0}",
37513     /**
37514      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
37515      */
37516     maxText : "The maximum value for this field is {0}",
37517     /**
37518      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
37519      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
37520      */
37521     nanText : "{0} is not a valid number",
37522
37523     // private
37524     initEvents : function(){
37525         Roo.form.NumberField.superclass.initEvents.call(this);
37526         var allowed = "0123456789";
37527         if(this.allowDecimals){
37528             allowed += this.decimalSeparator;
37529         }
37530         if(this.allowNegative){
37531             allowed += "-";
37532         }
37533         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
37534         var keyPress = function(e){
37535             var k = e.getKey();
37536             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
37537                 return;
37538             }
37539             var c = e.getCharCode();
37540             if(allowed.indexOf(String.fromCharCode(c)) === -1){
37541                 e.stopEvent();
37542             }
37543         };
37544         this.el.on("keypress", keyPress, this);
37545     },
37546
37547     // private
37548     validateValue : function(value){
37549         if(!Roo.form.NumberField.superclass.validateValue.call(this, value)){
37550             return false;
37551         }
37552         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
37553              return true;
37554         }
37555         var num = this.parseValue(value);
37556         if(isNaN(num)){
37557             this.markInvalid(String.format(this.nanText, value));
37558             return false;
37559         }
37560         if(num < this.minValue){
37561             this.markInvalid(String.format(this.minText, this.minValue));
37562             return false;
37563         }
37564         if(num > this.maxValue){
37565             this.markInvalid(String.format(this.maxText, this.maxValue));
37566             return false;
37567         }
37568         return true;
37569     },
37570
37571     getValue : function(){
37572         return this.fixPrecision(this.parseValue(Roo.form.NumberField.superclass.getValue.call(this)));
37573     },
37574
37575     // private
37576     parseValue : function(value){
37577         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
37578         return isNaN(value) ? '' : value;
37579     },
37580
37581     // private
37582     fixPrecision : function(value){
37583         var nan = isNaN(value);
37584         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
37585             return nan ? '' : value;
37586         }
37587         return parseFloat(value).toFixed(this.decimalPrecision);
37588     },
37589
37590     setValue : function(v){
37591         v = this.fixPrecision(v);
37592         Roo.form.NumberField.superclass.setValue.call(this, String(v).replace(".", this.decimalSeparator));
37593     },
37594
37595     // private
37596     decimalPrecisionFcn : function(v){
37597         return Math.floor(v);
37598     },
37599
37600     beforeBlur : function(){
37601         var v = this.parseValue(this.getRawValue());
37602         if(v){
37603             this.setValue(v);
37604         }
37605     }
37606 });/*
37607  * Based on:
37608  * Ext JS Library 1.1.1
37609  * Copyright(c) 2006-2007, Ext JS, LLC.
37610  *
37611  * Originally Released Under LGPL - original licence link has changed is not relivant.
37612  *
37613  * Fork - LGPL
37614  * <script type="text/javascript">
37615  */
37616  
37617 /**
37618  * @class Roo.form.DateField
37619  * @extends Roo.form.TriggerField
37620  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
37621 * @constructor
37622 * Create a new DateField
37623 * @param {Object} config
37624  */
37625 Roo.form.DateField = function(config){
37626     Roo.form.DateField.superclass.constructor.call(this, config);
37627     
37628       this.addEvents({
37629          
37630         /**
37631          * @event select
37632          * Fires when a date is selected
37633              * @param {Roo.form.DateField} combo This combo box
37634              * @param {Date} date The date selected
37635              */
37636         'select' : true
37637          
37638     });
37639     
37640     
37641     if(typeof this.minValue == "string") this.minValue = this.parseDate(this.minValue);
37642     if(typeof this.maxValue == "string") this.maxValue = this.parseDate(this.maxValue);
37643     this.ddMatch = null;
37644     if(this.disabledDates){
37645         var dd = this.disabledDates;
37646         var re = "(?:";
37647         for(var i = 0; i < dd.length; i++){
37648             re += dd[i];
37649             if(i != dd.length-1) re += "|";
37650         }
37651         this.ddMatch = new RegExp(re + ")");
37652     }
37653 };
37654
37655 Roo.extend(Roo.form.DateField, Roo.form.TriggerField,  {
37656     /**
37657      * @cfg {String} format
37658      * The default date format string which can be overriden for localization support.  The format must be
37659      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
37660      */
37661     format : "m/d/y",
37662     /**
37663      * @cfg {String} altFormats
37664      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
37665      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
37666      */
37667     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
37668     /**
37669      * @cfg {Array} disabledDays
37670      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
37671      */
37672     disabledDays : null,
37673     /**
37674      * @cfg {String} disabledDaysText
37675      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
37676      */
37677     disabledDaysText : "Disabled",
37678     /**
37679      * @cfg {Array} disabledDates
37680      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
37681      * expression so they are very powerful. Some examples:
37682      * <ul>
37683      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
37684      * <li>["03/08", "09/16"] would disable those days for every year</li>
37685      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
37686      * <li>["03/../2006"] would disable every day in March 2006</li>
37687      * <li>["^03"] would disable every day in every March</li>
37688      * </ul>
37689      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
37690      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
37691      */
37692     disabledDates : null,
37693     /**
37694      * @cfg {String} disabledDatesText
37695      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
37696      */
37697     disabledDatesText : "Disabled",
37698     /**
37699      * @cfg {Date/String} minValue
37700      * The minimum allowed date. Can be either a Javascript date object or a string date in a
37701      * valid format (defaults to null).
37702      */
37703     minValue : null,
37704     /**
37705      * @cfg {Date/String} maxValue
37706      * The maximum allowed date. Can be either a Javascript date object or a string date in a
37707      * valid format (defaults to null).
37708      */
37709     maxValue : null,
37710     /**
37711      * @cfg {String} minText
37712      * The error text to display when the date in the cell is before minValue (defaults to
37713      * 'The date in this field must be after {minValue}').
37714      */
37715     minText : "The date in this field must be equal to or after {0}",
37716     /**
37717      * @cfg {String} maxText
37718      * The error text to display when the date in the cell is after maxValue (defaults to
37719      * 'The date in this field must be before {maxValue}').
37720      */
37721     maxText : "The date in this field must be equal to or before {0}",
37722     /**
37723      * @cfg {String} invalidText
37724      * The error text to display when the date in the field is invalid (defaults to
37725      * '{value} is not a valid date - it must be in the format {format}').
37726      */
37727     invalidText : "{0} is not a valid date - it must be in the format {1}",
37728     /**
37729      * @cfg {String} triggerClass
37730      * An additional CSS class used to style the trigger button.  The trigger will always get the
37731      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
37732      * which displays a calendar icon).
37733      */
37734     triggerClass : 'x-form-date-trigger',
37735     
37736
37737     /**
37738      * @cfg {Boolean} useIso
37739      * if enabled, then the date field will use a hidden field to store the 
37740      * real value as iso formated date. default (false)
37741      */ 
37742     useIso : false,
37743     /**
37744      * @cfg {String/Object} autoCreate
37745      * A DomHelper element spec, or true for a default element spec (defaults to
37746      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
37747      */ 
37748     // private
37749     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "off"},
37750     
37751     // private
37752     hiddenField: false,
37753     
37754     onRender : function(ct, position)
37755     {
37756         Roo.form.DateField.superclass.onRender.call(this, ct, position);
37757         if (this.useIso) {
37758             //this.el.dom.removeAttribute('name'); 
37759             Roo.log("Changing name?");
37760             this.el.dom.setAttribute('name', this.name + '____hidden___' ); 
37761             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
37762                     'before', true);
37763             this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
37764             // prevent input submission
37765             this.hiddenName = this.name;
37766         }
37767             
37768             
37769     },
37770     
37771     // private
37772     validateValue : function(value)
37773     {
37774         value = this.formatDate(value);
37775         if(!Roo.form.DateField.superclass.validateValue.call(this, value)){
37776             Roo.log('super failed');
37777             return false;
37778         }
37779         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
37780              return true;
37781         }
37782         var svalue = value;
37783         value = this.parseDate(value);
37784         if(!value){
37785             Roo.log('parse date failed' + svalue);
37786             this.markInvalid(String.format(this.invalidText, svalue, this.format));
37787             return false;
37788         }
37789         var time = value.getTime();
37790         if(this.minValue && time < this.minValue.getTime()){
37791             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
37792             return false;
37793         }
37794         if(this.maxValue && time > this.maxValue.getTime()){
37795             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
37796             return false;
37797         }
37798         if(this.disabledDays){
37799             var day = value.getDay();
37800             for(var i = 0; i < this.disabledDays.length; i++) {
37801                 if(day === this.disabledDays[i]){
37802                     this.markInvalid(this.disabledDaysText);
37803                     return false;
37804                 }
37805             }
37806         }
37807         var fvalue = this.formatDate(value);
37808         if(this.ddMatch && this.ddMatch.test(fvalue)){
37809             this.markInvalid(String.format(this.disabledDatesText, fvalue));
37810             return false;
37811         }
37812         return true;
37813     },
37814
37815     // private
37816     // Provides logic to override the default TriggerField.validateBlur which just returns true
37817     validateBlur : function(){
37818         return !this.menu || !this.menu.isVisible();
37819     },
37820     
37821     getName: function()
37822     {
37823         // returns hidden if it's set..
37824         if (!this.rendered) {return ''};
37825         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
37826         
37827     },
37828
37829     /**
37830      * Returns the current date value of the date field.
37831      * @return {Date} The date value
37832      */
37833     getValue : function(){
37834         
37835         return  this.hiddenField ?
37836                 this.hiddenField.value :
37837                 this.parseDate(Roo.form.DateField.superclass.getValue.call(this)) || "";
37838     },
37839
37840     /**
37841      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
37842      * date, using DateField.format as the date format, according to the same rules as {@link Date#parseDate}
37843      * (the default format used is "m/d/y").
37844      * <br />Usage:
37845      * <pre><code>
37846 //All of these calls set the same date value (May 4, 2006)
37847
37848 //Pass a date object:
37849 var dt = new Date('5/4/06');
37850 dateField.setValue(dt);
37851
37852 //Pass a date string (default format):
37853 dateField.setValue('5/4/06');
37854
37855 //Pass a date string (custom format):
37856 dateField.format = 'Y-m-d';
37857 dateField.setValue('2006-5-4');
37858 </code></pre>
37859      * @param {String/Date} date The date or valid date string
37860      */
37861     setValue : function(date){
37862         if (this.hiddenField) {
37863             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
37864         }
37865         Roo.form.DateField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
37866         // make sure the value field is always stored as a date..
37867         this.value = this.parseDate(date);
37868         
37869         
37870     },
37871
37872     // private
37873     parseDate : function(value){
37874         if(!value || value instanceof Date){
37875             return value;
37876         }
37877         var v = Date.parseDate(value, this.format);
37878          if (!v && this.useIso) {
37879             v = Date.parseDate(value, 'Y-m-d');
37880         }
37881         if(!v && this.altFormats){
37882             if(!this.altFormatsArray){
37883                 this.altFormatsArray = this.altFormats.split("|");
37884             }
37885             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
37886                 v = Date.parseDate(value, this.altFormatsArray[i]);
37887             }
37888         }
37889         return v;
37890     },
37891
37892     // private
37893     formatDate : function(date, fmt){
37894         return (!date || !(date instanceof Date)) ?
37895                date : date.dateFormat(fmt || this.format);
37896     },
37897
37898     // private
37899     menuListeners : {
37900         select: function(m, d){
37901             
37902             this.setValue(d);
37903             this.fireEvent('select', this, d);
37904         },
37905         show : function(){ // retain focus styling
37906             this.onFocus();
37907         },
37908         hide : function(){
37909             this.focus.defer(10, this);
37910             var ml = this.menuListeners;
37911             this.menu.un("select", ml.select,  this);
37912             this.menu.un("show", ml.show,  this);
37913             this.menu.un("hide", ml.hide,  this);
37914         }
37915     },
37916
37917     // private
37918     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
37919     onTriggerClick : function(){
37920         if(this.disabled){
37921             return;
37922         }
37923         if(this.menu == null){
37924             this.menu = new Roo.menu.DateMenu();
37925         }
37926         Roo.apply(this.menu.picker,  {
37927             showClear: this.allowBlank,
37928             minDate : this.minValue,
37929             maxDate : this.maxValue,
37930             disabledDatesRE : this.ddMatch,
37931             disabledDatesText : this.disabledDatesText,
37932             disabledDays : this.disabledDays,
37933             disabledDaysText : this.disabledDaysText,
37934             format : this.useIso ? 'Y-m-d' : this.format,
37935             minText : String.format(this.minText, this.formatDate(this.minValue)),
37936             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
37937         });
37938         this.menu.on(Roo.apply({}, this.menuListeners, {
37939             scope:this
37940         }));
37941         this.menu.picker.setValue(this.getValue() || new Date());
37942         this.menu.show(this.el, "tl-bl?");
37943     },
37944
37945     beforeBlur : function(){
37946         var v = this.parseDate(this.getRawValue());
37947         if(v){
37948             this.setValue(v);
37949         }
37950     }
37951
37952     /** @cfg {Boolean} grow @hide */
37953     /** @cfg {Number} growMin @hide */
37954     /** @cfg {Number} growMax @hide */
37955     /**
37956      * @hide
37957      * @method autoSize
37958      */
37959 });/*
37960  * Based on:
37961  * Ext JS Library 1.1.1
37962  * Copyright(c) 2006-2007, Ext JS, LLC.
37963  *
37964  * Originally Released Under LGPL - original licence link has changed is not relivant.
37965  *
37966  * Fork - LGPL
37967  * <script type="text/javascript">
37968  */
37969  
37970 /**
37971  * @class Roo.form.MonthField
37972  * @extends Roo.form.TriggerField
37973  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
37974 * @constructor
37975 * Create a new MonthField
37976 * @param {Object} config
37977  */
37978 Roo.form.MonthField = function(config){
37979     
37980     Roo.form.MonthField.superclass.constructor.call(this, config);
37981     
37982       this.addEvents({
37983          
37984         /**
37985          * @event select
37986          * Fires when a date is selected
37987              * @param {Roo.form.MonthFieeld} combo This combo box
37988              * @param {Date} date The date selected
37989              */
37990         'select' : true
37991          
37992     });
37993     
37994     
37995     if(typeof this.minValue == "string") this.minValue = this.parseDate(this.minValue);
37996     if(typeof this.maxValue == "string") this.maxValue = this.parseDate(this.maxValue);
37997     this.ddMatch = null;
37998     if(this.disabledDates){
37999         var dd = this.disabledDates;
38000         var re = "(?:";
38001         for(var i = 0; i < dd.length; i++){
38002             re += dd[i];
38003             if(i != dd.length-1) re += "|";
38004         }
38005         this.ddMatch = new RegExp(re + ")");
38006     }
38007 };
38008
38009 Roo.extend(Roo.form.MonthField, Roo.form.TriggerField,  {
38010     /**
38011      * @cfg {String} format
38012      * The default date format string which can be overriden for localization support.  The format must be
38013      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
38014      */
38015     format : "M Y",
38016     /**
38017      * @cfg {String} altFormats
38018      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
38019      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
38020      */
38021     altFormats : "M Y|m/Y|m-y|m-Y|my|mY",
38022     /**
38023      * @cfg {Array} disabledDays
38024      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
38025      */
38026     disabledDays : [0,1,2,3,4,5,6],
38027     /**
38028      * @cfg {String} disabledDaysText
38029      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
38030      */
38031     disabledDaysText : "Disabled",
38032     /**
38033      * @cfg {Array} disabledDates
38034      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
38035      * expression so they are very powerful. Some examples:
38036      * <ul>
38037      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
38038      * <li>["03/08", "09/16"] would disable those days for every year</li>
38039      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
38040      * <li>["03/../2006"] would disable every day in March 2006</li>
38041      * <li>["^03"] would disable every day in every March</li>
38042      * </ul>
38043      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
38044      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
38045      */
38046     disabledDates : null,
38047     /**
38048      * @cfg {String} disabledDatesText
38049      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
38050      */
38051     disabledDatesText : "Disabled",
38052     /**
38053      * @cfg {Date/String} minValue
38054      * The minimum allowed date. Can be either a Javascript date object or a string date in a
38055      * valid format (defaults to null).
38056      */
38057     minValue : null,
38058     /**
38059      * @cfg {Date/String} maxValue
38060      * The maximum allowed date. Can be either a Javascript date object or a string date in a
38061      * valid format (defaults to null).
38062      */
38063     maxValue : null,
38064     /**
38065      * @cfg {String} minText
38066      * The error text to display when the date in the cell is before minValue (defaults to
38067      * 'The date in this field must be after {minValue}').
38068      */
38069     minText : "The date in this field must be equal to or after {0}",
38070     /**
38071      * @cfg {String} maxTextf
38072      * The error text to display when the date in the cell is after maxValue (defaults to
38073      * 'The date in this field must be before {maxValue}').
38074      */
38075     maxText : "The date in this field must be equal to or before {0}",
38076     /**
38077      * @cfg {String} invalidText
38078      * The error text to display when the date in the field is invalid (defaults to
38079      * '{value} is not a valid date - it must be in the format {format}').
38080      */
38081     invalidText : "{0} is not a valid date - it must be in the format {1}",
38082     /**
38083      * @cfg {String} triggerClass
38084      * An additional CSS class used to style the trigger button.  The trigger will always get the
38085      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
38086      * which displays a calendar icon).
38087      */
38088     triggerClass : 'x-form-date-trigger',
38089     
38090
38091     /**
38092      * @cfg {Boolean} useIso
38093      * if enabled, then the date field will use a hidden field to store the 
38094      * real value as iso formated date. default (true)
38095      */ 
38096     useIso : true,
38097     /**
38098      * @cfg {String/Object} autoCreate
38099      * A DomHelper element spec, or true for a default element spec (defaults to
38100      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
38101      */ 
38102     // private
38103     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "off"},
38104     
38105     // private
38106     hiddenField: false,
38107     
38108     hideMonthPicker : false,
38109     
38110     onRender : function(ct, position)
38111     {
38112         Roo.form.MonthField.superclass.onRender.call(this, ct, position);
38113         if (this.useIso) {
38114             this.el.dom.removeAttribute('name'); 
38115             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
38116                     'before', true);
38117             this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
38118             // prevent input submission
38119             this.hiddenName = this.name;
38120         }
38121             
38122             
38123     },
38124     
38125     // private
38126     validateValue : function(value)
38127     {
38128         value = this.formatDate(value);
38129         if(!Roo.form.MonthField.superclass.validateValue.call(this, value)){
38130             return false;
38131         }
38132         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
38133              return true;
38134         }
38135         var svalue = value;
38136         value = this.parseDate(value);
38137         if(!value){
38138             this.markInvalid(String.format(this.invalidText, svalue, this.format));
38139             return false;
38140         }
38141         var time = value.getTime();
38142         if(this.minValue && time < this.minValue.getTime()){
38143             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
38144             return false;
38145         }
38146         if(this.maxValue && time > this.maxValue.getTime()){
38147             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
38148             return false;
38149         }
38150         /*if(this.disabledDays){
38151             var day = value.getDay();
38152             for(var i = 0; i < this.disabledDays.length; i++) {
38153                 if(day === this.disabledDays[i]){
38154                     this.markInvalid(this.disabledDaysText);
38155                     return false;
38156                 }
38157             }
38158         }
38159         */
38160         var fvalue = this.formatDate(value);
38161         /*if(this.ddMatch && this.ddMatch.test(fvalue)){
38162             this.markInvalid(String.format(this.disabledDatesText, fvalue));
38163             return false;
38164         }
38165         */
38166         return true;
38167     },
38168
38169     // private
38170     // Provides logic to override the default TriggerField.validateBlur which just returns true
38171     validateBlur : function(){
38172         return !this.menu || !this.menu.isVisible();
38173     },
38174
38175     /**
38176      * Returns the current date value of the date field.
38177      * @return {Date} The date value
38178      */
38179     getValue : function(){
38180         
38181         
38182         
38183         return  this.hiddenField ?
38184                 this.hiddenField.value :
38185                 this.parseDate(Roo.form.MonthField.superclass.getValue.call(this)) || "";
38186     },
38187
38188     /**
38189      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
38190      * date, using MonthField.format as the date format, according to the same rules as {@link Date#parseDate}
38191      * (the default format used is "m/d/y").
38192      * <br />Usage:
38193      * <pre><code>
38194 //All of these calls set the same date value (May 4, 2006)
38195
38196 //Pass a date object:
38197 var dt = new Date('5/4/06');
38198 monthField.setValue(dt);
38199
38200 //Pass a date string (default format):
38201 monthField.setValue('5/4/06');
38202
38203 //Pass a date string (custom format):
38204 monthField.format = 'Y-m-d';
38205 monthField.setValue('2006-5-4');
38206 </code></pre>
38207      * @param {String/Date} date The date or valid date string
38208      */
38209     setValue : function(date){
38210         Roo.log('month setValue' + date);
38211         // can only be first of month..
38212         
38213         var val = this.parseDate(date);
38214         
38215         if (this.hiddenField) {
38216             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
38217         }
38218         Roo.form.MonthField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
38219         this.value = this.parseDate(date);
38220     },
38221
38222     // private
38223     parseDate : function(value){
38224         if(!value || value instanceof Date){
38225             value = value ? Date.parseDate(value.format('Y-m') + '-01', 'Y-m-d') : null;
38226             return value;
38227         }
38228         var v = Date.parseDate(value, this.format);
38229         if (!v && this.useIso) {
38230             v = Date.parseDate(value, 'Y-m-d');
38231         }
38232         if (v) {
38233             // 
38234             v = Date.parseDate(v.format('Y-m') +'-01', 'Y-m-d');
38235         }
38236         
38237         
38238         if(!v && this.altFormats){
38239             if(!this.altFormatsArray){
38240                 this.altFormatsArray = this.altFormats.split("|");
38241             }
38242             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
38243                 v = Date.parseDate(value, this.altFormatsArray[i]);
38244             }
38245         }
38246         return v;
38247     },
38248
38249     // private
38250     formatDate : function(date, fmt){
38251         return (!date || !(date instanceof Date)) ?
38252                date : date.dateFormat(fmt || this.format);
38253     },
38254
38255     // private
38256     menuListeners : {
38257         select: function(m, d){
38258             this.setValue(d);
38259             this.fireEvent('select', this, d);
38260         },
38261         show : function(){ // retain focus styling
38262             this.onFocus();
38263         },
38264         hide : function(){
38265             this.focus.defer(10, this);
38266             var ml = this.menuListeners;
38267             this.menu.un("select", ml.select,  this);
38268             this.menu.un("show", ml.show,  this);
38269             this.menu.un("hide", ml.hide,  this);
38270         }
38271     },
38272     // private
38273     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
38274     onTriggerClick : function(){
38275         if(this.disabled){
38276             return;
38277         }
38278         if(this.menu == null){
38279             this.menu = new Roo.menu.DateMenu();
38280            
38281         }
38282         
38283         Roo.apply(this.menu.picker,  {
38284             
38285             showClear: this.allowBlank,
38286             minDate : this.minValue,
38287             maxDate : this.maxValue,
38288             disabledDatesRE : this.ddMatch,
38289             disabledDatesText : this.disabledDatesText,
38290             
38291             format : this.useIso ? 'Y-m-d' : this.format,
38292             minText : String.format(this.minText, this.formatDate(this.minValue)),
38293             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
38294             
38295         });
38296          this.menu.on(Roo.apply({}, this.menuListeners, {
38297             scope:this
38298         }));
38299        
38300         
38301         var m = this.menu;
38302         var p = m.picker;
38303         
38304         // hide month picker get's called when we called by 'before hide';
38305         
38306         var ignorehide = true;
38307         p.hideMonthPicker  = function(disableAnim){
38308             if (ignorehide) {
38309                 return;
38310             }
38311              if(this.monthPicker){
38312                 Roo.log("hideMonthPicker called");
38313                 if(disableAnim === true){
38314                     this.monthPicker.hide();
38315                 }else{
38316                     this.monthPicker.slideOut('t', {duration:.2});
38317                     p.setValue(new Date(m.picker.mpSelYear, m.picker.mpSelMonth, 1));
38318                     p.fireEvent("select", this, this.value);
38319                     m.hide();
38320                 }
38321             }
38322         }
38323         
38324         Roo.log('picker set value');
38325         Roo.log(this.getValue());
38326         p.setValue(this.getValue() ? this.parseDate(this.getValue()) : new Date());
38327         m.show(this.el, 'tl-bl?');
38328         ignorehide  = false;
38329         // this will trigger hideMonthPicker..
38330         
38331         
38332         // hidden the day picker
38333         Roo.select('.x-date-picker table', true).first().dom.style.visibility = "hidden";
38334         
38335         
38336         
38337       
38338         
38339         p.showMonthPicker.defer(100, p);
38340     
38341         
38342        
38343     },
38344
38345     beforeBlur : function(){
38346         var v = this.parseDate(this.getRawValue());
38347         if(v){
38348             this.setValue(v);
38349         }
38350     }
38351
38352     /** @cfg {Boolean} grow @hide */
38353     /** @cfg {Number} growMin @hide */
38354     /** @cfg {Number} growMax @hide */
38355     /**
38356      * @hide
38357      * @method autoSize
38358      */
38359 });/*
38360  * Based on:
38361  * Ext JS Library 1.1.1
38362  * Copyright(c) 2006-2007, Ext JS, LLC.
38363  *
38364  * Originally Released Under LGPL - original licence link has changed is not relivant.
38365  *
38366  * Fork - LGPL
38367  * <script type="text/javascript">
38368  */
38369  
38370
38371 /**
38372  * @class Roo.form.ComboBox
38373  * @extends Roo.form.TriggerField
38374  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
38375  * @constructor
38376  * Create a new ComboBox.
38377  * @param {Object} config Configuration options
38378  */
38379 Roo.form.ComboBox = function(config){
38380     Roo.form.ComboBox.superclass.constructor.call(this, config);
38381     this.addEvents({
38382         /**
38383          * @event expand
38384          * Fires when the dropdown list is expanded
38385              * @param {Roo.form.ComboBox} combo This combo box
38386              */
38387         'expand' : true,
38388         /**
38389          * @event collapse
38390          * Fires when the dropdown list is collapsed
38391              * @param {Roo.form.ComboBox} combo This combo box
38392              */
38393         'collapse' : true,
38394         /**
38395          * @event beforeselect
38396          * Fires before a list item is selected. Return false to cancel the selection.
38397              * @param {Roo.form.ComboBox} combo This combo box
38398              * @param {Roo.data.Record} record The data record returned from the underlying store
38399              * @param {Number} index The index of the selected item in the dropdown list
38400              */
38401         'beforeselect' : true,
38402         /**
38403          * @event select
38404          * Fires when a list item is selected
38405              * @param {Roo.form.ComboBox} combo This combo box
38406              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
38407              * @param {Number} index The index of the selected item in the dropdown list
38408              */
38409         'select' : true,
38410         /**
38411          * @event beforequery
38412          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
38413          * The event object passed has these properties:
38414              * @param {Roo.form.ComboBox} combo This combo box
38415              * @param {String} query The query
38416              * @param {Boolean} forceAll true to force "all" query
38417              * @param {Boolean} cancel true to cancel the query
38418              * @param {Object} e The query event object
38419              */
38420         'beforequery': true,
38421          /**
38422          * @event add
38423          * Fires when the 'add' icon is pressed (add a listener to enable add button)
38424              * @param {Roo.form.ComboBox} combo This combo box
38425              */
38426         'add' : true,
38427         /**
38428          * @event edit
38429          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
38430              * @param {Roo.form.ComboBox} combo This combo box
38431              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
38432              */
38433         'edit' : true
38434         
38435         
38436     });
38437     if(this.transform){
38438         this.allowDomMove = false;
38439         var s = Roo.getDom(this.transform);
38440         if(!this.hiddenName){
38441             this.hiddenName = s.name;
38442         }
38443         if(!this.store){
38444             this.mode = 'local';
38445             var d = [], opts = s.options;
38446             for(var i = 0, len = opts.length;i < len; i++){
38447                 var o = opts[i];
38448                 var value = (Roo.isIE ? o.getAttributeNode('value').specified : o.hasAttribute('value')) ? o.value : o.text;
38449                 if(o.selected) {
38450                     this.value = value;
38451                 }
38452                 d.push([value, o.text]);
38453             }
38454             this.store = new Roo.data.SimpleStore({
38455                 'id': 0,
38456                 fields: ['value', 'text'],
38457                 data : d
38458             });
38459             this.valueField = 'value';
38460             this.displayField = 'text';
38461         }
38462         s.name = Roo.id(); // wipe out the name in case somewhere else they have a reference
38463         if(!this.lazyRender){
38464             this.target = true;
38465             this.el = Roo.DomHelper.insertBefore(s, this.autoCreate || this.defaultAutoCreate);
38466             s.parentNode.removeChild(s); // remove it
38467             this.render(this.el.parentNode);
38468         }else{
38469             s.parentNode.removeChild(s); // remove it
38470         }
38471
38472     }
38473     if (this.store) {
38474         this.store = Roo.factory(this.store, Roo.data);
38475     }
38476     
38477     this.selectedIndex = -1;
38478     if(this.mode == 'local'){
38479         if(config.queryDelay === undefined){
38480             this.queryDelay = 10;
38481         }
38482         if(config.minChars === undefined){
38483             this.minChars = 0;
38484         }
38485     }
38486 };
38487
38488 Roo.extend(Roo.form.ComboBox, Roo.form.TriggerField, {
38489     /**
38490      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
38491      */
38492     /**
38493      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
38494      * rendering into an Roo.Editor, defaults to false)
38495      */
38496     /**
38497      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
38498      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
38499      */
38500     /**
38501      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
38502      */
38503     /**
38504      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
38505      * the dropdown list (defaults to undefined, with no header element)
38506      */
38507
38508      /**
38509      * @cfg {String/Roo.Template} tpl The template to use to render the output
38510      */
38511      
38512     // private
38513     defaultAutoCreate : {tag: "input", type: "text", size: "24", autocomplete: "off"},
38514     /**
38515      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
38516      */
38517     listWidth: undefined,
38518     /**
38519      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
38520      * mode = 'remote' or 'text' if mode = 'local')
38521      */
38522     displayField: undefined,
38523     /**
38524      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
38525      * mode = 'remote' or 'value' if mode = 'local'). 
38526      * Note: use of a valueField requires the user make a selection
38527      * in order for a value to be mapped.
38528      */
38529     valueField: undefined,
38530     
38531     
38532     /**
38533      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
38534      * field's data value (defaults to the underlying DOM element's name)
38535      */
38536     hiddenName: undefined,
38537     /**
38538      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
38539      */
38540     listClass: '',
38541     /**
38542      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
38543      */
38544     selectedClass: 'x-combo-selected',
38545     /**
38546      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
38547      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
38548      * which displays a downward arrow icon).
38549      */
38550     triggerClass : 'x-form-arrow-trigger',
38551     /**
38552      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
38553      */
38554     shadow:'sides',
38555     /**
38556      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
38557      * anchor positions (defaults to 'tl-bl')
38558      */
38559     listAlign: 'tl-bl?',
38560     /**
38561      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
38562      */
38563     maxHeight: 300,
38564     /**
38565      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
38566      * query specified by the allQuery config option (defaults to 'query')
38567      */
38568     triggerAction: 'query',
38569     /**
38570      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
38571      * (defaults to 4, does not apply if editable = false)
38572      */
38573     minChars : 4,
38574     /**
38575      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
38576      * delay (typeAheadDelay) if it matches a known value (defaults to false)
38577      */
38578     typeAhead: false,
38579     /**
38580      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
38581      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
38582      */
38583     queryDelay: 500,
38584     /**
38585      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
38586      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
38587      */
38588     pageSize: 0,
38589     /**
38590      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
38591      * when editable = true (defaults to false)
38592      */
38593     selectOnFocus:false,
38594     /**
38595      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
38596      */
38597     queryParam: 'query',
38598     /**
38599      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
38600      * when mode = 'remote' (defaults to 'Loading...')
38601      */
38602     loadingText: 'Loading...',
38603     /**
38604      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
38605      */
38606     resizable: false,
38607     /**
38608      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
38609      */
38610     handleHeight : 8,
38611     /**
38612      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
38613      * traditional select (defaults to true)
38614      */
38615     editable: true,
38616     /**
38617      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
38618      */
38619     allQuery: '',
38620     /**
38621      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
38622      */
38623     mode: 'remote',
38624     /**
38625      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
38626      * listWidth has a higher value)
38627      */
38628     minListWidth : 70,
38629     /**
38630      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
38631      * allow the user to set arbitrary text into the field (defaults to false)
38632      */
38633     forceSelection:false,
38634     /**
38635      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
38636      * if typeAhead = true (defaults to 250)
38637      */
38638     typeAheadDelay : 250,
38639     /**
38640      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
38641      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
38642      */
38643     valueNotFoundText : undefined,
38644     /**
38645      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
38646      */
38647     blockFocus : false,
38648     
38649     /**
38650      * @cfg {Boolean} disableClear Disable showing of clear button.
38651      */
38652     disableClear : false,
38653     /**
38654      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
38655      */
38656     alwaysQuery : false,
38657     
38658     //private
38659     addicon : false,
38660     editicon: false,
38661     
38662     // element that contains real text value.. (when hidden is used..)
38663      
38664     // private
38665     onRender : function(ct, position){
38666         Roo.form.ComboBox.superclass.onRender.call(this, ct, position);
38667         if(this.hiddenName){
38668             this.hiddenField = this.el.insertSibling({tag:'input', type:'hidden', name: this.hiddenName, id:  (this.hiddenId||this.hiddenName)},
38669                     'before', true);
38670             this.hiddenField.value =
38671                 this.hiddenValue !== undefined ? this.hiddenValue :
38672                 this.value !== undefined ? this.value : '';
38673
38674             // prevent input submission
38675             this.el.dom.removeAttribute('name');
38676              
38677              
38678         }
38679         if(Roo.isGecko){
38680             this.el.dom.setAttribute('autocomplete', 'off');
38681         }
38682
38683         var cls = 'x-combo-list';
38684
38685         this.list = new Roo.Layer({
38686             shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
38687         });
38688
38689         var lw = this.listWidth || Math.max(this.wrap.getWidth(), this.minListWidth);
38690         this.list.setWidth(lw);
38691         this.list.swallowEvent('mousewheel');
38692         this.assetHeight = 0;
38693
38694         if(this.title){
38695             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
38696             this.assetHeight += this.header.getHeight();
38697         }
38698
38699         this.innerList = this.list.createChild({cls:cls+'-inner'});
38700         this.innerList.on('mouseover', this.onViewOver, this);
38701         this.innerList.on('mousemove', this.onViewMove, this);
38702         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
38703         
38704         if(this.allowBlank && !this.pageSize && !this.disableClear){
38705             this.footer = this.list.createChild({cls:cls+'-ft'});
38706             this.pageTb = new Roo.Toolbar(this.footer);
38707            
38708         }
38709         if(this.pageSize){
38710             this.footer = this.list.createChild({cls:cls+'-ft'});
38711             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
38712                     {pageSize: this.pageSize});
38713             
38714         }
38715         
38716         if (this.pageTb && this.allowBlank && !this.disableClear) {
38717             var _this = this;
38718             this.pageTb.add(new Roo.Toolbar.Fill(), {
38719                 cls: 'x-btn-icon x-btn-clear',
38720                 text: '&#160;',
38721                 handler: function()
38722                 {
38723                     _this.collapse();
38724                     _this.clearValue();
38725                     _this.onSelect(false, -1);
38726                 }
38727             });
38728         }
38729         if (this.footer) {
38730             this.assetHeight += this.footer.getHeight();
38731         }
38732         
38733
38734         if(!this.tpl){
38735             this.tpl = '<div class="'+cls+'-item">{' + this.displayField + '}</div>';
38736         }
38737
38738         this.view = new Roo.View(this.innerList, this.tpl, {
38739             singleSelect:true, store: this.store, selectedClass: this.selectedClass
38740         });
38741
38742         this.view.on('click', this.onViewClick, this);
38743
38744         this.store.on('beforeload', this.onBeforeLoad, this);
38745         this.store.on('load', this.onLoad, this);
38746         this.store.on('loadexception', this.onLoadException, this);
38747
38748         if(this.resizable){
38749             this.resizer = new Roo.Resizable(this.list,  {
38750                pinned:true, handles:'se'
38751             });
38752             this.resizer.on('resize', function(r, w, h){
38753                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
38754                 this.listWidth = w;
38755                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
38756                 this.restrictHeight();
38757             }, this);
38758             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
38759         }
38760         if(!this.editable){
38761             this.editable = true;
38762             this.setEditable(false);
38763         }  
38764         
38765         
38766         if (typeof(this.events.add.listeners) != 'undefined') {
38767             
38768             this.addicon = this.wrap.createChild(
38769                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
38770        
38771             this.addicon.on('click', function(e) {
38772                 this.fireEvent('add', this);
38773             }, this);
38774         }
38775         if (typeof(this.events.edit.listeners) != 'undefined') {
38776             
38777             this.editicon = this.wrap.createChild(
38778                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
38779             if (this.addicon) {
38780                 this.editicon.setStyle('margin-left', '40px');
38781             }
38782             this.editicon.on('click', function(e) {
38783                 
38784                 // we fire even  if inothing is selected..
38785                 this.fireEvent('edit', this, this.lastData );
38786                 
38787             }, this);
38788         }
38789         
38790         
38791         
38792     },
38793
38794     // private
38795     initEvents : function(){
38796         Roo.form.ComboBox.superclass.initEvents.call(this);
38797
38798         this.keyNav = new Roo.KeyNav(this.el, {
38799             "up" : function(e){
38800                 this.inKeyMode = true;
38801                 this.selectPrev();
38802             },
38803
38804             "down" : function(e){
38805                 if(!this.isExpanded()){
38806                     this.onTriggerClick();
38807                 }else{
38808                     this.inKeyMode = true;
38809                     this.selectNext();
38810                 }
38811             },
38812
38813             "enter" : function(e){
38814                 this.onViewClick();
38815                 //return true;
38816             },
38817
38818             "esc" : function(e){
38819                 this.collapse();
38820             },
38821
38822             "tab" : function(e){
38823                 this.onViewClick(false);
38824                 this.fireEvent("specialkey", this, e);
38825                 return true;
38826             },
38827
38828             scope : this,
38829
38830             doRelay : function(foo, bar, hname){
38831                 if(hname == 'down' || this.scope.isExpanded()){
38832                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
38833                 }
38834                 return true;
38835             },
38836
38837             forceKeyDown: true
38838         });
38839         this.queryDelay = Math.max(this.queryDelay || 10,
38840                 this.mode == 'local' ? 10 : 250);
38841         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
38842         if(this.typeAhead){
38843             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
38844         }
38845         if(this.editable !== false){
38846             this.el.on("keyup", this.onKeyUp, this);
38847         }
38848         if(this.forceSelection){
38849             this.on('blur', this.doForce, this);
38850         }
38851     },
38852
38853     onDestroy : function(){
38854         if(this.view){
38855             this.view.setStore(null);
38856             this.view.el.removeAllListeners();
38857             this.view.el.remove();
38858             this.view.purgeListeners();
38859         }
38860         if(this.list){
38861             this.list.destroy();
38862         }
38863         if(this.store){
38864             this.store.un('beforeload', this.onBeforeLoad, this);
38865             this.store.un('load', this.onLoad, this);
38866             this.store.un('loadexception', this.onLoadException, this);
38867         }
38868         Roo.form.ComboBox.superclass.onDestroy.call(this);
38869     },
38870
38871     // private
38872     fireKey : function(e){
38873         if(e.isNavKeyPress() && !this.list.isVisible()){
38874             this.fireEvent("specialkey", this, e);
38875         }
38876     },
38877
38878     // private
38879     onResize: function(w, h){
38880         Roo.form.ComboBox.superclass.onResize.apply(this, arguments);
38881         
38882         if(typeof w != 'number'){
38883             // we do not handle it!?!?
38884             return;
38885         }
38886         var tw = this.trigger.getWidth();
38887         tw += this.addicon ? this.addicon.getWidth() : 0;
38888         tw += this.editicon ? this.editicon.getWidth() : 0;
38889         var x = w - tw;
38890         this.el.setWidth( this.adjustWidth('input', x));
38891             
38892         this.trigger.setStyle('left', x+'px');
38893         
38894         if(this.list && this.listWidth === undefined){
38895             var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
38896             this.list.setWidth(lw);
38897             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
38898         }
38899         
38900     
38901         
38902     },
38903
38904     /**
38905      * Allow or prevent the user from directly editing the field text.  If false is passed,
38906      * the user will only be able to select from the items defined in the dropdown list.  This method
38907      * is the runtime equivalent of setting the 'editable' config option at config time.
38908      * @param {Boolean} value True to allow the user to directly edit the field text
38909      */
38910     setEditable : function(value){
38911         if(value == this.editable){
38912             return;
38913         }
38914         this.editable = value;
38915         if(!value){
38916             this.el.dom.setAttribute('readOnly', true);
38917             this.el.on('mousedown', this.onTriggerClick,  this);
38918             this.el.addClass('x-combo-noedit');
38919         }else{
38920             this.el.dom.setAttribute('readOnly', false);
38921             this.el.un('mousedown', this.onTriggerClick,  this);
38922             this.el.removeClass('x-combo-noedit');
38923         }
38924     },
38925
38926     // private
38927     onBeforeLoad : function(){
38928         if(!this.hasFocus){
38929             return;
38930         }
38931         this.innerList.update(this.loadingText ?
38932                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
38933         this.restrictHeight();
38934         this.selectedIndex = -1;
38935     },
38936
38937     // private
38938     onLoad : function(){
38939         if(!this.hasFocus){
38940             return;
38941         }
38942         if(this.store.getCount() > 0){
38943             this.expand();
38944             this.restrictHeight();
38945             if(this.lastQuery == this.allQuery){
38946                 if(this.editable){
38947                     this.el.dom.select();
38948                 }
38949                 if(!this.selectByValue(this.value, true)){
38950                     this.select(0, true);
38951                 }
38952             }else{
38953                 this.selectNext();
38954                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
38955                     this.taTask.delay(this.typeAheadDelay);
38956                 }
38957             }
38958         }else{
38959             this.onEmptyResults();
38960         }
38961         //this.el.focus();
38962     },
38963     // private
38964     onLoadException : function()
38965     {
38966         this.collapse();
38967         Roo.log(this.store.reader.jsonData);
38968         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
38969             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
38970         }
38971         
38972         
38973     },
38974     // private
38975     onTypeAhead : function(){
38976         if(this.store.getCount() > 0){
38977             var r = this.store.getAt(0);
38978             var newValue = r.data[this.displayField];
38979             var len = newValue.length;
38980             var selStart = this.getRawValue().length;
38981             if(selStart != len){
38982                 this.setRawValue(newValue);
38983                 this.selectText(selStart, newValue.length);
38984             }
38985         }
38986     },
38987
38988     // private
38989     onSelect : function(record, index){
38990         if(this.fireEvent('beforeselect', this, record, index) !== false){
38991             this.setFromData(index > -1 ? record.data : false);
38992             this.collapse();
38993             this.fireEvent('select', this, record, index);
38994         }
38995     },
38996
38997     /**
38998      * Returns the currently selected field value or empty string if no value is set.
38999      * @return {String} value The selected value
39000      */
39001     getValue : function(){
39002         if(this.valueField){
39003             return typeof this.value != 'undefined' ? this.value : '';
39004         }else{
39005             return Roo.form.ComboBox.superclass.getValue.call(this);
39006         }
39007     },
39008
39009     /**
39010      * Clears any text/value currently set in the field
39011      */
39012     clearValue : function(){
39013         if(this.hiddenField){
39014             this.hiddenField.value = '';
39015         }
39016         this.value = '';
39017         this.setRawValue('');
39018         this.lastSelectionText = '';
39019         
39020     },
39021
39022     /**
39023      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
39024      * will be displayed in the field.  If the value does not match the data value of an existing item,
39025      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
39026      * Otherwise the field will be blank (although the value will still be set).
39027      * @param {String} value The value to match
39028      */
39029     setValue : function(v){
39030         var text = v;
39031         if(this.valueField){
39032             var r = this.findRecord(this.valueField, v);
39033             if(r){
39034                 text = r.data[this.displayField];
39035             }else if(this.valueNotFoundText !== undefined){
39036                 text = this.valueNotFoundText;
39037             }
39038         }
39039         this.lastSelectionText = text;
39040         if(this.hiddenField){
39041             this.hiddenField.value = v;
39042         }
39043         Roo.form.ComboBox.superclass.setValue.call(this, text);
39044         this.value = v;
39045     },
39046     /**
39047      * @property {Object} the last set data for the element
39048      */
39049     
39050     lastData : false,
39051     /**
39052      * Sets the value of the field based on a object which is related to the record format for the store.
39053      * @param {Object} value the value to set as. or false on reset?
39054      */
39055     setFromData : function(o){
39056         var dv = ''; // display value
39057         var vv = ''; // value value..
39058         this.lastData = o;
39059         if (this.displayField) {
39060             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
39061         } else {
39062             // this is an error condition!!!
39063             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
39064         }
39065         
39066         if(this.valueField){
39067             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
39068         }
39069         if(this.hiddenField){
39070             this.hiddenField.value = vv;
39071             
39072             this.lastSelectionText = dv;
39073             Roo.form.ComboBox.superclass.setValue.call(this, dv);
39074             this.value = vv;
39075             return;
39076         }
39077         // no hidden field.. - we store the value in 'value', but still display
39078         // display field!!!!
39079         this.lastSelectionText = dv;
39080         Roo.form.ComboBox.superclass.setValue.call(this, dv);
39081         this.value = vv;
39082         
39083         
39084     },
39085     // private
39086     reset : function(){
39087         // overridden so that last data is reset..
39088         this.setValue(this.originalValue);
39089         this.clearInvalid();
39090         this.lastData = false;
39091         if (this.view) {
39092             this.view.clearSelections();
39093         }
39094     },
39095     // private
39096     findRecord : function(prop, value){
39097         var record;
39098         if(this.store.getCount() > 0){
39099             this.store.each(function(r){
39100                 if(r.data[prop] == value){
39101                     record = r;
39102                     return false;
39103                 }
39104                 return true;
39105             });
39106         }
39107         return record;
39108     },
39109     
39110     getName: function()
39111     {
39112         // returns hidden if it's set..
39113         if (!this.rendered) {return ''};
39114         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
39115         
39116     },
39117     // private
39118     onViewMove : function(e, t){
39119         this.inKeyMode = false;
39120     },
39121
39122     // private
39123     onViewOver : function(e, t){
39124         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
39125             return;
39126         }
39127         var item = this.view.findItemFromChild(t);
39128         if(item){
39129             var index = this.view.indexOf(item);
39130             this.select(index, false);
39131         }
39132     },
39133
39134     // private
39135     onViewClick : function(doFocus)
39136     {
39137         var index = this.view.getSelectedIndexes()[0];
39138         var r = this.store.getAt(index);
39139         if(r){
39140             this.onSelect(r, index);
39141         }
39142         if(doFocus !== false && !this.blockFocus){
39143             this.el.focus();
39144         }
39145     },
39146
39147     // private
39148     restrictHeight : function(){
39149         this.innerList.dom.style.height = '';
39150         var inner = this.innerList.dom;
39151         var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
39152         this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
39153         this.list.beginUpdate();
39154         this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
39155         this.list.alignTo(this.el, this.listAlign);
39156         this.list.endUpdate();
39157     },
39158
39159     // private
39160     onEmptyResults : function(){
39161         this.collapse();
39162     },
39163
39164     /**
39165      * Returns true if the dropdown list is expanded, else false.
39166      */
39167     isExpanded : function(){
39168         return this.list.isVisible();
39169     },
39170
39171     /**
39172      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
39173      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
39174      * @param {String} value The data value of the item to select
39175      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
39176      * selected item if it is not currently in view (defaults to true)
39177      * @return {Boolean} True if the value matched an item in the list, else false
39178      */
39179     selectByValue : function(v, scrollIntoView){
39180         if(v !== undefined && v !== null){
39181             var r = this.findRecord(this.valueField || this.displayField, v);
39182             if(r){
39183                 this.select(this.store.indexOf(r), scrollIntoView);
39184                 return true;
39185             }
39186         }
39187         return false;
39188     },
39189
39190     /**
39191      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
39192      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
39193      * @param {Number} index The zero-based index of the list item to select
39194      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
39195      * selected item if it is not currently in view (defaults to true)
39196      */
39197     select : function(index, scrollIntoView){
39198         this.selectedIndex = index;
39199         this.view.select(index);
39200         if(scrollIntoView !== false){
39201             var el = this.view.getNode(index);
39202             if(el){
39203                 this.innerList.scrollChildIntoView(el, false);
39204             }
39205         }
39206     },
39207
39208     // private
39209     selectNext : function(){
39210         var ct = this.store.getCount();
39211         if(ct > 0){
39212             if(this.selectedIndex == -1){
39213                 this.select(0);
39214             }else if(this.selectedIndex < ct-1){
39215                 this.select(this.selectedIndex+1);
39216             }
39217         }
39218     },
39219
39220     // private
39221     selectPrev : function(){
39222         var ct = this.store.getCount();
39223         if(ct > 0){
39224             if(this.selectedIndex == -1){
39225                 this.select(0);
39226             }else if(this.selectedIndex != 0){
39227                 this.select(this.selectedIndex-1);
39228             }
39229         }
39230     },
39231
39232     // private
39233     onKeyUp : function(e){
39234         if(this.editable !== false && !e.isSpecialKey()){
39235             this.lastKey = e.getKey();
39236             this.dqTask.delay(this.queryDelay);
39237         }
39238     },
39239
39240     // private
39241     validateBlur : function(){
39242         return !this.list || !this.list.isVisible();   
39243     },
39244
39245     // private
39246     initQuery : function(){
39247         this.doQuery(this.getRawValue());
39248     },
39249
39250     // private
39251     doForce : function(){
39252         if(this.el.dom.value.length > 0){
39253             this.el.dom.value =
39254                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
39255              
39256         }
39257     },
39258
39259     /**
39260      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
39261      * query allowing the query action to be canceled if needed.
39262      * @param {String} query The SQL query to execute
39263      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
39264      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
39265      * saved in the current store (defaults to false)
39266      */
39267     doQuery : function(q, forceAll){
39268         if(q === undefined || q === null){
39269             q = '';
39270         }
39271         var qe = {
39272             query: q,
39273             forceAll: forceAll,
39274             combo: this,
39275             cancel:false
39276         };
39277         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
39278             return false;
39279         }
39280         q = qe.query;
39281         forceAll = qe.forceAll;
39282         if(forceAll === true || (q.length >= this.minChars)){
39283             if(this.lastQuery != q || this.alwaysQuery){
39284                 this.lastQuery = q;
39285                 if(this.mode == 'local'){
39286                     this.selectedIndex = -1;
39287                     if(forceAll){
39288                         this.store.clearFilter();
39289                     }else{
39290                         this.store.filter(this.displayField, q);
39291                     }
39292                     this.onLoad();
39293                 }else{
39294                     this.store.baseParams[this.queryParam] = q;
39295                     this.store.load({
39296                         params: this.getParams(q)
39297                     });
39298                     this.expand();
39299                 }
39300             }else{
39301                 this.selectedIndex = -1;
39302                 this.onLoad();   
39303             }
39304         }
39305     },
39306
39307     // private
39308     getParams : function(q){
39309         var p = {};
39310         //p[this.queryParam] = q;
39311         if(this.pageSize){
39312             p.start = 0;
39313             p.limit = this.pageSize;
39314         }
39315         return p;
39316     },
39317
39318     /**
39319      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
39320      */
39321     collapse : function(){
39322         if(!this.isExpanded()){
39323             return;
39324         }
39325         this.list.hide();
39326         Roo.get(document).un('mousedown', this.collapseIf, this);
39327         Roo.get(document).un('mousewheel', this.collapseIf, this);
39328         if (!this.editable) {
39329             Roo.get(document).un('keydown', this.listKeyPress, this);
39330         }
39331         this.fireEvent('collapse', this);
39332     },
39333
39334     // private
39335     collapseIf : function(e){
39336         if(!e.within(this.wrap) && !e.within(this.list)){
39337             this.collapse();
39338         }
39339     },
39340
39341     /**
39342      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
39343      */
39344     expand : function(){
39345         if(this.isExpanded() || !this.hasFocus){
39346             return;
39347         }
39348         this.list.alignTo(this.el, this.listAlign);
39349         this.list.show();
39350         Roo.get(document).on('mousedown', this.collapseIf, this);
39351         Roo.get(document).on('mousewheel', this.collapseIf, this);
39352         if (!this.editable) {
39353             Roo.get(document).on('keydown', this.listKeyPress, this);
39354         }
39355         
39356         this.fireEvent('expand', this);
39357     },
39358
39359     // private
39360     // Implements the default empty TriggerField.onTriggerClick function
39361     onTriggerClick : function(){
39362         if(this.disabled){
39363             return;
39364         }
39365         if(this.isExpanded()){
39366             this.collapse();
39367             if (!this.blockFocus) {
39368                 this.el.focus();
39369             }
39370             
39371         }else {
39372             this.hasFocus = true;
39373             if(this.triggerAction == 'all') {
39374                 this.doQuery(this.allQuery, true);
39375             } else {
39376                 this.doQuery(this.getRawValue());
39377             }
39378             if (!this.blockFocus) {
39379                 this.el.focus();
39380             }
39381         }
39382     },
39383     listKeyPress : function(e)
39384     {
39385         //Roo.log('listkeypress');
39386         // scroll to first matching element based on key pres..
39387         if (e.isSpecialKey()) {
39388             return false;
39389         }
39390         var k = String.fromCharCode(e.getKey()).toUpperCase();
39391         //Roo.log(k);
39392         var match  = false;
39393         var csel = this.view.getSelectedNodes();
39394         var cselitem = false;
39395         if (csel.length) {
39396             var ix = this.view.indexOf(csel[0]);
39397             cselitem  = this.store.getAt(ix);
39398             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
39399                 cselitem = false;
39400             }
39401             
39402         }
39403         
39404         this.store.each(function(v) { 
39405             if (cselitem) {
39406                 // start at existing selection.
39407                 if (cselitem.id == v.id) {
39408                     cselitem = false;
39409                 }
39410                 return;
39411             }
39412                 
39413             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
39414                 match = this.store.indexOf(v);
39415                 return false;
39416             }
39417         }, this);
39418         
39419         if (match === false) {
39420             return true; // no more action?
39421         }
39422         // scroll to?
39423         this.view.select(match);
39424         var sn = Roo.get(this.view.getSelectedNodes()[0])
39425         sn.scrollIntoView(sn.dom.parentNode, false);
39426     }
39427
39428     /** 
39429     * @cfg {Boolean} grow 
39430     * @hide 
39431     */
39432     /** 
39433     * @cfg {Number} growMin 
39434     * @hide 
39435     */
39436     /** 
39437     * @cfg {Number} growMax 
39438     * @hide 
39439     */
39440     /**
39441      * @hide
39442      * @method autoSize
39443      */
39444 });/*
39445  * Copyright(c) 2010-2012, Roo J Solutions Limited
39446  *
39447  * Licence LGPL
39448  *
39449  */
39450
39451 /**
39452  * @class Roo.form.ComboBoxArray
39453  * @extends Roo.form.TextField
39454  * A facebook style adder... for lists of email / people / countries  etc...
39455  * pick multiple items from a combo box, and shows each one.
39456  *
39457  *  Fred [x]  Brian [x]  [Pick another |v]
39458  *
39459  *
39460  *  For this to work: it needs various extra information
39461  *    - normal combo problay has
39462  *      name, hiddenName
39463  *    + displayField, valueField
39464  *
39465  *    For our purpose...
39466  *
39467  *
39468  *   If we change from 'extends' to wrapping...
39469  *   
39470  *  
39471  *
39472  
39473  
39474  * @constructor
39475  * Create a new ComboBoxArray.
39476  * @param {Object} config Configuration options
39477  */
39478  
39479
39480 Roo.form.ComboBoxArray = function(config)
39481 {
39482     
39483     Roo.form.ComboBoxArray.superclass.constructor.call(this, config);
39484     
39485     this.items = new Roo.util.MixedCollection(false);
39486     
39487     // construct the child combo...
39488     
39489     
39490     
39491     
39492    
39493     
39494 }
39495
39496  
39497 Roo.extend(Roo.form.ComboBoxArray, Roo.form.TextField,
39498
39499     /**
39500      * @cfg {Roo.form.Combo} combo The combo box that is wrapped
39501      */
39502     
39503     lastData : false,
39504     
39505     // behavies liek a hiddne field
39506     inputType:      'hidden',
39507     /**
39508      * @cfg {Number} width The width of the box that displays the selected element
39509      */ 
39510     width:          300,
39511
39512     
39513     
39514     /**
39515      * @cfg {String} name    The name of the visable items on this form (eg. titles not ids)
39516      */
39517     name : false,
39518     /**
39519      * @cfg {String} hiddenName    The hidden name of the field, often contains an comma seperated list of names
39520      */
39521     hiddenName : false,
39522     
39523     
39524     // private the array of items that are displayed..
39525     items  : false,
39526     // private - the hidden field el.
39527     hiddenEl : false,
39528     // private - the filed el..
39529     el : false,
39530     
39531     //validateValue : function() { return true; }, // all values are ok!
39532     //onAddClick: function() { },
39533     
39534     onRender : function(ct, position) 
39535     {
39536         
39537         // create the standard hidden element
39538         //Roo.form.ComboBoxArray.superclass.onRender.call(this, ct, position);
39539         
39540         
39541         // give fake names to child combo;
39542         this.combo.hiddenName = this.hiddenName ? (this.hiddenName+'-subcombo') : this.hiddenName;
39543         this.combo.name = this.name? (this.name+'-subcombo') : this.name;
39544         
39545         this.combo = Roo.factory(this.combo, Roo.form);
39546         this.combo.onRender(ct, position);
39547         if (typeof(this.combo.width) != 'undefined') {
39548             this.combo.onResize(this.combo.width,0);
39549         }
39550         
39551         this.combo.initEvents();
39552         
39553         // assigned so form know we need to do this..
39554         this.store          = this.combo.store;
39555         this.valueField     = this.combo.valueField;
39556         this.displayField   = this.combo.displayField ;
39557         
39558         
39559         this.combo.wrap.addClass('x-cbarray-grp');
39560         
39561         var cbwrap = this.combo.wrap.createChild(
39562             {tag: 'div', cls: 'x-cbarray-cb'},
39563             this.combo.el.dom
39564         );
39565         
39566              
39567         this.hiddenEl = this.combo.wrap.createChild({
39568             tag: 'input',  type:'hidden' , name: this.hiddenName, value : ''
39569         });
39570         this.el = this.combo.wrap.createChild({
39571             tag: 'input',  type:'hidden' , name: this.name, value : ''
39572         });
39573          //   this.el.dom.removeAttribute("name");
39574         
39575         
39576         this.outerWrap = this.combo.wrap;
39577         this.wrap = cbwrap;
39578         
39579         this.outerWrap.setWidth(this.width);
39580         this.outerWrap.dom.removeChild(this.el.dom);
39581         
39582         this.wrap.dom.appendChild(this.el.dom);
39583         this.outerWrap.dom.removeChild(this.combo.trigger.dom);
39584         this.combo.wrap.dom.appendChild(this.combo.trigger.dom);
39585         
39586         this.combo.trigger.setStyle('position','relative');
39587         this.combo.trigger.setStyle('left', '0px');
39588         this.combo.trigger.setStyle('top', '2px');
39589         
39590         this.combo.el.setStyle('vertical-align', 'text-bottom');
39591         
39592         //this.trigger.setStyle('vertical-align', 'top');
39593         
39594         // this should use the code from combo really... on('add' ....)
39595         if (this.adder) {
39596             
39597         
39598             this.adder = this.outerWrap.createChild(
39599                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-adder', style: 'margin-left:2px'});  
39600             var _t = this;
39601             this.adder.on('click', function(e) {
39602                 _t.fireEvent('adderclick', this, e);
39603             }, _t);
39604         }
39605         //var _t = this;
39606         //this.adder.on('click', this.onAddClick, _t);
39607         
39608         
39609         this.combo.on('select', function(cb, rec, ix) {
39610             this.addItem(rec.data);
39611             
39612             cb.setValue('');
39613             cb.el.dom.value = '';
39614             //cb.lastData = rec.data;
39615             // add to list
39616             
39617         }, this);
39618         
39619         
39620     },
39621     
39622     
39623     getName: function()
39624     {
39625         // returns hidden if it's set..
39626         if (!this.rendered) {return ''};
39627         return  this.hiddenName ? this.hiddenName : this.name;
39628         
39629     },
39630     
39631     
39632     onResize: function(w, h){
39633         
39634         return;
39635         // not sure if this is needed..
39636         //this.combo.onResize(w,h);
39637         
39638         if(typeof w != 'number'){
39639             // we do not handle it!?!?
39640             return;
39641         }
39642         var tw = this.combo.trigger.getWidth();
39643         tw += this.addicon ? this.addicon.getWidth() : 0;
39644         tw += this.editicon ? this.editicon.getWidth() : 0;
39645         var x = w - tw;
39646         this.combo.el.setWidth( this.combo.adjustWidth('input', x));
39647             
39648         this.combo.trigger.setStyle('left', '0px');
39649         
39650         if(this.list && this.listWidth === undefined){
39651             var lw = Math.max(x + this.combo.trigger.getWidth(), this.combo.minListWidth);
39652             this.list.setWidth(lw);
39653             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
39654         }
39655         
39656     
39657         
39658     },
39659     
39660     addItem: function(rec)
39661     {
39662         var valueField = this.combo.valueField;
39663         var displayField = this.combo.displayField;
39664         if (this.items.indexOfKey(rec[valueField]) > -1) {
39665             //console.log("GOT " + rec.data.id);
39666             return;
39667         }
39668         
39669         var x = new Roo.form.ComboBoxArray.Item({
39670             //id : rec[this.idField],
39671             data : rec,
39672             displayField : displayField ,
39673             tipField : displayField ,
39674             cb : this
39675         });
39676         // use the 
39677         this.items.add(rec[valueField],x);
39678         // add it before the element..
39679         this.updateHiddenEl();
39680         x.render(this.outerWrap, this.wrap.dom);
39681         // add the image handler..
39682     },
39683     
39684     updateHiddenEl : function()
39685     {
39686         this.validate();
39687         if (!this.hiddenEl) {
39688             return;
39689         }
39690         var ar = [];
39691         var idField = this.combo.valueField;
39692         
39693         this.items.each(function(f) {
39694             ar.push(f.data[idField]);
39695            
39696         });
39697         this.hiddenEl.dom.value = ar.join(',');
39698         this.validate();
39699     },
39700     
39701     reset : function()
39702     {
39703         //Roo.form.ComboBoxArray.superclass.reset.call(this); 
39704         this.items.each(function(f) {
39705            f.remove(); 
39706         });
39707         this.el.dom.value = '';
39708         if (this.hiddenEl) {
39709             this.hiddenEl.dom.value = '';
39710         }
39711         
39712     },
39713     getValue: function()
39714     {
39715         return this.hiddenEl ? this.hiddenEl.dom.value : '';
39716     },
39717     setValue: function(v) // not a valid action - must use addItems..
39718     {
39719          
39720         this.reset();
39721         
39722         
39723         
39724         if (this.store.isLocal && (typeof(v) == 'string')) {
39725             // then we can use the store to find the values..
39726             // comma seperated at present.. this needs to allow JSON based encoding..
39727             this.hiddenEl.value  = v;
39728             var v_ar = [];
39729             Roo.each(v.split(','), function(k) {
39730                 Roo.log("CHECK " + this.valueField + ',' + k);
39731                 var li = this.store.query(this.valueField, k);
39732                 if (!li.length) {
39733                     return;
39734                 }
39735                 var add = {};
39736                 add[this.valueField] = k;
39737                 add[this.displayField] = li.item(0).data[this.displayField];
39738                 
39739                 this.addItem(add);
39740             }, this) 
39741              
39742         }
39743         if (typeof(v) == 'object') {
39744             // then let's assume it's an array of objects..
39745             Roo.each(v, function(l) {
39746                 this.addItem(l);
39747             }, this);
39748              
39749         }
39750         
39751         
39752     },
39753     setFromData: function(v)
39754     {
39755         // this recieves an object, if setValues is called.
39756         this.reset();
39757         this.el.dom.value = v[this.displayField];
39758         this.hiddenEl.dom.value = v[this.valueField];
39759         if (typeof(v[this.valueField]) != 'string' || !v[this.valueField].length) {
39760             return;
39761         }
39762         var kv = v[this.valueField];
39763         var dv = v[this.displayField];
39764         kv = typeof(kv) != 'string' ? '' : kv;
39765         dv = typeof(dv) != 'string' ? '' : dv;
39766         
39767         
39768         var keys = kv.split(',');
39769         var display = dv.split(',');
39770         for (var i = 0 ; i < keys.length; i++) {
39771             
39772             add = {};
39773             add[this.valueField] = keys[i];
39774             add[this.displayField] = display[i];
39775             this.addItem(add);
39776         }
39777       
39778         
39779     },
39780     
39781     
39782     validateValue : function(value){
39783         return Roo.form.ComboBoxArray.superclass.validateValue.call(this, this.getValue());
39784         
39785     }
39786     
39787 });
39788
39789
39790
39791 /**
39792  * @class Roo.form.ComboBoxArray.Item
39793  * @extends Roo.BoxComponent
39794  * A selected item in the list
39795  *  Fred [x]  Brian [x]  [Pick another |v]
39796  * 
39797  * @constructor
39798  * Create a new item.
39799  * @param {Object} config Configuration options
39800  */
39801  
39802 Roo.form.ComboBoxArray.Item = function(config) {
39803     config.id = Roo.id();
39804     Roo.form.ComboBoxArray.Item.superclass.constructor.call(this, config);
39805 }
39806
39807 Roo.extend(Roo.form.ComboBoxArray.Item, Roo.BoxComponent, {
39808     data : {},
39809     cb: false,
39810     displayField : false,
39811     tipField : false,
39812     
39813     
39814     defaultAutoCreate : {
39815         tag: 'div',
39816         cls: 'x-cbarray-item',
39817         cn : [ 
39818             { tag: 'div' },
39819             {
39820                 tag: 'img',
39821                 width:16,
39822                 height : 16,
39823                 src : Roo.BLANK_IMAGE_URL ,
39824                 align: 'center'
39825             }
39826         ]
39827         
39828     },
39829     
39830  
39831     onRender : function(ct, position)
39832     {
39833         Roo.form.Field.superclass.onRender.call(this, ct, position);
39834         
39835         if(!this.el){
39836             var cfg = this.getAutoCreate();
39837             this.el = ct.createChild(cfg, position);
39838         }
39839         
39840         this.el.child('img').dom.setAttribute('src', Roo.BLANK_IMAGE_URL);
39841         
39842         this.el.child('div').dom.innerHTML = this.cb.renderer ? 
39843             this.cb.renderer(this.data) :
39844             String.format('{0}',this.data[this.displayField]);
39845         
39846             
39847         this.el.child('div').dom.setAttribute('qtip',
39848                         String.format('{0}',this.data[this.tipField])
39849         );
39850         
39851         this.el.child('img').on('click', this.remove, this);
39852         
39853     },
39854    
39855     remove : function()
39856     {
39857         
39858         this.cb.items.remove(this);
39859         this.el.child('img').un('click', this.remove, this);
39860         this.el.remove();
39861         this.cb.updateHiddenEl();
39862     }
39863     
39864     
39865 });/*
39866  * Based on:
39867  * Ext JS Library 1.1.1
39868  * Copyright(c) 2006-2007, Ext JS, LLC.
39869  *
39870  * Originally Released Under LGPL - original licence link has changed is not relivant.
39871  *
39872  * Fork - LGPL
39873  * <script type="text/javascript">
39874  */
39875 /**
39876  * @class Roo.form.Checkbox
39877  * @extends Roo.form.Field
39878  * Single checkbox field.  Can be used as a direct replacement for traditional checkbox fields.
39879  * @constructor
39880  * Creates a new Checkbox
39881  * @param {Object} config Configuration options
39882  */
39883 Roo.form.Checkbox = function(config){
39884     Roo.form.Checkbox.superclass.constructor.call(this, config);
39885     this.addEvents({
39886         /**
39887          * @event check
39888          * Fires when the checkbox is checked or unchecked.
39889              * @param {Roo.form.Checkbox} this This checkbox
39890              * @param {Boolean} checked The new checked value
39891              */
39892         check : true
39893     });
39894 };
39895
39896 Roo.extend(Roo.form.Checkbox, Roo.form.Field,  {
39897     /**
39898      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
39899      */
39900     focusClass : undefined,
39901     /**
39902      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
39903      */
39904     fieldClass: "x-form-field",
39905     /**
39906      * @cfg {Boolean} checked True if the the checkbox should render already checked (defaults to false)
39907      */
39908     checked: false,
39909     /**
39910      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
39911      * {tag: "input", type: "checkbox", autocomplete: "off"})
39912      */
39913     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "off"},
39914     /**
39915      * @cfg {String} boxLabel The text that appears beside the checkbox
39916      */
39917     boxLabel : "",
39918     /**
39919      * @cfg {String} inputValue The value that should go into the generated input element's value attribute
39920      */  
39921     inputValue : '1',
39922     /**
39923      * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
39924      */
39925      valueOff: '0', // value when not checked..
39926
39927     actionMode : 'viewEl', 
39928     //
39929     // private
39930     itemCls : 'x-menu-check-item x-form-item',
39931     groupClass : 'x-menu-group-item',
39932     inputType : 'hidden',
39933     
39934     
39935     inSetChecked: false, // check that we are not calling self...
39936     
39937     inputElement: false, // real input element?
39938     basedOn: false, // ????
39939     
39940     isFormField: true, // not sure where this is needed!!!!
39941
39942     onResize : function(){
39943         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
39944         if(!this.boxLabel){
39945             this.el.alignTo(this.wrap, 'c-c');
39946         }
39947     },
39948
39949     initEvents : function(){
39950         Roo.form.Checkbox.superclass.initEvents.call(this);
39951         this.el.on("click", this.onClick,  this);
39952         this.el.on("change", this.onClick,  this);
39953     },
39954
39955
39956     getResizeEl : function(){
39957         return this.wrap;
39958     },
39959
39960     getPositionEl : function(){
39961         return this.wrap;
39962     },
39963
39964     // private
39965     onRender : function(ct, position){
39966         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
39967         /*
39968         if(this.inputValue !== undefined){
39969             this.el.dom.value = this.inputValue;
39970         }
39971         */
39972         //this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
39973         this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
39974         var viewEl = this.wrap.createChild({ 
39975             tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
39976         this.viewEl = viewEl;   
39977         this.wrap.on('click', this.onClick,  this); 
39978         
39979         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
39980         this.el.on('propertychange', this.setFromHidden,  this);  //ie
39981         
39982         
39983         
39984         if(this.boxLabel){
39985             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
39986         //    viewEl.on('click', this.onClick,  this); 
39987         }
39988         //if(this.checked){
39989             this.setChecked(this.checked);
39990         //}else{
39991             //this.checked = this.el.dom;
39992         //}
39993
39994     },
39995
39996     // private
39997     initValue : Roo.emptyFn,
39998
39999     /**
40000      * Returns the checked state of the checkbox.
40001      * @return {Boolean} True if checked, else false
40002      */
40003     getValue : function(){
40004         if(this.el){
40005             return String(this.el.dom.value) == String(this.inputValue ) ? this.inputValue : this.valueOff;
40006         }
40007         return this.valueOff;
40008         
40009     },
40010
40011         // private
40012     onClick : function(){ 
40013         this.setChecked(!this.checked);
40014
40015         //if(this.el.dom.checked != this.checked){
40016         //    this.setValue(this.el.dom.checked);
40017        // }
40018     },
40019
40020     /**
40021      * Sets the checked state of the checkbox.
40022      * On is always based on a string comparison between inputValue and the param.
40023      * @param {Boolean/String} value - the value to set 
40024      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
40025      */
40026     setValue : function(v,suppressEvent){
40027         
40028         
40029         //this.checked = (v === true || v === 'true' || v == '1' || String(v).toLowerCase() == 'on');
40030         //if(this.el && this.el.dom){
40031         //    this.el.dom.checked = this.checked;
40032         //    this.el.dom.defaultChecked = this.checked;
40033         //}
40034         this.setChecked(String(v) === String(this.inputValue), suppressEvent);
40035         //this.fireEvent("check", this, this.checked);
40036     },
40037     // private..
40038     setChecked : function(state,suppressEvent)
40039     {
40040         if (this.inSetChecked) {
40041             this.checked = state;
40042             return;
40043         }
40044         
40045     
40046         if(this.wrap){
40047             this.wrap[state ? 'addClass' : 'removeClass']('x-menu-item-checked');
40048         }
40049         this.checked = state;
40050         if(suppressEvent !== true){
40051             this.fireEvent('check', this, state);
40052         }
40053         this.inSetChecked = true;
40054         this.el.dom.value = state ? this.inputValue : this.valueOff;
40055         this.inSetChecked = false;
40056         
40057     },
40058     // handle setting of hidden value by some other method!!?!?
40059     setFromHidden: function()
40060     {
40061         if(!this.el){
40062             return;
40063         }
40064         //console.log("SET FROM HIDDEN");
40065         //alert('setFrom hidden');
40066         this.setValue(this.el.dom.value);
40067     },
40068     
40069     onDestroy : function()
40070     {
40071         if(this.viewEl){
40072             Roo.get(this.viewEl).remove();
40073         }
40074          
40075         Roo.form.Checkbox.superclass.onDestroy.call(this);
40076     }
40077
40078 });/*
40079  * Based on:
40080  * Ext JS Library 1.1.1
40081  * Copyright(c) 2006-2007, Ext JS, LLC.
40082  *
40083  * Originally Released Under LGPL - original licence link has changed is not relivant.
40084  *
40085  * Fork - LGPL
40086  * <script type="text/javascript">
40087  */
40088  
40089 /**
40090  * @class Roo.form.Radio
40091  * @extends Roo.form.Checkbox
40092  * Single radio field.  Same as Checkbox, but provided as a convenience for automatically setting the input type.
40093  * Radio grouping is handled automatically by the browser if you give each radio in a group the same name.
40094  * @constructor
40095  * Creates a new Radio
40096  * @param {Object} config Configuration options
40097  */
40098 Roo.form.Radio = function(){
40099     Roo.form.Radio.superclass.constructor.apply(this, arguments);
40100 };
40101 Roo.extend(Roo.form.Radio, Roo.form.Checkbox, {
40102     inputType: 'radio',
40103
40104     /**
40105      * If this radio is part of a group, it will return the selected value
40106      * @return {String}
40107      */
40108     getGroupValue : function(){
40109         return this.el.up('form').child('input[name='+this.el.dom.name+']:checked', true).value;
40110     },
40111     
40112     
40113     onRender : function(ct, position){
40114         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
40115         
40116         if(this.inputValue !== undefined){
40117             this.el.dom.value = this.inputValue;
40118         }
40119          
40120         this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
40121         //this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
40122         //var viewEl = this.wrap.createChild({ 
40123         //    tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
40124         //this.viewEl = viewEl;   
40125         //this.wrap.on('click', this.onClick,  this); 
40126         
40127         //this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
40128         //this.el.on('propertychange', this.setFromHidden,  this);  //ie
40129         
40130         
40131         
40132         if(this.boxLabel){
40133             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
40134         //    viewEl.on('click', this.onClick,  this); 
40135         }
40136          if(this.checked){
40137             this.el.dom.checked =   'checked' ;
40138         }
40139          
40140     } 
40141     
40142     
40143 });//<script type="text/javascript">
40144
40145 /*
40146  * Ext JS Library 1.1.1
40147  * Copyright(c) 2006-2007, Ext JS, LLC.
40148  * licensing@extjs.com
40149  * 
40150  * http://www.extjs.com/license
40151  */
40152  
40153  /*
40154   * 
40155   * Known bugs:
40156   * Default CSS appears to render it as fixed text by default (should really be Sans-Serif)
40157   * - IE ? - no idea how much works there.
40158   * 
40159   * 
40160   * 
40161   */
40162  
40163
40164 /**
40165  * @class Ext.form.HtmlEditor
40166  * @extends Ext.form.Field
40167  * Provides a lightweight HTML Editor component.
40168  *
40169  * This has been tested on Fireforx / Chrome.. IE may not be so great..
40170  * 
40171  * <br><br><b>Note: The focus/blur and validation marking functionality inherited from Ext.form.Field is NOT
40172  * supported by this editor.</b><br/><br/>
40173  * An Editor is a sensitive component that can't be used in all spots standard fields can be used. Putting an Editor within
40174  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
40175  */
40176 Roo.form.HtmlEditor = Roo.extend(Roo.form.Field, {
40177       /**
40178      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
40179      */
40180     toolbars : false,
40181     /**
40182      * @cfg {String} createLinkText The default text for the create link prompt
40183      */
40184     createLinkText : 'Please enter the URL for the link:',
40185     /**
40186      * @cfg {String} defaultLinkValue The default value for the create link prompt (defaults to http:/ /)
40187      */
40188     defaultLinkValue : 'http:/'+'/',
40189    
40190      /**
40191      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
40192      *                        Roo.resizable.
40193      */
40194     resizable : false,
40195      /**
40196      * @cfg {Number} height (in pixels)
40197      */   
40198     height: 300,
40199    /**
40200      * @cfg {Number} width (in pixels)
40201      */   
40202     width: 500,
40203     
40204     /**
40205      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
40206      * 
40207      */
40208     stylesheets: false,
40209     
40210     // id of frame..
40211     frameId: false,
40212     
40213     // private properties
40214     validationEvent : false,
40215     deferHeight: true,
40216     initialized : false,
40217     activated : false,
40218     sourceEditMode : false,
40219     onFocus : Roo.emptyFn,
40220     iframePad:3,
40221     hideMode:'offsets',
40222     
40223     defaultAutoCreate : { // modified by initCompnoent..
40224         tag: "textarea",
40225         style:"width:500px;height:300px;",
40226         autocomplete: "off"
40227     },
40228
40229     // private
40230     initComponent : function(){
40231         this.addEvents({
40232             /**
40233              * @event initialize
40234              * Fires when the editor is fully initialized (including the iframe)
40235              * @param {HtmlEditor} this
40236              */
40237             initialize: true,
40238             /**
40239              * @event activate
40240              * Fires when the editor is first receives the focus. Any insertion must wait
40241              * until after this event.
40242              * @param {HtmlEditor} this
40243              */
40244             activate: true,
40245              /**
40246              * @event beforesync
40247              * Fires before the textarea is updated with content from the editor iframe. Return false
40248              * to cancel the sync.
40249              * @param {HtmlEditor} this
40250              * @param {String} html
40251              */
40252             beforesync: true,
40253              /**
40254              * @event beforepush
40255              * Fires before the iframe editor is updated with content from the textarea. Return false
40256              * to cancel the push.
40257              * @param {HtmlEditor} this
40258              * @param {String} html
40259              */
40260             beforepush: true,
40261              /**
40262              * @event sync
40263              * Fires when the textarea is updated with content from the editor iframe.
40264              * @param {HtmlEditor} this
40265              * @param {String} html
40266              */
40267             sync: true,
40268              /**
40269              * @event push
40270              * Fires when the iframe editor is updated with content from the textarea.
40271              * @param {HtmlEditor} this
40272              * @param {String} html
40273              */
40274             push: true,
40275              /**
40276              * @event editmodechange
40277              * Fires when the editor switches edit modes
40278              * @param {HtmlEditor} this
40279              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
40280              */
40281             editmodechange: true,
40282             /**
40283              * @event editorevent
40284              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
40285              * @param {HtmlEditor} this
40286              */
40287             editorevent: true
40288         });
40289         this.defaultAutoCreate =  {
40290             tag: "textarea",
40291             style:'width: ' + this.width + 'px;height: ' + this.height + 'px;',
40292             autocomplete: "off"
40293         };
40294     },
40295
40296     /**
40297      * Protected method that will not generally be called directly. It
40298      * is called when the editor creates its toolbar. Override this method if you need to
40299      * add custom toolbar buttons.
40300      * @param {HtmlEditor} editor
40301      */
40302     createToolbar : function(editor){
40303         if (!editor.toolbars || !editor.toolbars.length) {
40304             editor.toolbars = [ new Roo.form.HtmlEditor.ToolbarStandard() ]; // can be empty?
40305         }
40306         
40307         for (var i =0 ; i < editor.toolbars.length;i++) {
40308             editor.toolbars[i] = Roo.factory(
40309                     typeof(editor.toolbars[i]) == 'string' ?
40310                         { xtype: editor.toolbars[i]} : editor.toolbars[i],
40311                 Roo.form.HtmlEditor);
40312             editor.toolbars[i].init(editor);
40313         }
40314          
40315         
40316     },
40317
40318     /**
40319      * Protected method that will not generally be called directly. It
40320      * is called when the editor initializes the iframe with HTML contents. Override this method if you
40321      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
40322      */
40323     getDocMarkup : function(){
40324         // body styles..
40325         var st = '';
40326         if (this.stylesheets === false) {
40327             
40328             Roo.get(document.head).select('style').each(function(node) {
40329                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
40330             });
40331             
40332             Roo.get(document.head).select('link').each(function(node) { 
40333                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
40334             });
40335             
40336         } else if (!this.stylesheets.length) {
40337                 // simple..
40338                 st = '<style type="text/css">' +
40339                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
40340                    '</style>';
40341         } else {
40342             Roo.each(this.stylesheets, function(s) {
40343                 st += '<link rel="stylesheet" type="text/css" href="' + s +'" />'
40344             });
40345             
40346         }
40347         
40348         st +=  '<style type="text/css">' +
40349             'IMG { cursor: pointer } ' +
40350         '</style>';
40351
40352         
40353         return '<html><head>' + st  +
40354             //<style type="text/css">' +
40355             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
40356             //'</style>' +
40357             ' </head><body class="roo-htmleditor-body"></body></html>';
40358     },
40359
40360     // private
40361     onRender : function(ct, position)
40362     {
40363         var _t = this;
40364         Roo.form.HtmlEditor.superclass.onRender.call(this, ct, position);
40365         this.el.dom.style.border = '0 none';
40366         this.el.dom.setAttribute('tabIndex', -1);
40367         this.el.addClass('x-hidden');
40368         if(Roo.isIE){ // fix IE 1px bogus margin
40369             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
40370         }
40371         this.wrap = this.el.wrap({
40372             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
40373         });
40374         
40375         if (this.resizable) {
40376             this.resizeEl = new Roo.Resizable(this.wrap, {
40377                 pinned : true,
40378                 wrap: true,
40379                 dynamic : true,
40380                 minHeight : this.height,
40381                 height: this.height,
40382                 handles : this.resizable,
40383                 width: this.width,
40384                 listeners : {
40385                     resize : function(r, w, h) {
40386                         _t.onResize(w,h); // -something
40387                     }
40388                 }
40389             });
40390             
40391         }
40392
40393         this.frameId = Roo.id();
40394         
40395         this.createToolbar(this);
40396         
40397       
40398         
40399         var iframe = this.wrap.createChild({
40400             tag: 'iframe',
40401             id: this.frameId,
40402             name: this.frameId,
40403             frameBorder : 'no',
40404             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
40405         }, this.el
40406         );
40407         
40408        // console.log(iframe);
40409         //this.wrap.dom.appendChild(iframe);
40410
40411         this.iframe = iframe.dom;
40412
40413          this.assignDocWin();
40414         
40415         this.doc.designMode = 'on';
40416        
40417         this.doc.open();
40418         this.doc.write(this.getDocMarkup());
40419         this.doc.close();
40420
40421         
40422         var task = { // must defer to wait for browser to be ready
40423             run : function(){
40424                 //console.log("run task?" + this.doc.readyState);
40425                 this.assignDocWin();
40426                 if(this.doc.body || this.doc.readyState == 'complete'){
40427                     try {
40428                         this.doc.designMode="on";
40429                     } catch (e) {
40430                         return;
40431                     }
40432                     Roo.TaskMgr.stop(task);
40433                     this.initEditor.defer(10, this);
40434                 }
40435             },
40436             interval : 10,
40437             duration:10000,
40438             scope: this
40439         };
40440         Roo.TaskMgr.start(task);
40441
40442         if(!this.width){
40443             this.setSize(this.wrap.getSize());
40444         }
40445         if (this.resizeEl) {
40446             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
40447             // should trigger onReize..
40448         }
40449     },
40450
40451     // private
40452     onResize : function(w, h)
40453     {
40454         //Roo.log('resize: ' +w + ',' + h );
40455         Roo.form.HtmlEditor.superclass.onResize.apply(this, arguments);
40456         if(this.el && this.iframe){
40457             if(typeof w == 'number'){
40458                 var aw = w - this.wrap.getFrameWidth('lr');
40459                 this.el.setWidth(this.adjustWidth('textarea', aw));
40460                 this.iframe.style.width = aw + 'px';
40461             }
40462             if(typeof h == 'number'){
40463                 var tbh = 0;
40464                 for (var i =0; i < this.toolbars.length;i++) {
40465                     // fixme - ask toolbars for heights?
40466                     tbh += this.toolbars[i].tb.el.getHeight();
40467                     if (this.toolbars[i].footer) {
40468                         tbh += this.toolbars[i].footer.el.getHeight();
40469                     }
40470                 }
40471                 
40472                 
40473                 
40474                 
40475                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
40476                 ah -= 5; // knock a few pixes off for look..
40477                 this.el.setHeight(this.adjustWidth('textarea', ah));
40478                 this.iframe.style.height = ah + 'px';
40479                 if(this.doc){
40480                     (this.doc.body || this.doc.documentElement).style.height = (ah - (this.iframePad*2)) + 'px';
40481                 }
40482             }
40483         }
40484     },
40485
40486     /**
40487      * Toggles the editor between standard and source edit mode.
40488      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
40489      */
40490     toggleSourceEdit : function(sourceEditMode){
40491         
40492         this.sourceEditMode = sourceEditMode === true;
40493         
40494         if(this.sourceEditMode){
40495 //            Roo.log('in');
40496 //            Roo.log(this.syncValue());
40497             this.syncValue();
40498             this.iframe.className = 'x-hidden';
40499             this.el.removeClass('x-hidden');
40500             this.el.dom.removeAttribute('tabIndex');
40501             this.el.focus();
40502         }else{
40503 //            Roo.log('out')
40504 //            Roo.log(this.pushValue()); 
40505             this.pushValue();
40506             this.iframe.className = '';
40507             this.el.addClass('x-hidden');
40508             this.el.dom.setAttribute('tabIndex', -1);
40509             this.deferFocus();
40510         }
40511         this.setSize(this.wrap.getSize());
40512         this.fireEvent('editmodechange', this, this.sourceEditMode);
40513     },
40514
40515     // private used internally
40516     createLink : function(){
40517         var url = prompt(this.createLinkText, this.defaultLinkValue);
40518         if(url && url != 'http:/'+'/'){
40519             this.relayCmd('createlink', url);
40520         }
40521     },
40522
40523     // private (for BoxComponent)
40524     adjustSize : Roo.BoxComponent.prototype.adjustSize,
40525
40526     // private (for BoxComponent)
40527     getResizeEl : function(){
40528         return this.wrap;
40529     },
40530
40531     // private (for BoxComponent)
40532     getPositionEl : function(){
40533         return this.wrap;
40534     },
40535
40536     // private
40537     initEvents : function(){
40538         this.originalValue = this.getValue();
40539     },
40540
40541     /**
40542      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
40543      * @method
40544      */
40545     markInvalid : Roo.emptyFn,
40546     /**
40547      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
40548      * @method
40549      */
40550     clearInvalid : Roo.emptyFn,
40551
40552     setValue : function(v){
40553         Roo.form.HtmlEditor.superclass.setValue.call(this, v);
40554         this.pushValue();
40555     },
40556
40557     /**
40558      * Protected method that will not generally be called directly. If you need/want
40559      * custom HTML cleanup, this is the method you should override.
40560      * @param {String} html The HTML to be cleaned
40561      * return {String} The cleaned HTML
40562      */
40563     cleanHtml : function(html){
40564         html = String(html);
40565         if(html.length > 5){
40566             if(Roo.isSafari){ // strip safari nonsense
40567                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
40568             }
40569         }
40570         if(html == '&nbsp;'){
40571             html = '';
40572         }
40573         return html;
40574     },
40575
40576     /**
40577      * Protected method that will not generally be called directly. Syncs the contents
40578      * of the editor iframe with the textarea.
40579      */
40580     syncValue : function(){
40581         if(this.initialized){
40582             var bd = (this.doc.body || this.doc.documentElement);
40583             //this.cleanUpPaste(); -- this is done else where and causes havoc..
40584             var html = bd.innerHTML;
40585             if(Roo.isSafari){
40586                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
40587                 var m = bs.match(/text-align:(.*?);/i);
40588                 if(m && m[1]){
40589                     html = '<div style="'+m[0]+'">' + html + '</div>';
40590                 }
40591             }
40592             html = this.cleanHtml(html);
40593             // fix up the special chars.. normaly like back quotes in word...
40594             // however we do not want to do this with chinese..
40595             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
40596                 var cc = b.charCodeAt();
40597                 if (
40598                     (cc >= 0x4E00 && cc < 0xA000 ) ||
40599                     (cc >= 0x3400 && cc < 0x4E00 ) ||
40600                     (cc >= 0xf900 && cc < 0xfb00 )
40601                 ) {
40602                         return b;
40603                 }
40604                 return "&#"+cc+";" 
40605             });
40606             if(this.fireEvent('beforesync', this, html) !== false){
40607                 this.el.dom.value = html;
40608                 this.fireEvent('sync', this, html);
40609             }
40610         }
40611     },
40612
40613     /**
40614      * Protected method that will not generally be called directly. Pushes the value of the textarea
40615      * into the iframe editor.
40616      */
40617     pushValue : function(){
40618         if(this.initialized){
40619             var v = this.el.dom.value;
40620             
40621             if(v.length < 1){
40622                 v = '&#160;';
40623             }
40624             
40625             if(this.fireEvent('beforepush', this, v) !== false){
40626                 var d = (this.doc.body || this.doc.documentElement);
40627                 d.innerHTML = v;
40628                 this.cleanUpPaste();
40629                 this.el.dom.value = d.innerHTML;
40630                 this.fireEvent('push', this, v);
40631             }
40632         }
40633     },
40634
40635     // private
40636     deferFocus : function(){
40637         this.focus.defer(10, this);
40638     },
40639
40640     // doc'ed in Field
40641     focus : function(){
40642         if(this.win && !this.sourceEditMode){
40643             this.win.focus();
40644         }else{
40645             this.el.focus();
40646         }
40647     },
40648     
40649     assignDocWin: function()
40650     {
40651         var iframe = this.iframe;
40652         
40653          if(Roo.isIE){
40654             this.doc = iframe.contentWindow.document;
40655             this.win = iframe.contentWindow;
40656         } else {
40657             if (!Roo.get(this.frameId)) {
40658                 return;
40659             }
40660             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
40661             this.win = Roo.get(this.frameId).dom.contentWindow;
40662         }
40663     },
40664     
40665     // private
40666     initEditor : function(){
40667         //console.log("INIT EDITOR");
40668         this.assignDocWin();
40669         
40670         
40671         
40672         this.doc.designMode="on";
40673         this.doc.open();
40674         this.doc.write(this.getDocMarkup());
40675         this.doc.close();
40676         
40677         var dbody = (this.doc.body || this.doc.documentElement);
40678         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
40679         // this copies styles from the containing element into thsi one..
40680         // not sure why we need all of this..
40681         var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
40682         ss['background-attachment'] = 'fixed'; // w3c
40683         dbody.bgProperties = 'fixed'; // ie
40684         Roo.DomHelper.applyStyles(dbody, ss);
40685         Roo.EventManager.on(this.doc, {
40686             //'mousedown': this.onEditorEvent,
40687             'mouseup': this.onEditorEvent,
40688             'dblclick': this.onEditorEvent,
40689             'click': this.onEditorEvent,
40690             'keyup': this.onEditorEvent,
40691             buffer:100,
40692             scope: this
40693         });
40694         if(Roo.isGecko){
40695             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
40696         }
40697         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
40698             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
40699         }
40700         this.initialized = true;
40701
40702         this.fireEvent('initialize', this);
40703         this.pushValue();
40704     },
40705
40706     // private
40707     onDestroy : function(){
40708         
40709         
40710         
40711         if(this.rendered){
40712             
40713             for (var i =0; i < this.toolbars.length;i++) {
40714                 // fixme - ask toolbars for heights?
40715                 this.toolbars[i].onDestroy();
40716             }
40717             
40718             this.wrap.dom.innerHTML = '';
40719             this.wrap.remove();
40720         }
40721     },
40722
40723     // private
40724     onFirstFocus : function(){
40725         
40726         this.assignDocWin();
40727         
40728         
40729         this.activated = true;
40730         for (var i =0; i < this.toolbars.length;i++) {
40731             this.toolbars[i].onFirstFocus();
40732         }
40733        
40734         if(Roo.isGecko){ // prevent silly gecko errors
40735             this.win.focus();
40736             var s = this.win.getSelection();
40737             if(!s.focusNode || s.focusNode.nodeType != 3){
40738                 var r = s.getRangeAt(0);
40739                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
40740                 r.collapse(true);
40741                 this.deferFocus();
40742             }
40743             try{
40744                 this.execCmd('useCSS', true);
40745                 this.execCmd('styleWithCSS', false);
40746             }catch(e){}
40747         }
40748         this.fireEvent('activate', this);
40749     },
40750
40751     // private
40752     adjustFont: function(btn){
40753         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
40754         //if(Roo.isSafari){ // safari
40755         //    adjust *= 2;
40756        // }
40757         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
40758         if(Roo.isSafari){ // safari
40759             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
40760             v =  (v < 10) ? 10 : v;
40761             v =  (v > 48) ? 48 : v;
40762             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
40763             
40764         }
40765         
40766         
40767         v = Math.max(1, v+adjust);
40768         
40769         this.execCmd('FontSize', v  );
40770     },
40771
40772     onEditorEvent : function(e){
40773         this.fireEvent('editorevent', this, e);
40774       //  this.updateToolbar();
40775         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
40776     },
40777
40778     insertTag : function(tg)
40779     {
40780         // could be a bit smarter... -> wrap the current selected tRoo..
40781         if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
40782             
40783             range = this.createRange(this.getSelection());
40784             var wrappingNode = this.doc.createElement(tg.toLowerCase());
40785             wrappingNode.appendChild(range.extractContents());
40786             range.insertNode(wrappingNode);
40787
40788             return;
40789             
40790             
40791             
40792         }
40793         this.execCmd("formatblock",   tg);
40794         
40795     },
40796     
40797     insertText : function(txt)
40798     {
40799         
40800         
40801         var range = this.createRange();
40802         range.deleteContents();
40803                //alert(Sender.getAttribute('label'));
40804                
40805         range.insertNode(this.doc.createTextNode(txt));
40806     } ,
40807     
40808     // private
40809     relayBtnCmd : function(btn){
40810         this.relayCmd(btn.cmd);
40811     },
40812
40813     /**
40814      * Executes a Midas editor command on the editor document and performs necessary focus and
40815      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
40816      * @param {String} cmd The Midas command
40817      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
40818      */
40819     relayCmd : function(cmd, value){
40820         this.win.focus();
40821         this.execCmd(cmd, value);
40822         this.fireEvent('editorevent', this);
40823         //this.updateToolbar();
40824         this.deferFocus();
40825     },
40826
40827     /**
40828      * Executes a Midas editor command directly on the editor document.
40829      * For visual commands, you should use {@link #relayCmd} instead.
40830      * <b>This should only be called after the editor is initialized.</b>
40831      * @param {String} cmd The Midas command
40832      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
40833      */
40834     execCmd : function(cmd, value){
40835         this.doc.execCommand(cmd, false, value === undefined ? null : value);
40836         this.syncValue();
40837     },
40838  
40839  
40840    
40841     /**
40842      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
40843      * to insert tRoo.
40844      * @param {String} text | dom node.. 
40845      */
40846     insertAtCursor : function(text)
40847     {
40848         
40849         
40850         
40851         if(!this.activated){
40852             return;
40853         }
40854         /*
40855         if(Roo.isIE){
40856             this.win.focus();
40857             var r = this.doc.selection.createRange();
40858             if(r){
40859                 r.collapse(true);
40860                 r.pasteHTML(text);
40861                 this.syncValue();
40862                 this.deferFocus();
40863             
40864             }
40865             return;
40866         }
40867         */
40868         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
40869             this.win.focus();
40870             
40871             
40872             // from jquery ui (MIT licenced)
40873             var range, node;
40874             var win = this.win;
40875             
40876             if (win.getSelection && win.getSelection().getRangeAt) {
40877                 range = win.getSelection().getRangeAt(0);
40878                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
40879                 range.insertNode(node);
40880             } else if (win.document.selection && win.document.selection.createRange) {
40881                 // no firefox support
40882                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
40883                 win.document.selection.createRange().pasteHTML(txt);
40884             } else {
40885                 // no firefox support
40886                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
40887                 this.execCmd('InsertHTML', txt);
40888             } 
40889             
40890             this.syncValue();
40891             
40892             this.deferFocus();
40893         }
40894     },
40895  // private
40896     mozKeyPress : function(e){
40897         if(e.ctrlKey){
40898             var c = e.getCharCode(), cmd;
40899           
40900             if(c > 0){
40901                 c = String.fromCharCode(c).toLowerCase();
40902                 switch(c){
40903                     case 'b':
40904                         cmd = 'bold';
40905                         break;
40906                     case 'i':
40907                         cmd = 'italic';
40908                         break;
40909                     
40910                     case 'u':
40911                         cmd = 'underline';
40912                         break;
40913                     
40914                     case 'v':
40915                         this.cleanUpPaste.defer(100, this);
40916                         return;
40917                         
40918                 }
40919                 if(cmd){
40920                     this.win.focus();
40921                     this.execCmd(cmd);
40922                     this.deferFocus();
40923                     e.preventDefault();
40924                 }
40925                 
40926             }
40927         }
40928     },
40929
40930     // private
40931     fixKeys : function(){ // load time branching for fastest keydown performance
40932         if(Roo.isIE){
40933             return function(e){
40934                 var k = e.getKey(), r;
40935                 if(k == e.TAB){
40936                     e.stopEvent();
40937                     r = this.doc.selection.createRange();
40938                     if(r){
40939                         r.collapse(true);
40940                         r.pasteHTML('&#160;&#160;&#160;&#160;');
40941                         this.deferFocus();
40942                     }
40943                     return;
40944                 }
40945                 
40946                 if(k == e.ENTER){
40947                     r = this.doc.selection.createRange();
40948                     if(r){
40949                         var target = r.parentElement();
40950                         if(!target || target.tagName.toLowerCase() != 'li'){
40951                             e.stopEvent();
40952                             r.pasteHTML('<br />');
40953                             r.collapse(false);
40954                             r.select();
40955                         }
40956                     }
40957                 }
40958                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
40959                     this.cleanUpPaste.defer(100, this);
40960                     return;
40961                 }
40962                 
40963                 
40964             };
40965         }else if(Roo.isOpera){
40966             return function(e){
40967                 var k = e.getKey();
40968                 if(k == e.TAB){
40969                     e.stopEvent();
40970                     this.win.focus();
40971                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
40972                     this.deferFocus();
40973                 }
40974                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
40975                     this.cleanUpPaste.defer(100, this);
40976                     return;
40977                 }
40978                 
40979             };
40980         }else if(Roo.isSafari){
40981             return function(e){
40982                 var k = e.getKey();
40983                 
40984                 if(k == e.TAB){
40985                     e.stopEvent();
40986                     this.execCmd('InsertText','\t');
40987                     this.deferFocus();
40988                     return;
40989                 }
40990                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
40991                     this.cleanUpPaste.defer(100, this);
40992                     return;
40993                 }
40994                 
40995              };
40996         }
40997     }(),
40998     
40999     getAllAncestors: function()
41000     {
41001         var p = this.getSelectedNode();
41002         var a = [];
41003         if (!p) {
41004             a.push(p); // push blank onto stack..
41005             p = this.getParentElement();
41006         }
41007         
41008         
41009         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
41010             a.push(p);
41011             p = p.parentNode;
41012         }
41013         a.push(this.doc.body);
41014         return a;
41015     },
41016     lastSel : false,
41017     lastSelNode : false,
41018     
41019     
41020     getSelection : function() 
41021     {
41022         this.assignDocWin();
41023         return Roo.isIE ? this.doc.selection : this.win.getSelection();
41024     },
41025     
41026     getSelectedNode: function() 
41027     {
41028         // this may only work on Gecko!!!
41029         
41030         // should we cache this!!!!
41031         
41032         
41033         
41034          
41035         var range = this.createRange(this.getSelection()).cloneRange();
41036         
41037         if (Roo.isIE) {
41038             var parent = range.parentElement();
41039             while (true) {
41040                 var testRange = range.duplicate();
41041                 testRange.moveToElementText(parent);
41042                 if (testRange.inRange(range)) {
41043                     break;
41044                 }
41045                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
41046                     break;
41047                 }
41048                 parent = parent.parentElement;
41049             }
41050             return parent;
41051         }
41052         
41053         // is ancestor a text element.
41054         var ac =  range.commonAncestorContainer;
41055         if (ac.nodeType == 3) {
41056             ac = ac.parentNode;
41057         }
41058         
41059         var ar = ac.childNodes;
41060          
41061         var nodes = [];
41062         var other_nodes = [];
41063         var has_other_nodes = false;
41064         for (var i=0;i<ar.length;i++) {
41065             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
41066                 continue;
41067             }
41068             // fullly contained node.
41069             
41070             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
41071                 nodes.push(ar[i]);
41072                 continue;
41073             }
41074             
41075             // probably selected..
41076             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
41077                 other_nodes.push(ar[i]);
41078                 continue;
41079             }
41080             // outer..
41081             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
41082                 continue;
41083             }
41084             
41085             
41086             has_other_nodes = true;
41087         }
41088         if (!nodes.length && other_nodes.length) {
41089             nodes= other_nodes;
41090         }
41091         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
41092             return false;
41093         }
41094         
41095         return nodes[0];
41096     },
41097     createRange: function(sel)
41098     {
41099         // this has strange effects when using with 
41100         // top toolbar - not sure if it's a great idea.
41101         //this.editor.contentWindow.focus();
41102         if (typeof sel != "undefined") {
41103             try {
41104                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
41105             } catch(e) {
41106                 return this.doc.createRange();
41107             }
41108         } else {
41109             return this.doc.createRange();
41110         }
41111     },
41112     getParentElement: function()
41113     {
41114         
41115         this.assignDocWin();
41116         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
41117         
41118         var range = this.createRange(sel);
41119          
41120         try {
41121             var p = range.commonAncestorContainer;
41122             while (p.nodeType == 3) { // text node
41123                 p = p.parentNode;
41124             }
41125             return p;
41126         } catch (e) {
41127             return null;
41128         }
41129     
41130     },
41131     /***
41132      *
41133      * Range intersection.. the hard stuff...
41134      *  '-1' = before
41135      *  '0' = hits..
41136      *  '1' = after.
41137      *         [ -- selected range --- ]
41138      *   [fail]                        [fail]
41139      *
41140      *    basically..
41141      *      if end is before start or  hits it. fail.
41142      *      if start is after end or hits it fail.
41143      *
41144      *   if either hits (but other is outside. - then it's not 
41145      *   
41146      *    
41147      **/
41148     
41149     
41150     // @see http://www.thismuchiknow.co.uk/?p=64.
41151     rangeIntersectsNode : function(range, node)
41152     {
41153         var nodeRange = node.ownerDocument.createRange();
41154         try {
41155             nodeRange.selectNode(node);
41156         } catch (e) {
41157             nodeRange.selectNodeContents(node);
41158         }
41159     
41160         var rangeStartRange = range.cloneRange();
41161         rangeStartRange.collapse(true);
41162     
41163         var rangeEndRange = range.cloneRange();
41164         rangeEndRange.collapse(false);
41165     
41166         var nodeStartRange = nodeRange.cloneRange();
41167         nodeStartRange.collapse(true);
41168     
41169         var nodeEndRange = nodeRange.cloneRange();
41170         nodeEndRange.collapse(false);
41171     
41172         return rangeStartRange.compareBoundaryPoints(
41173                  Range.START_TO_START, nodeEndRange) == -1 &&
41174                rangeEndRange.compareBoundaryPoints(
41175                  Range.START_TO_START, nodeStartRange) == 1;
41176         
41177          
41178     },
41179     rangeCompareNode : function(range, node)
41180     {
41181         var nodeRange = node.ownerDocument.createRange();
41182         try {
41183             nodeRange.selectNode(node);
41184         } catch (e) {
41185             nodeRange.selectNodeContents(node);
41186         }
41187         
41188         
41189         range.collapse(true);
41190     
41191         nodeRange.collapse(true);
41192      
41193         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
41194         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
41195          
41196         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
41197         
41198         var nodeIsBefore   =  ss == 1;
41199         var nodeIsAfter    = ee == -1;
41200         
41201         if (nodeIsBefore && nodeIsAfter)
41202             return 0; // outer
41203         if (!nodeIsBefore && nodeIsAfter)
41204             return 1; //right trailed.
41205         
41206         if (nodeIsBefore && !nodeIsAfter)
41207             return 2;  // left trailed.
41208         // fully contined.
41209         return 3;
41210     },
41211
41212     // private? - in a new class?
41213     cleanUpPaste :  function()
41214     {
41215         // cleans up the whole document..
41216          Roo.log('cleanuppaste');
41217         this.cleanUpChildren(this.doc.body);
41218         var clean = this.cleanWordChars(this.doc.body.innerHTML);
41219         if (clean != this.doc.body.innerHTML) {
41220             this.doc.body.innerHTML = clean;
41221         }
41222         
41223     },
41224     
41225     cleanWordChars : function(input) {// change the chars to hex code
41226         var he = Roo.form.HtmlEditor;
41227         
41228         var output = input;
41229         Roo.each(he.swapCodes, function(sw) { 
41230             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
41231             
41232             output = output.replace(swapper, sw[1]);
41233         });
41234         
41235         return output;
41236     },
41237     
41238     
41239     cleanUpChildren : function (n)
41240     {
41241         if (!n.childNodes.length) {
41242             return;
41243         }
41244         for (var i = n.childNodes.length-1; i > -1 ; i--) {
41245            this.cleanUpChild(n.childNodes[i]);
41246         }
41247     },
41248     
41249     
41250         
41251     
41252     cleanUpChild : function (node)
41253     {
41254         var ed = this;
41255         //console.log(node);
41256         if (node.nodeName == "#text") {
41257             // clean up silly Windows -- stuff?
41258             return; 
41259         }
41260         if (node.nodeName == "#comment") {
41261             node.parentNode.removeChild(node);
41262             // clean up silly Windows -- stuff?
41263             return; 
41264         }
41265         
41266         if (Roo.form.HtmlEditor.black.indexOf(node.tagName.toLowerCase()) > -1) {
41267             // remove node.
41268             node.parentNode.removeChild(node);
41269             return;
41270             
41271         }
41272         
41273         var remove_keep_children= Roo.form.HtmlEditor.remove.indexOf(node.tagName.toLowerCase()) > -1;
41274         
41275         // remove <a name=....> as rendering on yahoo mailer is borked with this.
41276         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
41277         
41278         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
41279         //    remove_keep_children = true;
41280         //}
41281         
41282         if (remove_keep_children) {
41283             this.cleanUpChildren(node);
41284             // inserts everything just before this node...
41285             while (node.childNodes.length) {
41286                 var cn = node.childNodes[0];
41287                 node.removeChild(cn);
41288                 node.parentNode.insertBefore(cn, node);
41289             }
41290             node.parentNode.removeChild(node);
41291             return;
41292         }
41293         
41294         if (!node.attributes || !node.attributes.length) {
41295             this.cleanUpChildren(node);
41296             return;
41297         }
41298         
41299         function cleanAttr(n,v)
41300         {
41301             
41302             if (v.match(/^\./) || v.match(/^\//)) {
41303                 return;
41304             }
41305             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
41306                 return;
41307             }
41308             if (v.match(/^#/)) {
41309                 return;
41310             }
41311 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
41312             node.removeAttribute(n);
41313             
41314         }
41315         
41316         function cleanStyle(n,v)
41317         {
41318             if (v.match(/expression/)) { //XSS?? should we even bother..
41319                 node.removeAttribute(n);
41320                 return;
41321             }
41322             var cwhite = typeof(ed.cwhite) == 'undefined' ? Roo.form.HtmlEditor.cwhite : ed.cwhite;
41323             var cblack = typeof(ed.cblack) == 'undefined' ? Roo.form.HtmlEditor.cblack : ed.cblack;
41324             
41325             
41326             var parts = v.split(/;/);
41327             var clean = [];
41328             
41329             Roo.each(parts, function(p) {
41330                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
41331                 if (!p.length) {
41332                     return true;
41333                 }
41334                 var l = p.split(':').shift().replace(/\s+/g,'');
41335                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
41336                 
41337                 
41338                 if ( cblack.indexOf(l) > -1) {
41339 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
41340                     //node.removeAttribute(n);
41341                     return true;
41342                 }
41343                 //Roo.log()
41344                 // only allow 'c whitelisted system attributes'
41345                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
41346 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
41347                     //node.removeAttribute(n);
41348                     return true;
41349                 }
41350                 
41351                 
41352                  
41353                 
41354                 clean.push(p);
41355                 return true;
41356             });
41357             if (clean.length) { 
41358                 node.setAttribute(n, clean.join(';'));
41359             } else {
41360                 node.removeAttribute(n);
41361             }
41362             
41363         }
41364         
41365         
41366         for (var i = node.attributes.length-1; i > -1 ; i--) {
41367             var a = node.attributes[i];
41368             //console.log(a);
41369             
41370             if (a.name.toLowerCase().substr(0,2)=='on')  {
41371                 node.removeAttribute(a.name);
41372                 continue;
41373             }
41374             if (Roo.form.HtmlEditor.ablack.indexOf(a.name.toLowerCase()) > -1) {
41375                 node.removeAttribute(a.name);
41376                 continue;
41377             }
41378             if (Roo.form.HtmlEditor.aclean.indexOf(a.name.toLowerCase()) > -1) {
41379                 cleanAttr(a.name,a.value); // fixme..
41380                 continue;
41381             }
41382             if (a.name == 'style') {
41383                 cleanStyle(a.name,a.value);
41384                 continue;
41385             }
41386             /// clean up MS crap..
41387             // tecnically this should be a list of valid class'es..
41388             
41389             
41390             if (a.name == 'class') {
41391                 if (a.value.match(/^Mso/)) {
41392                     node.className = '';
41393                 }
41394                 
41395                 if (a.value.match(/body/)) {
41396                     node.className = '';
41397                 }
41398                 continue;
41399             }
41400             
41401             // style cleanup!?
41402             // class cleanup?
41403             
41404         }
41405         
41406         
41407         this.cleanUpChildren(node);
41408         
41409         
41410     }
41411     
41412     
41413     // hide stuff that is not compatible
41414     /**
41415      * @event blur
41416      * @hide
41417      */
41418     /**
41419      * @event change
41420      * @hide
41421      */
41422     /**
41423      * @event focus
41424      * @hide
41425      */
41426     /**
41427      * @event specialkey
41428      * @hide
41429      */
41430     /**
41431      * @cfg {String} fieldClass @hide
41432      */
41433     /**
41434      * @cfg {String} focusClass @hide
41435      */
41436     /**
41437      * @cfg {String} autoCreate @hide
41438      */
41439     /**
41440      * @cfg {String} inputType @hide
41441      */
41442     /**
41443      * @cfg {String} invalidClass @hide
41444      */
41445     /**
41446      * @cfg {String} invalidText @hide
41447      */
41448     /**
41449      * @cfg {String} msgFx @hide
41450      */
41451     /**
41452      * @cfg {String} validateOnBlur @hide
41453      */
41454 });
41455
41456 Roo.form.HtmlEditor.white = [
41457         'area', 'br', 'img', 'input', 'hr', 'wbr',
41458         
41459        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
41460        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
41461        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
41462        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
41463        'table',   'ul',         'xmp', 
41464        
41465        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
41466       'thead',   'tr', 
41467      
41468       'dir', 'menu', 'ol', 'ul', 'dl',
41469        
41470       'embed',  'object'
41471 ];
41472
41473
41474 Roo.form.HtmlEditor.black = [
41475     //    'embed',  'object', // enable - backend responsiblity to clean thiese
41476         'applet', // 
41477         'base',   'basefont', 'bgsound', 'blink',  'body', 
41478         'frame',  'frameset', 'head',    'html',   'ilayer', 
41479         'iframe', 'layer',  'link',     'meta',    'object',   
41480         'script', 'style' ,'title',  'xml' // clean later..
41481 ];
41482 Roo.form.HtmlEditor.clean = [
41483     'script', 'style', 'title', 'xml'
41484 ];
41485 Roo.form.HtmlEditor.remove = [
41486     'font'
41487 ];
41488 // attributes..
41489
41490 Roo.form.HtmlEditor.ablack = [
41491     'on'
41492 ];
41493     
41494 Roo.form.HtmlEditor.aclean = [ 
41495     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
41496 ];
41497
41498 // protocols..
41499 Roo.form.HtmlEditor.pwhite= [
41500         'http',  'https',  'mailto'
41501 ];
41502
41503 // white listed style attributes.
41504 Roo.form.HtmlEditor.cwhite= [
41505       //  'text-align', /// default is to allow most things..
41506       
41507          
41508 //        'font-size'//??
41509 ];
41510
41511 // black listed style attributes.
41512 Roo.form.HtmlEditor.cblack= [
41513       //  'font-size' -- this can be set by the project 
41514 ];
41515
41516
41517 Roo.form.HtmlEditor.swapCodes   =[ 
41518     [    8211, "--" ], 
41519     [    8212, "--" ], 
41520     [    8216,  "'" ],  
41521     [    8217, "'" ],  
41522     [    8220, '"' ],  
41523     [    8221, '"' ],  
41524     [    8226, "*" ],  
41525     [    8230, "..." ]
41526 ]; 
41527
41528     // <script type="text/javascript">
41529 /*
41530  * Based on
41531  * Ext JS Library 1.1.1
41532  * Copyright(c) 2006-2007, Ext JS, LLC.
41533  *  
41534  
41535  */
41536
41537 /**
41538  * @class Roo.form.HtmlEditorToolbar1
41539  * Basic Toolbar
41540  * 
41541  * Usage:
41542  *
41543  new Roo.form.HtmlEditor({
41544     ....
41545     toolbars : [
41546         new Roo.form.HtmlEditorToolbar1({
41547             disable : { fonts: 1 , format: 1, ..., ... , ...],
41548             btns : [ .... ]
41549         })
41550     }
41551      
41552  * 
41553  * @cfg {Object} disable List of elements to disable..
41554  * @cfg {Array} btns List of additional buttons.
41555  * 
41556  * 
41557  * NEEDS Extra CSS? 
41558  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
41559  */
41560  
41561 Roo.form.HtmlEditor.ToolbarStandard = function(config)
41562 {
41563     
41564     Roo.apply(this, config);
41565     
41566     // default disabled, based on 'good practice'..
41567     this.disable = this.disable || {};
41568     Roo.applyIf(this.disable, {
41569         fontSize : true,
41570         colors : true,
41571         specialElements : true
41572     });
41573     
41574     
41575     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
41576     // dont call parent... till later.
41577 }
41578
41579 Roo.apply(Roo.form.HtmlEditor.ToolbarStandard.prototype,  {
41580     
41581     tb: false,
41582     
41583     rendered: false,
41584     
41585     editor : false,
41586     /**
41587      * @cfg {Object} disable  List of toolbar elements to disable
41588          
41589      */
41590     disable : false,
41591       /**
41592      * @cfg {Array} fontFamilies An array of available font families
41593      */
41594     fontFamilies : [
41595         'Arial',
41596         'Courier New',
41597         'Tahoma',
41598         'Times New Roman',
41599         'Verdana'
41600     ],
41601     
41602     specialChars : [
41603            "&#169;",
41604           "&#174;",     
41605           "&#8482;",    
41606           "&#163;" ,    
41607          // "&#8212;",    
41608           "&#8230;",    
41609           "&#247;" ,    
41610         //  "&#225;" ,     ?? a acute?
41611            "&#8364;"    , //Euro
41612        //   "&#8220;"    ,
41613         //  "&#8221;"    ,
41614         //  "&#8226;"    ,
41615           "&#176;"  //   , // degrees
41616
41617          // "&#233;"     , // e ecute
41618          // "&#250;"     , // u ecute?
41619     ],
41620     
41621     specialElements : [
41622         {
41623             text: "Insert Table",
41624             xtype: 'MenuItem',
41625             xns : Roo.Menu,
41626             ihtml :  '<table><tr><td>Cell</td></tr></table>' 
41627                 
41628         },
41629         {    
41630             text: "Insert Image",
41631             xtype: 'MenuItem',
41632             xns : Roo.Menu,
41633             ihtml : '<img src="about:blank"/>'
41634             
41635         }
41636         
41637          
41638     ],
41639     
41640     
41641     inputElements : [ 
41642             "form", "input:text", "input:hidden", "input:checkbox", "input:radio", "input:password", 
41643             "input:submit", "input:button", "select", "textarea", "label" ],
41644     formats : [
41645         ["p"] ,  
41646         ["h1"],["h2"],["h3"],["h4"],["h5"],["h6"], 
41647         ["pre"],[ "code"], 
41648         ["abbr"],[ "acronym"],[ "address"],[ "cite"],[ "samp"],[ "var"],
41649         ['div'],['span']
41650     ],
41651      /**
41652      * @cfg {String} defaultFont default font to use.
41653      */
41654     defaultFont: 'tahoma',
41655    
41656     fontSelect : false,
41657     
41658     
41659     formatCombo : false,
41660     
41661     init : function(editor)
41662     {
41663         this.editor = editor;
41664         
41665         
41666         var fid = editor.frameId;
41667         var etb = this;
41668         function btn(id, toggle, handler){
41669             var xid = fid + '-'+ id ;
41670             return {
41671                 id : xid,
41672                 cmd : id,
41673                 cls : 'x-btn-icon x-edit-'+id,
41674                 enableToggle:toggle !== false,
41675                 scope: editor, // was editor...
41676                 handler:handler||editor.relayBtnCmd,
41677                 clickEvent:'mousedown',
41678                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
41679                 tabIndex:-1
41680             };
41681         }
41682         
41683         
41684         
41685         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
41686         this.tb = tb;
41687          // stop form submits
41688         tb.el.on('click', function(e){
41689             e.preventDefault(); // what does this do?
41690         });
41691
41692         if(!this.disable.font) { // && !Roo.isSafari){
41693             /* why no safari for fonts 
41694             editor.fontSelect = tb.el.createChild({
41695                 tag:'select',
41696                 tabIndex: -1,
41697                 cls:'x-font-select',
41698                 html: this.createFontOptions()
41699             });
41700             
41701             editor.fontSelect.on('change', function(){
41702                 var font = editor.fontSelect.dom.value;
41703                 editor.relayCmd('fontname', font);
41704                 editor.deferFocus();
41705             }, editor);
41706             
41707             tb.add(
41708                 editor.fontSelect.dom,
41709                 '-'
41710             );
41711             */
41712             
41713         };
41714         if(!this.disable.formats){
41715             this.formatCombo = new Roo.form.ComboBox({
41716                 store: new Roo.data.SimpleStore({
41717                     id : 'tag',
41718                     fields: ['tag'],
41719                     data : this.formats // from states.js
41720                 }),
41721                 blockFocus : true,
41722                 name : '',
41723                 //autoCreate : {tag: "div",  size: "20"},
41724                 displayField:'tag',
41725                 typeAhead: false,
41726                 mode: 'local',
41727                 editable : false,
41728                 triggerAction: 'all',
41729                 emptyText:'Add tag',
41730                 selectOnFocus:true,
41731                 width:135,
41732                 listeners : {
41733                     'select': function(c, r, i) {
41734                         editor.insertTag(r.get('tag'));
41735                         editor.focus();
41736                     }
41737                 }
41738
41739             });
41740             tb.addField(this.formatCombo);
41741             
41742         }
41743         
41744         if(!this.disable.format){
41745             tb.add(
41746                 btn('bold'),
41747                 btn('italic'),
41748                 btn('underline')
41749             );
41750         };
41751         if(!this.disable.fontSize){
41752             tb.add(
41753                 '-',
41754                 
41755                 
41756                 btn('increasefontsize', false, editor.adjustFont),
41757                 btn('decreasefontsize', false, editor.adjustFont)
41758             );
41759         };
41760         
41761         
41762         if(!this.disable.colors){
41763             tb.add(
41764                 '-', {
41765                     id:editor.frameId +'-forecolor',
41766                     cls:'x-btn-icon x-edit-forecolor',
41767                     clickEvent:'mousedown',
41768                     tooltip: this.buttonTips['forecolor'] || undefined,
41769                     tabIndex:-1,
41770                     menu : new Roo.menu.ColorMenu({
41771                         allowReselect: true,
41772                         focus: Roo.emptyFn,
41773                         value:'000000',
41774                         plain:true,
41775                         selectHandler: function(cp, color){
41776                             editor.execCmd('forecolor', Roo.isSafari || Roo.isIE ? '#'+color : color);
41777                             editor.deferFocus();
41778                         },
41779                         scope: editor,
41780                         clickEvent:'mousedown'
41781                     })
41782                 }, {
41783                     id:editor.frameId +'backcolor',
41784                     cls:'x-btn-icon x-edit-backcolor',
41785                     clickEvent:'mousedown',
41786                     tooltip: this.buttonTips['backcolor'] || undefined,
41787                     tabIndex:-1,
41788                     menu : new Roo.menu.ColorMenu({
41789                         focus: Roo.emptyFn,
41790                         value:'FFFFFF',
41791                         plain:true,
41792                         allowReselect: true,
41793                         selectHandler: function(cp, color){
41794                             if(Roo.isGecko){
41795                                 editor.execCmd('useCSS', false);
41796                                 editor.execCmd('hilitecolor', color);
41797                                 editor.execCmd('useCSS', true);
41798                                 editor.deferFocus();
41799                             }else{
41800                                 editor.execCmd(Roo.isOpera ? 'hilitecolor' : 'backcolor', 
41801                                     Roo.isSafari || Roo.isIE ? '#'+color : color);
41802                                 editor.deferFocus();
41803                             }
41804                         },
41805                         scope:editor,
41806                         clickEvent:'mousedown'
41807                     })
41808                 }
41809             );
41810         };
41811         // now add all the items...
41812         
41813
41814         if(!this.disable.alignments){
41815             tb.add(
41816                 '-',
41817                 btn('justifyleft'),
41818                 btn('justifycenter'),
41819                 btn('justifyright')
41820             );
41821         };
41822
41823         //if(!Roo.isSafari){
41824             if(!this.disable.links){
41825                 tb.add(
41826                     '-',
41827                     btn('createlink', false, editor.createLink)    /// MOVE TO HERE?!!?!?!?!
41828                 );
41829             };
41830
41831             if(!this.disable.lists){
41832                 tb.add(
41833                     '-',
41834                     btn('insertorderedlist'),
41835                     btn('insertunorderedlist')
41836                 );
41837             }
41838             if(!this.disable.sourceEdit){
41839                 tb.add(
41840                     '-',
41841                     btn('sourceedit', true, function(btn){
41842                         this.toggleSourceEdit(btn.pressed);
41843                     })
41844                 );
41845             }
41846         //}
41847         
41848         var smenu = { };
41849         // special menu.. - needs to be tidied up..
41850         if (!this.disable.special) {
41851             smenu = {
41852                 text: "&#169;",
41853                 cls: 'x-edit-none',
41854                 
41855                 menu : {
41856                     items : []
41857                 }
41858             };
41859             for (var i =0; i < this.specialChars.length; i++) {
41860                 smenu.menu.items.push({
41861                     
41862                     html: this.specialChars[i],
41863                     handler: function(a,b) {
41864                         editor.insertAtCursor(String.fromCharCode(a.html.replace('&#','').replace(';', '')));
41865                         //editor.insertAtCursor(a.html);
41866                         
41867                     },
41868                     tabIndex:-1
41869                 });
41870             }
41871             
41872             
41873             tb.add(smenu);
41874             
41875             
41876         }
41877          
41878         if (!this.disable.specialElements) {
41879             var semenu = {
41880                 text: "Other;",
41881                 cls: 'x-edit-none',
41882                 menu : {
41883                     items : []
41884                 }
41885             };
41886             for (var i =0; i < this.specialElements.length; i++) {
41887                 semenu.menu.items.push(
41888                     Roo.apply({ 
41889                         handler: function(a,b) {
41890                             editor.insertAtCursor(this.ihtml);
41891                         }
41892                     }, this.specialElements[i])
41893                 );
41894                     
41895             }
41896             
41897             tb.add(semenu);
41898             
41899             
41900         }
41901          
41902         
41903         if (this.btns) {
41904             for(var i =0; i< this.btns.length;i++) {
41905                 var b = Roo.factory(this.btns[i],Roo.form);
41906                 b.cls =  'x-edit-none';
41907                 b.scope = editor;
41908                 tb.add(b);
41909             }
41910         
41911         }
41912         
41913         
41914         
41915         // disable everything...
41916         
41917         this.tb.items.each(function(item){
41918            if(item.id != editor.frameId+ '-sourceedit'){
41919                 item.disable();
41920             }
41921         });
41922         this.rendered = true;
41923         
41924         // the all the btns;
41925         editor.on('editorevent', this.updateToolbar, this);
41926         // other toolbars need to implement this..
41927         //editor.on('editmodechange', this.updateToolbar, this);
41928     },
41929     
41930     
41931     
41932     /**
41933      * Protected method that will not generally be called directly. It triggers
41934      * a toolbar update by reading the markup state of the current selection in the editor.
41935      */
41936     updateToolbar: function(){
41937
41938         if(!this.editor.activated){
41939             this.editor.onFirstFocus();
41940             return;
41941         }
41942
41943         var btns = this.tb.items.map, 
41944             doc = this.editor.doc,
41945             frameId = this.editor.frameId;
41946
41947         if(!this.disable.font && !Roo.isSafari){
41948             /*
41949             var name = (doc.queryCommandValue('FontName')||this.editor.defaultFont).toLowerCase();
41950             if(name != this.fontSelect.dom.value){
41951                 this.fontSelect.dom.value = name;
41952             }
41953             */
41954         }
41955         if(!this.disable.format){
41956             btns[frameId + '-bold'].toggle(doc.queryCommandState('bold'));
41957             btns[frameId + '-italic'].toggle(doc.queryCommandState('italic'));
41958             btns[frameId + '-underline'].toggle(doc.queryCommandState('underline'));
41959         }
41960         if(!this.disable.alignments){
41961             btns[frameId + '-justifyleft'].toggle(doc.queryCommandState('justifyleft'));
41962             btns[frameId + '-justifycenter'].toggle(doc.queryCommandState('justifycenter'));
41963             btns[frameId + '-justifyright'].toggle(doc.queryCommandState('justifyright'));
41964         }
41965         if(!Roo.isSafari && !this.disable.lists){
41966             btns[frameId + '-insertorderedlist'].toggle(doc.queryCommandState('insertorderedlist'));
41967             btns[frameId + '-insertunorderedlist'].toggle(doc.queryCommandState('insertunorderedlist'));
41968         }
41969         
41970         var ans = this.editor.getAllAncestors();
41971         if (this.formatCombo) {
41972             
41973             
41974             var store = this.formatCombo.store;
41975             this.formatCombo.setValue("");
41976             for (var i =0; i < ans.length;i++) {
41977                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
41978                     // select it..
41979                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
41980                     break;
41981                 }
41982             }
41983         }
41984         
41985         
41986         
41987         // hides menus... - so this cant be on a menu...
41988         Roo.menu.MenuMgr.hideAll();
41989
41990         //this.editorsyncValue();
41991     },
41992    
41993     
41994     createFontOptions : function(){
41995         var buf = [], fs = this.fontFamilies, ff, lc;
41996         
41997         
41998         
41999         for(var i = 0, len = fs.length; i< len; i++){
42000             ff = fs[i];
42001             lc = ff.toLowerCase();
42002             buf.push(
42003                 '<option value="',lc,'" style="font-family:',ff,';"',
42004                     (this.defaultFont == lc ? ' selected="true">' : '>'),
42005                     ff,
42006                 '</option>'
42007             );
42008         }
42009         return buf.join('');
42010     },
42011     
42012     toggleSourceEdit : function(sourceEditMode){
42013         if(sourceEditMode === undefined){
42014             sourceEditMode = !this.sourceEditMode;
42015         }
42016         this.sourceEditMode = sourceEditMode === true;
42017         var btn = this.tb.items.get(this.editor.frameId +'-sourceedit');
42018         // just toggle the button?
42019         if(btn.pressed !== this.editor.sourceEditMode){
42020             btn.toggle(this.editor.sourceEditMode);
42021             return;
42022         }
42023         
42024         if(this.sourceEditMode){
42025             this.tb.items.each(function(item){
42026                 if(item.cmd != 'sourceedit'){
42027                     item.disable();
42028                 }
42029             });
42030           
42031         }else{
42032             if(this.initialized){
42033                 this.tb.items.each(function(item){
42034                     item.enable();
42035                 });
42036             }
42037             
42038         }
42039         // tell the editor that it's been pressed..
42040         this.editor.toggleSourceEdit(sourceEditMode);
42041        
42042     },
42043      /**
42044      * Object collection of toolbar tooltips for the buttons in the editor. The key
42045      * is the command id associated with that button and the value is a valid QuickTips object.
42046      * For example:
42047 <pre><code>
42048 {
42049     bold : {
42050         title: 'Bold (Ctrl+B)',
42051         text: 'Make the selected text bold.',
42052         cls: 'x-html-editor-tip'
42053     },
42054     italic : {
42055         title: 'Italic (Ctrl+I)',
42056         text: 'Make the selected text italic.',
42057         cls: 'x-html-editor-tip'
42058     },
42059     ...
42060 </code></pre>
42061     * @type Object
42062      */
42063     buttonTips : {
42064         bold : {
42065             title: 'Bold (Ctrl+B)',
42066             text: 'Make the selected text bold.',
42067             cls: 'x-html-editor-tip'
42068         },
42069         italic : {
42070             title: 'Italic (Ctrl+I)',
42071             text: 'Make the selected text italic.',
42072             cls: 'x-html-editor-tip'
42073         },
42074         underline : {
42075             title: 'Underline (Ctrl+U)',
42076             text: 'Underline the selected text.',
42077             cls: 'x-html-editor-tip'
42078         },
42079         increasefontsize : {
42080             title: 'Grow Text',
42081             text: 'Increase the font size.',
42082             cls: 'x-html-editor-tip'
42083         },
42084         decreasefontsize : {
42085             title: 'Shrink Text',
42086             text: 'Decrease the font size.',
42087             cls: 'x-html-editor-tip'
42088         },
42089         backcolor : {
42090             title: 'Text Highlight Color',
42091             text: 'Change the background color of the selected text.',
42092             cls: 'x-html-editor-tip'
42093         },
42094         forecolor : {
42095             title: 'Font Color',
42096             text: 'Change the color of the selected text.',
42097             cls: 'x-html-editor-tip'
42098         },
42099         justifyleft : {
42100             title: 'Align Text Left',
42101             text: 'Align text to the left.',
42102             cls: 'x-html-editor-tip'
42103         },
42104         justifycenter : {
42105             title: 'Center Text',
42106             text: 'Center text in the editor.',
42107             cls: 'x-html-editor-tip'
42108         },
42109         justifyright : {
42110             title: 'Align Text Right',
42111             text: 'Align text to the right.',
42112             cls: 'x-html-editor-tip'
42113         },
42114         insertunorderedlist : {
42115             title: 'Bullet List',
42116             text: 'Start a bulleted list.',
42117             cls: 'x-html-editor-tip'
42118         },
42119         insertorderedlist : {
42120             title: 'Numbered List',
42121             text: 'Start a numbered list.',
42122             cls: 'x-html-editor-tip'
42123         },
42124         createlink : {
42125             title: 'Hyperlink',
42126             text: 'Make the selected text a hyperlink.',
42127             cls: 'x-html-editor-tip'
42128         },
42129         sourceedit : {
42130             title: 'Source Edit',
42131             text: 'Switch to source editing mode.',
42132             cls: 'x-html-editor-tip'
42133         }
42134     },
42135     // private
42136     onDestroy : function(){
42137         if(this.rendered){
42138             
42139             this.tb.items.each(function(item){
42140                 if(item.menu){
42141                     item.menu.removeAll();
42142                     if(item.menu.el){
42143                         item.menu.el.destroy();
42144                     }
42145                 }
42146                 item.destroy();
42147             });
42148              
42149         }
42150     },
42151     onFirstFocus: function() {
42152         this.tb.items.each(function(item){
42153            item.enable();
42154         });
42155     }
42156 });
42157
42158
42159
42160
42161 // <script type="text/javascript">
42162 /*
42163  * Based on
42164  * Ext JS Library 1.1.1
42165  * Copyright(c) 2006-2007, Ext JS, LLC.
42166  *  
42167  
42168  */
42169
42170  
42171 /**
42172  * @class Roo.form.HtmlEditor.ToolbarContext
42173  * Context Toolbar
42174  * 
42175  * Usage:
42176  *
42177  new Roo.form.HtmlEditor({
42178     ....
42179     toolbars : [
42180         { xtype: 'ToolbarStandard', styles : {} }
42181         { xtype: 'ToolbarContext', disable : {} }
42182     ]
42183 })
42184
42185      
42186  * 
42187  * @config : {Object} disable List of elements to disable.. (not done yet.)
42188  * @config : {Object} styles  Map of styles available.
42189  * 
42190  */
42191
42192 Roo.form.HtmlEditor.ToolbarContext = function(config)
42193 {
42194     
42195     Roo.apply(this, config);
42196     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
42197     // dont call parent... till later.
42198     this.styles = this.styles || {};
42199 }
42200
42201  
42202
42203 Roo.form.HtmlEditor.ToolbarContext.types = {
42204     'IMG' : {
42205         width : {
42206             title: "Width",
42207             width: 40
42208         },
42209         height:  {
42210             title: "Height",
42211             width: 40
42212         },
42213         align: {
42214             title: "Align",
42215             opts : [ [""],[ "left"],[ "right"],[ "center"],[ "top"]],
42216             width : 80
42217             
42218         },
42219         border: {
42220             title: "Border",
42221             width: 40
42222         },
42223         alt: {
42224             title: "Alt",
42225             width: 120
42226         },
42227         src : {
42228             title: "Src",
42229             width: 220
42230         }
42231         
42232     },
42233     'A' : {
42234         name : {
42235             title: "Name",
42236             width: 50
42237         },
42238         href:  {
42239             title: "Href",
42240             width: 220
42241         } // border?
42242         
42243     },
42244     'TABLE' : {
42245         rows : {
42246             title: "Rows",
42247             width: 20
42248         },
42249         cols : {
42250             title: "Cols",
42251             width: 20
42252         },
42253         width : {
42254             title: "Width",
42255             width: 40
42256         },
42257         height : {
42258             title: "Height",
42259             width: 40
42260         },
42261         border : {
42262             title: "Border",
42263             width: 20
42264         }
42265     },
42266     'TD' : {
42267         width : {
42268             title: "Width",
42269             width: 40
42270         },
42271         height : {
42272             title: "Height",
42273             width: 40
42274         },   
42275         align: {
42276             title: "Align",
42277             opts : [[""],[ "left"],[ "center"],[ "right"],[ "justify"],[ "char"]],
42278             width: 80
42279         },
42280         valign: {
42281             title: "Valign",
42282             opts : [[""],[ "top"],[ "middle"],[ "bottom"],[ "baseline"]],
42283             width: 80
42284         },
42285         colspan: {
42286             title: "Colspan",
42287             width: 20
42288             
42289         },
42290          'font-family'  : {
42291             title : "Font",
42292             style : 'fontFamily',
42293             displayField: 'display',
42294             optname : 'font-family',
42295             width: 140
42296         }
42297     },
42298     'INPUT' : {
42299         name : {
42300             title: "name",
42301             width: 120
42302         },
42303         value : {
42304             title: "Value",
42305             width: 120
42306         },
42307         width : {
42308             title: "Width",
42309             width: 40
42310         }
42311     },
42312     'LABEL' : {
42313         'for' : {
42314             title: "For",
42315             width: 120
42316         }
42317     },
42318     'TEXTAREA' : {
42319           name : {
42320             title: "name",
42321             width: 120
42322         },
42323         rows : {
42324             title: "Rows",
42325             width: 20
42326         },
42327         cols : {
42328             title: "Cols",
42329             width: 20
42330         }
42331     },
42332     'SELECT' : {
42333         name : {
42334             title: "name",
42335             width: 120
42336         },
42337         selectoptions : {
42338             title: "Options",
42339             width: 200
42340         }
42341     },
42342     
42343     // should we really allow this??
42344     // should this just be 
42345     'BODY' : {
42346         title : {
42347             title: "Title",
42348             width: 200,
42349             disabled : true
42350         }
42351     },
42352     'SPAN' : {
42353         'font-family'  : {
42354             title : "Font",
42355             style : 'fontFamily',
42356             displayField: 'display',
42357             optname : 'font-family',
42358             width: 140
42359         }
42360     },
42361     'DIV' : {
42362         'font-family'  : {
42363             title : "Font",
42364             style : 'fontFamily',
42365             displayField: 'display',
42366             optname : 'font-family',
42367             width: 140
42368         }
42369     },
42370      'P' : {
42371         'font-family'  : {
42372             title : "Font",
42373             style : 'fontFamily',
42374             displayField: 'display',
42375             optname : 'font-family',
42376             width: 140
42377         }
42378     },
42379     
42380     '*' : {
42381         // empty..
42382     }
42383
42384 };
42385
42386 // this should be configurable.. - you can either set it up using stores, or modify options somehwere..
42387 Roo.form.HtmlEditor.ToolbarContext.stores = false;
42388
42389 Roo.form.HtmlEditor.ToolbarContext.options = {
42390         'font-family'  : [ 
42391                 [ 'Helvetica,Arial,sans-serif', 'Helvetica'],
42392                 [ 'Courier New', 'Courier New'],
42393                 [ 'Tahoma', 'Tahoma'],
42394                 [ 'Times New Roman,serif', 'Times'],
42395                 [ 'Verdana','Verdana' ]
42396         ]
42397 };
42398
42399 // fixme - these need to be configurable..
42400  
42401
42402 Roo.form.HtmlEditor.ToolbarContext.types
42403
42404
42405 Roo.apply(Roo.form.HtmlEditor.ToolbarContext.prototype,  {
42406     
42407     tb: false,
42408     
42409     rendered: false,
42410     
42411     editor : false,
42412     /**
42413      * @cfg {Object} disable  List of toolbar elements to disable
42414          
42415      */
42416     disable : false,
42417     /**
42418      * @cfg {Object} styles List of styles 
42419      *    eg. { '*' : [ 'headline' ] , 'TD' : [ 'underline', 'double-underline' ] } 
42420      *
42421      * These must be defined in the page, so they get rendered correctly..
42422      * .headline { }
42423      * TD.underline { }
42424      * 
42425      */
42426     styles : false,
42427     
42428     options: false,
42429     
42430     toolbars : false,
42431     
42432     init : function(editor)
42433     {
42434         this.editor = editor;
42435         
42436         
42437         var fid = editor.frameId;
42438         var etb = this;
42439         function btn(id, toggle, handler){
42440             var xid = fid + '-'+ id ;
42441             return {
42442                 id : xid,
42443                 cmd : id,
42444                 cls : 'x-btn-icon x-edit-'+id,
42445                 enableToggle:toggle !== false,
42446                 scope: editor, // was editor...
42447                 handler:handler||editor.relayBtnCmd,
42448                 clickEvent:'mousedown',
42449                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
42450                 tabIndex:-1
42451             };
42452         }
42453         // create a new element.
42454         var wdiv = editor.wrap.createChild({
42455                 tag: 'div'
42456             }, editor.wrap.dom.firstChild.nextSibling, true);
42457         
42458         // can we do this more than once??
42459         
42460          // stop form submits
42461       
42462  
42463         // disable everything...
42464         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
42465         this.toolbars = {};
42466            
42467         for (var i in  ty) {
42468           
42469             this.toolbars[i] = this.buildToolbar(ty[i],i);
42470         }
42471         this.tb = this.toolbars.BODY;
42472         this.tb.el.show();
42473         this.buildFooter();
42474         this.footer.show();
42475         editor.on('hide', function( ) { this.footer.hide() }, this);
42476         editor.on('show', function( ) { this.footer.show() }, this);
42477         
42478          
42479         this.rendered = true;
42480         
42481         // the all the btns;
42482         editor.on('editorevent', this.updateToolbar, this);
42483         // other toolbars need to implement this..
42484         //editor.on('editmodechange', this.updateToolbar, this);
42485     },
42486     
42487     
42488     
42489     /**
42490      * Protected method that will not generally be called directly. It triggers
42491      * a toolbar update by reading the markup state of the current selection in the editor.
42492      */
42493     updateToolbar: function(editor,ev,sel){
42494
42495         //Roo.log(ev);
42496         // capture mouse up - this is handy for selecting images..
42497         // perhaps should go somewhere else...
42498         if(!this.editor.activated){
42499              this.editor.onFirstFocus();
42500             return;
42501         }
42502         
42503         // http://developer.yahoo.com/yui/docs/simple-editor.js.html
42504         // selectNode - might want to handle IE?
42505         if (ev &&
42506             (ev.type == 'mouseup' || ev.type == 'click' ) &&
42507             ev.target && ev.target.tagName == 'IMG') {
42508             // they have click on an image...
42509             // let's see if we can change the selection...
42510             sel = ev.target;
42511          
42512               var nodeRange = sel.ownerDocument.createRange();
42513             try {
42514                 nodeRange.selectNode(sel);
42515             } catch (e) {
42516                 nodeRange.selectNodeContents(sel);
42517             }
42518             //nodeRange.collapse(true);
42519             var s = editor.win.getSelection();
42520             s.removeAllRanges();
42521             s.addRange(nodeRange);
42522         }  
42523         
42524       
42525         var updateFooter = sel ? false : true;
42526         
42527         
42528         var ans = this.editor.getAllAncestors();
42529         
42530         // pick
42531         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
42532         
42533         if (!sel) { 
42534             sel = ans.length ? (ans[0] ?  ans[0]  : ans[1]) : this.editor.doc.body;
42535             sel = sel ? sel : this.editor.doc.body;
42536             sel = sel.tagName.length ? sel : this.editor.doc.body;
42537             
42538         }
42539         // pick a menu that exists..
42540         var tn = sel.tagName.toUpperCase();
42541         //sel = typeof(ty[tn]) != 'undefined' ? sel : this.editor.doc.body;
42542         
42543         tn = sel.tagName.toUpperCase();
42544         
42545         var lastSel = this.tb.selectedNode
42546         
42547         this.tb.selectedNode = sel;
42548         
42549         // if current menu does not match..
42550         if ((this.tb.name != tn) || (lastSel != this.tb.selectedNode)) {
42551                 
42552             this.tb.el.hide();
42553             ///console.log("show: " + tn);
42554             this.tb =  typeof(ty[tn]) != 'undefined' ? this.toolbars[tn] : this.toolbars['*'];
42555             this.tb.el.show();
42556             // update name
42557             this.tb.items.first().el.innerHTML = tn + ':&nbsp;';
42558             
42559             
42560             // update attributes
42561             if (this.tb.fields) {
42562                 this.tb.fields.each(function(e) {
42563                     if (e.stylename) {
42564                         e.setValue(sel.style[e.stylename]);
42565                         return;
42566                     } 
42567                    e.setValue(sel.getAttribute(e.attrname));
42568                 });
42569             }
42570             
42571             var hasStyles = false;
42572             for(var i in this.styles) {
42573                 hasStyles = true;
42574                 break;
42575             }
42576             
42577             // update styles
42578             if (hasStyles) { 
42579                 var st = this.tb.fields.item(0);
42580                 
42581                 st.store.removeAll();
42582                
42583                 
42584                 var cn = sel.className.split(/\s+/);
42585                 
42586                 var avs = [];
42587                 if (this.styles['*']) {
42588                     
42589                     Roo.each(this.styles['*'], function(v) {
42590                         avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
42591                     });
42592                 }
42593                 if (this.styles[tn]) { 
42594                     Roo.each(this.styles[tn], function(v) {
42595                         avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
42596                     });
42597                 }
42598                 
42599                 st.store.loadData(avs);
42600                 st.collapse();
42601                 st.setValue(cn);
42602             }
42603             // flag our selected Node.
42604             this.tb.selectedNode = sel;
42605            
42606            
42607             Roo.menu.MenuMgr.hideAll();
42608
42609         }
42610         
42611         if (!updateFooter) {
42612             //this.footDisp.dom.innerHTML = ''; 
42613             return;
42614         }
42615         // update the footer
42616         //
42617         var html = '';
42618         
42619         this.footerEls = ans.reverse();
42620         Roo.each(this.footerEls, function(a,i) {
42621             if (!a) { return; }
42622             html += html.length ? ' &gt; '  :  '';
42623             
42624             html += '<span class="x-ed-loc-' + i + '">' + a.tagName + '</span>';
42625             
42626         });
42627        
42628         // 
42629         var sz = this.footDisp.up('td').getSize();
42630         this.footDisp.dom.style.width = (sz.width -10) + 'px';
42631         this.footDisp.dom.style.marginLeft = '5px';
42632         
42633         this.footDisp.dom.style.overflow = 'hidden';
42634         
42635         this.footDisp.dom.innerHTML = html;
42636             
42637         //this.editorsyncValue();
42638     },
42639      
42640     
42641    
42642        
42643     // private
42644     onDestroy : function(){
42645         if(this.rendered){
42646             
42647             this.tb.items.each(function(item){
42648                 if(item.menu){
42649                     item.menu.removeAll();
42650                     if(item.menu.el){
42651                         item.menu.el.destroy();
42652                     }
42653                 }
42654                 item.destroy();
42655             });
42656              
42657         }
42658     },
42659     onFirstFocus: function() {
42660         // need to do this for all the toolbars..
42661         this.tb.items.each(function(item){
42662            item.enable();
42663         });
42664     },
42665     buildToolbar: function(tlist, nm)
42666     {
42667         var editor = this.editor;
42668          // create a new element.
42669         var wdiv = editor.wrap.createChild({
42670                 tag: 'div'
42671             }, editor.wrap.dom.firstChild.nextSibling, true);
42672         
42673        
42674         var tb = new Roo.Toolbar(wdiv);
42675         // add the name..
42676         
42677         tb.add(nm+ ":&nbsp;");
42678         
42679         var styles = [];
42680         for(var i in this.styles) {
42681             styles.push(i);
42682         }
42683         
42684         // styles...
42685         if (styles && styles.length) {
42686             
42687             // this needs a multi-select checkbox...
42688             tb.addField( new Roo.form.ComboBox({
42689                 store: new Roo.data.SimpleStore({
42690                     id : 'val',
42691                     fields: ['val', 'selected'],
42692                     data : [] 
42693                 }),
42694                 name : '-roo-edit-className',
42695                 attrname : 'className',
42696                 displayField: 'val',
42697                 typeAhead: false,
42698                 mode: 'local',
42699                 editable : false,
42700                 triggerAction: 'all',
42701                 emptyText:'Select Style',
42702                 selectOnFocus:true,
42703                 width: 130,
42704                 listeners : {
42705                     'select': function(c, r, i) {
42706                         // initial support only for on class per el..
42707                         tb.selectedNode.className =  r ? r.get('val') : '';
42708                         editor.syncValue();
42709                     }
42710                 }
42711     
42712             }));
42713         }
42714         
42715         var tbc = Roo.form.HtmlEditor.ToolbarContext;
42716         var tbops = tbc.options;
42717         
42718         for (var i in tlist) {
42719             
42720             var item = tlist[i];
42721             tb.add(item.title + ":&nbsp;");
42722             
42723             
42724             //optname == used so you can configure the options available..
42725             var opts = item.opts ? item.opts : false;
42726             if (item.optname) {
42727                 opts = tbops[item.optname];
42728            
42729             }
42730             
42731             if (opts) {
42732                 // opts == pulldown..
42733                 tb.addField( new Roo.form.ComboBox({
42734                     store:   typeof(tbc.stores[i]) != 'undefined' ?  Roo.factory(tbc.stores[i],Roo.data) : new Roo.data.SimpleStore({
42735                         id : 'val',
42736                         fields: ['val', 'display'],
42737                         data : opts  
42738                     }),
42739                     name : '-roo-edit-' + i,
42740                     attrname : i,
42741                     stylename : item.style ? item.style : false,
42742                     displayField: item.displayField ? item.displayField : 'val',
42743                     valueField :  'val',
42744                     typeAhead: false,
42745                     mode: typeof(tbc.stores[i]) != 'undefined'  ? 'remote' : 'local',
42746                     editable : false,
42747                     triggerAction: 'all',
42748                     emptyText:'Select',
42749                     selectOnFocus:true,
42750                     width: item.width ? item.width  : 130,
42751                     listeners : {
42752                         'select': function(c, r, i) {
42753                             if (c.stylename) {
42754                                 tb.selectedNode.style[c.stylename] =  r.get('val');
42755                                 return;
42756                             }
42757                             tb.selectedNode.setAttribute(c.attrname, r.get('val'));
42758                         }
42759                     }
42760
42761                 }));
42762                 continue;
42763                     
42764                  
42765                 
42766                 tb.addField( new Roo.form.TextField({
42767                     name: i,
42768                     width: 100,
42769                     //allowBlank:false,
42770                     value: ''
42771                 }));
42772                 continue;
42773             }
42774             tb.addField( new Roo.form.TextField({
42775                 name: '-roo-edit-' + i,
42776                 attrname : i,
42777                 
42778                 width: item.width,
42779                 //allowBlank:true,
42780                 value: '',
42781                 listeners: {
42782                     'change' : function(f, nv, ov) {
42783                         tb.selectedNode.setAttribute(f.attrname, nv);
42784                     }
42785                 }
42786             }));
42787              
42788         }
42789         tb.addFill();
42790         var _this = this;
42791         tb.addButton( {
42792             text: 'Remove Tag',
42793     
42794             listeners : {
42795                 click : function ()
42796                 {
42797                     // remove
42798                     // undo does not work.
42799                      
42800                     var sn = tb.selectedNode;
42801                     
42802                     var pn = sn.parentNode;
42803                     
42804                     var stn =  sn.childNodes[0];
42805                     var en = sn.childNodes[sn.childNodes.length - 1 ];
42806                     while (sn.childNodes.length) {
42807                         var node = sn.childNodes[0];
42808                         sn.removeChild(node);
42809                         //Roo.log(node);
42810                         pn.insertBefore(node, sn);
42811                         
42812                     }
42813                     pn.removeChild(sn);
42814                     var range = editor.createRange();
42815         
42816                     range.setStart(stn,0);
42817                     range.setEnd(en,0); //????
42818                     //range.selectNode(sel);
42819                     
42820                     
42821                     var selection = editor.getSelection();
42822                     selection.removeAllRanges();
42823                     selection.addRange(range);
42824                     
42825                     
42826                     
42827                     //_this.updateToolbar(null, null, pn);
42828                     _this.updateToolbar(null, null, null);
42829                     _this.footDisp.dom.innerHTML = ''; 
42830                 }
42831             }
42832             
42833                     
42834                 
42835             
42836         });
42837         
42838         
42839         tb.el.on('click', function(e){
42840             e.preventDefault(); // what does this do?
42841         });
42842         tb.el.setVisibilityMode( Roo.Element.DISPLAY);
42843         tb.el.hide();
42844         tb.name = nm;
42845         // dont need to disable them... as they will get hidden
42846         return tb;
42847          
42848         
42849     },
42850     buildFooter : function()
42851     {
42852         
42853         var fel = this.editor.wrap.createChild();
42854         this.footer = new Roo.Toolbar(fel);
42855         // toolbar has scrolly on left / right?
42856         var footDisp= new Roo.Toolbar.Fill();
42857         var _t = this;
42858         this.footer.add(
42859             {
42860                 text : '&lt;',
42861                 xtype: 'Button',
42862                 handler : function() {
42863                     _t.footDisp.scrollTo('left',0,true)
42864                 }
42865             }
42866         );
42867         this.footer.add( footDisp );
42868         this.footer.add( 
42869             {
42870                 text : '&gt;',
42871                 xtype: 'Button',
42872                 handler : function() {
42873                     // no animation..
42874                     _t.footDisp.select('span').last().scrollIntoView(_t.footDisp,true);
42875                 }
42876             }
42877         );
42878         var fel = Roo.get(footDisp.el);
42879         fel.addClass('x-editor-context');
42880         this.footDispWrap = fel; 
42881         this.footDispWrap.overflow  = 'hidden';
42882         
42883         this.footDisp = fel.createChild();
42884         this.footDispWrap.on('click', this.onContextClick, this)
42885         
42886         
42887     },
42888     onContextClick : function (ev,dom)
42889     {
42890         ev.preventDefault();
42891         var  cn = dom.className;
42892         //Roo.log(cn);
42893         if (!cn.match(/x-ed-loc-/)) {
42894             return;
42895         }
42896         var n = cn.split('-').pop();
42897         var ans = this.footerEls;
42898         var sel = ans[n];
42899         
42900          // pick
42901         var range = this.editor.createRange();
42902         
42903         range.selectNodeContents(sel);
42904         //range.selectNode(sel);
42905         
42906         
42907         var selection = this.editor.getSelection();
42908         selection.removeAllRanges();
42909         selection.addRange(range);
42910         
42911         
42912         
42913         this.updateToolbar(null, null, sel);
42914         
42915         
42916     }
42917     
42918     
42919     
42920     
42921     
42922 });
42923
42924
42925
42926
42927
42928 /*
42929  * Based on:
42930  * Ext JS Library 1.1.1
42931  * Copyright(c) 2006-2007, Ext JS, LLC.
42932  *
42933  * Originally Released Under LGPL - original licence link has changed is not relivant.
42934  *
42935  * Fork - LGPL
42936  * <script type="text/javascript">
42937  */
42938  
42939 /**
42940  * @class Roo.form.BasicForm
42941  * @extends Roo.util.Observable
42942  * Supplies the functionality to do "actions" on forms and initialize Roo.form.Field types on existing markup.
42943  * @constructor
42944  * @param {String/HTMLElement/Roo.Element} el The form element or its id
42945  * @param {Object} config Configuration options
42946  */
42947 Roo.form.BasicForm = function(el, config){
42948     this.allItems = [];
42949     this.childForms = [];
42950     Roo.apply(this, config);
42951     /*
42952      * The Roo.form.Field items in this form.
42953      * @type MixedCollection
42954      */
42955      
42956      
42957     this.items = new Roo.util.MixedCollection(false, function(o){
42958         return o.id || (o.id = Roo.id());
42959     });
42960     this.addEvents({
42961         /**
42962          * @event beforeaction
42963          * Fires before any action is performed. Return false to cancel the action.
42964          * @param {Form} this
42965          * @param {Action} action The action to be performed
42966          */
42967         beforeaction: true,
42968         /**
42969          * @event actionfailed
42970          * Fires when an action fails.
42971          * @param {Form} this
42972          * @param {Action} action The action that failed
42973          */
42974         actionfailed : true,
42975         /**
42976          * @event actioncomplete
42977          * Fires when an action is completed.
42978          * @param {Form} this
42979          * @param {Action} action The action that completed
42980          */
42981         actioncomplete : true
42982     });
42983     if(el){
42984         this.initEl(el);
42985     }
42986     Roo.form.BasicForm.superclass.constructor.call(this);
42987 };
42988
42989 Roo.extend(Roo.form.BasicForm, Roo.util.Observable, {
42990     /**
42991      * @cfg {String} method
42992      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
42993      */
42994     /**
42995      * @cfg {DataReader} reader
42996      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when executing "load" actions.
42997      * This is optional as there is built-in support for processing JSON.
42998      */
42999     /**
43000      * @cfg {DataReader} errorReader
43001      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when reading validation errors on "submit" actions.
43002      * This is completely optional as there is built-in support for processing JSON.
43003      */
43004     /**
43005      * @cfg {String} url
43006      * The URL to use for form actions if one isn't supplied in the action options.
43007      */
43008     /**
43009      * @cfg {Boolean} fileUpload
43010      * Set to true if this form is a file upload.
43011      */
43012      
43013     /**
43014      * @cfg {Object} baseParams
43015      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
43016      */
43017      /**
43018      
43019     /**
43020      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
43021      */
43022     timeout: 30,
43023
43024     // private
43025     activeAction : null,
43026
43027     /**
43028      * @cfg {Boolean} trackResetOnLoad If set to true, form.reset() resets to the last loaded
43029      * or setValues() data instead of when the form was first created.
43030      */
43031     trackResetOnLoad : false,
43032     
43033     
43034     /**
43035      * childForms - used for multi-tab forms
43036      * @type {Array}
43037      */
43038     childForms : false,
43039     
43040     /**
43041      * allItems - full list of fields.
43042      * @type {Array}
43043      */
43044     allItems : false,
43045     
43046     /**
43047      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
43048      * element by passing it or its id or mask the form itself by passing in true.
43049      * @type Mixed
43050      */
43051     waitMsgTarget : false,
43052
43053     // private
43054     initEl : function(el){
43055         this.el = Roo.get(el);
43056         this.id = this.el.id || Roo.id();
43057         this.el.on('submit', this.onSubmit, this);
43058         this.el.addClass('x-form');
43059     },
43060
43061     // private
43062     onSubmit : function(e){
43063         e.stopEvent();
43064     },
43065
43066     /**
43067      * Returns true if client-side validation on the form is successful.
43068      * @return Boolean
43069      */
43070     isValid : function(){
43071         var valid = true;
43072         this.items.each(function(f){
43073            if(!f.validate()){
43074                valid = false;
43075            }
43076         });
43077         return valid;
43078     },
43079
43080     /**
43081      * Returns true if any fields in this form have changed since their original load.
43082      * @return Boolean
43083      */
43084     isDirty : function(){
43085         var dirty = false;
43086         this.items.each(function(f){
43087            if(f.isDirty()){
43088                dirty = true;
43089                return false;
43090            }
43091         });
43092         return dirty;
43093     },
43094
43095     /**
43096      * Performs a predefined action (submit or load) or custom actions you define on this form.
43097      * @param {String} actionName The name of the action type
43098      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
43099      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
43100      * accept other config options):
43101      * <pre>
43102 Property          Type             Description
43103 ----------------  ---------------  ----------------------------------------------------------------------------------
43104 url               String           The url for the action (defaults to the form's url)
43105 method            String           The form method to use (defaults to the form's method, or POST if not defined)
43106 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
43107 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
43108                                    validate the form on the client (defaults to false)
43109      * </pre>
43110      * @return {BasicForm} this
43111      */
43112     doAction : function(action, options){
43113         if(typeof action == 'string'){
43114             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
43115         }
43116         if(this.fireEvent('beforeaction', this, action) !== false){
43117             this.beforeAction(action);
43118             action.run.defer(100, action);
43119         }
43120         return this;
43121     },
43122
43123     /**
43124      * Shortcut to do a submit action.
43125      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
43126      * @return {BasicForm} this
43127      */
43128     submit : function(options){
43129         this.doAction('submit', options);
43130         return this;
43131     },
43132
43133     /**
43134      * Shortcut to do a load action.
43135      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
43136      * @return {BasicForm} this
43137      */
43138     load : function(options){
43139         this.doAction('load', options);
43140         return this;
43141     },
43142
43143     /**
43144      * Persists the values in this form into the passed Roo.data.Record object in a beginEdit/endEdit block.
43145      * @param {Record} record The record to edit
43146      * @return {BasicForm} this
43147      */
43148     updateRecord : function(record){
43149         record.beginEdit();
43150         var fs = record.fields;
43151         fs.each(function(f){
43152             var field = this.findField(f.name);
43153             if(field){
43154                 record.set(f.name, field.getValue());
43155             }
43156         }, this);
43157         record.endEdit();
43158         return this;
43159     },
43160
43161     /**
43162      * Loads an Roo.data.Record into this form.
43163      * @param {Record} record The record to load
43164      * @return {BasicForm} this
43165      */
43166     loadRecord : function(record){
43167         this.setValues(record.data);
43168         return this;
43169     },
43170
43171     // private
43172     beforeAction : function(action){
43173         var o = action.options;
43174         
43175        
43176         if(this.waitMsgTarget === true){
43177             this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
43178         }else if(this.waitMsgTarget){
43179             this.waitMsgTarget = Roo.get(this.waitMsgTarget);
43180             this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
43181         }else {
43182             Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
43183         }
43184          
43185     },
43186
43187     // private
43188     afterAction : function(action, success){
43189         this.activeAction = null;
43190         var o = action.options;
43191         
43192         if(this.waitMsgTarget === true){
43193             this.el.unmask();
43194         }else if(this.waitMsgTarget){
43195             this.waitMsgTarget.unmask();
43196         }else{
43197             Roo.MessageBox.updateProgress(1);
43198             Roo.MessageBox.hide();
43199         }
43200          
43201         if(success){
43202             if(o.reset){
43203                 this.reset();
43204             }
43205             Roo.callback(o.success, o.scope, [this, action]);
43206             this.fireEvent('actioncomplete', this, action);
43207             
43208         }else{
43209             
43210             // failure condition..
43211             // we have a scenario where updates need confirming.
43212             // eg. if a locking scenario exists..
43213             // we look for { errors : { needs_confirm : true }} in the response.
43214             if (
43215                 (typeof(action.result) != 'undefined')  &&
43216                 (typeof(action.result.errors) != 'undefined')  &&
43217                 (typeof(action.result.errors.needs_confirm) != 'undefined')
43218            ){
43219                 var _t = this;
43220                 Roo.MessageBox.confirm(
43221                     "Change requires confirmation",
43222                     action.result.errorMsg,
43223                     function(r) {
43224                         if (r != 'yes') {
43225                             return;
43226                         }
43227                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
43228                     }
43229                     
43230                 );
43231                 
43232                 
43233                 
43234                 return;
43235             }
43236             
43237             Roo.callback(o.failure, o.scope, [this, action]);
43238             // show an error message if no failed handler is set..
43239             if (!this.hasListener('actionfailed')) {
43240                 Roo.MessageBox.alert("Error",
43241                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
43242                         action.result.errorMsg :
43243                         "Saving Failed, please check your entries or try again"
43244                 );
43245             }
43246             
43247             this.fireEvent('actionfailed', this, action);
43248         }
43249         
43250     },
43251
43252     /**
43253      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
43254      * @param {String} id The value to search for
43255      * @return Field
43256      */
43257     findField : function(id){
43258         var field = this.items.get(id);
43259         if(!field){
43260             this.items.each(function(f){
43261                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
43262                     field = f;
43263                     return false;
43264                 }
43265             });
43266         }
43267         return field || null;
43268     },
43269
43270     /**
43271      * Add a secondary form to this one, 
43272      * Used to provide tabbed forms. One form is primary, with hidden values 
43273      * which mirror the elements from the other forms.
43274      * 
43275      * @param {Roo.form.Form} form to add.
43276      * 
43277      */
43278     addForm : function(form)
43279     {
43280        
43281         if (this.childForms.indexOf(form) > -1) {
43282             // already added..
43283             return;
43284         }
43285         this.childForms.push(form);
43286         var n = '';
43287         Roo.each(form.allItems, function (fe) {
43288             
43289             n = typeof(fe.getName) == 'undefined' ? fe.name : fe.getName();
43290             if (this.findField(n)) { // already added..
43291                 return;
43292             }
43293             var add = new Roo.form.Hidden({
43294                 name : n
43295             });
43296             add.render(this.el);
43297             
43298             this.add( add );
43299         }, this);
43300         
43301     },
43302     /**
43303      * Mark fields in this form invalid in bulk.
43304      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
43305      * @return {BasicForm} this
43306      */
43307     markInvalid : function(errors){
43308         if(errors instanceof Array){
43309             for(var i = 0, len = errors.length; i < len; i++){
43310                 var fieldError = errors[i];
43311                 var f = this.findField(fieldError.id);
43312                 if(f){
43313                     f.markInvalid(fieldError.msg);
43314                 }
43315             }
43316         }else{
43317             var field, id;
43318             for(id in errors){
43319                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
43320                     field.markInvalid(errors[id]);
43321                 }
43322             }
43323         }
43324         Roo.each(this.childForms || [], function (f) {
43325             f.markInvalid(errors);
43326         });
43327         
43328         return this;
43329     },
43330
43331     /**
43332      * Set values for fields in this form in bulk.
43333      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
43334      * @return {BasicForm} this
43335      */
43336     setValues : function(values){
43337         if(values instanceof Array){ // array of objects
43338             for(var i = 0, len = values.length; i < len; i++){
43339                 var v = values[i];
43340                 var f = this.findField(v.id);
43341                 if(f){
43342                     f.setValue(v.value);
43343                     if(this.trackResetOnLoad){
43344                         f.originalValue = f.getValue();
43345                     }
43346                 }
43347             }
43348         }else{ // object hash
43349             var field, id;
43350             for(id in values){
43351                 if(typeof values[id] != 'function' && (field = this.findField(id))){
43352                     
43353                     if (field.setFromData && 
43354                         field.valueField && 
43355                         field.displayField &&
43356                         // combos' with local stores can 
43357                         // be queried via setValue()
43358                         // to set their value..
43359                         (field.store && !field.store.isLocal)
43360                         ) {
43361                         // it's a combo
43362                         var sd = { };
43363                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
43364                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
43365                         field.setFromData(sd);
43366                         
43367                     } else {
43368                         field.setValue(values[id]);
43369                     }
43370                     
43371                     
43372                     if(this.trackResetOnLoad){
43373                         field.originalValue = field.getValue();
43374                     }
43375                 }
43376             }
43377         }
43378          
43379         Roo.each(this.childForms || [], function (f) {
43380             f.setValues(values);
43381         });
43382                 
43383         return this;
43384     },
43385
43386     /**
43387      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
43388      * they are returned as an array.
43389      * @param {Boolean} asString
43390      * @return {Object}
43391      */
43392     getValues : function(asString){
43393         if (this.childForms) {
43394             // copy values from the child forms
43395             Roo.each(this.childForms, function (f) {
43396                 this.setValues(f.getValues());
43397             }, this);
43398         }
43399         
43400         
43401         
43402         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
43403         if(asString === true){
43404             return fs;
43405         }
43406         return Roo.urlDecode(fs);
43407     },
43408     
43409     /**
43410      * Returns the fields in this form as an object with key/value pairs. 
43411      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
43412      * @return {Object}
43413      */
43414     getFieldValues : function(with_hidden)
43415     {
43416         if (this.childForms) {
43417             // copy values from the child forms
43418             // should this call getFieldValues - probably not as we do not currently copy
43419             // hidden fields when we generate..
43420             Roo.each(this.childForms, function (f) {
43421                 this.setValues(f.getValues());
43422             }, this);
43423         }
43424         
43425         var ret = {};
43426         this.items.each(function(f){
43427             if (!f.getName()) {
43428                 return;
43429             }
43430             var v = f.getValue();
43431             if (f.inputType =='radio') {
43432                 if (typeof(ret[f.getName()]) == 'undefined') {
43433                     ret[f.getName()] = ''; // empty..
43434                 }
43435                 
43436                 if (!f.el.dom.checked) {
43437                     return;
43438                     
43439                 }
43440                 v = f.el.dom.value;
43441                 
43442             }
43443             
43444             // not sure if this supported any more..
43445             if ((typeof(v) == 'object') && f.getRawValue) {
43446                 v = f.getRawValue() ; // dates..
43447             }
43448             // combo boxes where name != hiddenName...
43449             if (f.name != f.getName()) {
43450                 ret[f.name] = f.getRawValue();
43451             }
43452             ret[f.getName()] = v;
43453         });
43454         
43455         return ret;
43456     },
43457
43458     /**
43459      * Clears all invalid messages in this form.
43460      * @return {BasicForm} this
43461      */
43462     clearInvalid : function(){
43463         this.items.each(function(f){
43464            f.clearInvalid();
43465         });
43466         
43467         Roo.each(this.childForms || [], function (f) {
43468             f.clearInvalid();
43469         });
43470         
43471         
43472         return this;
43473     },
43474
43475     /**
43476      * Resets this form.
43477      * @return {BasicForm} this
43478      */
43479     reset : function(){
43480         this.items.each(function(f){
43481             f.reset();
43482         });
43483         
43484         Roo.each(this.childForms || [], function (f) {
43485             f.reset();
43486         });
43487        
43488         
43489         return this;
43490     },
43491
43492     /**
43493      * Add Roo.form components to this form.
43494      * @param {Field} field1
43495      * @param {Field} field2 (optional)
43496      * @param {Field} etc (optional)
43497      * @return {BasicForm} this
43498      */
43499     add : function(){
43500         this.items.addAll(Array.prototype.slice.call(arguments, 0));
43501         return this;
43502     },
43503
43504
43505     /**
43506      * Removes a field from the items collection (does NOT remove its markup).
43507      * @param {Field} field
43508      * @return {BasicForm} this
43509      */
43510     remove : function(field){
43511         this.items.remove(field);
43512         return this;
43513     },
43514
43515     /**
43516      * Looks at the fields in this form, checks them for an id attribute,
43517      * and calls applyTo on the existing dom element with that id.
43518      * @return {BasicForm} this
43519      */
43520     render : function(){
43521         this.items.each(function(f){
43522             if(f.isFormField && !f.rendered && document.getElementById(f.id)){ // if the element exists
43523                 f.applyTo(f.id);
43524             }
43525         });
43526         return this;
43527     },
43528
43529     /**
43530      * Calls {@link Ext#apply} for all fields in this form with the passed object.
43531      * @param {Object} values
43532      * @return {BasicForm} this
43533      */
43534     applyToFields : function(o){
43535         this.items.each(function(f){
43536            Roo.apply(f, o);
43537         });
43538         return this;
43539     },
43540
43541     /**
43542      * Calls {@link Ext#applyIf} for all field in this form with the passed object.
43543      * @param {Object} values
43544      * @return {BasicForm} this
43545      */
43546     applyIfToFields : function(o){
43547         this.items.each(function(f){
43548            Roo.applyIf(f, o);
43549         });
43550         return this;
43551     }
43552 });
43553
43554 // back compat
43555 Roo.BasicForm = Roo.form.BasicForm;/*
43556  * Based on:
43557  * Ext JS Library 1.1.1
43558  * Copyright(c) 2006-2007, Ext JS, LLC.
43559  *
43560  * Originally Released Under LGPL - original licence link has changed is not relivant.
43561  *
43562  * Fork - LGPL
43563  * <script type="text/javascript">
43564  */
43565
43566 /**
43567  * @class Roo.form.Form
43568  * @extends Roo.form.BasicForm
43569  * Adds the ability to dynamically render forms with JavaScript to {@link Roo.form.BasicForm}.
43570  * @constructor
43571  * @param {Object} config Configuration options
43572  */
43573 Roo.form.Form = function(config){
43574     var xitems =  [];
43575     if (config.items) {
43576         xitems = config.items;
43577         delete config.items;
43578     }
43579    
43580     
43581     Roo.form.Form.superclass.constructor.call(this, null, config);
43582     this.url = this.url || this.action;
43583     if(!this.root){
43584         this.root = new Roo.form.Layout(Roo.applyIf({
43585             id: Roo.id()
43586         }, config));
43587     }
43588     this.active = this.root;
43589     /**
43590      * Array of all the buttons that have been added to this form via {@link addButton}
43591      * @type Array
43592      */
43593     this.buttons = [];
43594     this.allItems = [];
43595     this.addEvents({
43596         /**
43597          * @event clientvalidation
43598          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
43599          * @param {Form} this
43600          * @param {Boolean} valid true if the form has passed client-side validation
43601          */
43602         clientvalidation: true,
43603         /**
43604          * @event rendered
43605          * Fires when the form is rendered
43606          * @param {Roo.form.Form} form
43607          */
43608         rendered : true
43609     });
43610     
43611     if (this.progressUrl) {
43612             // push a hidden field onto the list of fields..
43613             this.addxtype( {
43614                     xns: Roo.form, 
43615                     xtype : 'Hidden', 
43616                     name : 'UPLOAD_IDENTIFIER' 
43617             });
43618         }
43619         
43620     
43621     Roo.each(xitems, this.addxtype, this);
43622     
43623     
43624     
43625 };
43626
43627 Roo.extend(Roo.form.Form, Roo.form.BasicForm, {
43628     /**
43629      * @cfg {Number} labelWidth The width of labels. This property cascades to child containers.
43630      */
43631     /**
43632      * @cfg {String} itemCls A css class to apply to the x-form-item of fields. This property cascades to child containers.
43633      */
43634     /**
43635      * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "center")
43636      */
43637     buttonAlign:'center',
43638
43639     /**
43640      * @cfg {Number} minButtonWidth Minimum width of all buttons in pixels (defaults to 75)
43641      */
43642     minButtonWidth:75,
43643
43644     /**
43645      * @cfg {String} labelAlign Valid values are "left," "top" and "right" (defaults to "left").
43646      * This property cascades to child containers if not set.
43647      */
43648     labelAlign:'left',
43649
43650     /**
43651      * @cfg {Boolean} monitorValid If true the form monitors its valid state <b>client-side</b> and
43652      * fires a looping event with that state. This is required to bind buttons to the valid
43653      * state using the config value formBind:true on the button.
43654      */
43655     monitorValid : false,
43656
43657     /**
43658      * @cfg {Number} monitorPoll The milliseconds to poll valid state, ignored if monitorValid is not true (defaults to 200)
43659      */
43660     monitorPoll : 200,
43661     
43662     /**
43663      * @cfg {String} progressUrl - Url to return progress data 
43664      */
43665     
43666     progressUrl : false,
43667   
43668     /**
43669      * Opens a new {@link Roo.form.Column} container in the layout stack. If fields are passed after the config, the
43670      * fields are added and the column is closed. If no fields are passed the column remains open
43671      * until end() is called.
43672      * @param {Object} config The config to pass to the column
43673      * @param {Field} field1 (optional)
43674      * @param {Field} field2 (optional)
43675      * @param {Field} etc (optional)
43676      * @return Column The column container object
43677      */
43678     column : function(c){
43679         var col = new Roo.form.Column(c);
43680         this.start(col);
43681         if(arguments.length > 1){ // duplicate code required because of Opera
43682             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
43683             this.end();
43684         }
43685         return col;
43686     },
43687
43688     /**
43689      * Opens a new {@link Roo.form.FieldSet} container in the layout stack. If fields are passed after the config, the
43690      * fields are added and the fieldset is closed. If no fields are passed the fieldset remains open
43691      * until end() is called.
43692      * @param {Object} config The config to pass to the fieldset
43693      * @param {Field} field1 (optional)
43694      * @param {Field} field2 (optional)
43695      * @param {Field} etc (optional)
43696      * @return FieldSet The fieldset container object
43697      */
43698     fieldset : function(c){
43699         var fs = new Roo.form.FieldSet(c);
43700         this.start(fs);
43701         if(arguments.length > 1){ // duplicate code required because of Opera
43702             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
43703             this.end();
43704         }
43705         return fs;
43706     },
43707
43708     /**
43709      * Opens a new {@link Roo.form.Layout} container in the layout stack. If fields are passed after the config, the
43710      * fields are added and the container is closed. If no fields are passed the container remains open
43711      * until end() is called.
43712      * @param {Object} config The config to pass to the Layout
43713      * @param {Field} field1 (optional)
43714      * @param {Field} field2 (optional)
43715      * @param {Field} etc (optional)
43716      * @return Layout The container object
43717      */
43718     container : function(c){
43719         var l = new Roo.form.Layout(c);
43720         this.start(l);
43721         if(arguments.length > 1){ // duplicate code required because of Opera
43722             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
43723             this.end();
43724         }
43725         return l;
43726     },
43727
43728     /**
43729      * Opens the passed container in the layout stack. The container can be any {@link Roo.form.Layout} or subclass.
43730      * @param {Object} container A Roo.form.Layout or subclass of Layout
43731      * @return {Form} this
43732      */
43733     start : function(c){
43734         // cascade label info
43735         Roo.applyIf(c, {'labelAlign': this.active.labelAlign, 'labelWidth': this.active.labelWidth, 'itemCls': this.active.itemCls});
43736         this.active.stack.push(c);
43737         c.ownerCt = this.active;
43738         this.active = c;
43739         return this;
43740     },
43741
43742     /**
43743      * Closes the current open container
43744      * @return {Form} this
43745      */
43746     end : function(){
43747         if(this.active == this.root){
43748             return this;
43749         }
43750         this.active = this.active.ownerCt;
43751         return this;
43752     },
43753
43754     /**
43755      * Add Roo.form components to the current open container (e.g. column, fieldset, etc.).  Fields added via this method
43756      * can also be passed with an additional property of fieldLabel, which if supplied, will provide the text to display
43757      * as the label of the field.
43758      * @param {Field} field1
43759      * @param {Field} field2 (optional)
43760      * @param {Field} etc. (optional)
43761      * @return {Form} this
43762      */
43763     add : function(){
43764         this.active.stack.push.apply(this.active.stack, arguments);
43765         this.allItems.push.apply(this.allItems,arguments);
43766         var r = [];
43767         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
43768             if(a[i].isFormField){
43769                 r.push(a[i]);
43770             }
43771         }
43772         if(r.length > 0){
43773             Roo.form.Form.superclass.add.apply(this, r);
43774         }
43775         return this;
43776     },
43777     
43778
43779     
43780     
43781     
43782      /**
43783      * Find any element that has been added to a form, using it's ID or name
43784      * This can include framesets, columns etc. along with regular fields..
43785      * @param {String} id - id or name to find.
43786      
43787      * @return {Element} e - or false if nothing found.
43788      */
43789     findbyId : function(id)
43790     {
43791         var ret = false;
43792         if (!id) {
43793             return ret;
43794         }
43795         Roo.each(this.allItems, function(f){
43796             if (f.id == id || f.name == id ){
43797                 ret = f;
43798                 return false;
43799             }
43800         });
43801         return ret;
43802     },
43803
43804     
43805     
43806     /**
43807      * Render this form into the passed container. This should only be called once!
43808      * @param {String/HTMLElement/Element} container The element this component should be rendered into
43809      * @return {Form} this
43810      */
43811     render : function(ct)
43812     {
43813         
43814         
43815         
43816         ct = Roo.get(ct);
43817         var o = this.autoCreate || {
43818             tag: 'form',
43819             method : this.method || 'POST',
43820             id : this.id || Roo.id()
43821         };
43822         this.initEl(ct.createChild(o));
43823
43824         this.root.render(this.el);
43825         
43826        
43827              
43828         this.items.each(function(f){
43829             f.render('x-form-el-'+f.id);
43830         });
43831
43832         if(this.buttons.length > 0){
43833             // tables are required to maintain order and for correct IE layout
43834             var tb = this.el.createChild({cls:'x-form-btns-ct', cn: {
43835                 cls:"x-form-btns x-form-btns-"+this.buttonAlign,
43836                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
43837             }}, null, true);
43838             var tr = tb.getElementsByTagName('tr')[0];
43839             for(var i = 0, len = this.buttons.length; i < len; i++) {
43840                 var b = this.buttons[i];
43841                 var td = document.createElement('td');
43842                 td.className = 'x-form-btn-td';
43843                 b.render(tr.appendChild(td));
43844             }
43845         }
43846         if(this.monitorValid){ // initialize after render
43847             this.startMonitoring();
43848         }
43849         this.fireEvent('rendered', this);
43850         return this;
43851     },
43852
43853     /**
43854      * Adds a button to the footer of the form - this <b>must</b> be called before the form is rendered.
43855      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
43856      * object or a valid Roo.DomHelper element config
43857      * @param {Function} handler The function called when the button is clicked
43858      * @param {Object} scope (optional) The scope of the handler function
43859      * @return {Roo.Button}
43860      */
43861     addButton : function(config, handler, scope){
43862         var bc = {
43863             handler: handler,
43864             scope: scope,
43865             minWidth: this.minButtonWidth,
43866             hideParent:true
43867         };
43868         if(typeof config == "string"){
43869             bc.text = config;
43870         }else{
43871             Roo.apply(bc, config);
43872         }
43873         var btn = new Roo.Button(null, bc);
43874         this.buttons.push(btn);
43875         return btn;
43876     },
43877
43878      /**
43879      * Adds a series of form elements (using the xtype property as the factory method.
43880      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column, (and 'end' to close a block)
43881      * @param {Object} config 
43882      */
43883     
43884     addxtype : function()
43885     {
43886         var ar = Array.prototype.slice.call(arguments, 0);
43887         var ret = false;
43888         for(var i = 0; i < ar.length; i++) {
43889             if (!ar[i]) {
43890                 continue; // skip -- if this happends something invalid got sent, we 
43891                 // should ignore it, as basically that interface element will not show up
43892                 // and that should be pretty obvious!!
43893             }
43894             
43895             if (Roo.form[ar[i].xtype]) {
43896                 ar[i].form = this;
43897                 var fe = Roo.factory(ar[i], Roo.form);
43898                 if (!ret) {
43899                     ret = fe;
43900                 }
43901                 fe.form = this;
43902                 if (fe.store) {
43903                     fe.store.form = this;
43904                 }
43905                 if (fe.isLayout) {  
43906                          
43907                     this.start(fe);
43908                     this.allItems.push(fe);
43909                     if (fe.items && fe.addxtype) {
43910                         fe.addxtype.apply(fe, fe.items);
43911                         delete fe.items;
43912                     }
43913                      this.end();
43914                     continue;
43915                 }
43916                 
43917                 
43918                  
43919                 this.add(fe);
43920               //  console.log('adding ' + ar[i].xtype);
43921             }
43922             if (ar[i].xtype == 'Button') {  
43923                 //console.log('adding button');
43924                 //console.log(ar[i]);
43925                 this.addButton(ar[i]);
43926                 this.allItems.push(fe);
43927                 continue;
43928             }
43929             
43930             if (ar[i].xtype == 'end') { // so we can add fieldsets... / layout etc.
43931                 alert('end is not supported on xtype any more, use items');
43932             //    this.end();
43933             //    //console.log('adding end');
43934             }
43935             
43936         }
43937         return ret;
43938     },
43939     
43940     /**
43941      * Starts monitoring of the valid state of this form. Usually this is done by passing the config
43942      * option "monitorValid"
43943      */
43944     startMonitoring : function(){
43945         if(!this.bound){
43946             this.bound = true;
43947             Roo.TaskMgr.start({
43948                 run : this.bindHandler,
43949                 interval : this.monitorPoll || 200,
43950                 scope: this
43951             });
43952         }
43953     },
43954
43955     /**
43956      * Stops monitoring of the valid state of this form
43957      */
43958     stopMonitoring : function(){
43959         this.bound = false;
43960     },
43961
43962     // private
43963     bindHandler : function(){
43964         if(!this.bound){
43965             return false; // stops binding
43966         }
43967         var valid = true;
43968         this.items.each(function(f){
43969             if(!f.isValid(true)){
43970                 valid = false;
43971                 return false;
43972             }
43973         });
43974         for(var i = 0, len = this.buttons.length; i < len; i++){
43975             var btn = this.buttons[i];
43976             if(btn.formBind === true && btn.disabled === valid){
43977                 btn.setDisabled(!valid);
43978             }
43979         }
43980         this.fireEvent('clientvalidation', this, valid);
43981     }
43982     
43983     
43984     
43985     
43986     
43987     
43988     
43989     
43990 });
43991
43992
43993 // back compat
43994 Roo.Form = Roo.form.Form;
43995 /*
43996  * Based on:
43997  * Ext JS Library 1.1.1
43998  * Copyright(c) 2006-2007, Ext JS, LLC.
43999  *
44000  * Originally Released Under LGPL - original licence link has changed is not relivant.
44001  *
44002  * Fork - LGPL
44003  * <script type="text/javascript">
44004  */
44005  
44006  /**
44007  * @class Roo.form.Action
44008  * Internal Class used to handle form actions
44009  * @constructor
44010  * @param {Roo.form.BasicForm} el The form element or its id
44011  * @param {Object} config Configuration options
44012  */
44013  
44014  
44015 // define the action interface
44016 Roo.form.Action = function(form, options){
44017     this.form = form;
44018     this.options = options || {};
44019 };
44020 /**
44021  * Client Validation Failed
44022  * @const 
44023  */
44024 Roo.form.Action.CLIENT_INVALID = 'client';
44025 /**
44026  * Server Validation Failed
44027  * @const 
44028  */
44029  Roo.form.Action.SERVER_INVALID = 'server';
44030  /**
44031  * Connect to Server Failed
44032  * @const 
44033  */
44034 Roo.form.Action.CONNECT_FAILURE = 'connect';
44035 /**
44036  * Reading Data from Server Failed
44037  * @const 
44038  */
44039 Roo.form.Action.LOAD_FAILURE = 'load';
44040
44041 Roo.form.Action.prototype = {
44042     type : 'default',
44043     failureType : undefined,
44044     response : undefined,
44045     result : undefined,
44046
44047     // interface method
44048     run : function(options){
44049
44050     },
44051
44052     // interface method
44053     success : function(response){
44054
44055     },
44056
44057     // interface method
44058     handleResponse : function(response){
44059
44060     },
44061
44062     // default connection failure
44063     failure : function(response){
44064         
44065         this.response = response;
44066         this.failureType = Roo.form.Action.CONNECT_FAILURE;
44067         this.form.afterAction(this, false);
44068     },
44069
44070     processResponse : function(response){
44071         this.response = response;
44072         if(!response.responseText){
44073             return true;
44074         }
44075         this.result = this.handleResponse(response);
44076         return this.result;
44077     },
44078
44079     // utility functions used internally
44080     getUrl : function(appendParams){
44081         var url = this.options.url || this.form.url || this.form.el.dom.action;
44082         if(appendParams){
44083             var p = this.getParams();
44084             if(p){
44085                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
44086             }
44087         }
44088         return url;
44089     },
44090
44091     getMethod : function(){
44092         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
44093     },
44094
44095     getParams : function(){
44096         var bp = this.form.baseParams;
44097         var p = this.options.params;
44098         if(p){
44099             if(typeof p == "object"){
44100                 p = Roo.urlEncode(Roo.applyIf(p, bp));
44101             }else if(typeof p == 'string' && bp){
44102                 p += '&' + Roo.urlEncode(bp);
44103             }
44104         }else if(bp){
44105             p = Roo.urlEncode(bp);
44106         }
44107         return p;
44108     },
44109
44110     createCallback : function(){
44111         return {
44112             success: this.success,
44113             failure: this.failure,
44114             scope: this,
44115             timeout: (this.form.timeout*1000),
44116             upload: this.form.fileUpload ? this.success : undefined
44117         };
44118     }
44119 };
44120
44121 Roo.form.Action.Submit = function(form, options){
44122     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
44123 };
44124
44125 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
44126     type : 'submit',
44127
44128     haveProgress : false,
44129     uploadComplete : false,
44130     
44131     // uploadProgress indicator.
44132     uploadProgress : function()
44133     {
44134         if (!this.form.progressUrl) {
44135             return;
44136         }
44137         
44138         if (!this.haveProgress) {
44139             Roo.MessageBox.progress("Uploading", "Uploading");
44140         }
44141         if (this.uploadComplete) {
44142            Roo.MessageBox.hide();
44143            return;
44144         }
44145         
44146         this.haveProgress = true;
44147    
44148         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
44149         
44150         var c = new Roo.data.Connection();
44151         c.request({
44152             url : this.form.progressUrl,
44153             params: {
44154                 id : uid
44155             },
44156             method: 'GET',
44157             success : function(req){
44158                //console.log(data);
44159                 var rdata = false;
44160                 var edata;
44161                 try  {
44162                    rdata = Roo.decode(req.responseText)
44163                 } catch (e) {
44164                     Roo.log("Invalid data from server..");
44165                     Roo.log(edata);
44166                     return;
44167                 }
44168                 if (!rdata || !rdata.success) {
44169                     Roo.log(rdata);
44170                     Roo.MessageBox.alert(Roo.encode(rdata));
44171                     return;
44172                 }
44173                 var data = rdata.data;
44174                 
44175                 if (this.uploadComplete) {
44176                    Roo.MessageBox.hide();
44177                    return;
44178                 }
44179                    
44180                 if (data){
44181                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
44182                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
44183                     );
44184                 }
44185                 this.uploadProgress.defer(2000,this);
44186             },
44187        
44188             failure: function(data) {
44189                 Roo.log('progress url failed ');
44190                 Roo.log(data);
44191             },
44192             scope : this
44193         });
44194            
44195     },
44196     
44197     
44198     run : function()
44199     {
44200         // run get Values on the form, so it syncs any secondary forms.
44201         this.form.getValues();
44202         
44203         var o = this.options;
44204         var method = this.getMethod();
44205         var isPost = method == 'POST';
44206         if(o.clientValidation === false || this.form.isValid()){
44207             
44208             if (this.form.progressUrl) {
44209                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
44210                     (new Date() * 1) + '' + Math.random());
44211                     
44212             } 
44213             
44214             
44215             Roo.Ajax.request(Roo.apply(this.createCallback(), {
44216                 form:this.form.el.dom,
44217                 url:this.getUrl(!isPost),
44218                 method: method,
44219                 params:isPost ? this.getParams() : null,
44220                 isUpload: this.form.fileUpload
44221             }));
44222             
44223             this.uploadProgress();
44224
44225         }else if (o.clientValidation !== false){ // client validation failed
44226             this.failureType = Roo.form.Action.CLIENT_INVALID;
44227             this.form.afterAction(this, false);
44228         }
44229     },
44230
44231     success : function(response)
44232     {
44233         this.uploadComplete= true;
44234         if (this.haveProgress) {
44235             Roo.MessageBox.hide();
44236         }
44237         
44238         
44239         var result = this.processResponse(response);
44240         if(result === true || result.success){
44241             this.form.afterAction(this, true);
44242             return;
44243         }
44244         if(result.errors){
44245             this.form.markInvalid(result.errors);
44246             this.failureType = Roo.form.Action.SERVER_INVALID;
44247         }
44248         this.form.afterAction(this, false);
44249     },
44250     failure : function(response)
44251     {
44252         this.uploadComplete= true;
44253         if (this.haveProgress) {
44254             Roo.MessageBox.hide();
44255         }
44256         
44257         this.response = response;
44258         this.failureType = Roo.form.Action.CONNECT_FAILURE;
44259         this.form.afterAction(this, false);
44260     },
44261     
44262     handleResponse : function(response){
44263         if(this.form.errorReader){
44264             var rs = this.form.errorReader.read(response);
44265             var errors = [];
44266             if(rs.records){
44267                 for(var i = 0, len = rs.records.length; i < len; i++) {
44268                     var r = rs.records[i];
44269                     errors[i] = r.data;
44270                 }
44271             }
44272             if(errors.length < 1){
44273                 errors = null;
44274             }
44275             return {
44276                 success : rs.success,
44277                 errors : errors
44278             };
44279         }
44280         var ret = false;
44281         try {
44282             ret = Roo.decode(response.responseText);
44283         } catch (e) {
44284             ret = {
44285                 success: false,
44286                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
44287                 errors : []
44288             };
44289         }
44290         return ret;
44291         
44292     }
44293 });
44294
44295
44296 Roo.form.Action.Load = function(form, options){
44297     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
44298     this.reader = this.form.reader;
44299 };
44300
44301 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
44302     type : 'load',
44303
44304     run : function(){
44305         
44306         Roo.Ajax.request(Roo.apply(
44307                 this.createCallback(), {
44308                     method:this.getMethod(),
44309                     url:this.getUrl(false),
44310                     params:this.getParams()
44311         }));
44312     },
44313
44314     success : function(response){
44315         
44316         var result = this.processResponse(response);
44317         if(result === true || !result.success || !result.data){
44318             this.failureType = Roo.form.Action.LOAD_FAILURE;
44319             this.form.afterAction(this, false);
44320             return;
44321         }
44322         this.form.clearInvalid();
44323         this.form.setValues(result.data);
44324         this.form.afterAction(this, true);
44325     },
44326
44327     handleResponse : function(response){
44328         if(this.form.reader){
44329             var rs = this.form.reader.read(response);
44330             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
44331             return {
44332                 success : rs.success,
44333                 data : data
44334             };
44335         }
44336         return Roo.decode(response.responseText);
44337     }
44338 });
44339
44340 Roo.form.Action.ACTION_TYPES = {
44341     'load' : Roo.form.Action.Load,
44342     'submit' : Roo.form.Action.Submit
44343 };/*
44344  * Based on:
44345  * Ext JS Library 1.1.1
44346  * Copyright(c) 2006-2007, Ext JS, LLC.
44347  *
44348  * Originally Released Under LGPL - original licence link has changed is not relivant.
44349  *
44350  * Fork - LGPL
44351  * <script type="text/javascript">
44352  */
44353  
44354 /**
44355  * @class Roo.form.Layout
44356  * @extends Roo.Component
44357  * Creates a container for layout and rendering of fields in an {@link Roo.form.Form}.
44358  * @constructor
44359  * @param {Object} config Configuration options
44360  */
44361 Roo.form.Layout = function(config){
44362     var xitems = [];
44363     if (config.items) {
44364         xitems = config.items;
44365         delete config.items;
44366     }
44367     Roo.form.Layout.superclass.constructor.call(this, config);
44368     this.stack = [];
44369     Roo.each(xitems, this.addxtype, this);
44370      
44371 };
44372
44373 Roo.extend(Roo.form.Layout, Roo.Component, {
44374     /**
44375      * @cfg {String/Object} autoCreate
44376      * A DomHelper element spec used to autocreate the layout (defaults to {tag: 'div', cls: 'x-form-ct'})
44377      */
44378     /**
44379      * @cfg {String/Object/Function} style
44380      * A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
44381      * a function which returns such a specification.
44382      */
44383     /**
44384      * @cfg {String} labelAlign
44385      * Valid values are "left," "top" and "right" (defaults to "left")
44386      */
44387     /**
44388      * @cfg {Number} labelWidth
44389      * Fixed width in pixels of all field labels (defaults to undefined)
44390      */
44391     /**
44392      * @cfg {Boolean} clear
44393      * True to add a clearing element at the end of this layout, equivalent to CSS clear: both (defaults to true)
44394      */
44395     clear : true,
44396     /**
44397      * @cfg {String} labelSeparator
44398      * The separator to use after field labels (defaults to ':')
44399      */
44400     labelSeparator : ':',
44401     /**
44402      * @cfg {Boolean} hideLabels
44403      * True to suppress the display of field labels in this layout (defaults to false)
44404      */
44405     hideLabels : false,
44406
44407     // private
44408     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct'},
44409     
44410     isLayout : true,
44411     
44412     // private
44413     onRender : function(ct, position){
44414         if(this.el){ // from markup
44415             this.el = Roo.get(this.el);
44416         }else {  // generate
44417             var cfg = this.getAutoCreate();
44418             this.el = ct.createChild(cfg, position);
44419         }
44420         if(this.style){
44421             this.el.applyStyles(this.style);
44422         }
44423         if(this.labelAlign){
44424             this.el.addClass('x-form-label-'+this.labelAlign);
44425         }
44426         if(this.hideLabels){
44427             this.labelStyle = "display:none";
44428             this.elementStyle = "padding-left:0;";
44429         }else{
44430             if(typeof this.labelWidth == 'number'){
44431                 this.labelStyle = "width:"+this.labelWidth+"px;";
44432                 this.elementStyle = "padding-left:"+((this.labelWidth+(typeof this.labelPad == 'number' ? this.labelPad : 5))+'px')+";";
44433             }
44434             if(this.labelAlign == 'top'){
44435                 this.labelStyle = "width:auto;";
44436                 this.elementStyle = "padding-left:0;";
44437             }
44438         }
44439         var stack = this.stack;
44440         var slen = stack.length;
44441         if(slen > 0){
44442             if(!this.fieldTpl){
44443                 var t = new Roo.Template(
44444                     '<div class="x-form-item {5}">',
44445                         '<label for="{0}" style="{2}">{1}{4}</label>',
44446                         '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
44447                         '</div>',
44448                     '</div><div class="x-form-clear-left"></div>'
44449                 );
44450                 t.disableFormats = true;
44451                 t.compile();
44452                 Roo.form.Layout.prototype.fieldTpl = t;
44453             }
44454             for(var i = 0; i < slen; i++) {
44455                 if(stack[i].isFormField){
44456                     this.renderField(stack[i]);
44457                 }else{
44458                     this.renderComponent(stack[i]);
44459                 }
44460             }
44461         }
44462         if(this.clear){
44463             this.el.createChild({cls:'x-form-clear'});
44464         }
44465     },
44466
44467     // private
44468     renderField : function(f){
44469         f.fieldEl = Roo.get(this.fieldTpl.append(this.el, [
44470                f.id, //0
44471                f.fieldLabel, //1
44472                f.labelStyle||this.labelStyle||'', //2
44473                this.elementStyle||'', //3
44474                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator, //4
44475                f.itemCls||this.itemCls||''  //5
44476        ], true).getPrevSibling());
44477     },
44478
44479     // private
44480     renderComponent : function(c){
44481         c.render(c.isLayout ? this.el : this.el.createChild());    
44482     },
44483     /**
44484      * Adds a object form elements (using the xtype property as the factory method.)
44485      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column
44486      * @param {Object} config 
44487      */
44488     addxtype : function(o)
44489     {
44490         // create the lement.
44491         o.form = this.form;
44492         var fe = Roo.factory(o, Roo.form);
44493         this.form.allItems.push(fe);
44494         this.stack.push(fe);
44495         
44496         if (fe.isFormField) {
44497             this.form.items.add(fe);
44498         }
44499          
44500         return fe;
44501     }
44502 });
44503
44504 /**
44505  * @class Roo.form.Column
44506  * @extends Roo.form.Layout
44507  * Creates a column container for layout and rendering of fields in an {@link Roo.form.Form}.
44508  * @constructor
44509  * @param {Object} config Configuration options
44510  */
44511 Roo.form.Column = function(config){
44512     Roo.form.Column.superclass.constructor.call(this, config);
44513 };
44514
44515 Roo.extend(Roo.form.Column, Roo.form.Layout, {
44516     /**
44517      * @cfg {Number/String} width
44518      * The fixed width of the column in pixels or CSS value (defaults to "auto")
44519      */
44520     /**
44521      * @cfg {String/Object} autoCreate
44522      * A DomHelper element spec used to autocreate the column (defaults to {tag: 'div', cls: 'x-form-ct x-form-column'})
44523      */
44524
44525     // private
44526     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-column'},
44527
44528     // private
44529     onRender : function(ct, position){
44530         Roo.form.Column.superclass.onRender.call(this, ct, position);
44531         if(this.width){
44532             this.el.setWidth(this.width);
44533         }
44534     }
44535 });
44536
44537
44538 /**
44539  * @class Roo.form.Row
44540  * @extends Roo.form.Layout
44541  * Creates a row container for layout and rendering of fields in an {@link Roo.form.Form}.
44542  * @constructor
44543  * @param {Object} config Configuration options
44544  */
44545
44546  
44547 Roo.form.Row = function(config){
44548     Roo.form.Row.superclass.constructor.call(this, config);
44549 };
44550  
44551 Roo.extend(Roo.form.Row, Roo.form.Layout, {
44552       /**
44553      * @cfg {Number/String} width
44554      * The fixed width of the column in pixels or CSS value (defaults to "auto")
44555      */
44556     /**
44557      * @cfg {Number/String} height
44558      * The fixed height of the column in pixels or CSS value (defaults to "auto")
44559      */
44560     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-row'},
44561     
44562     padWidth : 20,
44563     // private
44564     onRender : function(ct, position){
44565         //console.log('row render');
44566         if(!this.rowTpl){
44567             var t = new Roo.Template(
44568                 '<div class="x-form-item {5}" style="float:left;width:{6}px">',
44569                     '<label for="{0}" style="{2}">{1}{4}</label>',
44570                     '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
44571                     '</div>',
44572                 '</div>'
44573             );
44574             t.disableFormats = true;
44575             t.compile();
44576             Roo.form.Layout.prototype.rowTpl = t;
44577         }
44578         this.fieldTpl = this.rowTpl;
44579         
44580         //console.log('lw' + this.labelWidth +', la:' + this.labelAlign);
44581         var labelWidth = 100;
44582         
44583         if ((this.labelAlign != 'top')) {
44584             if (typeof this.labelWidth == 'number') {
44585                 labelWidth = this.labelWidth
44586             }
44587             this.padWidth =  20 + labelWidth;
44588             
44589         }
44590         
44591         Roo.form.Column.superclass.onRender.call(this, ct, position);
44592         if(this.width){
44593             this.el.setWidth(this.width);
44594         }
44595         if(this.height){
44596             this.el.setHeight(this.height);
44597         }
44598     },
44599     
44600     // private
44601     renderField : function(f){
44602         f.fieldEl = this.fieldTpl.append(this.el, [
44603                f.id, f.fieldLabel,
44604                f.labelStyle||this.labelStyle||'',
44605                this.elementStyle||'',
44606                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator,
44607                f.itemCls||this.itemCls||'',
44608                f.width ? f.width + this.padWidth : 160 + this.padWidth
44609        ],true);
44610     }
44611 });
44612  
44613
44614 /**
44615  * @class Roo.form.FieldSet
44616  * @extends Roo.form.Layout
44617  * Creates a fieldset container for layout and rendering of fields in an {@link Roo.form.Form}.
44618  * @constructor
44619  * @param {Object} config Configuration options
44620  */
44621 Roo.form.FieldSet = function(config){
44622     Roo.form.FieldSet.superclass.constructor.call(this, config);
44623 };
44624
44625 Roo.extend(Roo.form.FieldSet, Roo.form.Layout, {
44626     /**
44627      * @cfg {String} legend
44628      * The text to display as the legend for the FieldSet (defaults to '')
44629      */
44630     /**
44631      * @cfg {String/Object} autoCreate
44632      * A DomHelper element spec used to autocreate the fieldset (defaults to {tag: 'fieldset', cn: {tag:'legend'}})
44633      */
44634
44635     // private
44636     defaultAutoCreate : {tag: 'fieldset', cn: {tag:'legend'}},
44637
44638     // private
44639     onRender : function(ct, position){
44640         Roo.form.FieldSet.superclass.onRender.call(this, ct, position);
44641         if(this.legend){
44642             this.setLegend(this.legend);
44643         }
44644     },
44645
44646     // private
44647     setLegend : function(text){
44648         if(this.rendered){
44649             this.el.child('legend').update(text);
44650         }
44651     }
44652 });/*
44653  * Based on:
44654  * Ext JS Library 1.1.1
44655  * Copyright(c) 2006-2007, Ext JS, LLC.
44656  *
44657  * Originally Released Under LGPL - original licence link has changed is not relivant.
44658  *
44659  * Fork - LGPL
44660  * <script type="text/javascript">
44661  */
44662 /**
44663  * @class Roo.form.VTypes
44664  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
44665  * @singleton
44666  */
44667 Roo.form.VTypes = function(){
44668     // closure these in so they are only created once.
44669     var alpha = /^[a-zA-Z_]+$/;
44670     var alphanum = /^[a-zA-Z0-9_]+$/;
44671     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,4}$/;
44672     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
44673
44674     // All these messages and functions are configurable
44675     return {
44676         /**
44677          * The function used to validate email addresses
44678          * @param {String} value The email address
44679          */
44680         'email' : function(v){
44681             return email.test(v);
44682         },
44683         /**
44684          * The error text to display when the email validation function returns false
44685          * @type String
44686          */
44687         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
44688         /**
44689          * The keystroke filter mask to be applied on email input
44690          * @type RegExp
44691          */
44692         'emailMask' : /[a-z0-9_\.\-@]/i,
44693
44694         /**
44695          * The function used to validate URLs
44696          * @param {String} value The URL
44697          */
44698         'url' : function(v){
44699             return url.test(v);
44700         },
44701         /**
44702          * The error text to display when the url validation function returns false
44703          * @type String
44704          */
44705         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
44706         
44707         /**
44708          * The function used to validate alpha values
44709          * @param {String} value The value
44710          */
44711         'alpha' : function(v){
44712             return alpha.test(v);
44713         },
44714         /**
44715          * The error text to display when the alpha validation function returns false
44716          * @type String
44717          */
44718         'alphaText' : 'This field should only contain letters and _',
44719         /**
44720          * The keystroke filter mask to be applied on alpha input
44721          * @type RegExp
44722          */
44723         'alphaMask' : /[a-z_]/i,
44724
44725         /**
44726          * The function used to validate alphanumeric values
44727          * @param {String} value The value
44728          */
44729         'alphanum' : function(v){
44730             return alphanum.test(v);
44731         },
44732         /**
44733          * The error text to display when the alphanumeric validation function returns false
44734          * @type String
44735          */
44736         'alphanumText' : 'This field should only contain letters, numbers and _',
44737         /**
44738          * The keystroke filter mask to be applied on alphanumeric input
44739          * @type RegExp
44740          */
44741         'alphanumMask' : /[a-z0-9_]/i
44742     };
44743 }();//<script type="text/javascript">
44744
44745 /**
44746  * @class Roo.form.FCKeditor
44747  * @extends Roo.form.TextArea
44748  * Wrapper around the FCKEditor http://www.fckeditor.net
44749  * @constructor
44750  * Creates a new FCKeditor
44751  * @param {Object} config Configuration options
44752  */
44753 Roo.form.FCKeditor = function(config){
44754     Roo.form.FCKeditor.superclass.constructor.call(this, config);
44755     this.addEvents({
44756          /**
44757          * @event editorinit
44758          * Fired when the editor is initialized - you can add extra handlers here..
44759          * @param {FCKeditor} this
44760          * @param {Object} the FCK object.
44761          */
44762         editorinit : true
44763     });
44764     
44765     
44766 };
44767 Roo.form.FCKeditor.editors = { };
44768 Roo.extend(Roo.form.FCKeditor, Roo.form.TextArea,
44769 {
44770     //defaultAutoCreate : {
44771     //    tag : "textarea",style   : "width:100px;height:60px;" ,autocomplete    : "off"
44772     //},
44773     // private
44774     /**
44775      * @cfg {Object} fck options - see fck manual for details.
44776      */
44777     fckconfig : false,
44778     
44779     /**
44780      * @cfg {Object} fck toolbar set (Basic or Default)
44781      */
44782     toolbarSet : 'Basic',
44783     /**
44784      * @cfg {Object} fck BasePath
44785      */ 
44786     basePath : '/fckeditor/',
44787     
44788     
44789     frame : false,
44790     
44791     value : '',
44792     
44793    
44794     onRender : function(ct, position)
44795     {
44796         if(!this.el){
44797             this.defaultAutoCreate = {
44798                 tag: "textarea",
44799                 style:"width:300px;height:60px;",
44800                 autocomplete: "off"
44801             };
44802         }
44803         Roo.form.FCKeditor.superclass.onRender.call(this, ct, position);
44804         /*
44805         if(this.grow){
44806             this.textSizeEl = Roo.DomHelper.append(document.body, {tag: "pre", cls: "x-form-grow-sizer"});
44807             if(this.preventScrollbars){
44808                 this.el.setStyle("overflow", "hidden");
44809             }
44810             this.el.setHeight(this.growMin);
44811         }
44812         */
44813         //console.log('onrender' + this.getId() );
44814         Roo.form.FCKeditor.editors[this.getId()] = this;
44815          
44816
44817         this.replaceTextarea() ;
44818         
44819     },
44820     
44821     getEditor : function() {
44822         return this.fckEditor;
44823     },
44824     /**
44825      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
44826      * @param {Mixed} value The value to set
44827      */
44828     
44829     
44830     setValue : function(value)
44831     {
44832         //console.log('setValue: ' + value);
44833         
44834         if(typeof(value) == 'undefined') { // not sure why this is happending...
44835             return;
44836         }
44837         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
44838         
44839         //if(!this.el || !this.getEditor()) {
44840         //    this.value = value;
44841             //this.setValue.defer(100,this,[value]);    
44842         //    return;
44843         //} 
44844         
44845         if(!this.getEditor()) {
44846             return;
44847         }
44848         
44849         this.getEditor().SetData(value);
44850         
44851         //
44852
44853     },
44854
44855     /**
44856      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
44857      * @return {Mixed} value The field value
44858      */
44859     getValue : function()
44860     {
44861         
44862         if (this.frame && this.frame.dom.style.display == 'none') {
44863             return Roo.form.FCKeditor.superclass.getValue.call(this);
44864         }
44865         
44866         if(!this.el || !this.getEditor()) {
44867            
44868            // this.getValue.defer(100,this); 
44869             return this.value;
44870         }
44871        
44872         
44873         var value=this.getEditor().GetData();
44874         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
44875         return Roo.form.FCKeditor.superclass.getValue.call(this);
44876         
44877
44878     },
44879
44880     /**
44881      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
44882      * @return {Mixed} value The field value
44883      */
44884     getRawValue : function()
44885     {
44886         if (this.frame && this.frame.dom.style.display == 'none') {
44887             return Roo.form.FCKeditor.superclass.getRawValue.call(this);
44888         }
44889         
44890         if(!this.el || !this.getEditor()) {
44891             //this.getRawValue.defer(100,this); 
44892             return this.value;
44893             return;
44894         }
44895         
44896         
44897         
44898         var value=this.getEditor().GetData();
44899         Roo.form.FCKeditor.superclass.setRawValue.apply(this,[value]);
44900         return Roo.form.FCKeditor.superclass.getRawValue.call(this);
44901          
44902     },
44903     
44904     setSize : function(w,h) {
44905         
44906         
44907         
44908         //if (this.frame && this.frame.dom.style.display == 'none') {
44909         //    Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
44910         //    return;
44911         //}
44912         //if(!this.el || !this.getEditor()) {
44913         //    this.setSize.defer(100,this, [w,h]); 
44914         //    return;
44915         //}
44916         
44917         
44918         
44919         Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
44920         
44921         this.frame.dom.setAttribute('width', w);
44922         this.frame.dom.setAttribute('height', h);
44923         this.frame.setSize(w,h);
44924         
44925     },
44926     
44927     toggleSourceEdit : function(value) {
44928         
44929       
44930          
44931         this.el.dom.style.display = value ? '' : 'none';
44932         this.frame.dom.style.display = value ?  'none' : '';
44933         
44934     },
44935     
44936     
44937     focus: function(tag)
44938     {
44939         if (this.frame.dom.style.display == 'none') {
44940             return Roo.form.FCKeditor.superclass.focus.call(this);
44941         }
44942         if(!this.el || !this.getEditor()) {
44943             this.focus.defer(100,this, [tag]); 
44944             return;
44945         }
44946         
44947         
44948         
44949         
44950         var tgs = this.getEditor().EditorDocument.getElementsByTagName(tag);
44951         this.getEditor().Focus();
44952         if (tgs.length) {
44953             if (!this.getEditor().Selection.GetSelection()) {
44954                 this.focus.defer(100,this, [tag]); 
44955                 return;
44956             }
44957             
44958             
44959             var r = this.getEditor().EditorDocument.createRange();
44960             r.setStart(tgs[0],0);
44961             r.setEnd(tgs[0],0);
44962             this.getEditor().Selection.GetSelection().removeAllRanges();
44963             this.getEditor().Selection.GetSelection().addRange(r);
44964             this.getEditor().Focus();
44965         }
44966         
44967     },
44968     
44969     
44970     
44971     replaceTextarea : function()
44972     {
44973         if ( document.getElementById( this.getId() + '___Frame' ) )
44974             return ;
44975         //if ( !this.checkBrowser || this._isCompatibleBrowser() )
44976         //{
44977             // We must check the elements firstly using the Id and then the name.
44978         var oTextarea = document.getElementById( this.getId() );
44979         
44980         var colElementsByName = document.getElementsByName( this.getId() ) ;
44981          
44982         oTextarea.style.display = 'none' ;
44983
44984         if ( oTextarea.tabIndex ) {            
44985             this.TabIndex = oTextarea.tabIndex ;
44986         }
44987         
44988         this._insertHtmlBefore( this._getConfigHtml(), oTextarea ) ;
44989         this._insertHtmlBefore( this._getIFrameHtml(), oTextarea ) ;
44990         this.frame = Roo.get(this.getId() + '___Frame')
44991     },
44992     
44993     _getConfigHtml : function()
44994     {
44995         var sConfig = '' ;
44996
44997         for ( var o in this.fckconfig ) {
44998             sConfig += sConfig.length > 0  ? '&amp;' : '';
44999             sConfig += encodeURIComponent( o ) + '=' + encodeURIComponent( this.fckconfig[o] ) ;
45000         }
45001
45002         return '<input type="hidden" id="' + this.getId() + '___Config" value="' + sConfig + '" style="display:none" />' ;
45003     },
45004     
45005     
45006     _getIFrameHtml : function()
45007     {
45008         var sFile = 'fckeditor.html' ;
45009         /* no idea what this is about..
45010         try
45011         {
45012             if ( (/fcksource=true/i).test( window.top.location.search ) )
45013                 sFile = 'fckeditor.original.html' ;
45014         }
45015         catch (e) { 
45016         */
45017
45018         var sLink = this.basePath + 'editor/' + sFile + '?InstanceName=' + encodeURIComponent( this.getId() ) ;
45019         sLink += this.toolbarSet ? ( '&amp;Toolbar=' + this.toolbarSet)  : '';
45020         
45021         
45022         var html = '<iframe id="' + this.getId() +
45023             '___Frame" src="' + sLink +
45024             '" width="' + this.width +
45025             '" height="' + this.height + '"' +
45026             (this.tabIndex ?  ' tabindex="' + this.tabIndex + '"' :'' ) +
45027             ' frameborder="0" scrolling="no"></iframe>' ;
45028
45029         return html ;
45030     },
45031     
45032     _insertHtmlBefore : function( html, element )
45033     {
45034         if ( element.insertAdjacentHTML )       {
45035             // IE
45036             element.insertAdjacentHTML( 'beforeBegin', html ) ;
45037         } else { // Gecko
45038             var oRange = document.createRange() ;
45039             oRange.setStartBefore( element ) ;
45040             var oFragment = oRange.createContextualFragment( html );
45041             element.parentNode.insertBefore( oFragment, element ) ;
45042         }
45043     }
45044     
45045     
45046   
45047     
45048     
45049     
45050     
45051
45052 });
45053
45054 //Roo.reg('fckeditor', Roo.form.FCKeditor);
45055
45056 function FCKeditor_OnComplete(editorInstance){
45057     var f = Roo.form.FCKeditor.editors[editorInstance.Name];
45058     f.fckEditor = editorInstance;
45059     //console.log("loaded");
45060     f.fireEvent('editorinit', f, editorInstance);
45061
45062   
45063
45064  
45065
45066
45067
45068
45069
45070
45071
45072
45073
45074
45075
45076
45077
45078
45079
45080 //<script type="text/javascript">
45081 /**
45082  * @class Roo.form.GridField
45083  * @extends Roo.form.Field
45084  * Embed a grid (or editable grid into a form)
45085  * STATUS ALPHA
45086  * 
45087  * This embeds a grid in a form, the value of the field should be the json encoded array of rows
45088  * it needs 
45089  * xgrid.store = Roo.data.Store
45090  * xgrid.store.proxy = Roo.data.MemoryProxy (data = [] )
45091  * xgrid.store.reader = Roo.data.JsonReader 
45092  * 
45093  * 
45094  * @constructor
45095  * Creates a new GridField
45096  * @param {Object} config Configuration options
45097  */
45098 Roo.form.GridField = function(config){
45099     Roo.form.GridField.superclass.constructor.call(this, config);
45100      
45101 };
45102
45103 Roo.extend(Roo.form.GridField, Roo.form.Field,  {
45104     /**
45105      * @cfg {Number} width  - used to restrict width of grid..
45106      */
45107     width : 100,
45108     /**
45109      * @cfg {Number} height - used to restrict height of grid..
45110      */
45111     height : 50,
45112      /**
45113      * @cfg {Object} xgrid (xtype'd description of grid) { xtype : 'Grid', dataSource: .... }
45114          * 
45115          *}
45116      */
45117     xgrid : false, 
45118     /**
45119      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
45120      * {tag: "input", type: "checkbox", autocomplete: "off"})
45121      */
45122    // defaultAutoCreate : { tag: 'div' },
45123     defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
45124     /**
45125      * @cfg {String} addTitle Text to include for adding a title.
45126      */
45127     addTitle : false,
45128     //
45129     onResize : function(){
45130         Roo.form.Field.superclass.onResize.apply(this, arguments);
45131     },
45132
45133     initEvents : function(){
45134         // Roo.form.Checkbox.superclass.initEvents.call(this);
45135         // has no events...
45136        
45137     },
45138
45139
45140     getResizeEl : function(){
45141         return this.wrap;
45142     },
45143
45144     getPositionEl : function(){
45145         return this.wrap;
45146     },
45147
45148     // private
45149     onRender : function(ct, position){
45150         
45151         this.style = this.style || 'overflow: hidden; border:1px solid #c3daf9;';
45152         var style = this.style;
45153         delete this.style;
45154         
45155         Roo.form.GridField.superclass.onRender.call(this, ct, position);
45156         this.wrap = this.el.wrap({cls: ''}); // not sure why ive done thsi...
45157         this.viewEl = this.wrap.createChild({ tag: 'div' });
45158         if (style) {
45159             this.viewEl.applyStyles(style);
45160         }
45161         if (this.width) {
45162             this.viewEl.setWidth(this.width);
45163         }
45164         if (this.height) {
45165             this.viewEl.setHeight(this.height);
45166         }
45167         //if(this.inputValue !== undefined){
45168         //this.setValue(this.value);
45169         
45170         
45171         this.grid = new Roo.grid[this.xgrid.xtype](this.viewEl, this.xgrid);
45172         
45173         
45174         this.grid.render();
45175         this.grid.getDataSource().on('remove', this.refreshValue, this);
45176         this.grid.getDataSource().on('update', this.refreshValue, this);
45177         this.grid.on('afteredit', this.refreshValue, this);
45178  
45179     },
45180      
45181     
45182     /**
45183      * Sets the value of the item. 
45184      * @param {String} either an object  or a string..
45185      */
45186     setValue : function(v){
45187         //this.value = v;
45188         v = v || []; // empty set..
45189         // this does not seem smart - it really only affects memoryproxy grids..
45190         if (this.grid && this.grid.getDataSource() && typeof(v) != 'undefined') {
45191             var ds = this.grid.getDataSource();
45192             // assumes a json reader..
45193             var data = {}
45194             data[ds.reader.meta.root ] =  typeof(v) == 'string' ? Roo.decode(v) : v;
45195             ds.loadData( data);
45196         }
45197         // clear selection so it does not get stale.
45198         if (this.grid.sm) { 
45199             this.grid.sm.clearSelections();
45200         }
45201         
45202         Roo.form.GridField.superclass.setValue.call(this, v);
45203         this.refreshValue();
45204         // should load data in the grid really....
45205     },
45206     
45207     // private
45208     refreshValue: function() {
45209          var val = [];
45210         this.grid.getDataSource().each(function(r) {
45211             val.push(r.data);
45212         });
45213         this.el.dom.value = Roo.encode(val);
45214     }
45215     
45216      
45217     
45218     
45219 });/*
45220  * Based on:
45221  * Ext JS Library 1.1.1
45222  * Copyright(c) 2006-2007, Ext JS, LLC.
45223  *
45224  * Originally Released Under LGPL - original licence link has changed is not relivant.
45225  *
45226  * Fork - LGPL
45227  * <script type="text/javascript">
45228  */
45229 /**
45230  * @class Roo.form.DisplayField
45231  * @extends Roo.form.Field
45232  * A generic Field to display non-editable data.
45233  * @constructor
45234  * Creates a new Display Field item.
45235  * @param {Object} config Configuration options
45236  */
45237 Roo.form.DisplayField = function(config){
45238     Roo.form.DisplayField.superclass.constructor.call(this, config);
45239     
45240 };
45241
45242 Roo.extend(Roo.form.DisplayField, Roo.form.TextField,  {
45243     inputType:      'hidden',
45244     allowBlank:     true,
45245     readOnly:         true,
45246     
45247  
45248     /**
45249      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
45250      */
45251     focusClass : undefined,
45252     /**
45253      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
45254      */
45255     fieldClass: 'x-form-field',
45256     
45257      /**
45258      * @cfg {Function} valueRenderer The renderer for the field (so you can reformat output). should return raw HTML
45259      */
45260     valueRenderer: undefined,
45261     
45262     width: 100,
45263     /**
45264      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
45265      * {tag: "input", type: "checkbox", autocomplete: "off"})
45266      */
45267      
45268  //   defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
45269
45270     onResize : function(){
45271         Roo.form.DisplayField.superclass.onResize.apply(this, arguments);
45272         
45273     },
45274
45275     initEvents : function(){
45276         // Roo.form.Checkbox.superclass.initEvents.call(this);
45277         // has no events...
45278        
45279     },
45280
45281
45282     getResizeEl : function(){
45283         return this.wrap;
45284     },
45285
45286     getPositionEl : function(){
45287         return this.wrap;
45288     },
45289
45290     // private
45291     onRender : function(ct, position){
45292         
45293         Roo.form.DisplayField.superclass.onRender.call(this, ct, position);
45294         //if(this.inputValue !== undefined){
45295         this.wrap = this.el.wrap();
45296         
45297         this.viewEl = this.wrap.createChild({ tag: 'div', cls: 'x-form-displayfield'});
45298         
45299         if (this.bodyStyle) {
45300             this.viewEl.applyStyles(this.bodyStyle);
45301         }
45302         //this.viewEl.setStyle('padding', '2px');
45303         
45304         this.setValue(this.value);
45305         
45306     },
45307 /*
45308     // private
45309     initValue : Roo.emptyFn,
45310
45311   */
45312
45313         // private
45314     onClick : function(){
45315         
45316     },
45317
45318     /**
45319      * Sets the checked state of the checkbox.
45320      * @param {Boolean/String} checked True, 'true', '1', or 'on' to check the checkbox, any other value will uncheck it.
45321      */
45322     setValue : function(v){
45323         this.value = v;
45324         var html = this.valueRenderer ?  this.valueRenderer(v) : String.format('{0}', v);
45325         // this might be called before we have a dom element..
45326         if (!this.viewEl) {
45327             return;
45328         }
45329         this.viewEl.dom.innerHTML = html;
45330         Roo.form.DisplayField.superclass.setValue.call(this, v);
45331
45332     }
45333 });/*
45334  * 
45335  * Licence- LGPL
45336  * 
45337  */
45338
45339 /**
45340  * @class Roo.form.DayPicker
45341  * @extends Roo.form.Field
45342  * A Day picker show [M] [T] [W] ....
45343  * @constructor
45344  * Creates a new Day Picker
45345  * @param {Object} config Configuration options
45346  */
45347 Roo.form.DayPicker= function(config){
45348     Roo.form.DayPicker.superclass.constructor.call(this, config);
45349      
45350 };
45351
45352 Roo.extend(Roo.form.DayPicker, Roo.form.Field,  {
45353     /**
45354      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
45355      */
45356     focusClass : undefined,
45357     /**
45358      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
45359      */
45360     fieldClass: "x-form-field",
45361    
45362     /**
45363      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
45364      * {tag: "input", type: "checkbox", autocomplete: "off"})
45365      */
45366     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "off"},
45367     
45368    
45369     actionMode : 'viewEl', 
45370     //
45371     // private
45372  
45373     inputType : 'hidden',
45374     
45375      
45376     inputElement: false, // real input element?
45377     basedOn: false, // ????
45378     
45379     isFormField: true, // not sure where this is needed!!!!
45380
45381     onResize : function(){
45382         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
45383         if(!this.boxLabel){
45384             this.el.alignTo(this.wrap, 'c-c');
45385         }
45386     },
45387
45388     initEvents : function(){
45389         Roo.form.Checkbox.superclass.initEvents.call(this);
45390         this.el.on("click", this.onClick,  this);
45391         this.el.on("change", this.onClick,  this);
45392     },
45393
45394
45395     getResizeEl : function(){
45396         return this.wrap;
45397     },
45398
45399     getPositionEl : function(){
45400         return this.wrap;
45401     },
45402
45403     
45404     // private
45405     onRender : function(ct, position){
45406         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
45407        
45408         this.wrap = this.el.wrap({cls: 'x-form-daypick-item '});
45409         
45410         var r1 = '<table><tr>';
45411         var r2 = '<tr class="x-form-daypick-icons">';
45412         for (var i=0; i < 7; i++) {
45413             r1+= '<td><div>' + Date.dayNames[i].substring(0,3) + '</div></td>';
45414             r2+= '<td><img class="x-menu-item-icon" src="' + Roo.BLANK_IMAGE_URL  +'"></td>';
45415         }
45416         
45417         var viewEl = this.wrap.createChild( r1 + '</tr>' + r2 + '</tr></table>');
45418         viewEl.select('img').on('click', this.onClick, this);
45419         this.viewEl = viewEl;   
45420         
45421         
45422         // this will not work on Chrome!!!
45423         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
45424         this.el.on('propertychange', this.setFromHidden,  this);  //ie
45425         
45426         
45427           
45428
45429     },
45430
45431     // private
45432     initValue : Roo.emptyFn,
45433
45434     /**
45435      * Returns the checked state of the checkbox.
45436      * @return {Boolean} True if checked, else false
45437      */
45438     getValue : function(){
45439         return this.el.dom.value;
45440         
45441     },
45442
45443         // private
45444     onClick : function(e){ 
45445         //this.setChecked(!this.checked);
45446         Roo.get(e.target).toggleClass('x-menu-item-checked');
45447         this.refreshValue();
45448         //if(this.el.dom.checked != this.checked){
45449         //    this.setValue(this.el.dom.checked);
45450        // }
45451     },
45452     
45453     // private
45454     refreshValue : function()
45455     {
45456         var val = '';
45457         this.viewEl.select('img',true).each(function(e,i,n)  {
45458             val += e.is(".x-menu-item-checked") ? String(n) : '';
45459         });
45460         this.setValue(val, true);
45461     },
45462
45463     /**
45464      * Sets the checked state of the checkbox.
45465      * On is always based on a string comparison between inputValue and the param.
45466      * @param {Boolean/String} value - the value to set 
45467      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
45468      */
45469     setValue : function(v,suppressEvent){
45470         if (!this.el.dom) {
45471             return;
45472         }
45473         var old = this.el.dom.value ;
45474         this.el.dom.value = v;
45475         if (suppressEvent) {
45476             return ;
45477         }
45478          
45479         // update display..
45480         this.viewEl.select('img',true).each(function(e,i,n)  {
45481             
45482             var on = e.is(".x-menu-item-checked");
45483             var newv = v.indexOf(String(n)) > -1;
45484             if (on != newv) {
45485                 e.toggleClass('x-menu-item-checked');
45486             }
45487             
45488         });
45489         
45490         
45491         this.fireEvent('change', this, v, old);
45492         
45493         
45494     },
45495    
45496     // handle setting of hidden value by some other method!!?!?
45497     setFromHidden: function()
45498     {
45499         if(!this.el){
45500             return;
45501         }
45502         //console.log("SET FROM HIDDEN");
45503         //alert('setFrom hidden');
45504         this.setValue(this.el.dom.value);
45505     },
45506     
45507     onDestroy : function()
45508     {
45509         if(this.viewEl){
45510             Roo.get(this.viewEl).remove();
45511         }
45512          
45513         Roo.form.DayPicker.superclass.onDestroy.call(this);
45514     }
45515
45516 });/*
45517  * RooJS Library 1.1.1
45518  * Copyright(c) 2008-2011  Alan Knowles
45519  *
45520  * License - LGPL
45521  */
45522  
45523
45524 /**
45525  * @class Roo.form.ComboCheck
45526  * @extends Roo.form.ComboBox
45527  * A combobox for multiple select items.
45528  *
45529  * FIXME - could do with a reset button..
45530  * 
45531  * @constructor
45532  * Create a new ComboCheck
45533  * @param {Object} config Configuration options
45534  */
45535 Roo.form.ComboCheck = function(config){
45536     Roo.form.ComboCheck.superclass.constructor.call(this, config);
45537     // should verify some data...
45538     // like
45539     // hiddenName = required..
45540     // displayField = required
45541     // valudField == required
45542     var req= [ 'hiddenName', 'displayField', 'valueField' ];
45543     var _t = this;
45544     Roo.each(req, function(e) {
45545         if ((typeof(_t[e]) == 'undefined' ) || !_t[e].length) {
45546             throw "Roo.form.ComboCheck : missing value for: " + e;
45547         }
45548     });
45549     
45550     
45551 };
45552
45553 Roo.extend(Roo.form.ComboCheck, Roo.form.ComboBox, {
45554      
45555      
45556     editable : false,
45557      
45558     selectedClass: 'x-menu-item-checked', 
45559     
45560     // private
45561     onRender : function(ct, position){
45562         var _t = this;
45563         
45564         
45565         
45566         if(!this.tpl){
45567             var cls = 'x-combo-list';
45568
45569             
45570             this.tpl =  new Roo.Template({
45571                 html :  '<div class="'+cls+'-item x-menu-check-item">' +
45572                    '<img class="x-menu-item-icon" style="margin: 0px;" src="' + Roo.BLANK_IMAGE_URL + '">' + 
45573                    '<span>{' + this.displayField + '}</span>' +
45574                     '</div>' 
45575                 
45576             });
45577         }
45578  
45579         
45580         Roo.form.ComboCheck.superclass.onRender.call(this, ct, position);
45581         this.view.singleSelect = false;
45582         this.view.multiSelect = true;
45583         this.view.toggleSelect = true;
45584         this.pageTb.add(new Roo.Toolbar.Fill(), {
45585             
45586             text: 'Done',
45587             handler: function()
45588             {
45589                 _t.collapse();
45590             }
45591         });
45592     },
45593     
45594     onViewOver : function(e, t){
45595         // do nothing...
45596         return;
45597         
45598     },
45599     
45600     onViewClick : function(doFocus,index){
45601         return;
45602         
45603     },
45604     select: function () {
45605         //Roo.log("SELECT CALLED");
45606     },
45607      
45608     selectByValue : function(xv, scrollIntoView){
45609         var ar = this.getValueArray();
45610         var sels = [];
45611         
45612         Roo.each(ar, function(v) {
45613             if(v === undefined || v === null){
45614                 return;
45615             }
45616             var r = this.findRecord(this.valueField, v);
45617             if(r){
45618                 sels.push(this.store.indexOf(r))
45619                 
45620             }
45621         },this);
45622         this.view.select(sels);
45623         return false;
45624     },
45625     
45626     
45627     
45628     onSelect : function(record, index){
45629        // Roo.log("onselect Called");
45630        // this is only called by the clear button now..
45631         this.view.clearSelections();
45632         this.setValue('[]');
45633         if (this.value != this.valueBefore) {
45634             this.fireEvent('change', this, this.value, this.valueBefore);
45635             this.valueBefore = this.value;
45636         }
45637     },
45638     getValueArray : function()
45639     {
45640         var ar = [] ;
45641         
45642         try {
45643             //Roo.log(this.value);
45644             if (typeof(this.value) == 'undefined') {
45645                 return [];
45646             }
45647             var ar = Roo.decode(this.value);
45648             return  ar instanceof Array ? ar : []; //?? valid?
45649             
45650         } catch(e) {
45651             Roo.log(e + "\nRoo.form.ComboCheck:getValueArray  invalid data:" + this.getValue());
45652             return [];
45653         }
45654          
45655     },
45656     expand : function ()
45657     {
45658         
45659         Roo.form.ComboCheck.superclass.expand.call(this);
45660         this.valueBefore = typeof(this.value) == 'undefined' ? '' : this.value;
45661         //this.valueBefore = typeof(this.valueBefore) == 'undefined' ? '' : this.valueBefore;
45662         
45663
45664     },
45665     
45666     collapse : function(){
45667         Roo.form.ComboCheck.superclass.collapse.call(this);
45668         var sl = this.view.getSelectedIndexes();
45669         var st = this.store;
45670         var nv = [];
45671         var tv = [];
45672         var r;
45673         Roo.each(sl, function(i) {
45674             r = st.getAt(i);
45675             nv.push(r.get(this.valueField));
45676         },this);
45677         this.setValue(Roo.encode(nv));
45678         if (this.value != this.valueBefore) {
45679
45680             this.fireEvent('change', this, this.value, this.valueBefore);
45681             this.valueBefore = this.value;
45682         }
45683         
45684     },
45685     
45686     setValue : function(v){
45687         // Roo.log(v);
45688         this.value = v;
45689         
45690         var vals = this.getValueArray();
45691         var tv = [];
45692         Roo.each(vals, function(k) {
45693             var r = this.findRecord(this.valueField, k);
45694             if(r){
45695                 tv.push(r.data[this.displayField]);
45696             }else if(this.valueNotFoundText !== undefined){
45697                 tv.push( this.valueNotFoundText );
45698             }
45699         },this);
45700        // Roo.log(tv);
45701         
45702         Roo.form.ComboBox.superclass.setValue.call(this, tv.join(', '));
45703         this.hiddenField.value = v;
45704         this.value = v;
45705     }
45706     
45707 });/*
45708  * Based on:
45709  * Ext JS Library 1.1.1
45710  * Copyright(c) 2006-2007, Ext JS, LLC.
45711  *
45712  * Originally Released Under LGPL - original licence link has changed is not relivant.
45713  *
45714  * Fork - LGPL
45715  * <script type="text/javascript">
45716  */
45717  
45718 /**
45719  * @class Roo.form.Signature
45720  * @extends Roo.form.Field
45721  * Signature field.  
45722  * @constructor
45723  * 
45724  * @param {Object} config Configuration options
45725  */
45726
45727 Roo.form.Signature = function(config){
45728     Roo.form.Signature.superclass.constructor.call(this, config);
45729     
45730     this.addEvents({// not in used??
45731          /**
45732          * @event confirm
45733          * Fires when the 'confirm' icon is pressed (add a listener to enable add button)
45734              * @param {Roo.form.Signature} combo This combo box
45735              */
45736         'confirm' : true,
45737         /**
45738          * @event reset
45739          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
45740              * @param {Roo.form.ComboBox} combo This combo box
45741              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
45742              */
45743         'reset' : true
45744     });
45745 };
45746
45747 Roo.extend(Roo.form.Signature, Roo.form.Field,  {
45748     /**
45749      * @cfg {Object} labels Label to use when rendering a form.
45750      * defaults to 
45751      * labels : { 
45752      *      clear : "Clear",
45753      *      confirm : "Confirm"
45754      *  }
45755      */
45756     labels : { 
45757         clear : "Clear",
45758         confirm : "Confirm"
45759     },
45760     /**
45761      * @cfg {Number} width The signature panel width (defaults to 300)
45762      */
45763     width: 300,
45764     /**
45765      * @cfg {Number} height The signature panel height (defaults to 100)
45766      */
45767     height : 100,
45768     /**
45769      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to false)
45770      */
45771     allowBlank : false,
45772     
45773     //private
45774     // {Object} signPanel The signature SVG panel element (defaults to {})
45775     signPanel : {},
45776     // {Boolean} isMouseDown False to validate that the mouse down event (defaults to false)
45777     isMouseDown : false,
45778     // {Boolean} isConfirmed validate the signature is confirmed or not for submitting form (defaults to false)
45779     isConfirmed : false,
45780     // {String} signatureTmp SVG mapping string (defaults to empty string)
45781     signatureTmp : '',
45782     
45783     
45784     defaultAutoCreate : { // modified by initCompnoent..
45785         tag: "input",
45786         type:"hidden"
45787     },
45788
45789     // private
45790     onRender : function(ct, position){
45791         
45792         Roo.form.Signature.superclass.onRender.call(this, ct, position);
45793         
45794         this.wrap = this.el.wrap({
45795             cls:'x-form-signature-wrap', style : 'width: ' + this.width + 'px', cn:{cls:'x-form-signature'}
45796         });
45797         
45798         this.createToolbar(this);
45799         this.signPanel = this.wrap.createChild({
45800                 tag: 'div',
45801                 style: 'width: ' + this.width + 'px; height: ' + this.height + 'px; border: 0;'
45802             }, this.el
45803         );
45804             
45805         this.svgID = Roo.id();
45806         this.svgEl = this.signPanel.createChild({
45807               xmlns : 'http://www.w3.org/2000/svg',
45808               tag : 'svg',
45809               id : this.svgID + "-svg",
45810               width: this.width,
45811               height: this.height,
45812               viewBox: '0 0 '+this.width+' '+this.height,
45813               cn : [
45814                 {
45815                     tag: "rect",
45816                     id: this.svgID + "-svg-r",
45817                     width: this.width,
45818                     height: this.height,
45819                     fill: "#ffa"
45820                 },
45821                 {
45822                     tag: "line",
45823                     id: this.svgID + "-svg-l",
45824                     x1: "0", // start
45825                     y1: (this.height*0.8), // start set the line in 80% of height
45826                     x2: this.width, // end
45827                     y2: (this.height*0.8), // end set the line in 80% of height
45828                     'stroke': "#666",
45829                     'stroke-width': "1",
45830                     'stroke-dasharray': "3",
45831                     'shape-rendering': "crispEdges",
45832                     'pointer-events': "none"
45833                 },
45834                 {
45835                     tag: "path",
45836                     id: this.svgID + "-svg-p",
45837                     'stroke': "navy",
45838                     'stroke-width': "3",
45839                     'fill': "none",
45840                     'pointer-events': 'none'
45841                 }
45842               ]
45843         });
45844         this.createSVG();
45845         this.svgBox = this.svgEl.dom.getScreenCTM();
45846     },
45847     createSVG : function(){ 
45848         var svg = this.signPanel;
45849         var r = svg.select('#'+ this.svgID + '-svg-r', true).first().dom;
45850         var t = this;
45851
45852         r.addEventListener('mousedown', function(e) { return t.down(e); }, false);
45853         r.addEventListener('mousemove', function(e) { return t.move(e); }, false);
45854         r.addEventListener('mouseup', function(e) { return t.up(e); }, false);
45855         r.addEventListener('mouseout', function(e) { return t.up(e); }, false);
45856         r.addEventListener('touchstart', function(e) { return t.down(e); }, false);
45857         r.addEventListener('touchmove', function(e) { return t.move(e); }, false);
45858         r.addEventListener('touchend', function(e) { return t.up(e); }, false);
45859         
45860     },
45861     isTouchEvent : function(e){
45862         return e.type.match(/^touch/);
45863     },
45864     getCoords : function (e) {
45865         var pt    = this.svgEl.dom.createSVGPoint();
45866         pt.x = e.clientX; 
45867         pt.y = e.clientY;
45868         if (this.isTouchEvent(e)) {
45869             pt.x =  e.targetTouches[0].clientX 
45870             pt.y = e.targetTouches[0].clientY;
45871         }
45872         var a = this.svgEl.dom.getScreenCTM();
45873         var b = a.inverse();
45874         var mx = pt.matrixTransform(b);
45875         return mx.x + ',' + mx.y;
45876     },
45877     //mouse event headler 
45878     down : function (e) {
45879         this.signatureTmp += 'M' + this.getCoords(e) + ' ';
45880         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr('d', this.signatureTmp);
45881         
45882         this.isMouseDown = true;
45883         
45884         e.preventDefault();
45885     },
45886     move : function (e) {
45887         if (this.isMouseDown) {
45888             this.signatureTmp += 'L' + this.getCoords(e) + ' ';
45889             this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', this.signatureTmp);
45890         }
45891         
45892         e.preventDefault();
45893     },
45894     up : function (e) {
45895         this.isMouseDown = false;
45896         var sp = this.signatureTmp.split(' ');
45897         
45898         if(sp.length > 1){
45899             if(!sp[sp.length-2].match(/^L/)){
45900                 sp.pop();
45901                 sp.pop();
45902                 sp.push("");
45903                 this.signatureTmp = sp.join(" ");
45904             }
45905         }
45906         if(this.getValue() != this.signatureTmp){
45907             this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
45908             this.isConfirmed = false;
45909         }
45910         e.preventDefault();
45911     },
45912     
45913     /**
45914      * Protected method that will not generally be called directly. It
45915      * is called when the editor creates its toolbar. Override this method if you need to
45916      * add custom toolbar buttons.
45917      * @param {HtmlEditor} editor
45918      */
45919     createToolbar : function(editor){
45920          function btn(id, toggle, handler){
45921             var xid = fid + '-'+ id ;
45922             return {
45923                 id : xid,
45924                 cmd : id,
45925                 cls : 'x-btn-icon x-edit-'+id,
45926                 enableToggle:toggle !== false,
45927                 scope: editor, // was editor...
45928                 handler:handler||editor.relayBtnCmd,
45929                 clickEvent:'mousedown',
45930                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
45931                 tabIndex:-1
45932             };
45933         }
45934         
45935         
45936         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
45937         this.tb = tb;
45938         this.tb.add(
45939            {
45940                 cls : ' x-signature-btn x-signature-'+id,
45941                 scope: editor, // was editor...
45942                 handler: this.reset,
45943                 clickEvent:'mousedown',
45944                 text: this.labels.clear
45945             },
45946             {
45947                  xtype : 'Fill',
45948                  xns: Roo.Toolbar
45949             }, 
45950             {
45951                 cls : '  x-signature-btn x-signature-'+id,
45952                 scope: editor, // was editor...
45953                 handler: this.confirmHandler,
45954                 clickEvent:'mousedown',
45955                 text: this.labels.confirm
45956             }
45957         );
45958     
45959     },
45960     //public
45961     /**
45962      * when user is clicked confirm then show this image.....
45963      * 
45964      * @return {String} Image Data URI
45965      */
45966     getImageDataURI : function(){
45967         var svg = this.svgEl.dom.parentNode.innerHTML;
45968         var src = 'data:image/svg+xml;base64,'+window.btoa(svg);
45969         return src; 
45970     },
45971     /**
45972      * 
45973      * @return {Boolean} this.isConfirmed
45974      */
45975     getConfirmed : function(){
45976         return this.isConfirmed;
45977     },
45978     /**
45979      * 
45980      * @return {Number} this.width
45981      */
45982     getWidth : function(){
45983         return this.width;
45984     },
45985     /**
45986      * 
45987      * @return {Number} this.height
45988      */
45989     getHeight : function(){
45990         return this.height;
45991     },
45992     // private
45993     getSignature : function(){
45994         return this.signatureTmp;
45995     },
45996     // private
45997     reset : function(){
45998         this.signatureTmp = '';
45999         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
46000         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', '');
46001         this.isConfirmed = false;
46002         Roo.form.Signature.superclass.reset.call(this);
46003     },
46004     setSignature : function(s){
46005         this.signatureTmp = s;
46006         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
46007         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', s);
46008         this.setValue(s);
46009         this.isConfirmed = false;
46010         Roo.form.Signature.superclass.reset.call(this);
46011     }, 
46012     test : function(){
46013 //        Roo.log(this.signPanel.dom.contentWindow.up())
46014     },
46015     //private
46016     setConfirmed : function(){
46017         
46018         
46019         
46020 //        Roo.log(Roo.get(this.signPanel.dom.contentWindow.r).attr('fill', '#cfc'));
46021     },
46022     // private
46023     confirmHandler : function(){
46024         if(!this.getSignature()){
46025             return;
46026         }
46027         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#cfc');
46028         this.setValue(this.getSignature());
46029         this.isConfirmed = true;
46030         
46031         Roo.log('in confirm clicked');
46032 //        
46033 //        var valid = true;
46034 //        this.items.each(function(f){
46035 //            if(!f.isValid(true)){
46036 //                valid = false;
46037 //                return false;
46038 //            }
46039 //        });
46040 //        for(var i = 0, len = this.buttons.length; i < len; i++){
46041 //            var btn = this.buttons[i];
46042 //            if(btn.formBind === true && btn.disabled === valid){
46043 //                btn.setDisabled(!valid);
46044 //            }
46045 //        }
46046         this.fireEvent('confirm', this);
46047     },
46048     // private
46049     // Subclasses should provide the validation implementation by overriding this
46050     validateValue : function(value){
46051         if(this.allowBlank){
46052             return true;
46053         }
46054         
46055         if(this.isConfirmed){
46056             return true;
46057         }
46058         return false;
46059     }
46060 });//<script type="text/javasscript">
46061  
46062
46063 /**
46064  * @class Roo.DDView
46065  * A DnD enabled version of Roo.View.
46066  * @param {Element/String} container The Element in which to create the View.
46067  * @param {String} tpl The template string used to create the markup for each element of the View
46068  * @param {Object} config The configuration properties. These include all the config options of
46069  * {@link Roo.View} plus some specific to this class.<br>
46070  * <p>
46071  * Drag/drop is implemented by adding {@link Roo.data.Record}s to the target DDView. If copying is
46072  * not being performed, the original {@link Roo.data.Record} is removed from the source DDView.<br>
46073  * <p>
46074  * The following extra CSS rules are needed to provide insertion point highlighting:<pre><code>
46075 .x-view-drag-insert-above {
46076         border-top:1px dotted #3366cc;
46077 }
46078 .x-view-drag-insert-below {
46079         border-bottom:1px dotted #3366cc;
46080 }
46081 </code></pre>
46082  * 
46083  */
46084  
46085 Roo.DDView = function(container, tpl, config) {
46086     Roo.DDView.superclass.constructor.apply(this, arguments);
46087     this.getEl().setStyle("outline", "0px none");
46088     this.getEl().unselectable();
46089     if (this.dragGroup) {
46090                 this.setDraggable(this.dragGroup.split(","));
46091     }
46092     if (this.dropGroup) {
46093                 this.setDroppable(this.dropGroup.split(","));
46094     }
46095     if (this.deletable) {
46096         this.setDeletable();
46097     }
46098     this.isDirtyFlag = false;
46099         this.addEvents({
46100                 "drop" : true
46101         });
46102 };
46103
46104 Roo.extend(Roo.DDView, Roo.View, {
46105 /**     @cfg {String/Array} dragGroup The ddgroup name(s) for the View's DragZone. */
46106 /**     @cfg {String/Array} dropGroup The ddgroup name(s) for the View's DropZone. */
46107 /**     @cfg {Boolean} copy Causes drag operations to copy nodes rather than move. */
46108 /**     @cfg {Boolean} allowCopy Causes ctrl/drag operations to copy nodes rather than move. */
46109
46110         isFormField: true,
46111
46112         reset: Roo.emptyFn,
46113         
46114         clearInvalid: Roo.form.Field.prototype.clearInvalid,
46115
46116         validate: function() {
46117                 return true;
46118         },
46119         
46120         destroy: function() {
46121                 this.purgeListeners();
46122                 this.getEl.removeAllListeners();
46123                 this.getEl().remove();
46124                 if (this.dragZone) {
46125                         if (this.dragZone.destroy) {
46126                                 this.dragZone.destroy();
46127                         }
46128                 }
46129                 if (this.dropZone) {
46130                         if (this.dropZone.destroy) {
46131                                 this.dropZone.destroy();
46132                         }
46133                 }
46134         },
46135
46136 /**     Allows this class to be an Roo.form.Field so it can be found using {@link Roo.form.BasicForm#findField}. */
46137         getName: function() {
46138                 return this.name;
46139         },
46140
46141 /**     Loads the View from a JSON string representing the Records to put into the Store. */
46142         setValue: function(v) {
46143                 if (!this.store) {
46144                         throw "DDView.setValue(). DDView must be constructed with a valid Store";
46145                 }
46146                 var data = {};
46147                 data[this.store.reader.meta.root] = v ? [].concat(v) : [];
46148                 this.store.proxy = new Roo.data.MemoryProxy(data);
46149                 this.store.load();
46150         },
46151
46152 /**     @return {String} a parenthesised list of the ids of the Records in the View. */
46153         getValue: function() {
46154                 var result = '(';
46155                 this.store.each(function(rec) {
46156                         result += rec.id + ',';
46157                 });
46158                 return result.substr(0, result.length - 1) + ')';
46159         },
46160         
46161         getIds: function() {
46162                 var i = 0, result = new Array(this.store.getCount());
46163                 this.store.each(function(rec) {
46164                         result[i++] = rec.id;
46165                 });
46166                 return result;
46167         },
46168         
46169         isDirty: function() {
46170                 return this.isDirtyFlag;
46171         },
46172
46173 /**
46174  *      Part of the Roo.dd.DropZone interface. If no target node is found, the
46175  *      whole Element becomes the target, and this causes the drop gesture to append.
46176  */
46177     getTargetFromEvent : function(e) {
46178                 var target = e.getTarget();
46179                 while ((target !== null) && (target.parentNode != this.el.dom)) {
46180                 target = target.parentNode;
46181                 }
46182                 if (!target) {
46183                         target = this.el.dom.lastChild || this.el.dom;
46184                 }
46185                 return target;
46186     },
46187
46188 /**
46189  *      Create the drag data which consists of an object which has the property "ddel" as
46190  *      the drag proxy element. 
46191  */
46192     getDragData : function(e) {
46193         var target = this.findItemFromChild(e.getTarget());
46194                 if(target) {
46195                         this.handleSelection(e);
46196                         var selNodes = this.getSelectedNodes();
46197             var dragData = {
46198                 source: this,
46199                 copy: this.copy || (this.allowCopy && e.ctrlKey),
46200                 nodes: selNodes,
46201                 records: []
46202                         };
46203                         var selectedIndices = this.getSelectedIndexes();
46204                         for (var i = 0; i < selectedIndices.length; i++) {
46205                                 dragData.records.push(this.store.getAt(selectedIndices[i]));
46206                         }
46207                         if (selNodes.length == 1) {
46208                                 dragData.ddel = target.cloneNode(true); // the div element
46209                         } else {
46210                                 var div = document.createElement('div'); // create the multi element drag "ghost"
46211                                 div.className = 'multi-proxy';
46212                                 for (var i = 0, len = selNodes.length; i < len; i++) {
46213                                         div.appendChild(selNodes[i].cloneNode(true));
46214                                 }
46215                                 dragData.ddel = div;
46216                         }
46217             //console.log(dragData)
46218             //console.log(dragData.ddel.innerHTML)
46219                         return dragData;
46220                 }
46221         //console.log('nodragData')
46222                 return false;
46223     },
46224     
46225 /**     Specify to which ddGroup items in this DDView may be dragged. */
46226     setDraggable: function(ddGroup) {
46227         if (ddGroup instanceof Array) {
46228                 Roo.each(ddGroup, this.setDraggable, this);
46229                 return;
46230         }
46231         if (this.dragZone) {
46232                 this.dragZone.addToGroup(ddGroup);
46233         } else {
46234                         this.dragZone = new Roo.dd.DragZone(this.getEl(), {
46235                                 containerScroll: true,
46236                                 ddGroup: ddGroup 
46237
46238                         });
46239 //                      Draggability implies selection. DragZone's mousedown selects the element.
46240                         if (!this.multiSelect) { this.singleSelect = true; }
46241
46242 //                      Wire the DragZone's handlers up to methods in *this*
46243                         this.dragZone.getDragData = this.getDragData.createDelegate(this);
46244                 }
46245     },
46246
46247 /**     Specify from which ddGroup this DDView accepts drops. */
46248     setDroppable: function(ddGroup) {
46249         if (ddGroup instanceof Array) {
46250                 Roo.each(ddGroup, this.setDroppable, this);
46251                 return;
46252         }
46253         if (this.dropZone) {
46254                 this.dropZone.addToGroup(ddGroup);
46255         } else {
46256                         this.dropZone = new Roo.dd.DropZone(this.getEl(), {
46257                                 containerScroll: true,
46258                                 ddGroup: ddGroup
46259                         });
46260
46261 //                      Wire the DropZone's handlers up to methods in *this*
46262                         this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
46263                         this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
46264                         this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
46265                         this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
46266                         this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
46267                 }
46268     },
46269
46270 /**     Decide whether to drop above or below a View node. */
46271     getDropPoint : function(e, n, dd){
46272         if (n == this.el.dom) { return "above"; }
46273                 var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
46274                 var c = t + (b - t) / 2;
46275                 var y = Roo.lib.Event.getPageY(e);
46276                 if(y <= c) {
46277                         return "above";
46278                 }else{
46279                         return "below";
46280                 }
46281     },
46282
46283     onNodeEnter : function(n, dd, e, data){
46284                 return false;
46285     },
46286     
46287     onNodeOver : function(n, dd, e, data){
46288                 var pt = this.getDropPoint(e, n, dd);
46289                 // set the insert point style on the target node
46290                 var dragElClass = this.dropNotAllowed;
46291                 if (pt) {
46292                         var targetElClass;
46293                         if (pt == "above"){
46294                                 dragElClass = n.previousSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-above";
46295                                 targetElClass = "x-view-drag-insert-above";
46296                         } else {
46297                                 dragElClass = n.nextSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-below";
46298                                 targetElClass = "x-view-drag-insert-below";
46299                         }
46300                         if (this.lastInsertClass != targetElClass){
46301                                 Roo.fly(n).replaceClass(this.lastInsertClass, targetElClass);
46302                                 this.lastInsertClass = targetElClass;
46303                         }
46304                 }
46305                 return dragElClass;
46306         },
46307
46308     onNodeOut : function(n, dd, e, data){
46309                 this.removeDropIndicators(n);
46310     },
46311
46312     onNodeDrop : function(n, dd, e, data){
46313         if (this.fireEvent("drop", this, n, dd, e, data) === false) {
46314                 return false;
46315         }
46316         var pt = this.getDropPoint(e, n, dd);
46317                 var insertAt = (n == this.el.dom) ? this.nodes.length : n.nodeIndex;
46318                 if (pt == "below") { insertAt++; }
46319                 for (var i = 0; i < data.records.length; i++) {
46320                         var r = data.records[i];
46321                         var dup = this.store.getById(r.id);
46322                         if (dup && (dd != this.dragZone)) {
46323                                 Roo.fly(this.getNode(this.store.indexOf(dup))).frame("red", 1);
46324                         } else {
46325                                 if (data.copy) {
46326                                         this.store.insert(insertAt++, r.copy());
46327                                 } else {
46328                                         data.source.isDirtyFlag = true;
46329                                         r.store.remove(r);
46330                                         this.store.insert(insertAt++, r);
46331                                 }
46332                                 this.isDirtyFlag = true;
46333                         }
46334                 }
46335                 this.dragZone.cachedTarget = null;
46336                 return true;
46337     },
46338
46339     removeDropIndicators : function(n){
46340                 if(n){
46341                         Roo.fly(n).removeClass([
46342                                 "x-view-drag-insert-above",
46343                                 "x-view-drag-insert-below"]);
46344                         this.lastInsertClass = "_noclass";
46345                 }
46346     },
46347
46348 /**
46349  *      Utility method. Add a delete option to the DDView's context menu.
46350  *      @param {String} imageUrl The URL of the "delete" icon image.
46351  */
46352         setDeletable: function(imageUrl) {
46353                 if (!this.singleSelect && !this.multiSelect) {
46354                         this.singleSelect = true;
46355                 }
46356                 var c = this.getContextMenu();
46357                 this.contextMenu.on("itemclick", function(item) {
46358                         switch (item.id) {
46359                                 case "delete":
46360                                         this.remove(this.getSelectedIndexes());
46361                                         break;
46362                         }
46363                 }, this);
46364                 this.contextMenu.add({
46365                         icon: imageUrl,
46366                         id: "delete",
46367                         text: 'Delete'
46368                 });
46369         },
46370         
46371 /**     Return the context menu for this DDView. */
46372         getContextMenu: function() {
46373                 if (!this.contextMenu) {
46374 //                      Create the View's context menu
46375                         this.contextMenu = new Roo.menu.Menu({
46376                                 id: this.id + "-contextmenu"
46377                         });
46378                         this.el.on("contextmenu", this.showContextMenu, this);
46379                 }
46380                 return this.contextMenu;
46381         },
46382         
46383         disableContextMenu: function() {
46384                 if (this.contextMenu) {
46385                         this.el.un("contextmenu", this.showContextMenu, this);
46386                 }
46387         },
46388
46389         showContextMenu: function(e, item) {
46390         item = this.findItemFromChild(e.getTarget());
46391                 if (item) {
46392                         e.stopEvent();
46393                         this.select(this.getNode(item), this.multiSelect && e.ctrlKey, true);
46394                         this.contextMenu.showAt(e.getXY());
46395             }
46396     },
46397
46398 /**
46399  *      Remove {@link Roo.data.Record}s at the specified indices.
46400  *      @param {Array/Number} selectedIndices The index (or Array of indices) of Records to remove.
46401  */
46402     remove: function(selectedIndices) {
46403                 selectedIndices = [].concat(selectedIndices);
46404                 for (var i = 0; i < selectedIndices.length; i++) {
46405                         var rec = this.store.getAt(selectedIndices[i]);
46406                         this.store.remove(rec);
46407                 }
46408     },
46409
46410 /**
46411  *      Double click fires the event, but also, if this is draggable, and there is only one other
46412  *      related DropZone, it transfers the selected node.
46413  */
46414     onDblClick : function(e){
46415         var item = this.findItemFromChild(e.getTarget());
46416         if(item){
46417             if (this.fireEvent("dblclick", this, this.indexOf(item), item, e) === false) {
46418                 return false;
46419             }
46420             if (this.dragGroup) {
46421                     var targets = Roo.dd.DragDropMgr.getRelated(this.dragZone, true);
46422                     while (targets.indexOf(this.dropZone) > -1) {
46423                             targets.remove(this.dropZone);
46424                                 }
46425                     if (targets.length == 1) {
46426                                         this.dragZone.cachedTarget = null;
46427                         var el = Roo.get(targets[0].getEl());
46428                         var box = el.getBox(true);
46429                         targets[0].onNodeDrop(el.dom, {
46430                                 target: el.dom,
46431                                 xy: [box.x, box.y + box.height - 1]
46432                         }, null, this.getDragData(e));
46433                     }
46434                 }
46435         }
46436     },
46437     
46438     handleSelection: function(e) {
46439                 this.dragZone.cachedTarget = null;
46440         var item = this.findItemFromChild(e.getTarget());
46441         if (!item) {
46442                 this.clearSelections(true);
46443                 return;
46444         }
46445                 if (item && (this.multiSelect || this.singleSelect)){
46446                         if(this.multiSelect && e.shiftKey && (!e.ctrlKey) && this.lastSelection){
46447                                 this.select(this.getNodes(this.indexOf(this.lastSelection), item.nodeIndex), false);
46448                         }else if (this.isSelected(this.getNode(item)) && e.ctrlKey){
46449                                 this.unselect(item);
46450                         } else {
46451                                 this.select(item, this.multiSelect && e.ctrlKey);
46452                                 this.lastSelection = item;
46453                         }
46454                 }
46455     },
46456
46457     onItemClick : function(item, index, e){
46458                 if(this.fireEvent("beforeclick", this, index, item, e) === false){
46459                         return false;
46460                 }
46461                 return true;
46462     },
46463
46464     unselect : function(nodeInfo, suppressEvent){
46465                 var node = this.getNode(nodeInfo);
46466                 if(node && this.isSelected(node)){
46467                         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
46468                                 Roo.fly(node).removeClass(this.selectedClass);
46469                                 this.selections.remove(node);
46470                                 if(!suppressEvent){
46471                                         this.fireEvent("selectionchange", this, this.selections);
46472                                 }
46473                         }
46474                 }
46475     }
46476 });
46477 /*
46478  * Based on:
46479  * Ext JS Library 1.1.1
46480  * Copyright(c) 2006-2007, Ext JS, LLC.
46481  *
46482  * Originally Released Under LGPL - original licence link has changed is not relivant.
46483  *
46484  * Fork - LGPL
46485  * <script type="text/javascript">
46486  */
46487  
46488 /**
46489  * @class Roo.LayoutManager
46490  * @extends Roo.util.Observable
46491  * Base class for layout managers.
46492  */
46493 Roo.LayoutManager = function(container, config){
46494     Roo.LayoutManager.superclass.constructor.call(this);
46495     this.el = Roo.get(container);
46496     // ie scrollbar fix
46497     if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
46498         document.body.scroll = "no";
46499     }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
46500         this.el.position('relative');
46501     }
46502     this.id = this.el.id;
46503     this.el.addClass("x-layout-container");
46504     /** false to disable window resize monitoring @type Boolean */
46505     this.monitorWindowResize = true;
46506     this.regions = {};
46507     this.addEvents({
46508         /**
46509          * @event layout
46510          * Fires when a layout is performed. 
46511          * @param {Roo.LayoutManager} this
46512          */
46513         "layout" : true,
46514         /**
46515          * @event regionresized
46516          * Fires when the user resizes a region. 
46517          * @param {Roo.LayoutRegion} region The resized region
46518          * @param {Number} newSize The new size (width for east/west, height for north/south)
46519          */
46520         "regionresized" : true,
46521         /**
46522          * @event regioncollapsed
46523          * Fires when a region is collapsed. 
46524          * @param {Roo.LayoutRegion} region The collapsed region
46525          */
46526         "regioncollapsed" : true,
46527         /**
46528          * @event regionexpanded
46529          * Fires when a region is expanded.  
46530          * @param {Roo.LayoutRegion} region The expanded region
46531          */
46532         "regionexpanded" : true
46533     });
46534     this.updating = false;
46535     Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
46536 };
46537
46538 Roo.extend(Roo.LayoutManager, Roo.util.Observable, {
46539     /**
46540      * Returns true if this layout is currently being updated
46541      * @return {Boolean}
46542      */
46543     isUpdating : function(){
46544         return this.updating; 
46545     },
46546     
46547     /**
46548      * Suspend the LayoutManager from doing auto-layouts while
46549      * making multiple add or remove calls
46550      */
46551     beginUpdate : function(){
46552         this.updating = true;    
46553     },
46554     
46555     /**
46556      * Restore auto-layouts and optionally disable the manager from performing a layout
46557      * @param {Boolean} noLayout true to disable a layout update 
46558      */
46559     endUpdate : function(noLayout){
46560         this.updating = false;
46561         if(!noLayout){
46562             this.layout();
46563         }    
46564     },
46565     
46566     layout: function(){
46567         
46568     },
46569     
46570     onRegionResized : function(region, newSize){
46571         this.fireEvent("regionresized", region, newSize);
46572         this.layout();
46573     },
46574     
46575     onRegionCollapsed : function(region){
46576         this.fireEvent("regioncollapsed", region);
46577     },
46578     
46579     onRegionExpanded : function(region){
46580         this.fireEvent("regionexpanded", region);
46581     },
46582         
46583     /**
46584      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
46585      * performs box-model adjustments.
46586      * @return {Object} The size as an object {width: (the width), height: (the height)}
46587      */
46588     getViewSize : function(){
46589         var size;
46590         if(this.el.dom != document.body){
46591             size = this.el.getSize();
46592         }else{
46593             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
46594         }
46595         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
46596         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
46597         return size;
46598     },
46599     
46600     /**
46601      * Returns the Element this layout is bound to.
46602      * @return {Roo.Element}
46603      */
46604     getEl : function(){
46605         return this.el;
46606     },
46607     
46608     /**
46609      * Returns the specified region.
46610      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
46611      * @return {Roo.LayoutRegion}
46612      */
46613     getRegion : function(target){
46614         return this.regions[target.toLowerCase()];
46615     },
46616     
46617     onWindowResize : function(){
46618         if(this.monitorWindowResize){
46619             this.layout();
46620         }
46621     }
46622 });/*
46623  * Based on:
46624  * Ext JS Library 1.1.1
46625  * Copyright(c) 2006-2007, Ext JS, LLC.
46626  *
46627  * Originally Released Under LGPL - original licence link has changed is not relivant.
46628  *
46629  * Fork - LGPL
46630  * <script type="text/javascript">
46631  */
46632 /**
46633  * @class Roo.BorderLayout
46634  * @extends Roo.LayoutManager
46635  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
46636  * please see: <br><br>
46637  * <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>
46638  * <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>
46639  * Example:
46640  <pre><code>
46641  var layout = new Roo.BorderLayout(document.body, {
46642     north: {
46643         initialSize: 25,
46644         titlebar: false
46645     },
46646     west: {
46647         split:true,
46648         initialSize: 200,
46649         minSize: 175,
46650         maxSize: 400,
46651         titlebar: true,
46652         collapsible: true
46653     },
46654     east: {
46655         split:true,
46656         initialSize: 202,
46657         minSize: 175,
46658         maxSize: 400,
46659         titlebar: true,
46660         collapsible: true
46661     },
46662     south: {
46663         split:true,
46664         initialSize: 100,
46665         minSize: 100,
46666         maxSize: 200,
46667         titlebar: true,
46668         collapsible: true
46669     },
46670     center: {
46671         titlebar: true,
46672         autoScroll:true,
46673         resizeTabs: true,
46674         minTabWidth: 50,
46675         preferredTabWidth: 150
46676     }
46677 });
46678
46679 // shorthand
46680 var CP = Roo.ContentPanel;
46681
46682 layout.beginUpdate();
46683 layout.add("north", new CP("north", "North"));
46684 layout.add("south", new CP("south", {title: "South", closable: true}));
46685 layout.add("west", new CP("west", {title: "West"}));
46686 layout.add("east", new CP("autoTabs", {title: "Auto Tabs", closable: true}));
46687 layout.add("center", new CP("center1", {title: "Close Me", closable: true}));
46688 layout.add("center", new CP("center2", {title: "Center Panel", closable: false}));
46689 layout.getRegion("center").showPanel("center1");
46690 layout.endUpdate();
46691 </code></pre>
46692
46693 <b>The container the layout is rendered into can be either the body element or any other element.
46694 If it is not the body element, the container needs to either be an absolute positioned element,
46695 or you will need to add "position:relative" to the css of the container.  You will also need to specify
46696 the container size if it is not the body element.</b>
46697
46698 * @constructor
46699 * Create a new BorderLayout
46700 * @param {String/HTMLElement/Element} container The container this layout is bound to
46701 * @param {Object} config Configuration options
46702  */
46703 Roo.BorderLayout = function(container, config){
46704     config = config || {};
46705     Roo.BorderLayout.superclass.constructor.call(this, container, config);
46706     this.factory = config.factory || Roo.BorderLayout.RegionFactory;
46707     for(var i = 0, len = this.factory.validRegions.length; i < len; i++) {
46708         var target = this.factory.validRegions[i];
46709         if(config[target]){
46710             this.addRegion(target, config[target]);
46711         }
46712     }
46713 };
46714
46715 Roo.extend(Roo.BorderLayout, Roo.LayoutManager, {
46716     /**
46717      * Creates and adds a new region if it doesn't already exist.
46718      * @param {String} target The target region key (north, south, east, west or center).
46719      * @param {Object} config The regions config object
46720      * @return {BorderLayoutRegion} The new region
46721      */
46722     addRegion : function(target, config){
46723         if(!this.regions[target]){
46724             var r = this.factory.create(target, this, config);
46725             this.bindRegion(target, r);
46726         }
46727         return this.regions[target];
46728     },
46729
46730     // private (kinda)
46731     bindRegion : function(name, r){
46732         this.regions[name] = r;
46733         r.on("visibilitychange", this.layout, this);
46734         r.on("paneladded", this.layout, this);
46735         r.on("panelremoved", this.layout, this);
46736         r.on("invalidated", this.layout, this);
46737         r.on("resized", this.onRegionResized, this);
46738         r.on("collapsed", this.onRegionCollapsed, this);
46739         r.on("expanded", this.onRegionExpanded, this);
46740     },
46741
46742     /**
46743      * Performs a layout update.
46744      */
46745     layout : function(){
46746         if(this.updating) return;
46747         var size = this.getViewSize();
46748         var w = size.width;
46749         var h = size.height;
46750         var centerW = w;
46751         var centerH = h;
46752         var centerY = 0;
46753         var centerX = 0;
46754         //var x = 0, y = 0;
46755
46756         var rs = this.regions;
46757         var north = rs["north"];
46758         var south = rs["south"]; 
46759         var west = rs["west"];
46760         var east = rs["east"];
46761         var center = rs["center"];
46762         //if(this.hideOnLayout){ // not supported anymore
46763             //c.el.setStyle("display", "none");
46764         //}
46765         if(north && north.isVisible()){
46766             var b = north.getBox();
46767             var m = north.getMargins();
46768             b.width = w - (m.left+m.right);
46769             b.x = m.left;
46770             b.y = m.top;
46771             centerY = b.height + b.y + m.bottom;
46772             centerH -= centerY;
46773             north.updateBox(this.safeBox(b));
46774         }
46775         if(south && south.isVisible()){
46776             var b = south.getBox();
46777             var m = south.getMargins();
46778             b.width = w - (m.left+m.right);
46779             b.x = m.left;
46780             var totalHeight = (b.height + m.top + m.bottom);
46781             b.y = h - totalHeight + m.top;
46782             centerH -= totalHeight;
46783             south.updateBox(this.safeBox(b));
46784         }
46785         if(west && west.isVisible()){
46786             var b = west.getBox();
46787             var m = west.getMargins();
46788             b.height = centerH - (m.top+m.bottom);
46789             b.x = m.left;
46790             b.y = centerY + m.top;
46791             var totalWidth = (b.width + m.left + m.right);
46792             centerX += totalWidth;
46793             centerW -= totalWidth;
46794             west.updateBox(this.safeBox(b));
46795         }
46796         if(east && east.isVisible()){
46797             var b = east.getBox();
46798             var m = east.getMargins();
46799             b.height = centerH - (m.top+m.bottom);
46800             var totalWidth = (b.width + m.left + m.right);
46801             b.x = w - totalWidth + m.left;
46802             b.y = centerY + m.top;
46803             centerW -= totalWidth;
46804             east.updateBox(this.safeBox(b));
46805         }
46806         if(center){
46807             var m = center.getMargins();
46808             var centerBox = {
46809                 x: centerX + m.left,
46810                 y: centerY + m.top,
46811                 width: centerW - (m.left+m.right),
46812                 height: centerH - (m.top+m.bottom)
46813             };
46814             //if(this.hideOnLayout){
46815                 //center.el.setStyle("display", "block");
46816             //}
46817             center.updateBox(this.safeBox(centerBox));
46818         }
46819         this.el.repaint();
46820         this.fireEvent("layout", this);
46821     },
46822
46823     // private
46824     safeBox : function(box){
46825         box.width = Math.max(0, box.width);
46826         box.height = Math.max(0, box.height);
46827         return box;
46828     },
46829
46830     /**
46831      * Adds a ContentPanel (or subclass) to this layout.
46832      * @param {String} target The target region key (north, south, east, west or center).
46833      * @param {Roo.ContentPanel} panel The panel to add
46834      * @return {Roo.ContentPanel} The added panel
46835      */
46836     add : function(target, panel){
46837          
46838         target = target.toLowerCase();
46839         return this.regions[target].add(panel);
46840     },
46841
46842     /**
46843      * Remove a ContentPanel (or subclass) to this layout.
46844      * @param {String} target The target region key (north, south, east, west or center).
46845      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
46846      * @return {Roo.ContentPanel} The removed panel
46847      */
46848     remove : function(target, panel){
46849         target = target.toLowerCase();
46850         return this.regions[target].remove(panel);
46851     },
46852
46853     /**
46854      * Searches all regions for a panel with the specified id
46855      * @param {String} panelId
46856      * @return {Roo.ContentPanel} The panel or null if it wasn't found
46857      */
46858     findPanel : function(panelId){
46859         var rs = this.regions;
46860         for(var target in rs){
46861             if(typeof rs[target] != "function"){
46862                 var p = rs[target].getPanel(panelId);
46863                 if(p){
46864                     return p;
46865                 }
46866             }
46867         }
46868         return null;
46869     },
46870
46871     /**
46872      * Searches all regions for a panel with the specified id and activates (shows) it.
46873      * @param {String/ContentPanel} panelId The panels id or the panel itself
46874      * @return {Roo.ContentPanel} The shown panel or null
46875      */
46876     showPanel : function(panelId) {
46877       var rs = this.regions;
46878       for(var target in rs){
46879          var r = rs[target];
46880          if(typeof r != "function"){
46881             if(r.hasPanel(panelId)){
46882                return r.showPanel(panelId);
46883             }
46884          }
46885       }
46886       return null;
46887    },
46888
46889    /**
46890      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
46891      * @param {Roo.state.Provider} provider (optional) An alternate state provider
46892      */
46893     restoreState : function(provider){
46894         if(!provider){
46895             provider = Roo.state.Manager;
46896         }
46897         var sm = new Roo.LayoutStateManager();
46898         sm.init(this, provider);
46899     },
46900
46901     /**
46902      * Adds a batch of multiple ContentPanels dynamically by passing a special regions config object.  This config
46903      * object should contain properties for each region to add ContentPanels to, and each property's value should be
46904      * a valid ContentPanel config object.  Example:
46905      * <pre><code>
46906 // Create the main layout
46907 var layout = new Roo.BorderLayout('main-ct', {
46908     west: {
46909         split:true,
46910         minSize: 175,
46911         titlebar: true
46912     },
46913     center: {
46914         title:'Components'
46915     }
46916 }, 'main-ct');
46917
46918 // Create and add multiple ContentPanels at once via configs
46919 layout.batchAdd({
46920    west: {
46921        id: 'source-files',
46922        autoCreate:true,
46923        title:'Ext Source Files',
46924        autoScroll:true,
46925        fitToFrame:true
46926    },
46927    center : {
46928        el: cview,
46929        autoScroll:true,
46930        fitToFrame:true,
46931        toolbar: tb,
46932        resizeEl:'cbody'
46933    }
46934 });
46935 </code></pre>
46936      * @param {Object} regions An object containing ContentPanel configs by region name
46937      */
46938     batchAdd : function(regions){
46939         this.beginUpdate();
46940         for(var rname in regions){
46941             var lr = this.regions[rname];
46942             if(lr){
46943                 this.addTypedPanels(lr, regions[rname]);
46944             }
46945         }
46946         this.endUpdate();
46947     },
46948
46949     // private
46950     addTypedPanels : function(lr, ps){
46951         if(typeof ps == 'string'){
46952             lr.add(new Roo.ContentPanel(ps));
46953         }
46954         else if(ps instanceof Array){
46955             for(var i =0, len = ps.length; i < len; i++){
46956                 this.addTypedPanels(lr, ps[i]);
46957             }
46958         }
46959         else if(!ps.events){ // raw config?
46960             var el = ps.el;
46961             delete ps.el; // prevent conflict
46962             lr.add(new Roo.ContentPanel(el || Roo.id(), ps));
46963         }
46964         else {  // panel object assumed!
46965             lr.add(ps);
46966         }
46967     },
46968     /**
46969      * Adds a xtype elements to the layout.
46970      * <pre><code>
46971
46972 layout.addxtype({
46973        xtype : 'ContentPanel',
46974        region: 'west',
46975        items: [ .... ]
46976    }
46977 );
46978
46979 layout.addxtype({
46980         xtype : 'NestedLayoutPanel',
46981         region: 'west',
46982         layout: {
46983            center: { },
46984            west: { }   
46985         },
46986         items : [ ... list of content panels or nested layout panels.. ]
46987    }
46988 );
46989 </code></pre>
46990      * @param {Object} cfg Xtype definition of item to add.
46991      */
46992     addxtype : function(cfg)
46993     {
46994         // basically accepts a pannel...
46995         // can accept a layout region..!?!?
46996         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
46997         
46998         if (!cfg.xtype.match(/Panel$/)) {
46999             return false;
47000         }
47001         var ret = false;
47002         
47003         if (typeof(cfg.region) == 'undefined') {
47004             Roo.log("Failed to add Panel, region was not set");
47005             Roo.log(cfg);
47006             return false;
47007         }
47008         var region = cfg.region;
47009         delete cfg.region;
47010         
47011           
47012         var xitems = [];
47013         if (cfg.items) {
47014             xitems = cfg.items;
47015             delete cfg.items;
47016         }
47017         var nb = false;
47018         
47019         switch(cfg.xtype) 
47020         {
47021             case 'ContentPanel':  // ContentPanel (el, cfg)
47022             case 'ScrollPanel':  // ContentPanel (el, cfg)
47023             case 'ViewPanel': 
47024                 if(cfg.autoCreate) {
47025                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
47026                 } else {
47027                     var el = this.el.createChild();
47028                     ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
47029                 }
47030                 
47031                 this.add(region, ret);
47032                 break;
47033             
47034             
47035             case 'TreePanel': // our new panel!
47036                 cfg.el = this.el.createChild();
47037                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
47038                 this.add(region, ret);
47039                 break;
47040             
47041             case 'NestedLayoutPanel': 
47042                 // create a new Layout (which is  a Border Layout...
47043                 var el = this.el.createChild();
47044                 var clayout = cfg.layout;
47045                 delete cfg.layout;
47046                 clayout.items   = clayout.items  || [];
47047                 // replace this exitems with the clayout ones..
47048                 xitems = clayout.items;
47049                  
47050                 
47051                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
47052                     cfg.background = false;
47053                 }
47054                 var layout = new Roo.BorderLayout(el, clayout);
47055                 
47056                 ret = new Roo[cfg.xtype](layout, cfg); // new panel!!!!!
47057                 //console.log('adding nested layout panel '  + cfg.toSource());
47058                 this.add(region, ret);
47059                 nb = {}; /// find first...
47060                 break;
47061                 
47062             case 'GridPanel': 
47063             
47064                 // needs grid and region
47065                 
47066                 //var el = this.getRegion(region).el.createChild();
47067                 var el = this.el.createChild();
47068                 // create the grid first...
47069                 
47070                 var grid = new Roo.grid[cfg.grid.xtype](el, cfg.grid);
47071                 delete cfg.grid;
47072                 if (region == 'center' && this.active ) {
47073                     cfg.background = false;
47074                 }
47075                 ret = new Roo[cfg.xtype](grid, cfg); // new panel!!!!!
47076                 
47077                 this.add(region, ret);
47078                 if (cfg.background) {
47079                     ret.on('activate', function(gp) {
47080                         if (!gp.grid.rendered) {
47081                             gp.grid.render();
47082                         }
47083                     });
47084                 } else {
47085                     grid.render();
47086                 }
47087                 break;
47088            
47089            
47090            
47091                 
47092                 
47093                 
47094             default: 
47095                 alert("Can not add '" + cfg.xtype + "' to BorderLayout");
47096                 return null;
47097              // GridPanel (grid, cfg)
47098             
47099         }
47100         this.beginUpdate();
47101         // add children..
47102         var region = '';
47103         var abn = {};
47104         Roo.each(xitems, function(i)  {
47105             region = nb && i.region ? i.region : false;
47106             
47107             var add = ret.addxtype(i);
47108            
47109             if (region) {
47110                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
47111                 if (!i.background) {
47112                     abn[region] = nb[region] ;
47113                 }
47114             }
47115             
47116         });
47117         this.endUpdate();
47118
47119         // make the last non-background panel active..
47120         //if (nb) { Roo.log(abn); }
47121         if (nb) {
47122             
47123             for(var r in abn) {
47124                 region = this.getRegion(r);
47125                 if (region) {
47126                     // tried using nb[r], but it does not work..
47127                      
47128                     region.showPanel(abn[r]);
47129                    
47130                 }
47131             }
47132         }
47133         return ret;
47134         
47135     }
47136 });
47137
47138 /**
47139  * Shortcut for creating a new BorderLayout object and adding one or more ContentPanels to it in a single step, handling
47140  * the beginUpdate and endUpdate calls internally.  The key to this method is the <b>panels</b> property that can be
47141  * provided with each region config, which allows you to add ContentPanel configs in addition to the region configs
47142  * during creation.  The following code is equivalent to the constructor-based example at the beginning of this class:
47143  * <pre><code>
47144 // shorthand
47145 var CP = Roo.ContentPanel;
47146
47147 var layout = Roo.BorderLayout.create({
47148     north: {
47149         initialSize: 25,
47150         titlebar: false,
47151         panels: [new CP("north", "North")]
47152     },
47153     west: {
47154         split:true,
47155         initialSize: 200,
47156         minSize: 175,
47157         maxSize: 400,
47158         titlebar: true,
47159         collapsible: true,
47160         panels: [new CP("west", {title: "West"})]
47161     },
47162     east: {
47163         split:true,
47164         initialSize: 202,
47165         minSize: 175,
47166         maxSize: 400,
47167         titlebar: true,
47168         collapsible: true,
47169         panels: [new CP("autoTabs", {title: "Auto Tabs", closable: true})]
47170     },
47171     south: {
47172         split:true,
47173         initialSize: 100,
47174         minSize: 100,
47175         maxSize: 200,
47176         titlebar: true,
47177         collapsible: true,
47178         panels: [new CP("south", {title: "South", closable: true})]
47179     },
47180     center: {
47181         titlebar: true,
47182         autoScroll:true,
47183         resizeTabs: true,
47184         minTabWidth: 50,
47185         preferredTabWidth: 150,
47186         panels: [
47187             new CP("center1", {title: "Close Me", closable: true}),
47188             new CP("center2", {title: "Center Panel", closable: false})
47189         ]
47190     }
47191 }, document.body);
47192
47193 layout.getRegion("center").showPanel("center1");
47194 </code></pre>
47195  * @param config
47196  * @param targetEl
47197  */
47198 Roo.BorderLayout.create = function(config, targetEl){
47199     var layout = new Roo.BorderLayout(targetEl || document.body, config);
47200     layout.beginUpdate();
47201     var regions = Roo.BorderLayout.RegionFactory.validRegions;
47202     for(var j = 0, jlen = regions.length; j < jlen; j++){
47203         var lr = regions[j];
47204         if(layout.regions[lr] && config[lr].panels){
47205             var r = layout.regions[lr];
47206             var ps = config[lr].panels;
47207             layout.addTypedPanels(r, ps);
47208         }
47209     }
47210     layout.endUpdate();
47211     return layout;
47212 };
47213
47214 // private
47215 Roo.BorderLayout.RegionFactory = {
47216     // private
47217     validRegions : ["north","south","east","west","center"],
47218
47219     // private
47220     create : function(target, mgr, config){
47221         target = target.toLowerCase();
47222         if(config.lightweight || config.basic){
47223             return new Roo.BasicLayoutRegion(mgr, config, target);
47224         }
47225         switch(target){
47226             case "north":
47227                 return new Roo.NorthLayoutRegion(mgr, config);
47228             case "south":
47229                 return new Roo.SouthLayoutRegion(mgr, config);
47230             case "east":
47231                 return new Roo.EastLayoutRegion(mgr, config);
47232             case "west":
47233                 return new Roo.WestLayoutRegion(mgr, config);
47234             case "center":
47235                 return new Roo.CenterLayoutRegion(mgr, config);
47236         }
47237         throw 'Layout region "'+target+'" not supported.';
47238     }
47239 };/*
47240  * Based on:
47241  * Ext JS Library 1.1.1
47242  * Copyright(c) 2006-2007, Ext JS, LLC.
47243  *
47244  * Originally Released Under LGPL - original licence link has changed is not relivant.
47245  *
47246  * Fork - LGPL
47247  * <script type="text/javascript">
47248  */
47249  
47250 /**
47251  * @class Roo.BasicLayoutRegion
47252  * @extends Roo.util.Observable
47253  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
47254  * and does not have a titlebar, tabs or any other features. All it does is size and position 
47255  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
47256  */
47257 Roo.BasicLayoutRegion = function(mgr, config, pos, skipConfig){
47258     this.mgr = mgr;
47259     this.position  = pos;
47260     this.events = {
47261         /**
47262          * @scope Roo.BasicLayoutRegion
47263          */
47264         
47265         /**
47266          * @event beforeremove
47267          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
47268          * @param {Roo.LayoutRegion} this
47269          * @param {Roo.ContentPanel} panel The panel
47270          * @param {Object} e The cancel event object
47271          */
47272         "beforeremove" : true,
47273         /**
47274          * @event invalidated
47275          * Fires when the layout for this region is changed.
47276          * @param {Roo.LayoutRegion} this
47277          */
47278         "invalidated" : true,
47279         /**
47280          * @event visibilitychange
47281          * Fires when this region is shown or hidden 
47282          * @param {Roo.LayoutRegion} this
47283          * @param {Boolean} visibility true or false
47284          */
47285         "visibilitychange" : true,
47286         /**
47287          * @event paneladded
47288          * Fires when a panel is added. 
47289          * @param {Roo.LayoutRegion} this
47290          * @param {Roo.ContentPanel} panel The panel
47291          */
47292         "paneladded" : true,
47293         /**
47294          * @event panelremoved
47295          * Fires when a panel is removed. 
47296          * @param {Roo.LayoutRegion} this
47297          * @param {Roo.ContentPanel} panel The panel
47298          */
47299         "panelremoved" : true,
47300         /**
47301          * @event collapsed
47302          * Fires when this region is collapsed.
47303          * @param {Roo.LayoutRegion} this
47304          */
47305         "collapsed" : true,
47306         /**
47307          * @event expanded
47308          * Fires when this region is expanded.
47309          * @param {Roo.LayoutRegion} this
47310          */
47311         "expanded" : true,
47312         /**
47313          * @event slideshow
47314          * Fires when this region is slid into view.
47315          * @param {Roo.LayoutRegion} this
47316          */
47317         "slideshow" : true,
47318         /**
47319          * @event slidehide
47320          * Fires when this region slides out of view. 
47321          * @param {Roo.LayoutRegion} this
47322          */
47323         "slidehide" : true,
47324         /**
47325          * @event panelactivated
47326          * Fires when a panel is activated. 
47327          * @param {Roo.LayoutRegion} this
47328          * @param {Roo.ContentPanel} panel The activated panel
47329          */
47330         "panelactivated" : true,
47331         /**
47332          * @event resized
47333          * Fires when the user resizes this region. 
47334          * @param {Roo.LayoutRegion} this
47335          * @param {Number} newSize The new size (width for east/west, height for north/south)
47336          */
47337         "resized" : true
47338     };
47339     /** A collection of panels in this region. @type Roo.util.MixedCollection */
47340     this.panels = new Roo.util.MixedCollection();
47341     this.panels.getKey = this.getPanelId.createDelegate(this);
47342     this.box = null;
47343     this.activePanel = null;
47344     // ensure listeners are added...
47345     
47346     if (config.listeners || config.events) {
47347         Roo.BasicLayoutRegion.superclass.constructor.call(this, {
47348             listeners : config.listeners || {},
47349             events : config.events || {}
47350         });
47351     }
47352     
47353     if(skipConfig !== true){
47354         this.applyConfig(config);
47355     }
47356 };
47357
47358 Roo.extend(Roo.BasicLayoutRegion, Roo.util.Observable, {
47359     getPanelId : function(p){
47360         return p.getId();
47361     },
47362     
47363     applyConfig : function(config){
47364         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
47365         this.config = config;
47366         
47367     },
47368     
47369     /**
47370      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
47371      * the width, for horizontal (north, south) the height.
47372      * @param {Number} newSize The new width or height
47373      */
47374     resizeTo : function(newSize){
47375         var el = this.el ? this.el :
47376                  (this.activePanel ? this.activePanel.getEl() : null);
47377         if(el){
47378             switch(this.position){
47379                 case "east":
47380                 case "west":
47381                     el.setWidth(newSize);
47382                     this.fireEvent("resized", this, newSize);
47383                 break;
47384                 case "north":
47385                 case "south":
47386                     el.setHeight(newSize);
47387                     this.fireEvent("resized", this, newSize);
47388                 break;                
47389             }
47390         }
47391     },
47392     
47393     getBox : function(){
47394         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
47395     },
47396     
47397     getMargins : function(){
47398         return this.margins;
47399     },
47400     
47401     updateBox : function(box){
47402         this.box = box;
47403         var el = this.activePanel.getEl();
47404         el.dom.style.left = box.x + "px";
47405         el.dom.style.top = box.y + "px";
47406         this.activePanel.setSize(box.width, box.height);
47407     },
47408     
47409     /**
47410      * Returns the container element for this region.
47411      * @return {Roo.Element}
47412      */
47413     getEl : function(){
47414         return this.activePanel;
47415     },
47416     
47417     /**
47418      * Returns true if this region is currently visible.
47419      * @return {Boolean}
47420      */
47421     isVisible : function(){
47422         return this.activePanel ? true : false;
47423     },
47424     
47425     setActivePanel : function(panel){
47426         panel = this.getPanel(panel);
47427         if(this.activePanel && this.activePanel != panel){
47428             this.activePanel.setActiveState(false);
47429             this.activePanel.getEl().setLeftTop(-10000,-10000);
47430         }
47431         this.activePanel = panel;
47432         panel.setActiveState(true);
47433         if(this.box){
47434             panel.setSize(this.box.width, this.box.height);
47435         }
47436         this.fireEvent("panelactivated", this, panel);
47437         this.fireEvent("invalidated");
47438     },
47439     
47440     /**
47441      * Show the specified panel.
47442      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
47443      * @return {Roo.ContentPanel} The shown panel or null
47444      */
47445     showPanel : function(panel){
47446         if(panel = this.getPanel(panel)){
47447             this.setActivePanel(panel);
47448         }
47449         return panel;
47450     },
47451     
47452     /**
47453      * Get the active panel for this region.
47454      * @return {Roo.ContentPanel} The active panel or null
47455      */
47456     getActivePanel : function(){
47457         return this.activePanel;
47458     },
47459     
47460     /**
47461      * Add the passed ContentPanel(s)
47462      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
47463      * @return {Roo.ContentPanel} The panel added (if only one was added)
47464      */
47465     add : function(panel){
47466         if(arguments.length > 1){
47467             for(var i = 0, len = arguments.length; i < len; i++) {
47468                 this.add(arguments[i]);
47469             }
47470             return null;
47471         }
47472         if(this.hasPanel(panel)){
47473             this.showPanel(panel);
47474             return panel;
47475         }
47476         var el = panel.getEl();
47477         if(el.dom.parentNode != this.mgr.el.dom){
47478             this.mgr.el.dom.appendChild(el.dom);
47479         }
47480         if(panel.setRegion){
47481             panel.setRegion(this);
47482         }
47483         this.panels.add(panel);
47484         el.setStyle("position", "absolute");
47485         if(!panel.background){
47486             this.setActivePanel(panel);
47487             if(this.config.initialSize && this.panels.getCount()==1){
47488                 this.resizeTo(this.config.initialSize);
47489             }
47490         }
47491         this.fireEvent("paneladded", this, panel);
47492         return panel;
47493     },
47494     
47495     /**
47496      * Returns true if the panel is in this region.
47497      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
47498      * @return {Boolean}
47499      */
47500     hasPanel : function(panel){
47501         if(typeof panel == "object"){ // must be panel obj
47502             panel = panel.getId();
47503         }
47504         return this.getPanel(panel) ? true : false;
47505     },
47506     
47507     /**
47508      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
47509      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
47510      * @param {Boolean} preservePanel Overrides the config preservePanel option
47511      * @return {Roo.ContentPanel} The panel that was removed
47512      */
47513     remove : function(panel, preservePanel){
47514         panel = this.getPanel(panel);
47515         if(!panel){
47516             return null;
47517         }
47518         var e = {};
47519         this.fireEvent("beforeremove", this, panel, e);
47520         if(e.cancel === true){
47521             return null;
47522         }
47523         var panelId = panel.getId();
47524         this.panels.removeKey(panelId);
47525         return panel;
47526     },
47527     
47528     /**
47529      * Returns the panel specified or null if it's not in this region.
47530      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
47531      * @return {Roo.ContentPanel}
47532      */
47533     getPanel : function(id){
47534         if(typeof id == "object"){ // must be panel obj
47535             return id;
47536         }
47537         return this.panels.get(id);
47538     },
47539     
47540     /**
47541      * Returns this regions position (north/south/east/west/center).
47542      * @return {String} 
47543      */
47544     getPosition: function(){
47545         return this.position;    
47546     }
47547 });/*
47548  * Based on:
47549  * Ext JS Library 1.1.1
47550  * Copyright(c) 2006-2007, Ext JS, LLC.
47551  *
47552  * Originally Released Under LGPL - original licence link has changed is not relivant.
47553  *
47554  * Fork - LGPL
47555  * <script type="text/javascript">
47556  */
47557  
47558 /**
47559  * @class Roo.LayoutRegion
47560  * @extends Roo.BasicLayoutRegion
47561  * This class represents a region in a layout manager.
47562  * @cfg {Boolean}   collapsible     False to disable collapsing (defaults to true)
47563  * @cfg {Boolean}   collapsed       True to set the initial display to collapsed (defaults to false)
47564  * @cfg {Boolean}   floatable       False to disable floating (defaults to true)
47565  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
47566  * @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})
47567  * @cfg {String}    tabPosition     "top" or "bottom" (defaults to "bottom")
47568  * @cfg {String}    collapsedTitle  Optional string message to display in the collapsed block of a north or south region
47569  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
47570  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
47571  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
47572  * @cfg {String}    title           The title for the region (overrides panel titles)
47573  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
47574  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
47575  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
47576  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
47577  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
47578  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
47579  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
47580  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
47581  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
47582  * @cfg {Boolean}   showPin         True to show a pin button
47583  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
47584  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
47585  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
47586  * @cfg {Number}    width           For East/West panels
47587  * @cfg {Number}    height          For North/South panels
47588  * @cfg {Boolean}   split           To show the splitter
47589  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
47590  */
47591 Roo.LayoutRegion = function(mgr, config, pos){
47592     Roo.LayoutRegion.superclass.constructor.call(this, mgr, config, pos, true);
47593     var dh = Roo.DomHelper;
47594     /** This region's container element 
47595     * @type Roo.Element */
47596     this.el = dh.append(mgr.el.dom, {tag: "div", cls: "x-layout-panel x-layout-panel-" + this.position}, true);
47597     /** This region's title element 
47598     * @type Roo.Element */
47599
47600     this.titleEl = dh.append(this.el.dom, {tag: "div", unselectable: "on", cls: "x-unselectable x-layout-panel-hd x-layout-title-"+this.position, children:[
47601         {tag: "span", cls: "x-unselectable x-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
47602         {tag: "div", cls: "x-unselectable x-layout-panel-hd-tools", unselectable: "on"}
47603     ]}, true);
47604     this.titleEl.enableDisplayMode();
47605     /** This region's title text element 
47606     * @type HTMLElement */
47607     this.titleTextEl = this.titleEl.dom.firstChild;
47608     this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
47609     this.closeBtn = this.createTool(this.tools.dom, "x-layout-close");
47610     this.closeBtn.enableDisplayMode();
47611     this.closeBtn.on("click", this.closeClicked, this);
47612     this.closeBtn.hide();
47613
47614     this.createBody(config);
47615     this.visible = true;
47616     this.collapsed = false;
47617
47618     if(config.hideWhenEmpty){
47619         this.hide();
47620         this.on("paneladded", this.validateVisibility, this);
47621         this.on("panelremoved", this.validateVisibility, this);
47622     }
47623     this.applyConfig(config);
47624 };
47625
47626 Roo.extend(Roo.LayoutRegion, Roo.BasicLayoutRegion, {
47627
47628     createBody : function(){
47629         /** This region's body element 
47630         * @type Roo.Element */
47631         this.bodyEl = this.el.createChild({tag: "div", cls: "x-layout-panel-body"});
47632     },
47633
47634     applyConfig : function(c){
47635         if(c.collapsible && this.position != "center" && !this.collapsedEl){
47636             var dh = Roo.DomHelper;
47637             if(c.titlebar !== false){
47638                 this.collapseBtn = this.createTool(this.tools.dom, "x-layout-collapse-"+this.position);
47639                 this.collapseBtn.on("click", this.collapse, this);
47640                 this.collapseBtn.enableDisplayMode();
47641
47642                 if(c.showPin === true || this.showPin){
47643                     this.stickBtn = this.createTool(this.tools.dom, "x-layout-stick");
47644                     this.stickBtn.enableDisplayMode();
47645                     this.stickBtn.on("click", this.expand, this);
47646                     this.stickBtn.hide();
47647                 }
47648             }
47649             /** This region's collapsed element
47650             * @type Roo.Element */
47651             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
47652                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
47653             ]}, true);
47654             if(c.floatable !== false){
47655                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
47656                this.collapsedEl.on("click", this.collapseClick, this);
47657             }
47658
47659             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
47660                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
47661                    id: "message", unselectable: "on", style:{"float":"left"}});
47662                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
47663              }
47664             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
47665             this.expandBtn.on("click", this.expand, this);
47666         }
47667         if(this.collapseBtn){
47668             this.collapseBtn.setVisible(c.collapsible == true);
47669         }
47670         this.cmargins = c.cmargins || this.cmargins ||
47671                          (this.position == "west" || this.position == "east" ?
47672                              {top: 0, left: 2, right:2, bottom: 0} :
47673                              {top: 2, left: 0, right:0, bottom: 2});
47674         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
47675         this.bottomTabs = c.tabPosition != "top";
47676         this.autoScroll = c.autoScroll || false;
47677         if(this.autoScroll){
47678             this.bodyEl.setStyle("overflow", "auto");
47679         }else{
47680             this.bodyEl.setStyle("overflow", "hidden");
47681         }
47682         //if(c.titlebar !== false){
47683             if((!c.titlebar && !c.title) || c.titlebar === false){
47684                 this.titleEl.hide();
47685             }else{
47686                 this.titleEl.show();
47687                 if(c.title){
47688                     this.titleTextEl.innerHTML = c.title;
47689                 }
47690             }
47691         //}
47692         this.duration = c.duration || .30;
47693         this.slideDuration = c.slideDuration || .45;
47694         this.config = c;
47695         if(c.collapsed){
47696             this.collapse(true);
47697         }
47698         if(c.hidden){
47699             this.hide();
47700         }
47701     },
47702     /**
47703      * Returns true if this region is currently visible.
47704      * @return {Boolean}
47705      */
47706     isVisible : function(){
47707         return this.visible;
47708     },
47709
47710     /**
47711      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
47712      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
47713      */
47714     setCollapsedTitle : function(title){
47715         title = title || "&#160;";
47716         if(this.collapsedTitleTextEl){
47717             this.collapsedTitleTextEl.innerHTML = title;
47718         }
47719     },
47720
47721     getBox : function(){
47722         var b;
47723         if(!this.collapsed){
47724             b = this.el.getBox(false, true);
47725         }else{
47726             b = this.collapsedEl.getBox(false, true);
47727         }
47728         return b;
47729     },
47730
47731     getMargins : function(){
47732         return this.collapsed ? this.cmargins : this.margins;
47733     },
47734
47735     highlight : function(){
47736         this.el.addClass("x-layout-panel-dragover");
47737     },
47738
47739     unhighlight : function(){
47740         this.el.removeClass("x-layout-panel-dragover");
47741     },
47742
47743     updateBox : function(box){
47744         this.box = box;
47745         if(!this.collapsed){
47746             this.el.dom.style.left = box.x + "px";
47747             this.el.dom.style.top = box.y + "px";
47748             this.updateBody(box.width, box.height);
47749         }else{
47750             this.collapsedEl.dom.style.left = box.x + "px";
47751             this.collapsedEl.dom.style.top = box.y + "px";
47752             this.collapsedEl.setSize(box.width, box.height);
47753         }
47754         if(this.tabs){
47755             this.tabs.autoSizeTabs();
47756         }
47757     },
47758
47759     updateBody : function(w, h){
47760         if(w !== null){
47761             this.el.setWidth(w);
47762             w -= this.el.getBorderWidth("rl");
47763             if(this.config.adjustments){
47764                 w += this.config.adjustments[0];
47765             }
47766         }
47767         if(h !== null){
47768             this.el.setHeight(h);
47769             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
47770             h -= this.el.getBorderWidth("tb");
47771             if(this.config.adjustments){
47772                 h += this.config.adjustments[1];
47773             }
47774             this.bodyEl.setHeight(h);
47775             if(this.tabs){
47776                 h = this.tabs.syncHeight(h);
47777             }
47778         }
47779         if(this.panelSize){
47780             w = w !== null ? w : this.panelSize.width;
47781             h = h !== null ? h : this.panelSize.height;
47782         }
47783         if(this.activePanel){
47784             var el = this.activePanel.getEl();
47785             w = w !== null ? w : el.getWidth();
47786             h = h !== null ? h : el.getHeight();
47787             this.panelSize = {width: w, height: h};
47788             this.activePanel.setSize(w, h);
47789         }
47790         if(Roo.isIE && this.tabs){
47791             this.tabs.el.repaint();
47792         }
47793     },
47794
47795     /**
47796      * Returns the container element for this region.
47797      * @return {Roo.Element}
47798      */
47799     getEl : function(){
47800         return this.el;
47801     },
47802
47803     /**
47804      * Hides this region.
47805      */
47806     hide : function(){
47807         if(!this.collapsed){
47808             this.el.dom.style.left = "-2000px";
47809             this.el.hide();
47810         }else{
47811             this.collapsedEl.dom.style.left = "-2000px";
47812             this.collapsedEl.hide();
47813         }
47814         this.visible = false;
47815         this.fireEvent("visibilitychange", this, false);
47816     },
47817
47818     /**
47819      * Shows this region if it was previously hidden.
47820      */
47821     show : function(){
47822         if(!this.collapsed){
47823             this.el.show();
47824         }else{
47825             this.collapsedEl.show();
47826         }
47827         this.visible = true;
47828         this.fireEvent("visibilitychange", this, true);
47829     },
47830
47831     closeClicked : function(){
47832         if(this.activePanel){
47833             this.remove(this.activePanel);
47834         }
47835     },
47836
47837     collapseClick : function(e){
47838         if(this.isSlid){
47839            e.stopPropagation();
47840            this.slideIn();
47841         }else{
47842            e.stopPropagation();
47843            this.slideOut();
47844         }
47845     },
47846
47847     /**
47848      * Collapses this region.
47849      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
47850      */
47851     collapse : function(skipAnim){
47852         if(this.collapsed) return;
47853         this.collapsed = true;
47854         if(this.split){
47855             this.split.el.hide();
47856         }
47857         if(this.config.animate && skipAnim !== true){
47858             this.fireEvent("invalidated", this);
47859             this.animateCollapse();
47860         }else{
47861             this.el.setLocation(-20000,-20000);
47862             this.el.hide();
47863             this.collapsedEl.show();
47864             this.fireEvent("collapsed", this);
47865             this.fireEvent("invalidated", this);
47866         }
47867     },
47868
47869     animateCollapse : function(){
47870         // overridden
47871     },
47872
47873     /**
47874      * Expands this region if it was previously collapsed.
47875      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
47876      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
47877      */
47878     expand : function(e, skipAnim){
47879         if(e) e.stopPropagation();
47880         if(!this.collapsed || this.el.hasActiveFx()) return;
47881         if(this.isSlid){
47882             this.afterSlideIn();
47883             skipAnim = true;
47884         }
47885         this.collapsed = false;
47886         if(this.config.animate && skipAnim !== true){
47887             this.animateExpand();
47888         }else{
47889             this.el.show();
47890             if(this.split){
47891                 this.split.el.show();
47892             }
47893             this.collapsedEl.setLocation(-2000,-2000);
47894             this.collapsedEl.hide();
47895             this.fireEvent("invalidated", this);
47896             this.fireEvent("expanded", this);
47897         }
47898     },
47899
47900     animateExpand : function(){
47901         // overridden
47902     },
47903
47904     initTabs : function()
47905     {
47906         this.bodyEl.setStyle("overflow", "hidden");
47907         var ts = new Roo.TabPanel(
47908                 this.bodyEl.dom,
47909                 {
47910                     tabPosition: this.bottomTabs ? 'bottom' : 'top',
47911                     disableTooltips: this.config.disableTabTips,
47912                     toolbar : this.config.toolbar
47913                 }
47914         );
47915         if(this.config.hideTabs){
47916             ts.stripWrap.setDisplayed(false);
47917         }
47918         this.tabs = ts;
47919         ts.resizeTabs = this.config.resizeTabs === true;
47920         ts.minTabWidth = this.config.minTabWidth || 40;
47921         ts.maxTabWidth = this.config.maxTabWidth || 250;
47922         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
47923         ts.monitorResize = false;
47924         ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
47925         ts.bodyEl.addClass('x-layout-tabs-body');
47926         this.panels.each(this.initPanelAsTab, this);
47927     },
47928
47929     initPanelAsTab : function(panel){
47930         var ti = this.tabs.addTab(panel.getEl().id, panel.getTitle(), null,
47931                     this.config.closeOnTab && panel.isClosable());
47932         if(panel.tabTip !== undefined){
47933             ti.setTooltip(panel.tabTip);
47934         }
47935         ti.on("activate", function(){
47936               this.setActivePanel(panel);
47937         }, this);
47938         if(this.config.closeOnTab){
47939             ti.on("beforeclose", function(t, e){
47940                 e.cancel = true;
47941                 this.remove(panel);
47942             }, this);
47943         }
47944         return ti;
47945     },
47946
47947     updatePanelTitle : function(panel, title){
47948         if(this.activePanel == panel){
47949             this.updateTitle(title);
47950         }
47951         if(this.tabs){
47952             var ti = this.tabs.getTab(panel.getEl().id);
47953             ti.setText(title);
47954             if(panel.tabTip !== undefined){
47955                 ti.setTooltip(panel.tabTip);
47956             }
47957         }
47958     },
47959
47960     updateTitle : function(title){
47961         if(this.titleTextEl && !this.config.title){
47962             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
47963         }
47964     },
47965
47966     setActivePanel : function(panel){
47967         panel = this.getPanel(panel);
47968         if(this.activePanel && this.activePanel != panel){
47969             this.activePanel.setActiveState(false);
47970         }
47971         this.activePanel = panel;
47972         panel.setActiveState(true);
47973         if(this.panelSize){
47974             panel.setSize(this.panelSize.width, this.panelSize.height);
47975         }
47976         if(this.closeBtn){
47977             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
47978         }
47979         this.updateTitle(panel.getTitle());
47980         if(this.tabs){
47981             this.fireEvent("invalidated", this);
47982         }
47983         this.fireEvent("panelactivated", this, panel);
47984     },
47985
47986     /**
47987      * Shows the specified panel.
47988      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
47989      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
47990      */
47991     showPanel : function(panel){
47992         if(panel = this.getPanel(panel)){
47993             if(this.tabs){
47994                 var tab = this.tabs.getTab(panel.getEl().id);
47995                 if(tab.isHidden()){
47996                     this.tabs.unhideTab(tab.id);
47997                 }
47998                 tab.activate();
47999             }else{
48000                 this.setActivePanel(panel);
48001             }
48002         }
48003         return panel;
48004     },
48005
48006     /**
48007      * Get the active panel for this region.
48008      * @return {Roo.ContentPanel} The active panel or null
48009      */
48010     getActivePanel : function(){
48011         return this.activePanel;
48012     },
48013
48014     validateVisibility : function(){
48015         if(this.panels.getCount() < 1){
48016             this.updateTitle("&#160;");
48017             this.closeBtn.hide();
48018             this.hide();
48019         }else{
48020             if(!this.isVisible()){
48021                 this.show();
48022             }
48023         }
48024     },
48025
48026     /**
48027      * Adds the passed ContentPanel(s) to this region.
48028      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
48029      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
48030      */
48031     add : function(panel){
48032         if(arguments.length > 1){
48033             for(var i = 0, len = arguments.length; i < len; i++) {
48034                 this.add(arguments[i]);
48035             }
48036             return null;
48037         }
48038         if(this.hasPanel(panel)){
48039             this.showPanel(panel);
48040             return panel;
48041         }
48042         panel.setRegion(this);
48043         this.panels.add(panel);
48044         if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
48045             this.bodyEl.dom.appendChild(panel.getEl().dom);
48046             if(panel.background !== true){
48047                 this.setActivePanel(panel);
48048             }
48049             this.fireEvent("paneladded", this, panel);
48050             return panel;
48051         }
48052         if(!this.tabs){
48053             this.initTabs();
48054         }else{
48055             this.initPanelAsTab(panel);
48056         }
48057         if(panel.background !== true){
48058             this.tabs.activate(panel.getEl().id);
48059         }
48060         this.fireEvent("paneladded", this, panel);
48061         return panel;
48062     },
48063
48064     /**
48065      * Hides the tab for the specified panel.
48066      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
48067      */
48068     hidePanel : function(panel){
48069         if(this.tabs && (panel = this.getPanel(panel))){
48070             this.tabs.hideTab(panel.getEl().id);
48071         }
48072     },
48073
48074     /**
48075      * Unhides the tab for a previously hidden panel.
48076      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
48077      */
48078     unhidePanel : function(panel){
48079         if(this.tabs && (panel = this.getPanel(panel))){
48080             this.tabs.unhideTab(panel.getEl().id);
48081         }
48082     },
48083
48084     clearPanels : function(){
48085         while(this.panels.getCount() > 0){
48086              this.remove(this.panels.first());
48087         }
48088     },
48089
48090     /**
48091      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
48092      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
48093      * @param {Boolean} preservePanel Overrides the config preservePanel option
48094      * @return {Roo.ContentPanel} The panel that was removed
48095      */
48096     remove : function(panel, preservePanel){
48097         panel = this.getPanel(panel);
48098         if(!panel){
48099             return null;
48100         }
48101         var e = {};
48102         this.fireEvent("beforeremove", this, panel, e);
48103         if(e.cancel === true){
48104             return null;
48105         }
48106         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
48107         var panelId = panel.getId();
48108         this.panels.removeKey(panelId);
48109         if(preservePanel){
48110             document.body.appendChild(panel.getEl().dom);
48111         }
48112         if(this.tabs){
48113             this.tabs.removeTab(panel.getEl().id);
48114         }else if (!preservePanel){
48115             this.bodyEl.dom.removeChild(panel.getEl().dom);
48116         }
48117         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
48118             var p = this.panels.first();
48119             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
48120             tempEl.appendChild(p.getEl().dom);
48121             this.bodyEl.update("");
48122             this.bodyEl.dom.appendChild(p.getEl().dom);
48123             tempEl = null;
48124             this.updateTitle(p.getTitle());
48125             this.tabs = null;
48126             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
48127             this.setActivePanel(p);
48128         }
48129         panel.setRegion(null);
48130         if(this.activePanel == panel){
48131             this.activePanel = null;
48132         }
48133         if(this.config.autoDestroy !== false && preservePanel !== true){
48134             try{panel.destroy();}catch(e){}
48135         }
48136         this.fireEvent("panelremoved", this, panel);
48137         return panel;
48138     },
48139
48140     /**
48141      * Returns the TabPanel component used by this region
48142      * @return {Roo.TabPanel}
48143      */
48144     getTabs : function(){
48145         return this.tabs;
48146     },
48147
48148     createTool : function(parentEl, className){
48149         var btn = Roo.DomHelper.append(parentEl, {tag: "div", cls: "x-layout-tools-button",
48150             children: [{tag: "div", cls: "x-layout-tools-button-inner " + className, html: "&#160;"}]}, true);
48151         btn.addClassOnOver("x-layout-tools-button-over");
48152         return btn;
48153     }
48154 });/*
48155  * Based on:
48156  * Ext JS Library 1.1.1
48157  * Copyright(c) 2006-2007, Ext JS, LLC.
48158  *
48159  * Originally Released Under LGPL - original licence link has changed is not relivant.
48160  *
48161  * Fork - LGPL
48162  * <script type="text/javascript">
48163  */
48164  
48165
48166
48167 /**
48168  * @class Roo.SplitLayoutRegion
48169  * @extends Roo.LayoutRegion
48170  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
48171  */
48172 Roo.SplitLayoutRegion = function(mgr, config, pos, cursor){
48173     this.cursor = cursor;
48174     Roo.SplitLayoutRegion.superclass.constructor.call(this, mgr, config, pos);
48175 };
48176
48177 Roo.extend(Roo.SplitLayoutRegion, Roo.LayoutRegion, {
48178     splitTip : "Drag to resize.",
48179     collapsibleSplitTip : "Drag to resize. Double click to hide.",
48180     useSplitTips : false,
48181
48182     applyConfig : function(config){
48183         Roo.SplitLayoutRegion.superclass.applyConfig.call(this, config);
48184         if(config.split){
48185             if(!this.split){
48186                 var splitEl = Roo.DomHelper.append(this.mgr.el.dom, 
48187                         {tag: "div", id: this.el.id + "-split", cls: "x-layout-split x-layout-split-"+this.position, html: "&#160;"});
48188                 /** The SplitBar for this region 
48189                 * @type Roo.SplitBar */
48190                 this.split = new Roo.SplitBar(splitEl, this.el, this.orientation);
48191                 this.split.on("moved", this.onSplitMove, this);
48192                 this.split.useShim = config.useShim === true;
48193                 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
48194                 if(this.useSplitTips){
48195                     this.split.el.dom.title = config.collapsible ? this.collapsibleSplitTip : this.splitTip;
48196                 }
48197                 if(config.collapsible){
48198                     this.split.el.on("dblclick", this.collapse,  this);
48199                 }
48200             }
48201             if(typeof config.minSize != "undefined"){
48202                 this.split.minSize = config.minSize;
48203             }
48204             if(typeof config.maxSize != "undefined"){
48205                 this.split.maxSize = config.maxSize;
48206             }
48207             if(config.hideWhenEmpty || config.hidden || config.collapsed){
48208                 this.hideSplitter();
48209             }
48210         }
48211     },
48212
48213     getHMaxSize : function(){
48214          var cmax = this.config.maxSize || 10000;
48215          var center = this.mgr.getRegion("center");
48216          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
48217     },
48218
48219     getVMaxSize : function(){
48220          var cmax = this.config.maxSize || 10000;
48221          var center = this.mgr.getRegion("center");
48222          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
48223     },
48224
48225     onSplitMove : function(split, newSize){
48226         this.fireEvent("resized", this, newSize);
48227     },
48228     
48229     /** 
48230      * Returns the {@link Roo.SplitBar} for this region.
48231      * @return {Roo.SplitBar}
48232      */
48233     getSplitBar : function(){
48234         return this.split;
48235     },
48236     
48237     hide : function(){
48238         this.hideSplitter();
48239         Roo.SplitLayoutRegion.superclass.hide.call(this);
48240     },
48241
48242     hideSplitter : function(){
48243         if(this.split){
48244             this.split.el.setLocation(-2000,-2000);
48245             this.split.el.hide();
48246         }
48247     },
48248
48249     show : function(){
48250         if(this.split){
48251             this.split.el.show();
48252         }
48253         Roo.SplitLayoutRegion.superclass.show.call(this);
48254     },
48255     
48256     beforeSlide: function(){
48257         if(Roo.isGecko){// firefox overflow auto bug workaround
48258             this.bodyEl.clip();
48259             if(this.tabs) this.tabs.bodyEl.clip();
48260             if(this.activePanel){
48261                 this.activePanel.getEl().clip();
48262                 
48263                 if(this.activePanel.beforeSlide){
48264                     this.activePanel.beforeSlide();
48265                 }
48266             }
48267         }
48268     },
48269     
48270     afterSlide : function(){
48271         if(Roo.isGecko){// firefox overflow auto bug workaround
48272             this.bodyEl.unclip();
48273             if(this.tabs) this.tabs.bodyEl.unclip();
48274             if(this.activePanel){
48275                 this.activePanel.getEl().unclip();
48276                 if(this.activePanel.afterSlide){
48277                     this.activePanel.afterSlide();
48278                 }
48279             }
48280         }
48281     },
48282
48283     initAutoHide : function(){
48284         if(this.autoHide !== false){
48285             if(!this.autoHideHd){
48286                 var st = new Roo.util.DelayedTask(this.slideIn, this);
48287                 this.autoHideHd = {
48288                     "mouseout": function(e){
48289                         if(!e.within(this.el, true)){
48290                             st.delay(500);
48291                         }
48292                     },
48293                     "mouseover" : function(e){
48294                         st.cancel();
48295                     },
48296                     scope : this
48297                 };
48298             }
48299             this.el.on(this.autoHideHd);
48300         }
48301     },
48302
48303     clearAutoHide : function(){
48304         if(this.autoHide !== false){
48305             this.el.un("mouseout", this.autoHideHd.mouseout);
48306             this.el.un("mouseover", this.autoHideHd.mouseover);
48307         }
48308     },
48309
48310     clearMonitor : function(){
48311         Roo.get(document).un("click", this.slideInIf, this);
48312     },
48313
48314     // these names are backwards but not changed for compat
48315     slideOut : function(){
48316         if(this.isSlid || this.el.hasActiveFx()){
48317             return;
48318         }
48319         this.isSlid = true;
48320         if(this.collapseBtn){
48321             this.collapseBtn.hide();
48322         }
48323         this.closeBtnState = this.closeBtn.getStyle('display');
48324         this.closeBtn.hide();
48325         if(this.stickBtn){
48326             this.stickBtn.show();
48327         }
48328         this.el.show();
48329         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
48330         this.beforeSlide();
48331         this.el.setStyle("z-index", 10001);
48332         this.el.slideIn(this.getSlideAnchor(), {
48333             callback: function(){
48334                 this.afterSlide();
48335                 this.initAutoHide();
48336                 Roo.get(document).on("click", this.slideInIf, this);
48337                 this.fireEvent("slideshow", this);
48338             },
48339             scope: this,
48340             block: true
48341         });
48342     },
48343
48344     afterSlideIn : function(){
48345         this.clearAutoHide();
48346         this.isSlid = false;
48347         this.clearMonitor();
48348         this.el.setStyle("z-index", "");
48349         if(this.collapseBtn){
48350             this.collapseBtn.show();
48351         }
48352         this.closeBtn.setStyle('display', this.closeBtnState);
48353         if(this.stickBtn){
48354             this.stickBtn.hide();
48355         }
48356         this.fireEvent("slidehide", this);
48357     },
48358
48359     slideIn : function(cb){
48360         if(!this.isSlid || this.el.hasActiveFx()){
48361             Roo.callback(cb);
48362             return;
48363         }
48364         this.isSlid = false;
48365         this.beforeSlide();
48366         this.el.slideOut(this.getSlideAnchor(), {
48367             callback: function(){
48368                 this.el.setLeftTop(-10000, -10000);
48369                 this.afterSlide();
48370                 this.afterSlideIn();
48371                 Roo.callback(cb);
48372             },
48373             scope: this,
48374             block: true
48375         });
48376     },
48377     
48378     slideInIf : function(e){
48379         if(!e.within(this.el)){
48380             this.slideIn();
48381         }
48382     },
48383
48384     animateCollapse : function(){
48385         this.beforeSlide();
48386         this.el.setStyle("z-index", 20000);
48387         var anchor = this.getSlideAnchor();
48388         this.el.slideOut(anchor, {
48389             callback : function(){
48390                 this.el.setStyle("z-index", "");
48391                 this.collapsedEl.slideIn(anchor, {duration:.3});
48392                 this.afterSlide();
48393                 this.el.setLocation(-10000,-10000);
48394                 this.el.hide();
48395                 this.fireEvent("collapsed", this);
48396             },
48397             scope: this,
48398             block: true
48399         });
48400     },
48401
48402     animateExpand : function(){
48403         this.beforeSlide();
48404         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
48405         this.el.setStyle("z-index", 20000);
48406         this.collapsedEl.hide({
48407             duration:.1
48408         });
48409         this.el.slideIn(this.getSlideAnchor(), {
48410             callback : function(){
48411                 this.el.setStyle("z-index", "");
48412                 this.afterSlide();
48413                 if(this.split){
48414                     this.split.el.show();
48415                 }
48416                 this.fireEvent("invalidated", this);
48417                 this.fireEvent("expanded", this);
48418             },
48419             scope: this,
48420             block: true
48421         });
48422     },
48423
48424     anchors : {
48425         "west" : "left",
48426         "east" : "right",
48427         "north" : "top",
48428         "south" : "bottom"
48429     },
48430
48431     sanchors : {
48432         "west" : "l",
48433         "east" : "r",
48434         "north" : "t",
48435         "south" : "b"
48436     },
48437
48438     canchors : {
48439         "west" : "tl-tr",
48440         "east" : "tr-tl",
48441         "north" : "tl-bl",
48442         "south" : "bl-tl"
48443     },
48444
48445     getAnchor : function(){
48446         return this.anchors[this.position];
48447     },
48448
48449     getCollapseAnchor : function(){
48450         return this.canchors[this.position];
48451     },
48452
48453     getSlideAnchor : function(){
48454         return this.sanchors[this.position];
48455     },
48456
48457     getAlignAdj : function(){
48458         var cm = this.cmargins;
48459         switch(this.position){
48460             case "west":
48461                 return [0, 0];
48462             break;
48463             case "east":
48464                 return [0, 0];
48465             break;
48466             case "north":
48467                 return [0, 0];
48468             break;
48469             case "south":
48470                 return [0, 0];
48471             break;
48472         }
48473     },
48474
48475     getExpandAdj : function(){
48476         var c = this.collapsedEl, cm = this.cmargins;
48477         switch(this.position){
48478             case "west":
48479                 return [-(cm.right+c.getWidth()+cm.left), 0];
48480             break;
48481             case "east":
48482                 return [cm.right+c.getWidth()+cm.left, 0];
48483             break;
48484             case "north":
48485                 return [0, -(cm.top+cm.bottom+c.getHeight())];
48486             break;
48487             case "south":
48488                 return [0, cm.top+cm.bottom+c.getHeight()];
48489             break;
48490         }
48491     }
48492 });/*
48493  * Based on:
48494  * Ext JS Library 1.1.1
48495  * Copyright(c) 2006-2007, Ext JS, LLC.
48496  *
48497  * Originally Released Under LGPL - original licence link has changed is not relivant.
48498  *
48499  * Fork - LGPL
48500  * <script type="text/javascript">
48501  */
48502 /*
48503  * These classes are private internal classes
48504  */
48505 Roo.CenterLayoutRegion = function(mgr, config){
48506     Roo.LayoutRegion.call(this, mgr, config, "center");
48507     this.visible = true;
48508     this.minWidth = config.minWidth || 20;
48509     this.minHeight = config.minHeight || 20;
48510 };
48511
48512 Roo.extend(Roo.CenterLayoutRegion, Roo.LayoutRegion, {
48513     hide : function(){
48514         // center panel can't be hidden
48515     },
48516     
48517     show : function(){
48518         // center panel can't be hidden
48519     },
48520     
48521     getMinWidth: function(){
48522         return this.minWidth;
48523     },
48524     
48525     getMinHeight: function(){
48526         return this.minHeight;
48527     }
48528 });
48529
48530
48531 Roo.NorthLayoutRegion = function(mgr, config){
48532     Roo.LayoutRegion.call(this, mgr, config, "north", "n-resize");
48533     if(this.split){
48534         this.split.placement = Roo.SplitBar.TOP;
48535         this.split.orientation = Roo.SplitBar.VERTICAL;
48536         this.split.el.addClass("x-layout-split-v");
48537     }
48538     var size = config.initialSize || config.height;
48539     if(typeof size != "undefined"){
48540         this.el.setHeight(size);
48541     }
48542 };
48543 Roo.extend(Roo.NorthLayoutRegion, Roo.SplitLayoutRegion, {
48544     orientation: Roo.SplitBar.VERTICAL,
48545     getBox : function(){
48546         if(this.collapsed){
48547             return this.collapsedEl.getBox();
48548         }
48549         var box = this.el.getBox();
48550         if(this.split){
48551             box.height += this.split.el.getHeight();
48552         }
48553         return box;
48554     },
48555     
48556     updateBox : function(box){
48557         if(this.split && !this.collapsed){
48558             box.height -= this.split.el.getHeight();
48559             this.split.el.setLeft(box.x);
48560             this.split.el.setTop(box.y+box.height);
48561             this.split.el.setWidth(box.width);
48562         }
48563         if(this.collapsed){
48564             this.updateBody(box.width, null);
48565         }
48566         Roo.LayoutRegion.prototype.updateBox.call(this, box);
48567     }
48568 });
48569
48570 Roo.SouthLayoutRegion = function(mgr, config){
48571     Roo.SplitLayoutRegion.call(this, mgr, config, "south", "s-resize");
48572     if(this.split){
48573         this.split.placement = Roo.SplitBar.BOTTOM;
48574         this.split.orientation = Roo.SplitBar.VERTICAL;
48575         this.split.el.addClass("x-layout-split-v");
48576     }
48577     var size = config.initialSize || config.height;
48578     if(typeof size != "undefined"){
48579         this.el.setHeight(size);
48580     }
48581 };
48582 Roo.extend(Roo.SouthLayoutRegion, Roo.SplitLayoutRegion, {
48583     orientation: Roo.SplitBar.VERTICAL,
48584     getBox : function(){
48585         if(this.collapsed){
48586             return this.collapsedEl.getBox();
48587         }
48588         var box = this.el.getBox();
48589         if(this.split){
48590             var sh = this.split.el.getHeight();
48591             box.height += sh;
48592             box.y -= sh;
48593         }
48594         return box;
48595     },
48596     
48597     updateBox : function(box){
48598         if(this.split && !this.collapsed){
48599             var sh = this.split.el.getHeight();
48600             box.height -= sh;
48601             box.y += sh;
48602             this.split.el.setLeft(box.x);
48603             this.split.el.setTop(box.y-sh);
48604             this.split.el.setWidth(box.width);
48605         }
48606         if(this.collapsed){
48607             this.updateBody(box.width, null);
48608         }
48609         Roo.LayoutRegion.prototype.updateBox.call(this, box);
48610     }
48611 });
48612
48613 Roo.EastLayoutRegion = function(mgr, config){
48614     Roo.SplitLayoutRegion.call(this, mgr, config, "east", "e-resize");
48615     if(this.split){
48616         this.split.placement = Roo.SplitBar.RIGHT;
48617         this.split.orientation = Roo.SplitBar.HORIZONTAL;
48618         this.split.el.addClass("x-layout-split-h");
48619     }
48620     var size = config.initialSize || config.width;
48621     if(typeof size != "undefined"){
48622         this.el.setWidth(size);
48623     }
48624 };
48625 Roo.extend(Roo.EastLayoutRegion, Roo.SplitLayoutRegion, {
48626     orientation: Roo.SplitBar.HORIZONTAL,
48627     getBox : function(){
48628         if(this.collapsed){
48629             return this.collapsedEl.getBox();
48630         }
48631         var box = this.el.getBox();
48632         if(this.split){
48633             var sw = this.split.el.getWidth();
48634             box.width += sw;
48635             box.x -= sw;
48636         }
48637         return box;
48638     },
48639
48640     updateBox : function(box){
48641         if(this.split && !this.collapsed){
48642             var sw = this.split.el.getWidth();
48643             box.width -= sw;
48644             this.split.el.setLeft(box.x);
48645             this.split.el.setTop(box.y);
48646             this.split.el.setHeight(box.height);
48647             box.x += sw;
48648         }
48649         if(this.collapsed){
48650             this.updateBody(null, box.height);
48651         }
48652         Roo.LayoutRegion.prototype.updateBox.call(this, box);
48653     }
48654 });
48655
48656 Roo.WestLayoutRegion = function(mgr, config){
48657     Roo.SplitLayoutRegion.call(this, mgr, config, "west", "w-resize");
48658     if(this.split){
48659         this.split.placement = Roo.SplitBar.LEFT;
48660         this.split.orientation = Roo.SplitBar.HORIZONTAL;
48661         this.split.el.addClass("x-layout-split-h");
48662     }
48663     var size = config.initialSize || config.width;
48664     if(typeof size != "undefined"){
48665         this.el.setWidth(size);
48666     }
48667 };
48668 Roo.extend(Roo.WestLayoutRegion, Roo.SplitLayoutRegion, {
48669     orientation: Roo.SplitBar.HORIZONTAL,
48670     getBox : function(){
48671         if(this.collapsed){
48672             return this.collapsedEl.getBox();
48673         }
48674         var box = this.el.getBox();
48675         if(this.split){
48676             box.width += this.split.el.getWidth();
48677         }
48678         return box;
48679     },
48680     
48681     updateBox : function(box){
48682         if(this.split && !this.collapsed){
48683             var sw = this.split.el.getWidth();
48684             box.width -= sw;
48685             this.split.el.setLeft(box.x+box.width);
48686             this.split.el.setTop(box.y);
48687             this.split.el.setHeight(box.height);
48688         }
48689         if(this.collapsed){
48690             this.updateBody(null, box.height);
48691         }
48692         Roo.LayoutRegion.prototype.updateBox.call(this, box);
48693     }
48694 });
48695 /*
48696  * Based on:
48697  * Ext JS Library 1.1.1
48698  * Copyright(c) 2006-2007, Ext JS, LLC.
48699  *
48700  * Originally Released Under LGPL - original licence link has changed is not relivant.
48701  *
48702  * Fork - LGPL
48703  * <script type="text/javascript">
48704  */
48705  
48706  
48707 /*
48708  * Private internal class for reading and applying state
48709  */
48710 Roo.LayoutStateManager = function(layout){
48711      // default empty state
48712      this.state = {
48713         north: {},
48714         south: {},
48715         east: {},
48716         west: {}       
48717     };
48718 };
48719
48720 Roo.LayoutStateManager.prototype = {
48721     init : function(layout, provider){
48722         this.provider = provider;
48723         var state = provider.get(layout.id+"-layout-state");
48724         if(state){
48725             var wasUpdating = layout.isUpdating();
48726             if(!wasUpdating){
48727                 layout.beginUpdate();
48728             }
48729             for(var key in state){
48730                 if(typeof state[key] != "function"){
48731                     var rstate = state[key];
48732                     var r = layout.getRegion(key);
48733                     if(r && rstate){
48734                         if(rstate.size){
48735                             r.resizeTo(rstate.size);
48736                         }
48737                         if(rstate.collapsed == true){
48738                             r.collapse(true);
48739                         }else{
48740                             r.expand(null, true);
48741                         }
48742                     }
48743                 }
48744             }
48745             if(!wasUpdating){
48746                 layout.endUpdate();
48747             }
48748             this.state = state; 
48749         }
48750         this.layout = layout;
48751         layout.on("regionresized", this.onRegionResized, this);
48752         layout.on("regioncollapsed", this.onRegionCollapsed, this);
48753         layout.on("regionexpanded", this.onRegionExpanded, this);
48754     },
48755     
48756     storeState : function(){
48757         this.provider.set(this.layout.id+"-layout-state", this.state);
48758     },
48759     
48760     onRegionResized : function(region, newSize){
48761         this.state[region.getPosition()].size = newSize;
48762         this.storeState();
48763     },
48764     
48765     onRegionCollapsed : function(region){
48766         this.state[region.getPosition()].collapsed = true;
48767         this.storeState();
48768     },
48769     
48770     onRegionExpanded : function(region){
48771         this.state[region.getPosition()].collapsed = false;
48772         this.storeState();
48773     }
48774 };/*
48775  * Based on:
48776  * Ext JS Library 1.1.1
48777  * Copyright(c) 2006-2007, Ext JS, LLC.
48778  *
48779  * Originally Released Under LGPL - original licence link has changed is not relivant.
48780  *
48781  * Fork - LGPL
48782  * <script type="text/javascript">
48783  */
48784 /**
48785  * @class Roo.ContentPanel
48786  * @extends Roo.util.Observable
48787  * A basic ContentPanel element.
48788  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
48789  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
48790  * @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
48791  * @cfg {Boolean}   closable      True if the panel can be closed/removed
48792  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
48793  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
48794  * @cfg {Toolbar}   toolbar       A toolbar for this panel
48795  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
48796  * @cfg {String} title          The title for this panel
48797  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
48798  * @cfg {String} url            Calls {@link #setUrl} with this value
48799  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
48800  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
48801  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
48802  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
48803
48804  * @constructor
48805  * Create a new ContentPanel.
48806  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
48807  * @param {String/Object} config A string to set only the title or a config object
48808  * @param {String} content (optional) Set the HTML content for this panel
48809  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
48810  */
48811 Roo.ContentPanel = function(el, config, content){
48812     
48813      
48814     /*
48815     if(el.autoCreate || el.xtype){ // xtype is available if this is called from factory
48816         config = el;
48817         el = Roo.id();
48818     }
48819     if (config && config.parentLayout) { 
48820         el = config.parentLayout.el.createChild(); 
48821     }
48822     */
48823     if(el.autoCreate){ // xtype is available if this is called from factory
48824         config = el;
48825         el = Roo.id();
48826     }
48827     this.el = Roo.get(el);
48828     if(!this.el && config && config.autoCreate){
48829         if(typeof config.autoCreate == "object"){
48830             if(!config.autoCreate.id){
48831                 config.autoCreate.id = config.id||el;
48832             }
48833             this.el = Roo.DomHelper.append(document.body,
48834                         config.autoCreate, true);
48835         }else{
48836             this.el = Roo.DomHelper.append(document.body,
48837                         {tag: "div", cls: "x-layout-inactive-content", id: config.id||el}, true);
48838         }
48839     }
48840     this.closable = false;
48841     this.loaded = false;
48842     this.active = false;
48843     if(typeof config == "string"){
48844         this.title = config;
48845     }else{
48846         Roo.apply(this, config);
48847     }
48848     
48849     if (this.toolbar && !this.toolbar.el && this.toolbar.xtype) {
48850         this.wrapEl = this.el.wrap();
48851         this.toolbar.container = this.el.insertSibling(false, 'before');
48852         this.toolbar = new Roo.Toolbar(this.toolbar);
48853     }
48854     
48855     // xtype created footer. - not sure if will work as we normally have to render first..
48856     if (this.footer && !this.footer.el && this.footer.xtype) {
48857         if (!this.wrapEl) {
48858             this.wrapEl = this.el.wrap();
48859         }
48860     
48861         this.footer.container = this.wrapEl.createChild();
48862          
48863         this.footer = Roo.factory(this.footer, Roo);
48864         
48865     }
48866     
48867     if(this.resizeEl){
48868         this.resizeEl = Roo.get(this.resizeEl, true);
48869     }else{
48870         this.resizeEl = this.el;
48871     }
48872     // handle view.xtype
48873     
48874  
48875     
48876     
48877     this.addEvents({
48878         /**
48879          * @event activate
48880          * Fires when this panel is activated. 
48881          * @param {Roo.ContentPanel} this
48882          */
48883         "activate" : true,
48884         /**
48885          * @event deactivate
48886          * Fires when this panel is activated. 
48887          * @param {Roo.ContentPanel} this
48888          */
48889         "deactivate" : true,
48890
48891         /**
48892          * @event resize
48893          * Fires when this panel is resized if fitToFrame is true.
48894          * @param {Roo.ContentPanel} this
48895          * @param {Number} width The width after any component adjustments
48896          * @param {Number} height The height after any component adjustments
48897          */
48898         "resize" : true,
48899         
48900          /**
48901          * @event render
48902          * Fires when this tab is created
48903          * @param {Roo.ContentPanel} this
48904          */
48905         "render" : true
48906         
48907         
48908         
48909     });
48910     
48911
48912     
48913     
48914     if(this.autoScroll){
48915         this.resizeEl.setStyle("overflow", "auto");
48916     } else {
48917         // fix randome scrolling
48918         this.el.on('scroll', function() {
48919             Roo.log('fix random scolling');
48920             this.scrollTo('top',0); 
48921         });
48922     }
48923     content = content || this.content;
48924     if(content){
48925         this.setContent(content);
48926     }
48927     if(config && config.url){
48928         this.setUrl(this.url, this.params, this.loadOnce);
48929     }
48930     
48931     
48932     
48933     Roo.ContentPanel.superclass.constructor.call(this);
48934     
48935     if (this.view && typeof(this.view.xtype) != 'undefined') {
48936         this.view.el = this.el.appendChild(document.createElement("div"));
48937         this.view = Roo.factory(this.view); 
48938         this.view.render  &&  this.view.render(false, '');  
48939     }
48940     
48941     
48942     this.fireEvent('render', this);
48943 };
48944
48945 Roo.extend(Roo.ContentPanel, Roo.util.Observable, {
48946     tabTip:'',
48947     setRegion : function(region){
48948         this.region = region;
48949         if(region){
48950            this.el.replaceClass("x-layout-inactive-content", "x-layout-active-content");
48951         }else{
48952            this.el.replaceClass("x-layout-active-content", "x-layout-inactive-content");
48953         } 
48954     },
48955     
48956     /**
48957      * Returns the toolbar for this Panel if one was configured. 
48958      * @return {Roo.Toolbar} 
48959      */
48960     getToolbar : function(){
48961         return this.toolbar;
48962     },
48963     
48964     setActiveState : function(active){
48965         this.active = active;
48966         if(!active){
48967             this.fireEvent("deactivate", this);
48968         }else{
48969             this.fireEvent("activate", this);
48970         }
48971     },
48972     /**
48973      * Updates this panel's element
48974      * @param {String} content The new content
48975      * @param {Boolean} loadScripts (optional) true to look for and process scripts
48976     */
48977     setContent : function(content, loadScripts){
48978         this.el.update(content, loadScripts);
48979     },
48980
48981     ignoreResize : function(w, h){
48982         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
48983             return true;
48984         }else{
48985             this.lastSize = {width: w, height: h};
48986             return false;
48987         }
48988     },
48989     /**
48990      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
48991      * @return {Roo.UpdateManager} The UpdateManager
48992      */
48993     getUpdateManager : function(){
48994         return this.el.getUpdateManager();
48995     },
48996      /**
48997      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
48998      * @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:
48999 <pre><code>
49000 panel.load({
49001     url: "your-url.php",
49002     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
49003     callback: yourFunction,
49004     scope: yourObject, //(optional scope)
49005     discardUrl: false,
49006     nocache: false,
49007     text: "Loading...",
49008     timeout: 30,
49009     scripts: false
49010 });
49011 </code></pre>
49012      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
49013      * 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.
49014      * @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}
49015      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
49016      * @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.
49017      * @return {Roo.ContentPanel} this
49018      */
49019     load : function(){
49020         var um = this.el.getUpdateManager();
49021         um.update.apply(um, arguments);
49022         return this;
49023     },
49024
49025
49026     /**
49027      * 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.
49028      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
49029      * @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)
49030      * @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)
49031      * @return {Roo.UpdateManager} The UpdateManager
49032      */
49033     setUrl : function(url, params, loadOnce){
49034         if(this.refreshDelegate){
49035             this.removeListener("activate", this.refreshDelegate);
49036         }
49037         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
49038         this.on("activate", this.refreshDelegate);
49039         return this.el.getUpdateManager();
49040     },
49041     
49042     _handleRefresh : function(url, params, loadOnce){
49043         if(!loadOnce || !this.loaded){
49044             var updater = this.el.getUpdateManager();
49045             updater.update(url, params, this._setLoaded.createDelegate(this));
49046         }
49047     },
49048     
49049     _setLoaded : function(){
49050         this.loaded = true;
49051     }, 
49052     
49053     /**
49054      * Returns this panel's id
49055      * @return {String} 
49056      */
49057     getId : function(){
49058         return this.el.id;
49059     },
49060     
49061     /** 
49062      * Returns this panel's element - used by regiosn to add.
49063      * @return {Roo.Element} 
49064      */
49065     getEl : function(){
49066         return this.wrapEl || this.el;
49067     },
49068     
49069     adjustForComponents : function(width, height)
49070     {
49071         //Roo.log('adjustForComponents ');
49072         if(this.resizeEl != this.el){
49073             width -= this.el.getFrameWidth('lr');
49074             height -= this.el.getFrameWidth('tb');
49075         }
49076         if(this.toolbar){
49077             var te = this.toolbar.getEl();
49078             height -= te.getHeight();
49079             te.setWidth(width);
49080         }
49081         if(this.footer){
49082             var te = this.footer.getEl();
49083             Roo.log("footer:" + te.getHeight());
49084             
49085             height -= te.getHeight();
49086             te.setWidth(width);
49087         }
49088         
49089         
49090         if(this.adjustments){
49091             width += this.adjustments[0];
49092             height += this.adjustments[1];
49093         }
49094         return {"width": width, "height": height};
49095     },
49096     
49097     setSize : function(width, height){
49098         if(this.fitToFrame && !this.ignoreResize(width, height)){
49099             if(this.fitContainer && this.resizeEl != this.el){
49100                 this.el.setSize(width, height);
49101             }
49102             var size = this.adjustForComponents(width, height);
49103             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
49104             this.fireEvent('resize', this, size.width, size.height);
49105         }
49106     },
49107     
49108     /**
49109      * Returns this panel's title
49110      * @return {String} 
49111      */
49112     getTitle : function(){
49113         return this.title;
49114     },
49115     
49116     /**
49117      * Set this panel's title
49118      * @param {String} title
49119      */
49120     setTitle : function(title){
49121         this.title = title;
49122         if(this.region){
49123             this.region.updatePanelTitle(this, title);
49124         }
49125     },
49126     
49127     /**
49128      * Returns true is this panel was configured to be closable
49129      * @return {Boolean} 
49130      */
49131     isClosable : function(){
49132         return this.closable;
49133     },
49134     
49135     beforeSlide : function(){
49136         this.el.clip();
49137         this.resizeEl.clip();
49138     },
49139     
49140     afterSlide : function(){
49141         this.el.unclip();
49142         this.resizeEl.unclip();
49143     },
49144     
49145     /**
49146      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
49147      *   Will fail silently if the {@link #setUrl} method has not been called.
49148      *   This does not activate the panel, just updates its content.
49149      */
49150     refresh : function(){
49151         if(this.refreshDelegate){
49152            this.loaded = false;
49153            this.refreshDelegate();
49154         }
49155     },
49156     
49157     /**
49158      * Destroys this panel
49159      */
49160     destroy : function(){
49161         this.el.removeAllListeners();
49162         var tempEl = document.createElement("span");
49163         tempEl.appendChild(this.el.dom);
49164         tempEl.innerHTML = "";
49165         this.el.remove();
49166         this.el = null;
49167     },
49168     
49169     /**
49170      * form - if the content panel contains a form - this is a reference to it.
49171      * @type {Roo.form.Form}
49172      */
49173     form : false,
49174     /**
49175      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
49176      *    This contains a reference to it.
49177      * @type {Roo.View}
49178      */
49179     view : false,
49180     
49181       /**
49182      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
49183      * <pre><code>
49184
49185 layout.addxtype({
49186        xtype : 'Form',
49187        items: [ .... ]
49188    }
49189 );
49190
49191 </code></pre>
49192      * @param {Object} cfg Xtype definition of item to add.
49193      */
49194     
49195     addxtype : function(cfg) {
49196         // add form..
49197         if (cfg.xtype.match(/^Form$/)) {
49198             
49199             var el;
49200             //if (this.footer) {
49201             //    el = this.footer.container.insertSibling(false, 'before');
49202             //} else {
49203                 el = this.el.createChild();
49204             //}
49205
49206             this.form = new  Roo.form.Form(cfg);
49207             
49208             
49209             if ( this.form.allItems.length) this.form.render(el.dom);
49210             return this.form;
49211         }
49212         // should only have one of theses..
49213         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
49214             // views.. should not be just added - used named prop 'view''
49215             
49216             cfg.el = this.el.appendChild(document.createElement("div"));
49217             // factory?
49218             
49219             var ret = new Roo.factory(cfg);
49220              
49221              ret.render && ret.render(false, ''); // render blank..
49222             this.view = ret;
49223             return ret;
49224         }
49225         return false;
49226     }
49227 });
49228
49229 /**
49230  * @class Roo.GridPanel
49231  * @extends Roo.ContentPanel
49232  * @constructor
49233  * Create a new GridPanel.
49234  * @param {Roo.grid.Grid} grid The grid for this panel
49235  * @param {String/Object} config A string to set only the panel's title, or a config object
49236  */
49237 Roo.GridPanel = function(grid, config){
49238     
49239   
49240     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
49241         {tag: "div", cls: "x-layout-grid-wrapper x-layout-inactive-content"}, true);
49242         
49243     this.wrapper.dom.appendChild(grid.getGridEl().dom);
49244     
49245     Roo.GridPanel.superclass.constructor.call(this, this.wrapper, config);
49246     
49247     if(this.toolbar){
49248         this.toolbar.el.insertBefore(this.wrapper.dom.firstChild);
49249     }
49250     // xtype created footer. - not sure if will work as we normally have to render first..
49251     if (this.footer && !this.footer.el && this.footer.xtype) {
49252         
49253         this.footer.container = this.grid.getView().getFooterPanel(true);
49254         this.footer.dataSource = this.grid.dataSource;
49255         this.footer = Roo.factory(this.footer, Roo);
49256         
49257     }
49258     
49259     grid.monitorWindowResize = false; // turn off autosizing
49260     grid.autoHeight = false;
49261     grid.autoWidth = false;
49262     this.grid = grid;
49263     this.grid.getGridEl().replaceClass("x-layout-inactive-content", "x-layout-component-panel");
49264 };
49265
49266 Roo.extend(Roo.GridPanel, Roo.ContentPanel, {
49267     getId : function(){
49268         return this.grid.id;
49269     },
49270     
49271     /**
49272      * Returns the grid for this panel
49273      * @return {Roo.grid.Grid} 
49274      */
49275     getGrid : function(){
49276         return this.grid;    
49277     },
49278     
49279     setSize : function(width, height){
49280         if(!this.ignoreResize(width, height)){
49281             var grid = this.grid;
49282             var size = this.adjustForComponents(width, height);
49283             grid.getGridEl().setSize(size.width, size.height);
49284             grid.autoSize();
49285         }
49286     },
49287     
49288     beforeSlide : function(){
49289         this.grid.getView().scroller.clip();
49290     },
49291     
49292     afterSlide : function(){
49293         this.grid.getView().scroller.unclip();
49294     },
49295     
49296     destroy : function(){
49297         this.grid.destroy();
49298         delete this.grid;
49299         Roo.GridPanel.superclass.destroy.call(this); 
49300     }
49301 });
49302
49303
49304 /**
49305  * @class Roo.NestedLayoutPanel
49306  * @extends Roo.ContentPanel
49307  * @constructor
49308  * Create a new NestedLayoutPanel.
49309  * 
49310  * 
49311  * @param {Roo.BorderLayout} layout The layout for this panel
49312  * @param {String/Object} config A string to set only the title or a config object
49313  */
49314 Roo.NestedLayoutPanel = function(layout, config)
49315 {
49316     // construct with only one argument..
49317     /* FIXME - implement nicer consturctors
49318     if (layout.layout) {
49319         config = layout;
49320         layout = config.layout;
49321         delete config.layout;
49322     }
49323     if (layout.xtype && !layout.getEl) {
49324         // then layout needs constructing..
49325         layout = Roo.factory(layout, Roo);
49326     }
49327     */
49328     
49329     
49330     Roo.NestedLayoutPanel.superclass.constructor.call(this, layout.getEl(), config);
49331     
49332     layout.monitorWindowResize = false; // turn off autosizing
49333     this.layout = layout;
49334     this.layout.getEl().addClass("x-layout-nested-layout");
49335     
49336     
49337     
49338     
49339 };
49340
49341 Roo.extend(Roo.NestedLayoutPanel, Roo.ContentPanel, {
49342
49343     setSize : function(width, height){
49344         if(!this.ignoreResize(width, height)){
49345             var size = this.adjustForComponents(width, height);
49346             var el = this.layout.getEl();
49347             el.setSize(size.width, size.height);
49348             var touch = el.dom.offsetWidth;
49349             this.layout.layout();
49350             // ie requires a double layout on the first pass
49351             if(Roo.isIE && !this.initialized){
49352                 this.initialized = true;
49353                 this.layout.layout();
49354             }
49355         }
49356     },
49357     
49358     // activate all subpanels if not currently active..
49359     
49360     setActiveState : function(active){
49361         this.active = active;
49362         if(!active){
49363             this.fireEvent("deactivate", this);
49364             return;
49365         }
49366         
49367         this.fireEvent("activate", this);
49368         // not sure if this should happen before or after..
49369         if (!this.layout) {
49370             return; // should not happen..
49371         }
49372         var reg = false;
49373         for (var r in this.layout.regions) {
49374             reg = this.layout.getRegion(r);
49375             if (reg.getActivePanel()) {
49376                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
49377                 reg.setActivePanel(reg.getActivePanel());
49378                 continue;
49379             }
49380             if (!reg.panels.length) {
49381                 continue;
49382             }
49383             reg.showPanel(reg.getPanel(0));
49384         }
49385         
49386         
49387         
49388         
49389     },
49390     
49391     /**
49392      * Returns the nested BorderLayout for this panel
49393      * @return {Roo.BorderLayout} 
49394      */
49395     getLayout : function(){
49396         return this.layout;
49397     },
49398     
49399      /**
49400      * Adds a xtype elements to the layout of the nested panel
49401      * <pre><code>
49402
49403 panel.addxtype({
49404        xtype : 'ContentPanel',
49405        region: 'west',
49406        items: [ .... ]
49407    }
49408 );
49409
49410 panel.addxtype({
49411         xtype : 'NestedLayoutPanel',
49412         region: 'west',
49413         layout: {
49414            center: { },
49415            west: { }   
49416         },
49417         items : [ ... list of content panels or nested layout panels.. ]
49418    }
49419 );
49420 </code></pre>
49421      * @param {Object} cfg Xtype definition of item to add.
49422      */
49423     addxtype : function(cfg) {
49424         return this.layout.addxtype(cfg);
49425     
49426     }
49427 });
49428
49429 Roo.ScrollPanel = function(el, config, content){
49430     config = config || {};
49431     config.fitToFrame = true;
49432     Roo.ScrollPanel.superclass.constructor.call(this, el, config, content);
49433     
49434     this.el.dom.style.overflow = "hidden";
49435     var wrap = this.el.wrap({cls: "x-scroller x-layout-inactive-content"});
49436     this.el.removeClass("x-layout-inactive-content");
49437     this.el.on("mousewheel", this.onWheel, this);
49438
49439     var up = wrap.createChild({cls: "x-scroller-up", html: "&#160;"}, this.el.dom);
49440     var down = wrap.createChild({cls: "x-scroller-down", html: "&#160;"});
49441     up.unselectable(); down.unselectable();
49442     up.on("click", this.scrollUp, this);
49443     down.on("click", this.scrollDown, this);
49444     up.addClassOnOver("x-scroller-btn-over");
49445     down.addClassOnOver("x-scroller-btn-over");
49446     up.addClassOnClick("x-scroller-btn-click");
49447     down.addClassOnClick("x-scroller-btn-click");
49448     this.adjustments = [0, -(up.getHeight() + down.getHeight())];
49449
49450     this.resizeEl = this.el;
49451     this.el = wrap; this.up = up; this.down = down;
49452 };
49453
49454 Roo.extend(Roo.ScrollPanel, Roo.ContentPanel, {
49455     increment : 100,
49456     wheelIncrement : 5,
49457     scrollUp : function(){
49458         this.resizeEl.scroll("up", this.increment, {callback: this.afterScroll, scope: this});
49459     },
49460
49461     scrollDown : function(){
49462         this.resizeEl.scroll("down", this.increment, {callback: this.afterScroll, scope: this});
49463     },
49464
49465     afterScroll : function(){
49466         var el = this.resizeEl;
49467         var t = el.dom.scrollTop, h = el.dom.scrollHeight, ch = el.dom.clientHeight;
49468         this.up[t == 0 ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
49469         this.down[h - t <= ch ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
49470     },
49471
49472     setSize : function(){
49473         Roo.ScrollPanel.superclass.setSize.apply(this, arguments);
49474         this.afterScroll();
49475     },
49476
49477     onWheel : function(e){
49478         var d = e.getWheelDelta();
49479         this.resizeEl.dom.scrollTop -= (d*this.wheelIncrement);
49480         this.afterScroll();
49481         e.stopEvent();
49482     },
49483
49484     setContent : function(content, loadScripts){
49485         this.resizeEl.update(content, loadScripts);
49486     }
49487
49488 });
49489
49490
49491
49492
49493
49494
49495
49496
49497
49498 /**
49499  * @class Roo.TreePanel
49500  * @extends Roo.ContentPanel
49501  * @constructor
49502  * Create a new TreePanel. - defaults to fit/scoll contents.
49503  * @param {String/Object} config A string to set only the panel's title, or a config object
49504  * @cfg {Roo.tree.TreePanel} tree The tree TreePanel, with config etc.
49505  */
49506 Roo.TreePanel = function(config){
49507     var el = config.el;
49508     var tree = config.tree;
49509     delete config.tree; 
49510     delete config.el; // hopefull!
49511     
49512     // wrapper for IE7 strict & safari scroll issue
49513     
49514     var treeEl = el.createChild();
49515     config.resizeEl = treeEl;
49516     
49517     
49518     
49519     Roo.TreePanel.superclass.constructor.call(this, el, config);
49520  
49521  
49522     this.tree = new Roo.tree.TreePanel(treeEl , tree);
49523     //console.log(tree);
49524     this.on('activate', function()
49525     {
49526         if (this.tree.rendered) {
49527             return;
49528         }
49529         //console.log('render tree');
49530         this.tree.render();
49531     });
49532     // this should not be needed.. - it's actually the 'el' that resizes?
49533     // actuall it breaks the containerScroll - dragging nodes auto scroll at top
49534     
49535     //this.on('resize',  function (cp, w, h) {
49536     //        this.tree.innerCt.setWidth(w);
49537     //        this.tree.innerCt.setHeight(h);
49538     //        //this.tree.innerCt.setStyle('overflow-y', 'auto');
49539     //});
49540
49541         
49542     
49543 };
49544
49545 Roo.extend(Roo.TreePanel, Roo.ContentPanel, {   
49546     fitToFrame : true,
49547     autoScroll : true
49548 });
49549
49550
49551
49552
49553
49554
49555
49556
49557
49558
49559
49560 /*
49561  * Based on:
49562  * Ext JS Library 1.1.1
49563  * Copyright(c) 2006-2007, Ext JS, LLC.
49564  *
49565  * Originally Released Under LGPL - original licence link has changed is not relivant.
49566  *
49567  * Fork - LGPL
49568  * <script type="text/javascript">
49569  */
49570  
49571
49572 /**
49573  * @class Roo.ReaderLayout
49574  * @extends Roo.BorderLayout
49575  * This is a pre-built layout that represents a classic, 5-pane application.  It consists of a header, a primary
49576  * center region containing two nested regions (a top one for a list view and one for item preview below),
49577  * and regions on either side that can be used for navigation, application commands, informational displays, etc.
49578  * The setup and configuration work exactly the same as it does for a {@link Roo.BorderLayout} - this class simply
49579  * expedites the setup of the overall layout and regions for this common application style.
49580  * Example:
49581  <pre><code>
49582 var reader = new Roo.ReaderLayout();
49583 var CP = Roo.ContentPanel;  // shortcut for adding
49584
49585 reader.beginUpdate();
49586 reader.add("north", new CP("north", "North"));
49587 reader.add("west", new CP("west", {title: "West"}));
49588 reader.add("east", new CP("east", {title: "East"}));
49589
49590 reader.regions.listView.add(new CP("listView", "List"));
49591 reader.regions.preview.add(new CP("preview", "Preview"));
49592 reader.endUpdate();
49593 </code></pre>
49594 * @constructor
49595 * Create a new ReaderLayout
49596 * @param {Object} config Configuration options
49597 * @param {String/HTMLElement/Element} container (optional) The container this layout is bound to (defaults to
49598 * document.body if omitted)
49599 */
49600 Roo.ReaderLayout = function(config, renderTo){
49601     var c = config || {size:{}};
49602     Roo.ReaderLayout.superclass.constructor.call(this, renderTo || document.body, {
49603         north: c.north !== false ? Roo.apply({
49604             split:false,
49605             initialSize: 32,
49606             titlebar: false
49607         }, c.north) : false,
49608         west: c.west !== false ? Roo.apply({
49609             split:true,
49610             initialSize: 200,
49611             minSize: 175,
49612             maxSize: 400,
49613             titlebar: true,
49614             collapsible: true,
49615             animate: true,
49616             margins:{left:5,right:0,bottom:5,top:5},
49617             cmargins:{left:5,right:5,bottom:5,top:5}
49618         }, c.west) : false,
49619         east: c.east !== false ? Roo.apply({
49620             split:true,
49621             initialSize: 200,
49622             minSize: 175,
49623             maxSize: 400,
49624             titlebar: true,
49625             collapsible: true,
49626             animate: true,
49627             margins:{left:0,right:5,bottom:5,top:5},
49628             cmargins:{left:5,right:5,bottom:5,top:5}
49629         }, c.east) : false,
49630         center: Roo.apply({
49631             tabPosition: 'top',
49632             autoScroll:false,
49633             closeOnTab: true,
49634             titlebar:false,
49635             margins:{left:c.west!==false ? 0 : 5,right:c.east!==false ? 0 : 5,bottom:5,top:2}
49636         }, c.center)
49637     });
49638
49639     this.el.addClass('x-reader');
49640
49641     this.beginUpdate();
49642
49643     var inner = new Roo.BorderLayout(Roo.get(document.body).createChild(), {
49644         south: c.preview !== false ? Roo.apply({
49645             split:true,
49646             initialSize: 200,
49647             minSize: 100,
49648             autoScroll:true,
49649             collapsible:true,
49650             titlebar: true,
49651             cmargins:{top:5,left:0, right:0, bottom:0}
49652         }, c.preview) : false,
49653         center: Roo.apply({
49654             autoScroll:false,
49655             titlebar:false,
49656             minHeight:200
49657         }, c.listView)
49658     });
49659     this.add('center', new Roo.NestedLayoutPanel(inner,
49660             Roo.apply({title: c.mainTitle || '',tabTip:''},c.innerPanelCfg)));
49661
49662     this.endUpdate();
49663
49664     this.regions.preview = inner.getRegion('south');
49665     this.regions.listView = inner.getRegion('center');
49666 };
49667
49668 Roo.extend(Roo.ReaderLayout, Roo.BorderLayout);/*
49669  * Based on:
49670  * Ext JS Library 1.1.1
49671  * Copyright(c) 2006-2007, Ext JS, LLC.
49672  *
49673  * Originally Released Under LGPL - original licence link has changed is not relivant.
49674  *
49675  * Fork - LGPL
49676  * <script type="text/javascript">
49677  */
49678  
49679 /**
49680  * @class Roo.grid.Grid
49681  * @extends Roo.util.Observable
49682  * This class represents the primary interface of a component based grid control.
49683  * <br><br>Usage:<pre><code>
49684  var grid = new Roo.grid.Grid("my-container-id", {
49685      ds: myDataStore,
49686      cm: myColModel,
49687      selModel: mySelectionModel,
49688      autoSizeColumns: true,
49689      monitorWindowResize: false,
49690      trackMouseOver: true
49691  });
49692  // set any options
49693  grid.render();
49694  * </code></pre>
49695  * <b>Common Problems:</b><br/>
49696  * - Grid does not resize properly when going smaller: Setting overflow hidden on the container
49697  * element will correct this<br/>
49698  * - If you get el.style[camel]= NaNpx or -2px or something related, be certain you have given your container element
49699  * dimensions. The grid adapts to your container's size, if your container has no size defined then the results
49700  * are unpredictable.<br/>
49701  * - Do not render the grid into an element with display:none. Try using visibility:hidden. Otherwise there is no way for the
49702  * grid to calculate dimensions/offsets.<br/>
49703   * @constructor
49704  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
49705  * The container MUST have some type of size defined for the grid to fill. The container will be
49706  * automatically set to position relative if it isn't already.
49707  * @param {Object} config A config object that sets properties on this grid.
49708  */
49709 Roo.grid.Grid = function(container, config){
49710         // initialize the container
49711         this.container = Roo.get(container);
49712         this.container.update("");
49713         this.container.setStyle("overflow", "hidden");
49714     this.container.addClass('x-grid-container');
49715
49716     this.id = this.container.id;
49717
49718     Roo.apply(this, config);
49719     // check and correct shorthanded configs
49720     if(this.ds){
49721         this.dataSource = this.ds;
49722         delete this.ds;
49723     }
49724     if(this.cm){
49725         this.colModel = this.cm;
49726         delete this.cm;
49727     }
49728     if(this.sm){
49729         this.selModel = this.sm;
49730         delete this.sm;
49731     }
49732
49733     if (this.selModel) {
49734         this.selModel = Roo.factory(this.selModel, Roo.grid);
49735         this.sm = this.selModel;
49736         this.sm.xmodule = this.xmodule || false;
49737     }
49738     if (typeof(this.colModel.config) == 'undefined') {
49739         this.colModel = new Roo.grid.ColumnModel(this.colModel);
49740         this.cm = this.colModel;
49741         this.cm.xmodule = this.xmodule || false;
49742     }
49743     if (this.dataSource) {
49744         this.dataSource= Roo.factory(this.dataSource, Roo.data);
49745         this.ds = this.dataSource;
49746         this.ds.xmodule = this.xmodule || false;
49747          
49748     }
49749     
49750     
49751     
49752     if(this.width){
49753         this.container.setWidth(this.width);
49754     }
49755
49756     if(this.height){
49757         this.container.setHeight(this.height);
49758     }
49759     /** @private */
49760         this.addEvents({
49761         // raw events
49762         /**
49763          * @event click
49764          * The raw click event for the entire grid.
49765          * @param {Roo.EventObject} e
49766          */
49767         "click" : true,
49768         /**
49769          * @event dblclick
49770          * The raw dblclick event for the entire grid.
49771          * @param {Roo.EventObject} e
49772          */
49773         "dblclick" : true,
49774         /**
49775          * @event contextmenu
49776          * The raw contextmenu event for the entire grid.
49777          * @param {Roo.EventObject} e
49778          */
49779         "contextmenu" : true,
49780         /**
49781          * @event mousedown
49782          * The raw mousedown event for the entire grid.
49783          * @param {Roo.EventObject} e
49784          */
49785         "mousedown" : true,
49786         /**
49787          * @event mouseup
49788          * The raw mouseup event for the entire grid.
49789          * @param {Roo.EventObject} e
49790          */
49791         "mouseup" : true,
49792         /**
49793          * @event mouseover
49794          * The raw mouseover event for the entire grid.
49795          * @param {Roo.EventObject} e
49796          */
49797         "mouseover" : true,
49798         /**
49799          * @event mouseout
49800          * The raw mouseout event for the entire grid.
49801          * @param {Roo.EventObject} e
49802          */
49803         "mouseout" : true,
49804         /**
49805          * @event keypress
49806          * The raw keypress event for the entire grid.
49807          * @param {Roo.EventObject} e
49808          */
49809         "keypress" : true,
49810         /**
49811          * @event keydown
49812          * The raw keydown event for the entire grid.
49813          * @param {Roo.EventObject} e
49814          */
49815         "keydown" : true,
49816
49817         // custom events
49818
49819         /**
49820          * @event cellclick
49821          * Fires when a cell is clicked
49822          * @param {Grid} this
49823          * @param {Number} rowIndex
49824          * @param {Number} columnIndex
49825          * @param {Roo.EventObject} e
49826          */
49827         "cellclick" : true,
49828         /**
49829          * @event celldblclick
49830          * Fires when a cell is double clicked
49831          * @param {Grid} this
49832          * @param {Number} rowIndex
49833          * @param {Number} columnIndex
49834          * @param {Roo.EventObject} e
49835          */
49836         "celldblclick" : true,
49837         /**
49838          * @event rowclick
49839          * Fires when a row is clicked
49840          * @param {Grid} this
49841          * @param {Number} rowIndex
49842          * @param {Roo.EventObject} e
49843          */
49844         "rowclick" : true,
49845         /**
49846          * @event rowdblclick
49847          * Fires when a row is double clicked
49848          * @param {Grid} this
49849          * @param {Number} rowIndex
49850          * @param {Roo.EventObject} e
49851          */
49852         "rowdblclick" : true,
49853         /**
49854          * @event headerclick
49855          * Fires when a header is clicked
49856          * @param {Grid} this
49857          * @param {Number} columnIndex
49858          * @param {Roo.EventObject} e
49859          */
49860         "headerclick" : true,
49861         /**
49862          * @event headerdblclick
49863          * Fires when a header cell is double clicked
49864          * @param {Grid} this
49865          * @param {Number} columnIndex
49866          * @param {Roo.EventObject} e
49867          */
49868         "headerdblclick" : true,
49869         /**
49870          * @event rowcontextmenu
49871          * Fires when a row is right clicked
49872          * @param {Grid} this
49873          * @param {Number} rowIndex
49874          * @param {Roo.EventObject} e
49875          */
49876         "rowcontextmenu" : true,
49877         /**
49878          * @event cellcontextmenu
49879          * Fires when a cell is right clicked
49880          * @param {Grid} this
49881          * @param {Number} rowIndex
49882          * @param {Number} cellIndex
49883          * @param {Roo.EventObject} e
49884          */
49885          "cellcontextmenu" : true,
49886         /**
49887          * @event headercontextmenu
49888          * Fires when a header is right clicked
49889          * @param {Grid} this
49890          * @param {Number} columnIndex
49891          * @param {Roo.EventObject} e
49892          */
49893         "headercontextmenu" : true,
49894         /**
49895          * @event bodyscroll
49896          * Fires when the body element is scrolled
49897          * @param {Number} scrollLeft
49898          * @param {Number} scrollTop
49899          */
49900         "bodyscroll" : true,
49901         /**
49902          * @event columnresize
49903          * Fires when the user resizes a column
49904          * @param {Number} columnIndex
49905          * @param {Number} newSize
49906          */
49907         "columnresize" : true,
49908         /**
49909          * @event columnmove
49910          * Fires when the user moves a column
49911          * @param {Number} oldIndex
49912          * @param {Number} newIndex
49913          */
49914         "columnmove" : true,
49915         /**
49916          * @event startdrag
49917          * Fires when row(s) start being dragged
49918          * @param {Grid} this
49919          * @param {Roo.GridDD} dd The drag drop object
49920          * @param {event} e The raw browser event
49921          */
49922         "startdrag" : true,
49923         /**
49924          * @event enddrag
49925          * Fires when a drag operation is complete
49926          * @param {Grid} this
49927          * @param {Roo.GridDD} dd The drag drop object
49928          * @param {event} e The raw browser event
49929          */
49930         "enddrag" : true,
49931         /**
49932          * @event dragdrop
49933          * Fires when dragged row(s) are dropped on a valid DD target
49934          * @param {Grid} this
49935          * @param {Roo.GridDD} dd The drag drop object
49936          * @param {String} targetId The target drag drop object
49937          * @param {event} e The raw browser event
49938          */
49939         "dragdrop" : true,
49940         /**
49941          * @event dragover
49942          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
49943          * @param {Grid} this
49944          * @param {Roo.GridDD} dd The drag drop object
49945          * @param {String} targetId The target drag drop object
49946          * @param {event} e The raw browser event
49947          */
49948         "dragover" : true,
49949         /**
49950          * @event dragenter
49951          *  Fires when the dragged row(s) first cross another DD target while being dragged
49952          * @param {Grid} this
49953          * @param {Roo.GridDD} dd The drag drop object
49954          * @param {String} targetId The target drag drop object
49955          * @param {event} e The raw browser event
49956          */
49957         "dragenter" : true,
49958         /**
49959          * @event dragout
49960          * Fires when the dragged row(s) leave another DD target while being dragged
49961          * @param {Grid} this
49962          * @param {Roo.GridDD} dd The drag drop object
49963          * @param {String} targetId The target drag drop object
49964          * @param {event} e The raw browser event
49965          */
49966         "dragout" : true,
49967         /**
49968          * @event rowclass
49969          * Fires when a row is rendered, so you can change add a style to it.
49970          * @param {GridView} gridview   The grid view
49971          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
49972          */
49973         'rowclass' : true,
49974
49975         /**
49976          * @event render
49977          * Fires when the grid is rendered
49978          * @param {Grid} grid
49979          */
49980         'render' : true
49981     });
49982
49983     Roo.grid.Grid.superclass.constructor.call(this);
49984 };
49985 Roo.extend(Roo.grid.Grid, Roo.util.Observable, {
49986     
49987     /**
49988      * @cfg {String} ddGroup - drag drop group.
49989      */
49990
49991     /**
49992      * @cfg {Number} minColumnWidth The minimum width a column can be resized to. Default is 25.
49993      */
49994     minColumnWidth : 25,
49995
49996     /**
49997      * @cfg {Boolean} autoSizeColumns True to automatically resize the columns to fit their content
49998      * <b>on initial render.</b> It is more efficient to explicitly size the columns
49999      * through the ColumnModel's {@link Roo.grid.ColumnModel#width} config option.  Default is false.
50000      */
50001     autoSizeColumns : false,
50002
50003     /**
50004      * @cfg {Boolean} autoSizeHeaders True to measure headers with column data when auto sizing columns. Default is true.
50005      */
50006     autoSizeHeaders : true,
50007
50008     /**
50009      * @cfg {Boolean} monitorWindowResize True to autoSize the grid when the window resizes. Default is true.
50010      */
50011     monitorWindowResize : true,
50012
50013     /**
50014      * @cfg {Boolean} maxRowsToMeasure If autoSizeColumns is on, maxRowsToMeasure can be used to limit the number of
50015      * rows measured to get a columns size. Default is 0 (all rows).
50016      */
50017     maxRowsToMeasure : 0,
50018
50019     /**
50020      * @cfg {Boolean} trackMouseOver True to highlight rows when the mouse is over. Default is true.
50021      */
50022     trackMouseOver : true,
50023
50024     /**
50025     * @cfg {Boolean} enableDrag  True to enable drag of rows. Default is false. (double check if this is needed?)
50026     */
50027     
50028     /**
50029     * @cfg {Boolean} enableDragDrop True to enable drag and drop of rows. Default is false.
50030     */
50031     enableDragDrop : false,
50032     
50033     /**
50034     * @cfg {Boolean} enableColumnMove True to enable drag and drop reorder of columns. Default is true.
50035     */
50036     enableColumnMove : true,
50037     
50038     /**
50039     * @cfg {Boolean} enableColumnHide True to enable hiding of columns with the header context menu. Default is true.
50040     */
50041     enableColumnHide : true,
50042     
50043     /**
50044     * @cfg {Boolean} enableRowHeightSync True to manually sync row heights across locked and not locked rows. Default is false.
50045     */
50046     enableRowHeightSync : false,
50047     
50048     /**
50049     * @cfg {Boolean} stripeRows True to stripe the rows.  Default is true.
50050     */
50051     stripeRows : true,
50052     
50053     /**
50054     * @cfg {Boolean} autoHeight True to fit the height of the grid container to the height of the data. Default is false.
50055     */
50056     autoHeight : false,
50057
50058     /**
50059      * @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.
50060      */
50061     autoExpandColumn : false,
50062
50063     /**
50064     * @cfg {Number} autoExpandMin The minimum width the autoExpandColumn can have (if enabled).
50065     * Default is 50.
50066     */
50067     autoExpandMin : 50,
50068
50069     /**
50070     * @cfg {Number} autoExpandMax The maximum width the autoExpandColumn can have (if enabled). Default is 1000.
50071     */
50072     autoExpandMax : 1000,
50073
50074     /**
50075     * @cfg {Object} view The {@link Roo.grid.GridView} used by the grid. This can be set before a call to render().
50076     */
50077     view : null,
50078
50079     /**
50080     * @cfg {Object} loadMask An {@link Roo.LoadMask} config or true to mask the grid while loading. Default is false.
50081     */
50082     loadMask : false,
50083     /**
50084     * @cfg {Roo.dd.DropTarget} dropTarget An {@link Roo.dd.DropTarget} config
50085     */
50086     dropTarget: false,
50087     
50088    
50089     
50090     // private
50091     rendered : false,
50092
50093     /**
50094     * @cfg {Boolean} autoWidth True to set the grid's width to the default total width of the grid's columns instead
50095     * of a fixed width. Default is false.
50096     */
50097     /**
50098     * @cfg {Number} maxHeight Sets the maximum height of the grid - ignored if autoHeight is not on.
50099     */
50100     /**
50101      * Called once after all setup has been completed and the grid is ready to be rendered.
50102      * @return {Roo.grid.Grid} this
50103      */
50104     render : function()
50105     {
50106         var c = this.container;
50107         // try to detect autoHeight/width mode
50108         if((!c.dom.offsetHeight || c.dom.offsetHeight < 20) || c.getStyle("height") == "auto"){
50109             this.autoHeight = true;
50110         }
50111         var view = this.getView();
50112         view.init(this);
50113
50114         c.on("click", this.onClick, this);
50115         c.on("dblclick", this.onDblClick, this);
50116         c.on("contextmenu", this.onContextMenu, this);
50117         c.on("keydown", this.onKeyDown, this);
50118
50119         this.relayEvents(c, ["mousedown","mouseup","mouseover","mouseout","keypress"]);
50120
50121         this.getSelectionModel().init(this);
50122
50123         view.render();
50124
50125         if(this.loadMask){
50126             this.loadMask = new Roo.LoadMask(this.container,
50127                     Roo.apply({store:this.dataSource}, this.loadMask));
50128         }
50129         
50130         
50131         if (this.toolbar && this.toolbar.xtype) {
50132             this.toolbar.container = this.getView().getHeaderPanel(true);
50133             this.toolbar = new Roo.Toolbar(this.toolbar);
50134         }
50135         if (this.footer && this.footer.xtype) {
50136             this.footer.dataSource = this.getDataSource();
50137             this.footer.container = this.getView().getFooterPanel(true);
50138             this.footer = Roo.factory(this.footer, Roo);
50139         }
50140         if (this.dropTarget && this.dropTarget.xtype) {
50141             delete this.dropTarget.xtype;
50142             this.dropTarget =  new Roo.dd.DropTarget(this.getView().mainBody, this.dropTarget);
50143         }
50144         
50145         
50146         this.rendered = true;
50147         this.fireEvent('render', this);
50148         return this;
50149     },
50150
50151         /**
50152          * Reconfigures the grid to use a different Store and Column Model.
50153          * The View will be bound to the new objects and refreshed.
50154          * @param {Roo.data.Store} dataSource The new {@link Roo.data.Store} object
50155          * @param {Roo.grid.ColumnModel} The new {@link Roo.grid.ColumnModel} object
50156          */
50157     reconfigure : function(dataSource, colModel){
50158         if(this.loadMask){
50159             this.loadMask.destroy();
50160             this.loadMask = new Roo.LoadMask(this.container,
50161                     Roo.apply({store:dataSource}, this.loadMask));
50162         }
50163         this.view.bind(dataSource, colModel);
50164         this.dataSource = dataSource;
50165         this.colModel = colModel;
50166         this.view.refresh(true);
50167     },
50168
50169     // private
50170     onKeyDown : function(e){
50171         this.fireEvent("keydown", e);
50172     },
50173
50174     /**
50175      * Destroy this grid.
50176      * @param {Boolean} removeEl True to remove the element
50177      */
50178     destroy : function(removeEl, keepListeners){
50179         if(this.loadMask){
50180             this.loadMask.destroy();
50181         }
50182         var c = this.container;
50183         c.removeAllListeners();
50184         this.view.destroy();
50185         this.colModel.purgeListeners();
50186         if(!keepListeners){
50187             this.purgeListeners();
50188         }
50189         c.update("");
50190         if(removeEl === true){
50191             c.remove();
50192         }
50193     },
50194
50195     // private
50196     processEvent : function(name, e){
50197         this.fireEvent(name, e);
50198         var t = e.getTarget();
50199         var v = this.view;
50200         var header = v.findHeaderIndex(t);
50201         if(header !== false){
50202             this.fireEvent("header" + name, this, header, e);
50203         }else{
50204             var row = v.findRowIndex(t);
50205             var cell = v.findCellIndex(t);
50206             if(row !== false){
50207                 this.fireEvent("row" + name, this, row, e);
50208                 if(cell !== false){
50209                     this.fireEvent("cell" + name, this, row, cell, e);
50210                 }
50211             }
50212         }
50213     },
50214
50215     // private
50216     onClick : function(e){
50217         this.processEvent("click", e);
50218     },
50219
50220     // private
50221     onContextMenu : function(e, t){
50222         this.processEvent("contextmenu", e);
50223     },
50224
50225     // private
50226     onDblClick : function(e){
50227         this.processEvent("dblclick", e);
50228     },
50229
50230     // private
50231     walkCells : function(row, col, step, fn, scope){
50232         var cm = this.colModel, clen = cm.getColumnCount();
50233         var ds = this.dataSource, rlen = ds.getCount(), first = true;
50234         if(step < 0){
50235             if(col < 0){
50236                 row--;
50237                 first = false;
50238             }
50239             while(row >= 0){
50240                 if(!first){
50241                     col = clen-1;
50242                 }
50243                 first = false;
50244                 while(col >= 0){
50245                     if(fn.call(scope || this, row, col, cm) === true){
50246                         return [row, col];
50247                     }
50248                     col--;
50249                 }
50250                 row--;
50251             }
50252         } else {
50253             if(col >= clen){
50254                 row++;
50255                 first = false;
50256             }
50257             while(row < rlen){
50258                 if(!first){
50259                     col = 0;
50260                 }
50261                 first = false;
50262                 while(col < clen){
50263                     if(fn.call(scope || this, row, col, cm) === true){
50264                         return [row, col];
50265                     }
50266                     col++;
50267                 }
50268                 row++;
50269             }
50270         }
50271         return null;
50272     },
50273
50274     // private
50275     getSelections : function(){
50276         return this.selModel.getSelections();
50277     },
50278
50279     /**
50280      * Causes the grid to manually recalculate its dimensions. Generally this is done automatically,
50281      * but if manual update is required this method will initiate it.
50282      */
50283     autoSize : function(){
50284         if(this.rendered){
50285             this.view.layout();
50286             if(this.view.adjustForScroll){
50287                 this.view.adjustForScroll();
50288             }
50289         }
50290     },
50291
50292     /**
50293      * Returns the grid's underlying element.
50294      * @return {Element} The element
50295      */
50296     getGridEl : function(){
50297         return this.container;
50298     },
50299
50300     // private for compatibility, overridden by editor grid
50301     stopEditing : function(){},
50302
50303     /**
50304      * Returns the grid's SelectionModel.
50305      * @return {SelectionModel}
50306      */
50307     getSelectionModel : function(){
50308         if(!this.selModel){
50309             this.selModel = new Roo.grid.RowSelectionModel();
50310         }
50311         return this.selModel;
50312     },
50313
50314     /**
50315      * Returns the grid's DataSource.
50316      * @return {DataSource}
50317      */
50318     getDataSource : function(){
50319         return this.dataSource;
50320     },
50321
50322     /**
50323      * Returns the grid's ColumnModel.
50324      * @return {ColumnModel}
50325      */
50326     getColumnModel : function(){
50327         return this.colModel;
50328     },
50329
50330     /**
50331      * Returns the grid's GridView object.
50332      * @return {GridView}
50333      */
50334     getView : function(){
50335         if(!this.view){
50336             this.view = new Roo.grid.GridView(this.viewConfig);
50337         }
50338         return this.view;
50339     },
50340     /**
50341      * Called to get grid's drag proxy text, by default returns this.ddText.
50342      * @return {String}
50343      */
50344     getDragDropText : function(){
50345         var count = this.selModel.getCount();
50346         return String.format(this.ddText, count, count == 1 ? '' : 's');
50347     }
50348 });
50349 /**
50350  * Configures the text is the drag proxy (defaults to "%0 selected row(s)").
50351  * %0 is replaced with the number of selected rows.
50352  * @type String
50353  */
50354 Roo.grid.Grid.prototype.ddText = "{0} selected row{1}";/*
50355  * Based on:
50356  * Ext JS Library 1.1.1
50357  * Copyright(c) 2006-2007, Ext JS, LLC.
50358  *
50359  * Originally Released Under LGPL - original licence link has changed is not relivant.
50360  *
50361  * Fork - LGPL
50362  * <script type="text/javascript">
50363  */
50364  
50365 Roo.grid.AbstractGridView = function(){
50366         this.grid = null;
50367         
50368         this.events = {
50369             "beforerowremoved" : true,
50370             "beforerowsinserted" : true,
50371             "beforerefresh" : true,
50372             "rowremoved" : true,
50373             "rowsinserted" : true,
50374             "rowupdated" : true,
50375             "refresh" : true
50376         };
50377     Roo.grid.AbstractGridView.superclass.constructor.call(this);
50378 };
50379
50380 Roo.extend(Roo.grid.AbstractGridView, Roo.util.Observable, {
50381     rowClass : "x-grid-row",
50382     cellClass : "x-grid-cell",
50383     tdClass : "x-grid-td",
50384     hdClass : "x-grid-hd",
50385     splitClass : "x-grid-hd-split",
50386     
50387         init: function(grid){
50388         this.grid = grid;
50389                 var cid = this.grid.getGridEl().id;
50390         this.colSelector = "#" + cid + " ." + this.cellClass + "-";
50391         this.tdSelector = "#" + cid + " ." + this.tdClass + "-";
50392         this.hdSelector = "#" + cid + " ." + this.hdClass + "-";
50393         this.splitSelector = "#" + cid + " ." + this.splitClass + "-";
50394         },
50395         
50396         getColumnRenderers : function(){
50397         var renderers = [];
50398         var cm = this.grid.colModel;
50399         var colCount = cm.getColumnCount();
50400         for(var i = 0; i < colCount; i++){
50401             renderers[i] = cm.getRenderer(i);
50402         }
50403         return renderers;
50404     },
50405     
50406     getColumnIds : function(){
50407         var ids = [];
50408         var cm = this.grid.colModel;
50409         var colCount = cm.getColumnCount();
50410         for(var i = 0; i < colCount; i++){
50411             ids[i] = cm.getColumnId(i);
50412         }
50413         return ids;
50414     },
50415     
50416     getDataIndexes : function(){
50417         if(!this.indexMap){
50418             this.indexMap = this.buildIndexMap();
50419         }
50420         return this.indexMap.colToData;
50421     },
50422     
50423     getColumnIndexByDataIndex : function(dataIndex){
50424         if(!this.indexMap){
50425             this.indexMap = this.buildIndexMap();
50426         }
50427         return this.indexMap.dataToCol[dataIndex];
50428     },
50429     
50430     /**
50431      * Set a css style for a column dynamically. 
50432      * @param {Number} colIndex The index of the column
50433      * @param {String} name The css property name
50434      * @param {String} value The css value
50435      */
50436     setCSSStyle : function(colIndex, name, value){
50437         var selector = "#" + this.grid.id + " .x-grid-col-" + colIndex;
50438         Roo.util.CSS.updateRule(selector, name, value);
50439     },
50440     
50441     generateRules : function(cm){
50442         var ruleBuf = [], rulesId = this.grid.id + '-cssrules';
50443         Roo.util.CSS.removeStyleSheet(rulesId);
50444         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
50445             var cid = cm.getColumnId(i);
50446             ruleBuf.push(this.colSelector, cid, " {\n", cm.config[i].css, "}\n",
50447                          this.tdSelector, cid, " {\n}\n",
50448                          this.hdSelector, cid, " {\n}\n",
50449                          this.splitSelector, cid, " {\n}\n");
50450         }
50451         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
50452     }
50453 });/*
50454  * Based on:
50455  * Ext JS Library 1.1.1
50456  * Copyright(c) 2006-2007, Ext JS, LLC.
50457  *
50458  * Originally Released Under LGPL - original licence link has changed is not relivant.
50459  *
50460  * Fork - LGPL
50461  * <script type="text/javascript">
50462  */
50463
50464 // private
50465 // This is a support class used internally by the Grid components
50466 Roo.grid.HeaderDragZone = function(grid, hd, hd2){
50467     this.grid = grid;
50468     this.view = grid.getView();
50469     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
50470     Roo.grid.HeaderDragZone.superclass.constructor.call(this, hd);
50471     if(hd2){
50472         this.setHandleElId(Roo.id(hd));
50473         this.setOuterHandleElId(Roo.id(hd2));
50474     }
50475     this.scroll = false;
50476 };
50477 Roo.extend(Roo.grid.HeaderDragZone, Roo.dd.DragZone, {
50478     maxDragWidth: 120,
50479     getDragData : function(e){
50480         var t = Roo.lib.Event.getTarget(e);
50481         var h = this.view.findHeaderCell(t);
50482         if(h){
50483             return {ddel: h.firstChild, header:h};
50484         }
50485         return false;
50486     },
50487
50488     onInitDrag : function(e){
50489         this.view.headersDisabled = true;
50490         var clone = this.dragData.ddel.cloneNode(true);
50491         clone.id = Roo.id();
50492         clone.style.width = Math.min(this.dragData.header.offsetWidth,this.maxDragWidth) + "px";
50493         this.proxy.update(clone);
50494         return true;
50495     },
50496
50497     afterValidDrop : function(){
50498         var v = this.view;
50499         setTimeout(function(){
50500             v.headersDisabled = false;
50501         }, 50);
50502     },
50503
50504     afterInvalidDrop : function(){
50505         var v = this.view;
50506         setTimeout(function(){
50507             v.headersDisabled = false;
50508         }, 50);
50509     }
50510 });
50511 /*
50512  * Based on:
50513  * Ext JS Library 1.1.1
50514  * Copyright(c) 2006-2007, Ext JS, LLC.
50515  *
50516  * Originally Released Under LGPL - original licence link has changed is not relivant.
50517  *
50518  * Fork - LGPL
50519  * <script type="text/javascript">
50520  */
50521 // private
50522 // This is a support class used internally by the Grid components
50523 Roo.grid.HeaderDropZone = function(grid, hd, hd2){
50524     this.grid = grid;
50525     this.view = grid.getView();
50526     // split the proxies so they don't interfere with mouse events
50527     this.proxyTop = Roo.DomHelper.append(document.body, {
50528         cls:"col-move-top", html:"&#160;"
50529     }, true);
50530     this.proxyBottom = Roo.DomHelper.append(document.body, {
50531         cls:"col-move-bottom", html:"&#160;"
50532     }, true);
50533     this.proxyTop.hide = this.proxyBottom.hide = function(){
50534         this.setLeftTop(-100,-100);
50535         this.setStyle("visibility", "hidden");
50536     };
50537     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
50538     // temporarily disabled
50539     //Roo.dd.ScrollManager.register(this.view.scroller.dom);
50540     Roo.grid.HeaderDropZone.superclass.constructor.call(this, grid.getGridEl().dom);
50541 };
50542 Roo.extend(Roo.grid.HeaderDropZone, Roo.dd.DropZone, {
50543     proxyOffsets : [-4, -9],
50544     fly: Roo.Element.fly,
50545
50546     getTargetFromEvent : function(e){
50547         var t = Roo.lib.Event.getTarget(e);
50548         var cindex = this.view.findCellIndex(t);
50549         if(cindex !== false){
50550             return this.view.getHeaderCell(cindex);
50551         }
50552         return null;
50553     },
50554
50555     nextVisible : function(h){
50556         var v = this.view, cm = this.grid.colModel;
50557         h = h.nextSibling;
50558         while(h){
50559             if(!cm.isHidden(v.getCellIndex(h))){
50560                 return h;
50561             }
50562             h = h.nextSibling;
50563         }
50564         return null;
50565     },
50566
50567     prevVisible : function(h){
50568         var v = this.view, cm = this.grid.colModel;
50569         h = h.prevSibling;
50570         while(h){
50571             if(!cm.isHidden(v.getCellIndex(h))){
50572                 return h;
50573             }
50574             h = h.prevSibling;
50575         }
50576         return null;
50577     },
50578
50579     positionIndicator : function(h, n, e){
50580         var x = Roo.lib.Event.getPageX(e);
50581         var r = Roo.lib.Dom.getRegion(n.firstChild);
50582         var px, pt, py = r.top + this.proxyOffsets[1];
50583         if((r.right - x) <= (r.right-r.left)/2){
50584             px = r.right+this.view.borderWidth;
50585             pt = "after";
50586         }else{
50587             px = r.left;
50588             pt = "before";
50589         }
50590         var oldIndex = this.view.getCellIndex(h);
50591         var newIndex = this.view.getCellIndex(n);
50592
50593         if(this.grid.colModel.isFixed(newIndex)){
50594             return false;
50595         }
50596
50597         var locked = this.grid.colModel.isLocked(newIndex);
50598
50599         if(pt == "after"){
50600             newIndex++;
50601         }
50602         if(oldIndex < newIndex){
50603             newIndex--;
50604         }
50605         if(oldIndex == newIndex && (locked == this.grid.colModel.isLocked(oldIndex))){
50606             return false;
50607         }
50608         px +=  this.proxyOffsets[0];
50609         this.proxyTop.setLeftTop(px, py);
50610         this.proxyTop.show();
50611         if(!this.bottomOffset){
50612             this.bottomOffset = this.view.mainHd.getHeight();
50613         }
50614         this.proxyBottom.setLeftTop(px, py+this.proxyTop.dom.offsetHeight+this.bottomOffset);
50615         this.proxyBottom.show();
50616         return pt;
50617     },
50618
50619     onNodeEnter : function(n, dd, e, data){
50620         if(data.header != n){
50621             this.positionIndicator(data.header, n, e);
50622         }
50623     },
50624
50625     onNodeOver : function(n, dd, e, data){
50626         var result = false;
50627         if(data.header != n){
50628             result = this.positionIndicator(data.header, n, e);
50629         }
50630         if(!result){
50631             this.proxyTop.hide();
50632             this.proxyBottom.hide();
50633         }
50634         return result ? this.dropAllowed : this.dropNotAllowed;
50635     },
50636
50637     onNodeOut : function(n, dd, e, data){
50638         this.proxyTop.hide();
50639         this.proxyBottom.hide();
50640     },
50641
50642     onNodeDrop : function(n, dd, e, data){
50643         var h = data.header;
50644         if(h != n){
50645             var cm = this.grid.colModel;
50646             var x = Roo.lib.Event.getPageX(e);
50647             var r = Roo.lib.Dom.getRegion(n.firstChild);
50648             var pt = (r.right - x) <= ((r.right-r.left)/2) ? "after" : "before";
50649             var oldIndex = this.view.getCellIndex(h);
50650             var newIndex = this.view.getCellIndex(n);
50651             var locked = cm.isLocked(newIndex);
50652             if(pt == "after"){
50653                 newIndex++;
50654             }
50655             if(oldIndex < newIndex){
50656                 newIndex--;
50657             }
50658             if(oldIndex == newIndex && (locked == cm.isLocked(oldIndex))){
50659                 return false;
50660             }
50661             cm.setLocked(oldIndex, locked, true);
50662             cm.moveColumn(oldIndex, newIndex);
50663             this.grid.fireEvent("columnmove", oldIndex, newIndex);
50664             return true;
50665         }
50666         return false;
50667     }
50668 });
50669 /*
50670  * Based on:
50671  * Ext JS Library 1.1.1
50672  * Copyright(c) 2006-2007, Ext JS, LLC.
50673  *
50674  * Originally Released Under LGPL - original licence link has changed is not relivant.
50675  *
50676  * Fork - LGPL
50677  * <script type="text/javascript">
50678  */
50679   
50680 /**
50681  * @class Roo.grid.GridView
50682  * @extends Roo.util.Observable
50683  *
50684  * @constructor
50685  * @param {Object} config
50686  */
50687 Roo.grid.GridView = function(config){
50688     Roo.grid.GridView.superclass.constructor.call(this);
50689     this.el = null;
50690
50691     Roo.apply(this, config);
50692 };
50693
50694 Roo.extend(Roo.grid.GridView, Roo.grid.AbstractGridView, {
50695
50696     unselectable :  'unselectable="on"',
50697     unselectableCls :  'x-unselectable',
50698     
50699     
50700     rowClass : "x-grid-row",
50701
50702     cellClass : "x-grid-col",
50703
50704     tdClass : "x-grid-td",
50705
50706     hdClass : "x-grid-hd",
50707
50708     splitClass : "x-grid-split",
50709
50710     sortClasses : ["sort-asc", "sort-desc"],
50711
50712     enableMoveAnim : false,
50713
50714     hlColor: "C3DAF9",
50715
50716     dh : Roo.DomHelper,
50717
50718     fly : Roo.Element.fly,
50719
50720     css : Roo.util.CSS,
50721
50722     borderWidth: 1,
50723
50724     splitOffset: 3,
50725
50726     scrollIncrement : 22,
50727
50728     cellRE: /(?:.*?)x-grid-(?:hd|cell|csplit)-(?:[\d]+)-([\d]+)(?:.*?)/,
50729
50730     findRE: /\s?(?:x-grid-hd|x-grid-col|x-grid-csplit)\s/,
50731
50732     bind : function(ds, cm){
50733         if(this.ds){
50734             this.ds.un("load", this.onLoad, this);
50735             this.ds.un("datachanged", this.onDataChange, this);
50736             this.ds.un("add", this.onAdd, this);
50737             this.ds.un("remove", this.onRemove, this);
50738             this.ds.un("update", this.onUpdate, this);
50739             this.ds.un("clear", this.onClear, this);
50740         }
50741         if(ds){
50742             ds.on("load", this.onLoad, this);
50743             ds.on("datachanged", this.onDataChange, this);
50744             ds.on("add", this.onAdd, this);
50745             ds.on("remove", this.onRemove, this);
50746             ds.on("update", this.onUpdate, this);
50747             ds.on("clear", this.onClear, this);
50748         }
50749         this.ds = ds;
50750
50751         if(this.cm){
50752             this.cm.un("widthchange", this.onColWidthChange, this);
50753             this.cm.un("headerchange", this.onHeaderChange, this);
50754             this.cm.un("hiddenchange", this.onHiddenChange, this);
50755             this.cm.un("columnmoved", this.onColumnMove, this);
50756             this.cm.un("columnlockchange", this.onColumnLock, this);
50757         }
50758         if(cm){
50759             this.generateRules(cm);
50760             cm.on("widthchange", this.onColWidthChange, this);
50761             cm.on("headerchange", this.onHeaderChange, this);
50762             cm.on("hiddenchange", this.onHiddenChange, this);
50763             cm.on("columnmoved", this.onColumnMove, this);
50764             cm.on("columnlockchange", this.onColumnLock, this);
50765         }
50766         this.cm = cm;
50767     },
50768
50769     init: function(grid){
50770         Roo.grid.GridView.superclass.init.call(this, grid);
50771
50772         this.bind(grid.dataSource, grid.colModel);
50773
50774         grid.on("headerclick", this.handleHeaderClick, this);
50775
50776         if(grid.trackMouseOver){
50777             grid.on("mouseover", this.onRowOver, this);
50778             grid.on("mouseout", this.onRowOut, this);
50779         }
50780         grid.cancelTextSelection = function(){};
50781         this.gridId = grid.id;
50782
50783         var tpls = this.templates || {};
50784
50785         if(!tpls.master){
50786             tpls.master = new Roo.Template(
50787                '<div class="x-grid" hidefocus="true">',
50788                 '<a href="#" class="x-grid-focus" tabIndex="-1"></a>',
50789                   '<div class="x-grid-topbar"></div>',
50790                   '<div class="x-grid-scroller"><div></div></div>',
50791                   '<div class="x-grid-locked">',
50792                       '<div class="x-grid-header">{lockedHeader}</div>',
50793                       '<div class="x-grid-body">{lockedBody}</div>',
50794                   "</div>",
50795                   '<div class="x-grid-viewport">',
50796                       '<div class="x-grid-header">{header}</div>',
50797                       '<div class="x-grid-body">{body}</div>',
50798                   "</div>",
50799                   '<div class="x-grid-bottombar"></div>',
50800                  
50801                   '<div class="x-grid-resize-proxy">&#160;</div>',
50802                "</div>"
50803             );
50804             tpls.master.disableformats = true;
50805         }
50806
50807         if(!tpls.header){
50808             tpls.header = new Roo.Template(
50809                '<table border="0" cellspacing="0" cellpadding="0">',
50810                '<tbody><tr class="x-grid-hd-row">{cells}</tr></tbody>',
50811                "</table>{splits}"
50812             );
50813             tpls.header.disableformats = true;
50814         }
50815         tpls.header.compile();
50816
50817         if(!tpls.hcell){
50818             tpls.hcell = new Roo.Template(
50819                 '<td class="x-grid-hd x-grid-td-{id} {cellId}"><div title="{title}" class="x-grid-hd-inner x-grid-hd-{id}">',
50820                 '<div class="x-grid-hd-text ' + this.unselectableCls +  '" ' + this.unselectable +'>{value}<img class="x-grid-sort-icon" src="', Roo.BLANK_IMAGE_URL, '" /></div>',
50821                 "</div></td>"
50822              );
50823              tpls.hcell.disableFormats = true;
50824         }
50825         tpls.hcell.compile();
50826
50827         if(!tpls.hsplit){
50828             tpls.hsplit = new Roo.Template('<div class="x-grid-split {splitId} x-grid-split-{id}" style="{style} ' +
50829                                             this.unselectableCls +  '" ' + this.unselectable +'>&#160;</div>');
50830             tpls.hsplit.disableFormats = true;
50831         }
50832         tpls.hsplit.compile();
50833
50834         if(!tpls.body){
50835             tpls.body = new Roo.Template(
50836                '<table border="0" cellspacing="0" cellpadding="0">',
50837                "<tbody>{rows}</tbody>",
50838                "</table>"
50839             );
50840             tpls.body.disableFormats = true;
50841         }
50842         tpls.body.compile();
50843
50844         if(!tpls.row){
50845             tpls.row = new Roo.Template('<tr class="x-grid-row {alt}">{cells}</tr>');
50846             tpls.row.disableFormats = true;
50847         }
50848         tpls.row.compile();
50849
50850         if(!tpls.cell){
50851             tpls.cell = new Roo.Template(
50852                 '<td class="x-grid-col x-grid-td-{id} {cellId} {css}" tabIndex="0">',
50853                 '<div class="x-grid-col-{id} x-grid-cell-inner"><div class="x-grid-cell-text ' +
50854                     this.unselectableCls +  '" ' + this.unselectable +'" {attr}>{value}</div></div>',
50855                 "</td>"
50856             );
50857             tpls.cell.disableFormats = true;
50858         }
50859         tpls.cell.compile();
50860
50861         this.templates = tpls;
50862     },
50863
50864     // remap these for backwards compat
50865     onColWidthChange : function(){
50866         this.updateColumns.apply(this, arguments);
50867     },
50868     onHeaderChange : function(){
50869         this.updateHeaders.apply(this, arguments);
50870     }, 
50871     onHiddenChange : function(){
50872         this.handleHiddenChange.apply(this, arguments);
50873     },
50874     onColumnMove : function(){
50875         this.handleColumnMove.apply(this, arguments);
50876     },
50877     onColumnLock : function(){
50878         this.handleLockChange.apply(this, arguments);
50879     },
50880
50881     onDataChange : function(){
50882         this.refresh();
50883         this.updateHeaderSortState();
50884     },
50885
50886     onClear : function(){
50887         this.refresh();
50888     },
50889
50890     onUpdate : function(ds, record){
50891         this.refreshRow(record);
50892     },
50893
50894     refreshRow : function(record){
50895         var ds = this.ds, index;
50896         if(typeof record == 'number'){
50897             index = record;
50898             record = ds.getAt(index);
50899         }else{
50900             index = ds.indexOf(record);
50901         }
50902         this.insertRows(ds, index, index, true);
50903         this.onRemove(ds, record, index+1, true);
50904         this.syncRowHeights(index, index);
50905         this.layout();
50906         this.fireEvent("rowupdated", this, index, record);
50907     },
50908
50909     onAdd : function(ds, records, index){
50910         this.insertRows(ds, index, index + (records.length-1));
50911     },
50912
50913     onRemove : function(ds, record, index, isUpdate){
50914         if(isUpdate !== true){
50915             this.fireEvent("beforerowremoved", this, index, record);
50916         }
50917         var bt = this.getBodyTable(), lt = this.getLockedTable();
50918         if(bt.rows[index]){
50919             bt.firstChild.removeChild(bt.rows[index]);
50920         }
50921         if(lt.rows[index]){
50922             lt.firstChild.removeChild(lt.rows[index]);
50923         }
50924         if(isUpdate !== true){
50925             this.stripeRows(index);
50926             this.syncRowHeights(index, index);
50927             this.layout();
50928             this.fireEvent("rowremoved", this, index, record);
50929         }
50930     },
50931
50932     onLoad : function(){
50933         this.scrollToTop();
50934     },
50935
50936     /**
50937      * Scrolls the grid to the top
50938      */
50939     scrollToTop : function(){
50940         if(this.scroller){
50941             this.scroller.dom.scrollTop = 0;
50942             this.syncScroll();
50943         }
50944     },
50945
50946     /**
50947      * Gets a panel in the header of the grid that can be used for toolbars etc.
50948      * After modifying the contents of this panel a call to grid.autoSize() may be
50949      * required to register any changes in size.
50950      * @param {Boolean} doShow By default the header is hidden. Pass true to show the panel
50951      * @return Roo.Element
50952      */
50953     getHeaderPanel : function(doShow){
50954         if(doShow){
50955             this.headerPanel.show();
50956         }
50957         return this.headerPanel;
50958     },
50959
50960     /**
50961      * Gets a panel in the footer of the grid that can be used for toolbars etc.
50962      * After modifying the contents of this panel a call to grid.autoSize() may be
50963      * required to register any changes in size.
50964      * @param {Boolean} doShow By default the footer is hidden. Pass true to show the panel
50965      * @return Roo.Element
50966      */
50967     getFooterPanel : function(doShow){
50968         if(doShow){
50969             this.footerPanel.show();
50970         }
50971         return this.footerPanel;
50972     },
50973
50974     initElements : function(){
50975         var E = Roo.Element;
50976         var el = this.grid.getGridEl().dom.firstChild;
50977         var cs = el.childNodes;
50978
50979         this.el = new E(el);
50980         
50981          this.focusEl = new E(el.firstChild);
50982         this.focusEl.swallowEvent("click", true);
50983         
50984         this.headerPanel = new E(cs[1]);
50985         this.headerPanel.enableDisplayMode("block");
50986
50987         this.scroller = new E(cs[2]);
50988         this.scrollSizer = new E(this.scroller.dom.firstChild);
50989
50990         this.lockedWrap = new E(cs[3]);
50991         this.lockedHd = new E(this.lockedWrap.dom.firstChild);
50992         this.lockedBody = new E(this.lockedWrap.dom.childNodes[1]);
50993
50994         this.mainWrap = new E(cs[4]);
50995         this.mainHd = new E(this.mainWrap.dom.firstChild);
50996         this.mainBody = new E(this.mainWrap.dom.childNodes[1]);
50997
50998         this.footerPanel = new E(cs[5]);
50999         this.footerPanel.enableDisplayMode("block");
51000
51001         this.resizeProxy = new E(cs[6]);
51002
51003         this.headerSelector = String.format(
51004            '#{0} td.x-grid-hd, #{1} td.x-grid-hd',
51005            this.lockedHd.id, this.mainHd.id
51006         );
51007
51008         this.splitterSelector = String.format(
51009            '#{0} div.x-grid-split, #{1} div.x-grid-split',
51010            this.idToCssName(this.lockedHd.id), this.idToCssName(this.mainHd.id)
51011         );
51012     },
51013     idToCssName : function(s)
51014     {
51015         return s.replace(/[^a-z0-9]+/ig, '-');
51016     },
51017
51018     getHeaderCell : function(index){
51019         return Roo.DomQuery.select(this.headerSelector)[index];
51020     },
51021
51022     getHeaderCellMeasure : function(index){
51023         return this.getHeaderCell(index).firstChild;
51024     },
51025
51026     getHeaderCellText : function(index){
51027         return this.getHeaderCell(index).firstChild.firstChild;
51028     },
51029
51030     getLockedTable : function(){
51031         return this.lockedBody.dom.firstChild;
51032     },
51033
51034     getBodyTable : function(){
51035         return this.mainBody.dom.firstChild;
51036     },
51037
51038     getLockedRow : function(index){
51039         return this.getLockedTable().rows[index];
51040     },
51041
51042     getRow : function(index){
51043         return this.getBodyTable().rows[index];
51044     },
51045
51046     getRowComposite : function(index){
51047         if(!this.rowEl){
51048             this.rowEl = new Roo.CompositeElementLite();
51049         }
51050         var els = [], lrow, mrow;
51051         if(lrow = this.getLockedRow(index)){
51052             els.push(lrow);
51053         }
51054         if(mrow = this.getRow(index)){
51055             els.push(mrow);
51056         }
51057         this.rowEl.elements = els;
51058         return this.rowEl;
51059     },
51060     /**
51061      * Gets the 'td' of the cell
51062      * 
51063      * @param {Integer} rowIndex row to select
51064      * @param {Integer} colIndex column to select
51065      * 
51066      * @return {Object} 
51067      */
51068     getCell : function(rowIndex, colIndex){
51069         var locked = this.cm.getLockedCount();
51070         var source;
51071         if(colIndex < locked){
51072             source = this.lockedBody.dom.firstChild;
51073         }else{
51074             source = this.mainBody.dom.firstChild;
51075             colIndex -= locked;
51076         }
51077         return source.rows[rowIndex].childNodes[colIndex];
51078     },
51079
51080     getCellText : function(rowIndex, colIndex){
51081         return this.getCell(rowIndex, colIndex).firstChild.firstChild;
51082     },
51083
51084     getCellBox : function(cell){
51085         var b = this.fly(cell).getBox();
51086         if(Roo.isOpera){ // opera fails to report the Y
51087             b.y = cell.offsetTop + this.mainBody.getY();
51088         }
51089         return b;
51090     },
51091
51092     getCellIndex : function(cell){
51093         var id = String(cell.className).match(this.cellRE);
51094         if(id){
51095             return parseInt(id[1], 10);
51096         }
51097         return 0;
51098     },
51099
51100     findHeaderIndex : function(n){
51101         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
51102         return r ? this.getCellIndex(r) : false;
51103     },
51104
51105     findHeaderCell : function(n){
51106         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
51107         return r ? r : false;
51108     },
51109
51110     findRowIndex : function(n){
51111         if(!n){
51112             return false;
51113         }
51114         var r = Roo.fly(n).findParent("tr." + this.rowClass, 6);
51115         return r ? r.rowIndex : false;
51116     },
51117
51118     findCellIndex : function(node){
51119         var stop = this.el.dom;
51120         while(node && node != stop){
51121             if(this.findRE.test(node.className)){
51122                 return this.getCellIndex(node);
51123             }
51124             node = node.parentNode;
51125         }
51126         return false;
51127     },
51128
51129     getColumnId : function(index){
51130         return this.cm.getColumnId(index);
51131     },
51132
51133     getSplitters : function()
51134     {
51135         if(this.splitterSelector){
51136            return Roo.DomQuery.select(this.splitterSelector);
51137         }else{
51138             return null;
51139       }
51140     },
51141
51142     getSplitter : function(index){
51143         return this.getSplitters()[index];
51144     },
51145
51146     onRowOver : function(e, t){
51147         var row;
51148         if((row = this.findRowIndex(t)) !== false){
51149             this.getRowComposite(row).addClass("x-grid-row-over");
51150         }
51151     },
51152
51153     onRowOut : function(e, t){
51154         var row;
51155         if((row = this.findRowIndex(t)) !== false && row !== this.findRowIndex(e.getRelatedTarget())){
51156             this.getRowComposite(row).removeClass("x-grid-row-over");
51157         }
51158     },
51159
51160     renderHeaders : function(){
51161         var cm = this.cm;
51162         var ct = this.templates.hcell, ht = this.templates.header, st = this.templates.hsplit;
51163         var cb = [], lb = [], sb = [], lsb = [], p = {};
51164         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
51165             p.cellId = "x-grid-hd-0-" + i;
51166             p.splitId = "x-grid-csplit-0-" + i;
51167             p.id = cm.getColumnId(i);
51168             p.title = cm.getColumnTooltip(i) || "";
51169             p.value = cm.getColumnHeader(i) || "";
51170             p.style = (this.grid.enableColumnResize === false || !cm.isResizable(i) || cm.isFixed(i)) ? 'cursor:default' : '';
51171             if(!cm.isLocked(i)){
51172                 cb[cb.length] = ct.apply(p);
51173                 sb[sb.length] = st.apply(p);
51174             }else{
51175                 lb[lb.length] = ct.apply(p);
51176                 lsb[lsb.length] = st.apply(p);
51177             }
51178         }
51179         return [ht.apply({cells: lb.join(""), splits:lsb.join("")}),
51180                 ht.apply({cells: cb.join(""), splits:sb.join("")})];
51181     },
51182
51183     updateHeaders : function(){
51184         var html = this.renderHeaders();
51185         this.lockedHd.update(html[0]);
51186         this.mainHd.update(html[1]);
51187     },
51188
51189     /**
51190      * Focuses the specified row.
51191      * @param {Number} row The row index
51192      */
51193     focusRow : function(row)
51194     {
51195         //Roo.log('GridView.focusRow');
51196         var x = this.scroller.dom.scrollLeft;
51197         this.focusCell(row, 0, false);
51198         this.scroller.dom.scrollLeft = x;
51199     },
51200
51201     /**
51202      * Focuses the specified cell.
51203      * @param {Number} row The row index
51204      * @param {Number} col The column index
51205      * @param {Boolean} hscroll false to disable horizontal scrolling
51206      */
51207     focusCell : function(row, col, hscroll)
51208     {
51209         //Roo.log('GridView.focusCell');
51210         var el = this.ensureVisible(row, col, hscroll);
51211         this.focusEl.alignTo(el, "tl-tl");
51212         if(Roo.isGecko){
51213             this.focusEl.focus();
51214         }else{
51215             this.focusEl.focus.defer(1, this.focusEl);
51216         }
51217     },
51218
51219     /**
51220      * Scrolls the specified cell into view
51221      * @param {Number} row The row index
51222      * @param {Number} col The column index
51223      * @param {Boolean} hscroll false to disable horizontal scrolling
51224      */
51225     ensureVisible : function(row, col, hscroll)
51226     {
51227         //Roo.log('GridView.ensureVisible,' + row + ',' + col);
51228         //return null; //disable for testing.
51229         if(typeof row != "number"){
51230             row = row.rowIndex;
51231         }
51232         if(row < 0 && row >= this.ds.getCount()){
51233             return  null;
51234         }
51235         col = (col !== undefined ? col : 0);
51236         var cm = this.grid.colModel;
51237         while(cm.isHidden(col)){
51238             col++;
51239         }
51240
51241         var el = this.getCell(row, col);
51242         if(!el){
51243             return null;
51244         }
51245         var c = this.scroller.dom;
51246
51247         var ctop = parseInt(el.offsetTop, 10);
51248         var cleft = parseInt(el.offsetLeft, 10);
51249         var cbot = ctop + el.offsetHeight;
51250         var cright = cleft + el.offsetWidth;
51251         
51252         var ch = c.clientHeight - this.mainHd.dom.offsetHeight;
51253         var stop = parseInt(c.scrollTop, 10);
51254         var sleft = parseInt(c.scrollLeft, 10);
51255         var sbot = stop + ch;
51256         var sright = sleft + c.clientWidth;
51257         /*
51258         Roo.log('GridView.ensureVisible:' +
51259                 ' ctop:' + ctop +
51260                 ' c.clientHeight:' + c.clientHeight +
51261                 ' this.mainHd.dom.offsetHeight:' + this.mainHd.dom.offsetHeight +
51262                 ' stop:' + stop +
51263                 ' cbot:' + cbot +
51264                 ' sbot:' + sbot +
51265                 ' ch:' + ch  
51266                 );
51267         */
51268         if(ctop < stop){
51269              c.scrollTop = ctop;
51270             //Roo.log("set scrolltop to ctop DISABLE?");
51271         }else if(cbot > sbot){
51272             //Roo.log("set scrolltop to cbot-ch");
51273             c.scrollTop = cbot-ch;
51274         }
51275         
51276         if(hscroll !== false){
51277             if(cleft < sleft){
51278                 c.scrollLeft = cleft;
51279             }else if(cright > sright){
51280                 c.scrollLeft = cright-c.clientWidth;
51281             }
51282         }
51283          
51284         return el;
51285     },
51286
51287     updateColumns : function(){
51288         this.grid.stopEditing();
51289         var cm = this.grid.colModel, colIds = this.getColumnIds();
51290         //var totalWidth = cm.getTotalWidth();
51291         var pos = 0;
51292         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
51293             //if(cm.isHidden(i)) continue;
51294             var w = cm.getColumnWidth(i);
51295             this.css.updateRule(this.colSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
51296             this.css.updateRule(this.hdSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
51297         }
51298         this.updateSplitters();
51299     },
51300
51301     generateRules : function(cm){
51302         var ruleBuf = [], rulesId = this.idToCssName(this.grid.id)+ '-cssrules';
51303         Roo.util.CSS.removeStyleSheet(rulesId);
51304         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
51305             var cid = cm.getColumnId(i);
51306             var align = '';
51307             if(cm.config[i].align){
51308                 align = 'text-align:'+cm.config[i].align+';';
51309             }
51310             var hidden = '';
51311             if(cm.isHidden(i)){
51312                 hidden = 'display:none;';
51313             }
51314             var width = "width:" + (cm.getColumnWidth(i) - this.borderWidth) + "px;";
51315             ruleBuf.push(
51316                     this.colSelector, cid, " {\n", cm.config[i].css, align, width, "\n}\n",
51317                     this.hdSelector, cid, " {\n", align, width, "}\n",
51318                     this.tdSelector, cid, " {\n",hidden,"\n}\n",
51319                     this.splitSelector, cid, " {\n", hidden , "\n}\n");
51320         }
51321         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
51322     },
51323
51324     updateSplitters : function(){
51325         var cm = this.cm, s = this.getSplitters();
51326         if(s){ // splitters not created yet
51327             var pos = 0, locked = true;
51328             for(var i = 0, len = cm.getColumnCount(); i < len; i++){
51329                 if(cm.isHidden(i)) continue;
51330                 var w = cm.getColumnWidth(i); // make sure it's a number
51331                 if(!cm.isLocked(i) && locked){
51332                     pos = 0;
51333                     locked = false;
51334                 }
51335                 pos += w;
51336                 s[i].style.left = (pos-this.splitOffset) + "px";
51337             }
51338         }
51339     },
51340
51341     handleHiddenChange : function(colModel, colIndex, hidden){
51342         if(hidden){
51343             this.hideColumn(colIndex);
51344         }else{
51345             this.unhideColumn(colIndex);
51346         }
51347     },
51348
51349     hideColumn : function(colIndex){
51350         var cid = this.getColumnId(colIndex);
51351         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "none");
51352         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "none");
51353         if(Roo.isSafari){
51354             this.updateHeaders();
51355         }
51356         this.updateSplitters();
51357         this.layout();
51358     },
51359
51360     unhideColumn : function(colIndex){
51361         var cid = this.getColumnId(colIndex);
51362         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "");
51363         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "");
51364
51365         if(Roo.isSafari){
51366             this.updateHeaders();
51367         }
51368         this.updateSplitters();
51369         this.layout();
51370     },
51371
51372     insertRows : function(dm, firstRow, lastRow, isUpdate){
51373         if(firstRow == 0 && lastRow == dm.getCount()-1){
51374             this.refresh();
51375         }else{
51376             if(!isUpdate){
51377                 this.fireEvent("beforerowsinserted", this, firstRow, lastRow);
51378             }
51379             var s = this.getScrollState();
51380             var markup = this.renderRows(firstRow, lastRow);
51381             this.bufferRows(markup[0], this.getLockedTable(), firstRow);
51382             this.bufferRows(markup[1], this.getBodyTable(), firstRow);
51383             this.restoreScroll(s);
51384             if(!isUpdate){
51385                 this.fireEvent("rowsinserted", this, firstRow, lastRow);
51386                 this.syncRowHeights(firstRow, lastRow);
51387                 this.stripeRows(firstRow);
51388                 this.layout();
51389             }
51390         }
51391     },
51392
51393     bufferRows : function(markup, target, index){
51394         var before = null, trows = target.rows, tbody = target.tBodies[0];
51395         if(index < trows.length){
51396             before = trows[index];
51397         }
51398         var b = document.createElement("div");
51399         b.innerHTML = "<table><tbody>"+markup+"</tbody></table>";
51400         var rows = b.firstChild.rows;
51401         for(var i = 0, len = rows.length; i < len; i++){
51402             if(before){
51403                 tbody.insertBefore(rows[0], before);
51404             }else{
51405                 tbody.appendChild(rows[0]);
51406             }
51407         }
51408         b.innerHTML = "";
51409         b = null;
51410     },
51411
51412     deleteRows : function(dm, firstRow, lastRow){
51413         if(dm.getRowCount()<1){
51414             this.fireEvent("beforerefresh", this);
51415             this.mainBody.update("");
51416             this.lockedBody.update("");
51417             this.fireEvent("refresh", this);
51418         }else{
51419             this.fireEvent("beforerowsdeleted", this, firstRow, lastRow);
51420             var bt = this.getBodyTable();
51421             var tbody = bt.firstChild;
51422             var rows = bt.rows;
51423             for(var rowIndex = firstRow; rowIndex <= lastRow; rowIndex++){
51424                 tbody.removeChild(rows[firstRow]);
51425             }
51426             this.stripeRows(firstRow);
51427             this.fireEvent("rowsdeleted", this, firstRow, lastRow);
51428         }
51429     },
51430
51431     updateRows : function(dataSource, firstRow, lastRow){
51432         var s = this.getScrollState();
51433         this.refresh();
51434         this.restoreScroll(s);
51435     },
51436
51437     handleSort : function(dataSource, sortColumnIndex, sortDir, noRefresh){
51438         if(!noRefresh){
51439            this.refresh();
51440         }
51441         this.updateHeaderSortState();
51442     },
51443
51444     getScrollState : function(){
51445         
51446         var sb = this.scroller.dom;
51447         return {left: sb.scrollLeft, top: sb.scrollTop};
51448     },
51449
51450     stripeRows : function(startRow){
51451         if(!this.grid.stripeRows || this.ds.getCount() < 1){
51452             return;
51453         }
51454         startRow = startRow || 0;
51455         var rows = this.getBodyTable().rows;
51456         var lrows = this.getLockedTable().rows;
51457         var cls = ' x-grid-row-alt ';
51458         for(var i = startRow, len = rows.length; i < len; i++){
51459             var row = rows[i], lrow = lrows[i];
51460             var isAlt = ((i+1) % 2 == 0);
51461             var hasAlt = (' '+row.className + ' ').indexOf(cls) != -1;
51462             if(isAlt == hasAlt){
51463                 continue;
51464             }
51465             if(isAlt){
51466                 row.className += " x-grid-row-alt";
51467             }else{
51468                 row.className = row.className.replace("x-grid-row-alt", "");
51469             }
51470             if(lrow){
51471                 lrow.className = row.className;
51472             }
51473         }
51474     },
51475
51476     restoreScroll : function(state){
51477         //Roo.log('GridView.restoreScroll');
51478         var sb = this.scroller.dom;
51479         sb.scrollLeft = state.left;
51480         sb.scrollTop = state.top;
51481         this.syncScroll();
51482     },
51483
51484     syncScroll : function(){
51485         //Roo.log('GridView.syncScroll');
51486         var sb = this.scroller.dom;
51487         var sh = this.mainHd.dom;
51488         var bs = this.mainBody.dom;
51489         var lv = this.lockedBody.dom;
51490         sh.scrollLeft = bs.scrollLeft = sb.scrollLeft;
51491         lv.scrollTop = bs.scrollTop = sb.scrollTop;
51492     },
51493
51494     handleScroll : function(e){
51495         this.syncScroll();
51496         var sb = this.scroller.dom;
51497         this.grid.fireEvent("bodyscroll", sb.scrollLeft, sb.scrollTop);
51498         e.stopEvent();
51499     },
51500
51501     handleWheel : function(e){
51502         var d = e.getWheelDelta();
51503         this.scroller.dom.scrollTop -= d*22;
51504         // set this here to prevent jumpy scrolling on large tables
51505         this.lockedBody.dom.scrollTop = this.mainBody.dom.scrollTop = this.scroller.dom.scrollTop;
51506         e.stopEvent();
51507     },
51508
51509     renderRows : function(startRow, endRow){
51510         // pull in all the crap needed to render rows
51511         var g = this.grid, cm = g.colModel, ds = g.dataSource, stripe = g.stripeRows;
51512         var colCount = cm.getColumnCount();
51513
51514         if(ds.getCount() < 1){
51515             return ["", ""];
51516         }
51517
51518         // build a map for all the columns
51519         var cs = [];
51520         for(var i = 0; i < colCount; i++){
51521             var name = cm.getDataIndex(i);
51522             cs[i] = {
51523                 name : typeof name == 'undefined' ? ds.fields.get(i).name : name,
51524                 renderer : cm.getRenderer(i),
51525                 id : cm.getColumnId(i),
51526                 locked : cm.isLocked(i)
51527             };
51528         }
51529
51530         startRow = startRow || 0;
51531         endRow = typeof endRow == "undefined"? ds.getCount()-1 : endRow;
51532
51533         // records to render
51534         var rs = ds.getRange(startRow, endRow);
51535
51536         return this.doRender(cs, rs, ds, startRow, colCount, stripe);
51537     },
51538
51539     // As much as I hate to duplicate code, this was branched because FireFox really hates
51540     // [].join("") on strings. The performance difference was substantial enough to
51541     // branch this function
51542     doRender : Roo.isGecko ?
51543             function(cs, rs, ds, startRow, colCount, stripe){
51544                 var ts = this.templates, ct = ts.cell, rt = ts.row;
51545                 // buffers
51546                 var buf = "", lbuf = "", cb, lcb, c, p = {}, rp = {}, r, rowIndex;
51547                 
51548                 var hasListener = this.grid.hasListener('rowclass');
51549                 var rowcfg = {};
51550                 for(var j = 0, len = rs.length; j < len; j++){
51551                     r = rs[j]; cb = ""; lcb = ""; rowIndex = (j+startRow);
51552                     for(var i = 0; i < colCount; i++){
51553                         c = cs[i];
51554                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
51555                         p.id = c.id;
51556                         p.css = p.attr = "";
51557                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
51558                         if(p.value == undefined || p.value === "") p.value = "&#160;";
51559                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
51560                             p.css += p.css ? ' x-grid-dirty-cell' : 'x-grid-dirty-cell';
51561                         }
51562                         var markup = ct.apply(p);
51563                         if(!c.locked){
51564                             cb+= markup;
51565                         }else{
51566                             lcb+= markup;
51567                         }
51568                     }
51569                     var alt = [];
51570                     if(stripe && ((rowIndex+1) % 2 == 0)){
51571                         alt.push("x-grid-row-alt")
51572                     }
51573                     if(r.dirty){
51574                         alt.push(  " x-grid-dirty-row");
51575                     }
51576                     rp.cells = lcb;
51577                     if(this.getRowClass){
51578                         alt.push(this.getRowClass(r, rowIndex));
51579                     }
51580                     if (hasListener) {
51581                         rowcfg = {
51582                              
51583                             record: r,
51584                             rowIndex : rowIndex,
51585                             rowClass : ''
51586                         }
51587                         this.grid.fireEvent('rowclass', this, rowcfg);
51588                         alt.push(rowcfg.rowClass);
51589                     }
51590                     rp.alt = alt.join(" ");
51591                     lbuf+= rt.apply(rp);
51592                     rp.cells = cb;
51593                     buf+=  rt.apply(rp);
51594                 }
51595                 return [lbuf, buf];
51596             } :
51597             function(cs, rs, ds, startRow, colCount, stripe){
51598                 var ts = this.templates, ct = ts.cell, rt = ts.row;
51599                 // buffers
51600                 var buf = [], lbuf = [], cb, lcb, c, p = {}, rp = {}, r, rowIndex;
51601                 var hasListener = this.grid.hasListener('rowclass');
51602  
51603                 var rowcfg = {};
51604                 for(var j = 0, len = rs.length; j < len; j++){
51605                     r = rs[j]; cb = []; lcb = []; rowIndex = (j+startRow);
51606                     for(var i = 0; i < colCount; i++){
51607                         c = cs[i];
51608                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
51609                         p.id = c.id;
51610                         p.css = p.attr = "";
51611                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
51612                         if(p.value == undefined || p.value === "") p.value = "&#160;";
51613                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
51614                             p.css += p.css ? ' x-grid-dirty-cell' : 'x-grid-dirty-cell';
51615                         }
51616                         
51617                         var markup = ct.apply(p);
51618                         if(!c.locked){
51619                             cb[cb.length] = markup;
51620                         }else{
51621                             lcb[lcb.length] = markup;
51622                         }
51623                     }
51624                     var alt = [];
51625                     if(stripe && ((rowIndex+1) % 2 == 0)){
51626                         alt.push( "x-grid-row-alt");
51627                     }
51628                     if(r.dirty){
51629                         alt.push(" x-grid-dirty-row");
51630                     }
51631                     rp.cells = lcb;
51632                     if(this.getRowClass){
51633                         alt.push( this.getRowClass(r, rowIndex));
51634                     }
51635                     if (hasListener) {
51636                         rowcfg = {
51637                              
51638                             record: r,
51639                             rowIndex : rowIndex,
51640                             rowClass : ''
51641                         }
51642                         this.grid.fireEvent('rowclass', this, rowcfg);
51643                         alt.push(rowcfg.rowClass);
51644                     }
51645                     rp.alt = alt.join(" ");
51646                     rp.cells = lcb.join("");
51647                     lbuf[lbuf.length] = rt.apply(rp);
51648                     rp.cells = cb.join("");
51649                     buf[buf.length] =  rt.apply(rp);
51650                 }
51651                 return [lbuf.join(""), buf.join("")];
51652             },
51653
51654     renderBody : function(){
51655         var markup = this.renderRows();
51656         var bt = this.templates.body;
51657         return [bt.apply({rows: markup[0]}), bt.apply({rows: markup[1]})];
51658     },
51659
51660     /**
51661      * Refreshes the grid
51662      * @param {Boolean} headersToo
51663      */
51664     refresh : function(headersToo){
51665         this.fireEvent("beforerefresh", this);
51666         this.grid.stopEditing();
51667         var result = this.renderBody();
51668         this.lockedBody.update(result[0]);
51669         this.mainBody.update(result[1]);
51670         if(headersToo === true){
51671             this.updateHeaders();
51672             this.updateColumns();
51673             this.updateSplitters();
51674             this.updateHeaderSortState();
51675         }
51676         this.syncRowHeights();
51677         this.layout();
51678         this.fireEvent("refresh", this);
51679     },
51680
51681     handleColumnMove : function(cm, oldIndex, newIndex){
51682         this.indexMap = null;
51683         var s = this.getScrollState();
51684         this.refresh(true);
51685         this.restoreScroll(s);
51686         this.afterMove(newIndex);
51687     },
51688
51689     afterMove : function(colIndex){
51690         if(this.enableMoveAnim && Roo.enableFx){
51691             this.fly(this.getHeaderCell(colIndex).firstChild).highlight(this.hlColor);
51692         }
51693         // if multisort - fix sortOrder, and reload..
51694         if (this.grid.dataSource.multiSort) {
51695             // the we can call sort again..
51696             var dm = this.grid.dataSource;
51697             var cm = this.grid.colModel;
51698             var so = [];
51699             for(var i = 0; i < cm.config.length; i++ ) {
51700                 
51701                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined')) {
51702                     continue; // dont' bother, it's not in sort list or being set.
51703                 }
51704                 
51705                 so.push(cm.config[i].dataIndex);
51706             };
51707             dm.sortOrder = so;
51708             dm.load(dm.lastOptions);
51709             
51710             
51711         }
51712         
51713     },
51714
51715     updateCell : function(dm, rowIndex, dataIndex){
51716         var colIndex = this.getColumnIndexByDataIndex(dataIndex);
51717         if(typeof colIndex == "undefined"){ // not present in grid
51718             return;
51719         }
51720         var cm = this.grid.colModel;
51721         var cell = this.getCell(rowIndex, colIndex);
51722         var cellText = this.getCellText(rowIndex, colIndex);
51723
51724         var p = {
51725             cellId : "x-grid-cell-" + rowIndex + "-" + colIndex,
51726             id : cm.getColumnId(colIndex),
51727             css: colIndex == cm.getColumnCount()-1 ? "x-grid-col-last" : ""
51728         };
51729         var renderer = cm.getRenderer(colIndex);
51730         var val = renderer(dm.getValueAt(rowIndex, dataIndex), p, rowIndex, colIndex, dm);
51731         if(typeof val == "undefined" || val === "") val = "&#160;";
51732         cellText.innerHTML = val;
51733         cell.className = this.cellClass + " " + this.idToCssName(p.cellId) + " " + p.css;
51734         this.syncRowHeights(rowIndex, rowIndex);
51735     },
51736
51737     calcColumnWidth : function(colIndex, maxRowsToMeasure){
51738         var maxWidth = 0;
51739         if(this.grid.autoSizeHeaders){
51740             var h = this.getHeaderCellMeasure(colIndex);
51741             maxWidth = Math.max(maxWidth, h.scrollWidth);
51742         }
51743         var tb, index;
51744         if(this.cm.isLocked(colIndex)){
51745             tb = this.getLockedTable();
51746             index = colIndex;
51747         }else{
51748             tb = this.getBodyTable();
51749             index = colIndex - this.cm.getLockedCount();
51750         }
51751         if(tb && tb.rows){
51752             var rows = tb.rows;
51753             var stopIndex = Math.min(maxRowsToMeasure || rows.length, rows.length);
51754             for(var i = 0; i < stopIndex; i++){
51755                 var cell = rows[i].childNodes[index].firstChild;
51756                 maxWidth = Math.max(maxWidth, cell.scrollWidth);
51757             }
51758         }
51759         return maxWidth + /*margin for error in IE*/ 5;
51760     },
51761     /**
51762      * Autofit a column to its content.
51763      * @param {Number} colIndex
51764      * @param {Boolean} forceMinSize true to force the column to go smaller if possible
51765      */
51766      autoSizeColumn : function(colIndex, forceMinSize, suppressEvent){
51767          if(this.cm.isHidden(colIndex)){
51768              return; // can't calc a hidden column
51769          }
51770         if(forceMinSize){
51771             var cid = this.cm.getColumnId(colIndex);
51772             this.css.updateRule(this.colSelector +this.idToCssName( cid), "width", this.grid.minColumnWidth + "px");
51773            if(this.grid.autoSizeHeaders){
51774                this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", this.grid.minColumnWidth + "px");
51775            }
51776         }
51777         var newWidth = this.calcColumnWidth(colIndex);
51778         this.cm.setColumnWidth(colIndex,
51779             Math.max(this.grid.minColumnWidth, newWidth), suppressEvent);
51780         if(!suppressEvent){
51781             this.grid.fireEvent("columnresize", colIndex, newWidth);
51782         }
51783     },
51784
51785     /**
51786      * Autofits all columns to their content and then expands to fit any extra space in the grid
51787      */
51788      autoSizeColumns : function(){
51789         var cm = this.grid.colModel;
51790         var colCount = cm.getColumnCount();
51791         for(var i = 0; i < colCount; i++){
51792             this.autoSizeColumn(i, true, true);
51793         }
51794         if(cm.getTotalWidth() < this.scroller.dom.clientWidth){
51795             this.fitColumns();
51796         }else{
51797             this.updateColumns();
51798             this.layout();
51799         }
51800     },
51801
51802     /**
51803      * Autofits all columns to the grid's width proportionate with their current size
51804      * @param {Boolean} reserveScrollSpace Reserve space for a scrollbar
51805      */
51806     fitColumns : function(reserveScrollSpace){
51807         var cm = this.grid.colModel;
51808         var colCount = cm.getColumnCount();
51809         var cols = [];
51810         var width = 0;
51811         var i, w;
51812         for (i = 0; i < colCount; i++){
51813             if(!cm.isHidden(i) && !cm.isFixed(i)){
51814                 w = cm.getColumnWidth(i);
51815                 cols.push(i);
51816                 cols.push(w);
51817                 width += w;
51818             }
51819         }
51820         var avail = Math.min(this.scroller.dom.clientWidth, this.el.getWidth());
51821         if(reserveScrollSpace){
51822             avail -= 17;
51823         }
51824         var frac = (avail - cm.getTotalWidth())/width;
51825         while (cols.length){
51826             w = cols.pop();
51827             i = cols.pop();
51828             cm.setColumnWidth(i, Math.floor(w + w*frac), true);
51829         }
51830         this.updateColumns();
51831         this.layout();
51832     },
51833
51834     onRowSelect : function(rowIndex){
51835         var row = this.getRowComposite(rowIndex);
51836         row.addClass("x-grid-row-selected");
51837     },
51838
51839     onRowDeselect : function(rowIndex){
51840         var row = this.getRowComposite(rowIndex);
51841         row.removeClass("x-grid-row-selected");
51842     },
51843
51844     onCellSelect : function(row, col){
51845         var cell = this.getCell(row, col);
51846         if(cell){
51847             Roo.fly(cell).addClass("x-grid-cell-selected");
51848         }
51849     },
51850
51851     onCellDeselect : function(row, col){
51852         var cell = this.getCell(row, col);
51853         if(cell){
51854             Roo.fly(cell).removeClass("x-grid-cell-selected");
51855         }
51856     },
51857
51858     updateHeaderSortState : function(){
51859         
51860         // sort state can be single { field: xxx, direction : yyy}
51861         // or   { xxx=>ASC , yyy : DESC ..... }
51862         
51863         var mstate = {};
51864         if (!this.ds.multiSort) { 
51865             var state = this.ds.getSortState();
51866             if(!state){
51867                 return;
51868             }
51869             mstate[state.field] = state.direction;
51870             // FIXME... - this is not used here.. but might be elsewhere..
51871             this.sortState = state;
51872             
51873         } else {
51874             mstate = this.ds.sortToggle;
51875         }
51876         //remove existing sort classes..
51877         
51878         var sc = this.sortClasses;
51879         var hds = this.el.select(this.headerSelector).removeClass(sc);
51880         
51881         for(var f in mstate) {
51882         
51883             var sortColumn = this.cm.findColumnIndex(f);
51884             
51885             if(sortColumn != -1){
51886                 var sortDir = mstate[f];        
51887                 hds.item(sortColumn).addClass(sc[sortDir == "DESC" ? 1 : 0]);
51888             }
51889         }
51890         
51891          
51892         
51893     },
51894
51895
51896     handleHeaderClick : function(g, index){
51897         if(this.headersDisabled){
51898             return;
51899         }
51900         var dm = g.dataSource, cm = g.colModel;
51901         if(!cm.isSortable(index)){
51902             return;
51903         }
51904         g.stopEditing();
51905         
51906         if (dm.multiSort) {
51907             // update the sortOrder
51908             var so = [];
51909             for(var i = 0; i < cm.config.length; i++ ) {
51910                 
51911                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined') && (index != i)) {
51912                     continue; // dont' bother, it's not in sort list or being set.
51913                 }
51914                 
51915                 so.push(cm.config[i].dataIndex);
51916             };
51917             dm.sortOrder = so;
51918         }
51919         
51920         
51921         dm.sort(cm.getDataIndex(index));
51922     },
51923
51924
51925     destroy : function(){
51926         if(this.colMenu){
51927             this.colMenu.removeAll();
51928             Roo.menu.MenuMgr.unregister(this.colMenu);
51929             this.colMenu.getEl().remove();
51930             delete this.colMenu;
51931         }
51932         if(this.hmenu){
51933             this.hmenu.removeAll();
51934             Roo.menu.MenuMgr.unregister(this.hmenu);
51935             this.hmenu.getEl().remove();
51936             delete this.hmenu;
51937         }
51938         if(this.grid.enableColumnMove){
51939             var dds = Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
51940             if(dds){
51941                 for(var dd in dds){
51942                     if(!dds[dd].config.isTarget && dds[dd].dragElId){
51943                         var elid = dds[dd].dragElId;
51944                         dds[dd].unreg();
51945                         Roo.get(elid).remove();
51946                     } else if(dds[dd].config.isTarget){
51947                         dds[dd].proxyTop.remove();
51948                         dds[dd].proxyBottom.remove();
51949                         dds[dd].unreg();
51950                     }
51951                     if(Roo.dd.DDM.locationCache[dd]){
51952                         delete Roo.dd.DDM.locationCache[dd];
51953                     }
51954                 }
51955                 delete Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
51956             }
51957         }
51958         Roo.util.CSS.removeStyleSheet(this.idToCssName(this.grid.id) + '-cssrules');
51959         this.bind(null, null);
51960         Roo.EventManager.removeResizeListener(this.onWindowResize, this);
51961     },
51962
51963     handleLockChange : function(){
51964         this.refresh(true);
51965     },
51966
51967     onDenyColumnLock : function(){
51968
51969     },
51970
51971     onDenyColumnHide : function(){
51972
51973     },
51974
51975     handleHdMenuClick : function(item){
51976         var index = this.hdCtxIndex;
51977         var cm = this.cm, ds = this.ds;
51978         switch(item.id){
51979             case "asc":
51980                 ds.sort(cm.getDataIndex(index), "ASC");
51981                 break;
51982             case "desc":
51983                 ds.sort(cm.getDataIndex(index), "DESC");
51984                 break;
51985             case "lock":
51986                 var lc = cm.getLockedCount();
51987                 if(cm.getColumnCount(true) <= lc+1){
51988                     this.onDenyColumnLock();
51989                     return;
51990                 }
51991                 if(lc != index){
51992                     cm.setLocked(index, true, true);
51993                     cm.moveColumn(index, lc);
51994                     this.grid.fireEvent("columnmove", index, lc);
51995                 }else{
51996                     cm.setLocked(index, true);
51997                 }
51998             break;
51999             case "unlock":
52000                 var lc = cm.getLockedCount();
52001                 if((lc-1) != index){
52002                     cm.setLocked(index, false, true);
52003                     cm.moveColumn(index, lc-1);
52004                     this.grid.fireEvent("columnmove", index, lc-1);
52005                 }else{
52006                     cm.setLocked(index, false);
52007                 }
52008             break;
52009             default:
52010                 index = cm.getIndexById(item.id.substr(4));
52011                 if(index != -1){
52012                     if(item.checked && cm.getColumnCount(true) <= 1){
52013                         this.onDenyColumnHide();
52014                         return false;
52015                     }
52016                     cm.setHidden(index, item.checked);
52017                 }
52018         }
52019         return true;
52020     },
52021
52022     beforeColMenuShow : function(){
52023         var cm = this.cm,  colCount = cm.getColumnCount();
52024         this.colMenu.removeAll();
52025         for(var i = 0; i < colCount; i++){
52026             this.colMenu.add(new Roo.menu.CheckItem({
52027                 id: "col-"+cm.getColumnId(i),
52028                 text: cm.getColumnHeader(i),
52029                 checked: !cm.isHidden(i),
52030                 hideOnClick:false
52031             }));
52032         }
52033     },
52034
52035     handleHdCtx : function(g, index, e){
52036         e.stopEvent();
52037         var hd = this.getHeaderCell(index);
52038         this.hdCtxIndex = index;
52039         var ms = this.hmenu.items, cm = this.cm;
52040         ms.get("asc").setDisabled(!cm.isSortable(index));
52041         ms.get("desc").setDisabled(!cm.isSortable(index));
52042         if(this.grid.enableColLock !== false){
52043             ms.get("lock").setDisabled(cm.isLocked(index));
52044             ms.get("unlock").setDisabled(!cm.isLocked(index));
52045         }
52046         this.hmenu.show(hd, "tl-bl");
52047     },
52048
52049     handleHdOver : function(e){
52050         var hd = this.findHeaderCell(e.getTarget());
52051         if(hd && !this.headersDisabled){
52052             if(this.grid.colModel.isSortable(this.getCellIndex(hd))){
52053                this.fly(hd).addClass("x-grid-hd-over");
52054             }
52055         }
52056     },
52057
52058     handleHdOut : function(e){
52059         var hd = this.findHeaderCell(e.getTarget());
52060         if(hd){
52061             this.fly(hd).removeClass("x-grid-hd-over");
52062         }
52063     },
52064
52065     handleSplitDblClick : function(e, t){
52066         var i = this.getCellIndex(t);
52067         if(this.grid.enableColumnResize !== false && this.cm.isResizable(i) && !this.cm.isFixed(i)){
52068             this.autoSizeColumn(i, true);
52069             this.layout();
52070         }
52071     },
52072
52073     render : function(){
52074
52075         var cm = this.cm;
52076         var colCount = cm.getColumnCount();
52077
52078         if(this.grid.monitorWindowResize === true){
52079             Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
52080         }
52081         var header = this.renderHeaders();
52082         var body = this.templates.body.apply({rows:""});
52083         var html = this.templates.master.apply({
52084             lockedBody: body,
52085             body: body,
52086             lockedHeader: header[0],
52087             header: header[1]
52088         });
52089
52090         //this.updateColumns();
52091
52092         this.grid.getGridEl().dom.innerHTML = html;
52093
52094         this.initElements();
52095         
52096         // a kludge to fix the random scolling effect in webkit
52097         this.el.on("scroll", function() {
52098             this.el.dom.scrollTop=0; // hopefully not recursive..
52099         },this);
52100
52101         this.scroller.on("scroll", this.handleScroll, this);
52102         this.lockedBody.on("mousewheel", this.handleWheel, this);
52103         this.mainBody.on("mousewheel", this.handleWheel, this);
52104
52105         this.mainHd.on("mouseover", this.handleHdOver, this);
52106         this.mainHd.on("mouseout", this.handleHdOut, this);
52107         this.mainHd.on("dblclick", this.handleSplitDblClick, this,
52108                 {delegate: "."+this.splitClass});
52109
52110         this.lockedHd.on("mouseover", this.handleHdOver, this);
52111         this.lockedHd.on("mouseout", this.handleHdOut, this);
52112         this.lockedHd.on("dblclick", this.handleSplitDblClick, this,
52113                 {delegate: "."+this.splitClass});
52114
52115         if(this.grid.enableColumnResize !== false && Roo.grid.SplitDragZone){
52116             new Roo.grid.SplitDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
52117         }
52118
52119         this.updateSplitters();
52120
52121         if(this.grid.enableColumnMove && Roo.grid.HeaderDragZone){
52122             new Roo.grid.HeaderDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
52123             new Roo.grid.HeaderDropZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
52124         }
52125
52126         if(this.grid.enableCtxMenu !== false && Roo.menu.Menu){
52127             this.hmenu = new Roo.menu.Menu({id: this.grid.id + "-hctx"});
52128             this.hmenu.add(
52129                 {id:"asc", text: this.sortAscText, cls: "xg-hmenu-sort-asc"},
52130                 {id:"desc", text: this.sortDescText, cls: "xg-hmenu-sort-desc"}
52131             );
52132             if(this.grid.enableColLock !== false){
52133                 this.hmenu.add('-',
52134                     {id:"lock", text: this.lockText, cls: "xg-hmenu-lock"},
52135                     {id:"unlock", text: this.unlockText, cls: "xg-hmenu-unlock"}
52136                 );
52137             }
52138             if(this.grid.enableColumnHide !== false){
52139
52140                 this.colMenu = new Roo.menu.Menu({id:this.grid.id + "-hcols-menu"});
52141                 this.colMenu.on("beforeshow", this.beforeColMenuShow, this);
52142                 this.colMenu.on("itemclick", this.handleHdMenuClick, this);
52143
52144                 this.hmenu.add('-',
52145                     {id:"columns", text: this.columnsText, menu: this.colMenu}
52146                 );
52147             }
52148             this.hmenu.on("itemclick", this.handleHdMenuClick, this);
52149
52150             this.grid.on("headercontextmenu", this.handleHdCtx, this);
52151         }
52152
52153         if((this.grid.enableDragDrop || this.grid.enableDrag) && Roo.grid.GridDragZone){
52154             this.dd = new Roo.grid.GridDragZone(this.grid, {
52155                 ddGroup : this.grid.ddGroup || 'GridDD'
52156             });
52157             
52158         }
52159
52160         /*
52161         for(var i = 0; i < colCount; i++){
52162             if(cm.isHidden(i)){
52163                 this.hideColumn(i);
52164             }
52165             if(cm.config[i].align){
52166                 this.css.updateRule(this.colSelector + i, "textAlign", cm.config[i].align);
52167                 this.css.updateRule(this.hdSelector + i, "textAlign", cm.config[i].align);
52168             }
52169         }*/
52170         
52171         this.updateHeaderSortState();
52172
52173         this.beforeInitialResize();
52174         this.layout(true);
52175
52176         // two part rendering gives faster view to the user
52177         this.renderPhase2.defer(1, this);
52178     },
52179
52180     renderPhase2 : function(){
52181         // render the rows now
52182         this.refresh();
52183         if(this.grid.autoSizeColumns){
52184             this.autoSizeColumns();
52185         }
52186     },
52187
52188     beforeInitialResize : function(){
52189
52190     },
52191
52192     onColumnSplitterMoved : function(i, w){
52193         this.userResized = true;
52194         var cm = this.grid.colModel;
52195         cm.setColumnWidth(i, w, true);
52196         var cid = cm.getColumnId(i);
52197         this.css.updateRule(this.colSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
52198         this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
52199         this.updateSplitters();
52200         this.layout();
52201         this.grid.fireEvent("columnresize", i, w);
52202     },
52203
52204     syncRowHeights : function(startIndex, endIndex){
52205         if(this.grid.enableRowHeightSync === true && this.cm.getLockedCount() > 0){
52206             startIndex = startIndex || 0;
52207             var mrows = this.getBodyTable().rows;
52208             var lrows = this.getLockedTable().rows;
52209             var len = mrows.length-1;
52210             endIndex = Math.min(endIndex || len, len);
52211             for(var i = startIndex; i <= endIndex; i++){
52212                 var m = mrows[i], l = lrows[i];
52213                 var h = Math.max(m.offsetHeight, l.offsetHeight);
52214                 m.style.height = l.style.height = h + "px";
52215             }
52216         }
52217     },
52218
52219     layout : function(initialRender, is2ndPass){
52220         var g = this.grid;
52221         var auto = g.autoHeight;
52222         var scrollOffset = 16;
52223         var c = g.getGridEl(), cm = this.cm,
52224                 expandCol = g.autoExpandColumn,
52225                 gv = this;
52226         //c.beginMeasure();
52227
52228         if(!c.dom.offsetWidth){ // display:none?
52229             if(initialRender){
52230                 this.lockedWrap.show();
52231                 this.mainWrap.show();
52232             }
52233             return;
52234         }
52235
52236         var hasLock = this.cm.isLocked(0);
52237
52238         var tbh = this.headerPanel.getHeight();
52239         var bbh = this.footerPanel.getHeight();
52240
52241         if(auto){
52242             var ch = this.getBodyTable().offsetHeight + tbh + bbh + this.mainHd.getHeight();
52243             var newHeight = ch + c.getBorderWidth("tb");
52244             if(g.maxHeight){
52245                 newHeight = Math.min(g.maxHeight, newHeight);
52246             }
52247             c.setHeight(newHeight);
52248         }
52249
52250         if(g.autoWidth){
52251             c.setWidth(cm.getTotalWidth()+c.getBorderWidth('lr'));
52252         }
52253
52254         var s = this.scroller;
52255
52256         var csize = c.getSize(true);
52257
52258         this.el.setSize(csize.width, csize.height);
52259
52260         this.headerPanel.setWidth(csize.width);
52261         this.footerPanel.setWidth(csize.width);
52262
52263         var hdHeight = this.mainHd.getHeight();
52264         var vw = csize.width;
52265         var vh = csize.height - (tbh + bbh);
52266
52267         s.setSize(vw, vh);
52268
52269         var bt = this.getBodyTable();
52270         var ltWidth = hasLock ?
52271                       Math.max(this.getLockedTable().offsetWidth, this.lockedHd.dom.firstChild.offsetWidth) : 0;
52272
52273         var scrollHeight = bt.offsetHeight;
52274         var scrollWidth = ltWidth + bt.offsetWidth;
52275         var vscroll = false, hscroll = false;
52276
52277         this.scrollSizer.setSize(scrollWidth, scrollHeight+hdHeight);
52278
52279         var lw = this.lockedWrap, mw = this.mainWrap;
52280         var lb = this.lockedBody, mb = this.mainBody;
52281
52282         setTimeout(function(){
52283             var t = s.dom.offsetTop;
52284             var w = s.dom.clientWidth,
52285                 h = s.dom.clientHeight;
52286
52287             lw.setTop(t);
52288             lw.setSize(ltWidth, h);
52289
52290             mw.setLeftTop(ltWidth, t);
52291             mw.setSize(w-ltWidth, h);
52292
52293             lb.setHeight(h-hdHeight);
52294             mb.setHeight(h-hdHeight);
52295
52296             if(is2ndPass !== true && !gv.userResized && expandCol){
52297                 // high speed resize without full column calculation
52298                 
52299                 var ci = cm.getIndexById(expandCol);
52300                 if (ci < 0) {
52301                     ci = cm.findColumnIndex(expandCol);
52302                 }
52303                 ci = Math.max(0, ci); // make sure it's got at least the first col.
52304                 var expandId = cm.getColumnId(ci);
52305                 var  tw = cm.getTotalWidth(false);
52306                 var currentWidth = cm.getColumnWidth(ci);
52307                 var cw = Math.min(Math.max(((w-tw)+currentWidth-2)-/*scrollbar*/(w <= s.dom.offsetWidth ? 0 : 18), g.autoExpandMin), g.autoExpandMax);
52308                 if(currentWidth != cw){
52309                     cm.setColumnWidth(ci, cw, true);
52310                     gv.css.updateRule(gv.colSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
52311                     gv.css.updateRule(gv.hdSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
52312                     gv.updateSplitters();
52313                     gv.layout(false, true);
52314                 }
52315             }
52316
52317             if(initialRender){
52318                 lw.show();
52319                 mw.show();
52320             }
52321             //c.endMeasure();
52322         }, 10);
52323     },
52324
52325     onWindowResize : function(){
52326         if(!this.grid.monitorWindowResize || this.grid.autoHeight){
52327             return;
52328         }
52329         this.layout();
52330     },
52331
52332     appendFooter : function(parentEl){
52333         return null;
52334     },
52335
52336     sortAscText : "Sort Ascending",
52337     sortDescText : "Sort Descending",
52338     lockText : "Lock Column",
52339     unlockText : "Unlock Column",
52340     columnsText : "Columns"
52341 });
52342
52343
52344 Roo.grid.GridView.ColumnDragZone = function(grid, hd){
52345     Roo.grid.GridView.ColumnDragZone.superclass.constructor.call(this, grid, hd, null);
52346     this.proxy.el.addClass('x-grid3-col-dd');
52347 };
52348
52349 Roo.extend(Roo.grid.GridView.ColumnDragZone, Roo.grid.HeaderDragZone, {
52350     handleMouseDown : function(e){
52351
52352     },
52353
52354     callHandleMouseDown : function(e){
52355         Roo.grid.GridView.ColumnDragZone.superclass.handleMouseDown.call(this, e);
52356     }
52357 });
52358 /*
52359  * Based on:
52360  * Ext JS Library 1.1.1
52361  * Copyright(c) 2006-2007, Ext JS, LLC.
52362  *
52363  * Originally Released Under LGPL - original licence link has changed is not relivant.
52364  *
52365  * Fork - LGPL
52366  * <script type="text/javascript">
52367  */
52368  
52369 // private
52370 // This is a support class used internally by the Grid components
52371 Roo.grid.SplitDragZone = function(grid, hd, hd2){
52372     this.grid = grid;
52373     this.view = grid.getView();
52374     this.proxy = this.view.resizeProxy;
52375     Roo.grid.SplitDragZone.superclass.constructor.call(this, hd,
52376         "gridSplitters" + this.grid.getGridEl().id, {
52377         dragElId : Roo.id(this.proxy.dom), resizeFrame:false
52378     });
52379     this.setHandleElId(Roo.id(hd));
52380     this.setOuterHandleElId(Roo.id(hd2));
52381     this.scroll = false;
52382 };
52383 Roo.extend(Roo.grid.SplitDragZone, Roo.dd.DDProxy, {
52384     fly: Roo.Element.fly,
52385
52386     b4StartDrag : function(x, y){
52387         this.view.headersDisabled = true;
52388         this.proxy.setHeight(this.view.mainWrap.getHeight());
52389         var w = this.cm.getColumnWidth(this.cellIndex);
52390         var minw = Math.max(w-this.grid.minColumnWidth, 0);
52391         this.resetConstraints();
52392         this.setXConstraint(minw, 1000);
52393         this.setYConstraint(0, 0);
52394         this.minX = x - minw;
52395         this.maxX = x + 1000;
52396         this.startPos = x;
52397         Roo.dd.DDProxy.prototype.b4StartDrag.call(this, x, y);
52398     },
52399
52400
52401     handleMouseDown : function(e){
52402         ev = Roo.EventObject.setEvent(e);
52403         var t = this.fly(ev.getTarget());
52404         if(t.hasClass("x-grid-split")){
52405             this.cellIndex = this.view.getCellIndex(t.dom);
52406             this.split = t.dom;
52407             this.cm = this.grid.colModel;
52408             if(this.cm.isResizable(this.cellIndex) && !this.cm.isFixed(this.cellIndex)){
52409                 Roo.grid.SplitDragZone.superclass.handleMouseDown.apply(this, arguments);
52410             }
52411         }
52412     },
52413
52414     endDrag : function(e){
52415         this.view.headersDisabled = false;
52416         var endX = Math.max(this.minX, Roo.lib.Event.getPageX(e));
52417         var diff = endX - this.startPos;
52418         this.view.onColumnSplitterMoved(this.cellIndex, this.cm.getColumnWidth(this.cellIndex)+diff);
52419     },
52420
52421     autoOffset : function(){
52422         this.setDelta(0,0);
52423     }
52424 });/*
52425  * Based on:
52426  * Ext JS Library 1.1.1
52427  * Copyright(c) 2006-2007, Ext JS, LLC.
52428  *
52429  * Originally Released Under LGPL - original licence link has changed is not relivant.
52430  *
52431  * Fork - LGPL
52432  * <script type="text/javascript">
52433  */
52434  
52435 // private
52436 // This is a support class used internally by the Grid components
52437 Roo.grid.GridDragZone = function(grid, config){
52438     this.view = grid.getView();
52439     Roo.grid.GridDragZone.superclass.constructor.call(this, this.view.mainBody.dom, config);
52440     if(this.view.lockedBody){
52441         this.setHandleElId(Roo.id(this.view.mainBody.dom));
52442         this.setOuterHandleElId(Roo.id(this.view.lockedBody.dom));
52443     }
52444     this.scroll = false;
52445     this.grid = grid;
52446     this.ddel = document.createElement('div');
52447     this.ddel.className = 'x-grid-dd-wrap';
52448 };
52449
52450 Roo.extend(Roo.grid.GridDragZone, Roo.dd.DragZone, {
52451     ddGroup : "GridDD",
52452
52453     getDragData : function(e){
52454         var t = Roo.lib.Event.getTarget(e);
52455         var rowIndex = this.view.findRowIndex(t);
52456         var sm = this.grid.selModel;
52457             
52458         //Roo.log(rowIndex);
52459         
52460         if (sm.getSelectedCell) {
52461             // cell selection..
52462             if (!sm.getSelectedCell()) {
52463                 return false;
52464             }
52465             if (rowIndex != sm.getSelectedCell()[0]) {
52466                 return false;
52467             }
52468         
52469         }
52470         
52471         if(rowIndex !== false){
52472             
52473             // if editorgrid.. 
52474             
52475             
52476             //Roo.log([ sm.getSelectedCell() ? sm.getSelectedCell()[0] : 'NO' , rowIndex ]);
52477                
52478             //if(!sm.isSelected(rowIndex) || e.hasModifier()){
52479               //  
52480             //}
52481             if (e.hasModifier()){
52482                 sm.handleMouseDown(e, t); // non modifier buttons are handled by row select.
52483             }
52484             
52485             Roo.log("getDragData");
52486             
52487             return {
52488                 grid: this.grid,
52489                 ddel: this.ddel,
52490                 rowIndex: rowIndex,
52491                 selections:sm.getSelections ? sm.getSelections() : (
52492                     sm.getSelectedCell() ? [ this.grid.ds.getAt(sm.getSelectedCell()[0]) ] : []
52493                 )
52494             };
52495         }
52496         return false;
52497     },
52498
52499     onInitDrag : function(e){
52500         var data = this.dragData;
52501         this.ddel.innerHTML = this.grid.getDragDropText();
52502         this.proxy.update(this.ddel);
52503         // fire start drag?
52504     },
52505
52506     afterRepair : function(){
52507         this.dragging = false;
52508     },
52509
52510     getRepairXY : function(e, data){
52511         return false;
52512     },
52513
52514     onEndDrag : function(data, e){
52515         // fire end drag?
52516     },
52517
52518     onValidDrop : function(dd, e, id){
52519         // fire drag drop?
52520         this.hideProxy();
52521     },
52522
52523     beforeInvalidDrop : function(e, id){
52524
52525     }
52526 });/*
52527  * Based on:
52528  * Ext JS Library 1.1.1
52529  * Copyright(c) 2006-2007, Ext JS, LLC.
52530  *
52531  * Originally Released Under LGPL - original licence link has changed is not relivant.
52532  *
52533  * Fork - LGPL
52534  * <script type="text/javascript">
52535  */
52536  
52537
52538 /**
52539  * @class Roo.grid.ColumnModel
52540  * @extends Roo.util.Observable
52541  * This is the default implementation of a ColumnModel used by the Grid. It defines
52542  * the columns in the grid.
52543  * <br>Usage:<br>
52544  <pre><code>
52545  var colModel = new Roo.grid.ColumnModel([
52546         {header: "Ticker", width: 60, sortable: true, locked: true},
52547         {header: "Company Name", width: 150, sortable: true},
52548         {header: "Market Cap.", width: 100, sortable: true},
52549         {header: "$ Sales", width: 100, sortable: true, renderer: money},
52550         {header: "Employees", width: 100, sortable: true, resizable: false}
52551  ]);
52552  </code></pre>
52553  * <p>
52554  
52555  * The config options listed for this class are options which may appear in each
52556  * individual column definition.
52557  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
52558  * @constructor
52559  * @param {Object} config An Array of column config objects. See this class's
52560  * config objects for details.
52561 */
52562 Roo.grid.ColumnModel = function(config){
52563         /**
52564      * The config passed into the constructor
52565      */
52566     this.config = config;
52567     this.lookup = {};
52568
52569     // if no id, create one
52570     // if the column does not have a dataIndex mapping,
52571     // map it to the order it is in the config
52572     for(var i = 0, len = config.length; i < len; i++){
52573         var c = config[i];
52574         if(typeof c.dataIndex == "undefined"){
52575             c.dataIndex = i;
52576         }
52577         if(typeof c.renderer == "string"){
52578             c.renderer = Roo.util.Format[c.renderer];
52579         }
52580         if(typeof c.id == "undefined"){
52581             c.id = Roo.id();
52582         }
52583         if(c.editor && c.editor.xtype){
52584             c.editor  = Roo.factory(c.editor, Roo.grid);
52585         }
52586         if(c.editor && c.editor.isFormField){
52587             c.editor = new Roo.grid.GridEditor(c.editor);
52588         }
52589         this.lookup[c.id] = c;
52590     }
52591
52592     /**
52593      * The width of columns which have no width specified (defaults to 100)
52594      * @type Number
52595      */
52596     this.defaultWidth = 100;
52597
52598     /**
52599      * Default sortable of columns which have no sortable specified (defaults to false)
52600      * @type Boolean
52601      */
52602     this.defaultSortable = false;
52603
52604     this.addEvents({
52605         /**
52606              * @event widthchange
52607              * Fires when the width of a column changes.
52608              * @param {ColumnModel} this
52609              * @param {Number} columnIndex The column index
52610              * @param {Number} newWidth The new width
52611              */
52612             "widthchange": true,
52613         /**
52614              * @event headerchange
52615              * Fires when the text of a header changes.
52616              * @param {ColumnModel} this
52617              * @param {Number} columnIndex The column index
52618              * @param {Number} newText The new header text
52619              */
52620             "headerchange": true,
52621         /**
52622              * @event hiddenchange
52623              * Fires when a column is hidden or "unhidden".
52624              * @param {ColumnModel} this
52625              * @param {Number} columnIndex The column index
52626              * @param {Boolean} hidden true if hidden, false otherwise
52627              */
52628             "hiddenchange": true,
52629             /**
52630          * @event columnmoved
52631          * Fires when a column is moved.
52632          * @param {ColumnModel} this
52633          * @param {Number} oldIndex
52634          * @param {Number} newIndex
52635          */
52636         "columnmoved" : true,
52637         /**
52638          * @event columlockchange
52639          * Fires when a column's locked state is changed
52640          * @param {ColumnModel} this
52641          * @param {Number} colIndex
52642          * @param {Boolean} locked true if locked
52643          */
52644         "columnlockchange" : true
52645     });
52646     Roo.grid.ColumnModel.superclass.constructor.call(this);
52647 };
52648 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
52649     /**
52650      * @cfg {String} header The header text to display in the Grid view.
52651      */
52652     /**
52653      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
52654      * {@link Roo.data.Record} definition from which to draw the column's value. If not
52655      * specified, the column's index is used as an index into the Record's data Array.
52656      */
52657     /**
52658      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
52659      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
52660      */
52661     /**
52662      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
52663      * Defaults to the value of the {@link #defaultSortable} property.
52664      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
52665      */
52666     /**
52667      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
52668      */
52669     /**
52670      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
52671      */
52672     /**
52673      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
52674      */
52675     /**
52676      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
52677      */
52678     /**
52679      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
52680      * given the cell's data value. See {@link #setRenderer}. If not specified, the
52681      * default renderer uses the raw data value.
52682      */
52683        /**
52684      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
52685      */
52686     /**
52687      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
52688      */
52689
52690     /**
52691      * Returns the id of the column at the specified index.
52692      * @param {Number} index The column index
52693      * @return {String} the id
52694      */
52695     getColumnId : function(index){
52696         return this.config[index].id;
52697     },
52698
52699     /**
52700      * Returns the column for a specified id.
52701      * @param {String} id The column id
52702      * @return {Object} the column
52703      */
52704     getColumnById : function(id){
52705         return this.lookup[id];
52706     },
52707
52708     
52709     /**
52710      * Returns the column for a specified dataIndex.
52711      * @param {String} dataIndex The column dataIndex
52712      * @return {Object|Boolean} the column or false if not found
52713      */
52714     getColumnByDataIndex: function(dataIndex){
52715         var index = this.findColumnIndex(dataIndex);
52716         return index > -1 ? this.config[index] : false;
52717     },
52718     
52719     /**
52720      * Returns the index for a specified column id.
52721      * @param {String} id The column id
52722      * @return {Number} the index, or -1 if not found
52723      */
52724     getIndexById : function(id){
52725         for(var i = 0, len = this.config.length; i < len; i++){
52726             if(this.config[i].id == id){
52727                 return i;
52728             }
52729         }
52730         return -1;
52731     },
52732     
52733     /**
52734      * Returns the index for a specified column dataIndex.
52735      * @param {String} dataIndex The column dataIndex
52736      * @return {Number} the index, or -1 if not found
52737      */
52738     
52739     findColumnIndex : function(dataIndex){
52740         for(var i = 0, len = this.config.length; i < len; i++){
52741             if(this.config[i].dataIndex == dataIndex){
52742                 return i;
52743             }
52744         }
52745         return -1;
52746     },
52747     
52748     
52749     moveColumn : function(oldIndex, newIndex){
52750         var c = this.config[oldIndex];
52751         this.config.splice(oldIndex, 1);
52752         this.config.splice(newIndex, 0, c);
52753         this.dataMap = null;
52754         this.fireEvent("columnmoved", this, oldIndex, newIndex);
52755     },
52756
52757     isLocked : function(colIndex){
52758         return this.config[colIndex].locked === true;
52759     },
52760
52761     setLocked : function(colIndex, value, suppressEvent){
52762         if(this.isLocked(colIndex) == value){
52763             return;
52764         }
52765         this.config[colIndex].locked = value;
52766         if(!suppressEvent){
52767             this.fireEvent("columnlockchange", this, colIndex, value);
52768         }
52769     },
52770
52771     getTotalLockedWidth : function(){
52772         var totalWidth = 0;
52773         for(var i = 0; i < this.config.length; i++){
52774             if(this.isLocked(i) && !this.isHidden(i)){
52775                 this.totalWidth += this.getColumnWidth(i);
52776             }
52777         }
52778         return totalWidth;
52779     },
52780
52781     getLockedCount : function(){
52782         for(var i = 0, len = this.config.length; i < len; i++){
52783             if(!this.isLocked(i)){
52784                 return i;
52785             }
52786         }
52787     },
52788
52789     /**
52790      * Returns the number of columns.
52791      * @return {Number}
52792      */
52793     getColumnCount : function(visibleOnly){
52794         if(visibleOnly === true){
52795             var c = 0;
52796             for(var i = 0, len = this.config.length; i < len; i++){
52797                 if(!this.isHidden(i)){
52798                     c++;
52799                 }
52800             }
52801             return c;
52802         }
52803         return this.config.length;
52804     },
52805
52806     /**
52807      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
52808      * @param {Function} fn
52809      * @param {Object} scope (optional)
52810      * @return {Array} result
52811      */
52812     getColumnsBy : function(fn, scope){
52813         var r = [];
52814         for(var i = 0, len = this.config.length; i < len; i++){
52815             var c = this.config[i];
52816             if(fn.call(scope||this, c, i) === true){
52817                 r[r.length] = c;
52818             }
52819         }
52820         return r;
52821     },
52822
52823     /**
52824      * Returns true if the specified column is sortable.
52825      * @param {Number} col The column index
52826      * @return {Boolean}
52827      */
52828     isSortable : function(col){
52829         if(typeof this.config[col].sortable == "undefined"){
52830             return this.defaultSortable;
52831         }
52832         return this.config[col].sortable;
52833     },
52834
52835     /**
52836      * Returns the rendering (formatting) function defined for the column.
52837      * @param {Number} col The column index.
52838      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
52839      */
52840     getRenderer : function(col){
52841         if(!this.config[col].renderer){
52842             return Roo.grid.ColumnModel.defaultRenderer;
52843         }
52844         return this.config[col].renderer;
52845     },
52846
52847     /**
52848      * Sets the rendering (formatting) function for a column.
52849      * @param {Number} col The column index
52850      * @param {Function} fn The function to use to process the cell's raw data
52851      * to return HTML markup for the grid view. The render function is called with
52852      * the following parameters:<ul>
52853      * <li>Data value.</li>
52854      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
52855      * <li>css A CSS style string to apply to the table cell.</li>
52856      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
52857      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
52858      * <li>Row index</li>
52859      * <li>Column index</li>
52860      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
52861      */
52862     setRenderer : function(col, fn){
52863         this.config[col].renderer = fn;
52864     },
52865
52866     /**
52867      * Returns the width for the specified column.
52868      * @param {Number} col The column index
52869      * @return {Number}
52870      */
52871     getColumnWidth : function(col){
52872         return this.config[col].width * 1 || this.defaultWidth;
52873     },
52874
52875     /**
52876      * Sets the width for a column.
52877      * @param {Number} col The column index
52878      * @param {Number} width The new width
52879      */
52880     setColumnWidth : function(col, width, suppressEvent){
52881         this.config[col].width = width;
52882         this.totalWidth = null;
52883         if(!suppressEvent){
52884              this.fireEvent("widthchange", this, col, width);
52885         }
52886     },
52887
52888     /**
52889      * Returns the total width of all columns.
52890      * @param {Boolean} includeHidden True to include hidden column widths
52891      * @return {Number}
52892      */
52893     getTotalWidth : function(includeHidden){
52894         if(!this.totalWidth){
52895             this.totalWidth = 0;
52896             for(var i = 0, len = this.config.length; i < len; i++){
52897                 if(includeHidden || !this.isHidden(i)){
52898                     this.totalWidth += this.getColumnWidth(i);
52899                 }
52900             }
52901         }
52902         return this.totalWidth;
52903     },
52904
52905     /**
52906      * Returns the header for the specified column.
52907      * @param {Number} col The column index
52908      * @return {String}
52909      */
52910     getColumnHeader : function(col){
52911         return this.config[col].header;
52912     },
52913
52914     /**
52915      * Sets the header for a column.
52916      * @param {Number} col The column index
52917      * @param {String} header The new header
52918      */
52919     setColumnHeader : function(col, header){
52920         this.config[col].header = header;
52921         this.fireEvent("headerchange", this, col, header);
52922     },
52923
52924     /**
52925      * Returns the tooltip for the specified column.
52926      * @param {Number} col The column index
52927      * @return {String}
52928      */
52929     getColumnTooltip : function(col){
52930             return this.config[col].tooltip;
52931     },
52932     /**
52933      * Sets the tooltip for a column.
52934      * @param {Number} col The column index
52935      * @param {String} tooltip The new tooltip
52936      */
52937     setColumnTooltip : function(col, tooltip){
52938             this.config[col].tooltip = tooltip;
52939     },
52940
52941     /**
52942      * Returns the dataIndex for the specified column.
52943      * @param {Number} col The column index
52944      * @return {Number}
52945      */
52946     getDataIndex : function(col){
52947         return this.config[col].dataIndex;
52948     },
52949
52950     /**
52951      * Sets the dataIndex for a column.
52952      * @param {Number} col The column index
52953      * @param {Number} dataIndex The new dataIndex
52954      */
52955     setDataIndex : function(col, dataIndex){
52956         this.config[col].dataIndex = dataIndex;
52957     },
52958
52959     
52960     
52961     /**
52962      * Returns true if the cell is editable.
52963      * @param {Number} colIndex The column index
52964      * @param {Number} rowIndex The row index
52965      * @return {Boolean}
52966      */
52967     isCellEditable : function(colIndex, rowIndex){
52968         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
52969     },
52970
52971     /**
52972      * Returns the editor defined for the cell/column.
52973      * return false or null to disable editing.
52974      * @param {Number} colIndex The column index
52975      * @param {Number} rowIndex The row index
52976      * @return {Object}
52977      */
52978     getCellEditor : function(colIndex, rowIndex){
52979         return this.config[colIndex].editor;
52980     },
52981
52982     /**
52983      * Sets if a column is editable.
52984      * @param {Number} col The column index
52985      * @param {Boolean} editable True if the column is editable
52986      */
52987     setEditable : function(col, editable){
52988         this.config[col].editable = editable;
52989     },
52990
52991
52992     /**
52993      * Returns true if the column is hidden.
52994      * @param {Number} colIndex The column index
52995      * @return {Boolean}
52996      */
52997     isHidden : function(colIndex){
52998         return this.config[colIndex].hidden;
52999     },
53000
53001
53002     /**
53003      * Returns true if the column width cannot be changed
53004      */
53005     isFixed : function(colIndex){
53006         return this.config[colIndex].fixed;
53007     },
53008
53009     /**
53010      * Returns true if the column can be resized
53011      * @return {Boolean}
53012      */
53013     isResizable : function(colIndex){
53014         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
53015     },
53016     /**
53017      * Sets if a column is hidden.
53018      * @param {Number} colIndex The column index
53019      * @param {Boolean} hidden True if the column is hidden
53020      */
53021     setHidden : function(colIndex, hidden){
53022         this.config[colIndex].hidden = hidden;
53023         this.totalWidth = null;
53024         this.fireEvent("hiddenchange", this, colIndex, hidden);
53025     },
53026
53027     /**
53028      * Sets the editor for a column.
53029      * @param {Number} col The column index
53030      * @param {Object} editor The editor object
53031      */
53032     setEditor : function(col, editor){
53033         this.config[col].editor = editor;
53034     }
53035 });
53036
53037 Roo.grid.ColumnModel.defaultRenderer = function(value){
53038         if(typeof value == "string" && value.length < 1){
53039             return "&#160;";
53040         }
53041         return value;
53042 };
53043
53044 // Alias for backwards compatibility
53045 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
53046 /*
53047  * Based on:
53048  * Ext JS Library 1.1.1
53049  * Copyright(c) 2006-2007, Ext JS, LLC.
53050  *
53051  * Originally Released Under LGPL - original licence link has changed is not relivant.
53052  *
53053  * Fork - LGPL
53054  * <script type="text/javascript">
53055  */
53056
53057 /**
53058  * @class Roo.grid.AbstractSelectionModel
53059  * @extends Roo.util.Observable
53060  * Abstract base class for grid SelectionModels.  It provides the interface that should be
53061  * implemented by descendant classes.  This class should not be directly instantiated.
53062  * @constructor
53063  */
53064 Roo.grid.AbstractSelectionModel = function(){
53065     this.locked = false;
53066     Roo.grid.AbstractSelectionModel.superclass.constructor.call(this);
53067 };
53068
53069 Roo.extend(Roo.grid.AbstractSelectionModel, Roo.util.Observable,  {
53070     /** @ignore Called by the grid automatically. Do not call directly. */
53071     init : function(grid){
53072         this.grid = grid;
53073         this.initEvents();
53074     },
53075
53076     /**
53077      * Locks the selections.
53078      */
53079     lock : function(){
53080         this.locked = true;
53081     },
53082
53083     /**
53084      * Unlocks the selections.
53085      */
53086     unlock : function(){
53087         this.locked = false;
53088     },
53089
53090     /**
53091      * Returns true if the selections are locked.
53092      * @return {Boolean}
53093      */
53094     isLocked : function(){
53095         return this.locked;
53096     }
53097 });/*
53098  * Based on:
53099  * Ext JS Library 1.1.1
53100  * Copyright(c) 2006-2007, Ext JS, LLC.
53101  *
53102  * Originally Released Under LGPL - original licence link has changed is not relivant.
53103  *
53104  * Fork - LGPL
53105  * <script type="text/javascript">
53106  */
53107 /**
53108  * @extends Roo.grid.AbstractSelectionModel
53109  * @class Roo.grid.RowSelectionModel
53110  * The default SelectionModel used by {@link Roo.grid.Grid}.
53111  * It supports multiple selections and keyboard selection/navigation. 
53112  * @constructor
53113  * @param {Object} config
53114  */
53115 Roo.grid.RowSelectionModel = function(config){
53116     Roo.apply(this, config);
53117     this.selections = new Roo.util.MixedCollection(false, function(o){
53118         return o.id;
53119     });
53120
53121     this.last = false;
53122     this.lastActive = false;
53123
53124     this.addEvents({
53125         /**
53126              * @event selectionchange
53127              * Fires when the selection changes
53128              * @param {SelectionModel} this
53129              */
53130             "selectionchange" : true,
53131         /**
53132              * @event afterselectionchange
53133              * Fires after the selection changes (eg. by key press or clicking)
53134              * @param {SelectionModel} this
53135              */
53136             "afterselectionchange" : true,
53137         /**
53138              * @event beforerowselect
53139              * Fires when a row is selected being selected, return false to cancel.
53140              * @param {SelectionModel} this
53141              * @param {Number} rowIndex The selected index
53142              * @param {Boolean} keepExisting False if other selections will be cleared
53143              */
53144             "beforerowselect" : true,
53145         /**
53146              * @event rowselect
53147              * Fires when a row is selected.
53148              * @param {SelectionModel} this
53149              * @param {Number} rowIndex The selected index
53150              * @param {Roo.data.Record} r The record
53151              */
53152             "rowselect" : true,
53153         /**
53154              * @event rowdeselect
53155              * Fires when a row is deselected.
53156              * @param {SelectionModel} this
53157              * @param {Number} rowIndex The selected index
53158              */
53159         "rowdeselect" : true
53160     });
53161     Roo.grid.RowSelectionModel.superclass.constructor.call(this);
53162     this.locked = false;
53163 };
53164
53165 Roo.extend(Roo.grid.RowSelectionModel, Roo.grid.AbstractSelectionModel,  {
53166     /**
53167      * @cfg {Boolean} singleSelect
53168      * True to allow selection of only one row at a time (defaults to false)
53169      */
53170     singleSelect : false,
53171
53172     // private
53173     initEvents : function(){
53174
53175         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
53176             this.grid.on("mousedown", this.handleMouseDown, this);
53177         }else{ // allow click to work like normal
53178             this.grid.on("rowclick", this.handleDragableRowClick, this);
53179         }
53180
53181         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
53182             "up" : function(e){
53183                 if(!e.shiftKey){
53184                     this.selectPrevious(e.shiftKey);
53185                 }else if(this.last !== false && this.lastActive !== false){
53186                     var last = this.last;
53187                     this.selectRange(this.last,  this.lastActive-1);
53188                     this.grid.getView().focusRow(this.lastActive);
53189                     if(last !== false){
53190                         this.last = last;
53191                     }
53192                 }else{
53193                     this.selectFirstRow();
53194                 }
53195                 this.fireEvent("afterselectionchange", this);
53196             },
53197             "down" : function(e){
53198                 if(!e.shiftKey){
53199                     this.selectNext(e.shiftKey);
53200                 }else if(this.last !== false && this.lastActive !== false){
53201                     var last = this.last;
53202                     this.selectRange(this.last,  this.lastActive+1);
53203                     this.grid.getView().focusRow(this.lastActive);
53204                     if(last !== false){
53205                         this.last = last;
53206                     }
53207                 }else{
53208                     this.selectFirstRow();
53209                 }
53210                 this.fireEvent("afterselectionchange", this);
53211             },
53212             scope: this
53213         });
53214
53215         var view = this.grid.view;
53216         view.on("refresh", this.onRefresh, this);
53217         view.on("rowupdated", this.onRowUpdated, this);
53218         view.on("rowremoved", this.onRemove, this);
53219     },
53220
53221     // private
53222     onRefresh : function(){
53223         var ds = this.grid.dataSource, i, v = this.grid.view;
53224         var s = this.selections;
53225         s.each(function(r){
53226             if((i = ds.indexOfId(r.id)) != -1){
53227                 v.onRowSelect(i);
53228             }else{
53229                 s.remove(r);
53230             }
53231         });
53232     },
53233
53234     // private
53235     onRemove : function(v, index, r){
53236         this.selections.remove(r);
53237     },
53238
53239     // private
53240     onRowUpdated : function(v, index, r){
53241         if(this.isSelected(r)){
53242             v.onRowSelect(index);
53243         }
53244     },
53245
53246     /**
53247      * Select records.
53248      * @param {Array} records The records to select
53249      * @param {Boolean} keepExisting (optional) True to keep existing selections
53250      */
53251     selectRecords : function(records, keepExisting){
53252         if(!keepExisting){
53253             this.clearSelections();
53254         }
53255         var ds = this.grid.dataSource;
53256         for(var i = 0, len = records.length; i < len; i++){
53257             this.selectRow(ds.indexOf(records[i]), true);
53258         }
53259     },
53260
53261     /**
53262      * Gets the number of selected rows.
53263      * @return {Number}
53264      */
53265     getCount : function(){
53266         return this.selections.length;
53267     },
53268
53269     /**
53270      * Selects the first row in the grid.
53271      */
53272     selectFirstRow : function(){
53273         this.selectRow(0);
53274     },
53275
53276     /**
53277      * Select the last row.
53278      * @param {Boolean} keepExisting (optional) True to keep existing selections
53279      */
53280     selectLastRow : function(keepExisting){
53281         this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
53282     },
53283
53284     /**
53285      * Selects the row immediately following the last selected row.
53286      * @param {Boolean} keepExisting (optional) True to keep existing selections
53287      */
53288     selectNext : function(keepExisting){
53289         if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
53290             this.selectRow(this.last+1, keepExisting);
53291             this.grid.getView().focusRow(this.last);
53292         }
53293     },
53294
53295     /**
53296      * Selects the row that precedes the last selected row.
53297      * @param {Boolean} keepExisting (optional) True to keep existing selections
53298      */
53299     selectPrevious : function(keepExisting){
53300         if(this.last){
53301             this.selectRow(this.last-1, keepExisting);
53302             this.grid.getView().focusRow(this.last);
53303         }
53304     },
53305
53306     /**
53307      * Returns the selected records
53308      * @return {Array} Array of selected records
53309      */
53310     getSelections : function(){
53311         return [].concat(this.selections.items);
53312     },
53313
53314     /**
53315      * Returns the first selected record.
53316      * @return {Record}
53317      */
53318     getSelected : function(){
53319         return this.selections.itemAt(0);
53320     },
53321
53322
53323     /**
53324      * Clears all selections.
53325      */
53326     clearSelections : function(fast){
53327         if(this.locked) return;
53328         if(fast !== true){
53329             var ds = this.grid.dataSource;
53330             var s = this.selections;
53331             s.each(function(r){
53332                 this.deselectRow(ds.indexOfId(r.id));
53333             }, this);
53334             s.clear();
53335         }else{
53336             this.selections.clear();
53337         }
53338         this.last = false;
53339     },
53340
53341
53342     /**
53343      * Selects all rows.
53344      */
53345     selectAll : function(){
53346         if(this.locked) return;
53347         this.selections.clear();
53348         for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
53349             this.selectRow(i, true);
53350         }
53351     },
53352
53353     /**
53354      * Returns True if there is a selection.
53355      * @return {Boolean}
53356      */
53357     hasSelection : function(){
53358         return this.selections.length > 0;
53359     },
53360
53361     /**
53362      * Returns True if the specified row is selected.
53363      * @param {Number/Record} record The record or index of the record to check
53364      * @return {Boolean}
53365      */
53366     isSelected : function(index){
53367         var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
53368         return (r && this.selections.key(r.id) ? true : false);
53369     },
53370
53371     /**
53372      * Returns True if the specified record id is selected.
53373      * @param {String} id The id of record to check
53374      * @return {Boolean}
53375      */
53376     isIdSelected : function(id){
53377         return (this.selections.key(id) ? true : false);
53378     },
53379
53380     // private
53381     handleMouseDown : function(e, t){
53382         var view = this.grid.getView(), rowIndex;
53383         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
53384             return;
53385         };
53386         if(e.shiftKey && this.last !== false){
53387             var last = this.last;
53388             this.selectRange(last, rowIndex, e.ctrlKey);
53389             this.last = last; // reset the last
53390             view.focusRow(rowIndex);
53391         }else{
53392             var isSelected = this.isSelected(rowIndex);
53393             if(e.button !== 0 && isSelected){
53394                 view.focusRow(rowIndex);
53395             }else if(e.ctrlKey && isSelected){
53396                 this.deselectRow(rowIndex);
53397             }else if(!isSelected){
53398                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
53399                 view.focusRow(rowIndex);
53400             }
53401         }
53402         this.fireEvent("afterselectionchange", this);
53403     },
53404     // private
53405     handleDragableRowClick :  function(grid, rowIndex, e) 
53406     {
53407         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
53408             this.selectRow(rowIndex, false);
53409             grid.view.focusRow(rowIndex);
53410              this.fireEvent("afterselectionchange", this);
53411         }
53412     },
53413     
53414     /**
53415      * Selects multiple rows.
53416      * @param {Array} rows Array of the indexes of the row to select
53417      * @param {Boolean} keepExisting (optional) True to keep existing selections
53418      */
53419     selectRows : function(rows, keepExisting){
53420         if(!keepExisting){
53421             this.clearSelections();
53422         }
53423         for(var i = 0, len = rows.length; i < len; i++){
53424             this.selectRow(rows[i], true);
53425         }
53426     },
53427
53428     /**
53429      * Selects a range of rows. All rows in between startRow and endRow are also selected.
53430      * @param {Number} startRow The index of the first row in the range
53431      * @param {Number} endRow The index of the last row in the range
53432      * @param {Boolean} keepExisting (optional) True to retain existing selections
53433      */
53434     selectRange : function(startRow, endRow, keepExisting){
53435         if(this.locked) return;
53436         if(!keepExisting){
53437             this.clearSelections();
53438         }
53439         if(startRow <= endRow){
53440             for(var i = startRow; i <= endRow; i++){
53441                 this.selectRow(i, true);
53442             }
53443         }else{
53444             for(var i = startRow; i >= endRow; i--){
53445                 this.selectRow(i, true);
53446             }
53447         }
53448     },
53449
53450     /**
53451      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
53452      * @param {Number} startRow The index of the first row in the range
53453      * @param {Number} endRow The index of the last row in the range
53454      */
53455     deselectRange : function(startRow, endRow, preventViewNotify){
53456         if(this.locked) return;
53457         for(var i = startRow; i <= endRow; i++){
53458             this.deselectRow(i, preventViewNotify);
53459         }
53460     },
53461
53462     /**
53463      * Selects a row.
53464      * @param {Number} row The index of the row to select
53465      * @param {Boolean} keepExisting (optional) True to keep existing selections
53466      */
53467     selectRow : function(index, keepExisting, preventViewNotify){
53468         if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) return;
53469         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
53470             if(!keepExisting || this.singleSelect){
53471                 this.clearSelections();
53472             }
53473             var r = this.grid.dataSource.getAt(index);
53474             this.selections.add(r);
53475             this.last = this.lastActive = index;
53476             if(!preventViewNotify){
53477                 this.grid.getView().onRowSelect(index);
53478             }
53479             this.fireEvent("rowselect", this, index, r);
53480             this.fireEvent("selectionchange", this);
53481         }
53482     },
53483
53484     /**
53485      * Deselects a row.
53486      * @param {Number} row The index of the row to deselect
53487      */
53488     deselectRow : function(index, preventViewNotify){
53489         if(this.locked) return;
53490         if(this.last == index){
53491             this.last = false;
53492         }
53493         if(this.lastActive == index){
53494             this.lastActive = false;
53495         }
53496         var r = this.grid.dataSource.getAt(index);
53497         this.selections.remove(r);
53498         if(!preventViewNotify){
53499             this.grid.getView().onRowDeselect(index);
53500         }
53501         this.fireEvent("rowdeselect", this, index);
53502         this.fireEvent("selectionchange", this);
53503     },
53504
53505     // private
53506     restoreLast : function(){
53507         if(this._last){
53508             this.last = this._last;
53509         }
53510     },
53511
53512     // private
53513     acceptsNav : function(row, col, cm){
53514         return !cm.isHidden(col) && cm.isCellEditable(col, row);
53515     },
53516
53517     // private
53518     onEditorKey : function(field, e){
53519         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
53520         if(k == e.TAB){
53521             e.stopEvent();
53522             ed.completeEdit();
53523             if(e.shiftKey){
53524                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
53525             }else{
53526                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
53527             }
53528         }else if(k == e.ENTER && !e.ctrlKey){
53529             e.stopEvent();
53530             ed.completeEdit();
53531             if(e.shiftKey){
53532                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
53533             }else{
53534                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
53535             }
53536         }else if(k == e.ESC){
53537             ed.cancelEdit();
53538         }
53539         if(newCell){
53540             g.startEditing(newCell[0], newCell[1]);
53541         }
53542     }
53543 });/*
53544  * Based on:
53545  * Ext JS Library 1.1.1
53546  * Copyright(c) 2006-2007, Ext JS, LLC.
53547  *
53548  * Originally Released Under LGPL - original licence link has changed is not relivant.
53549  *
53550  * Fork - LGPL
53551  * <script type="text/javascript">
53552  */
53553 /**
53554  * @class Roo.grid.CellSelectionModel
53555  * @extends Roo.grid.AbstractSelectionModel
53556  * This class provides the basic implementation for cell selection in a grid.
53557  * @constructor
53558  * @param {Object} config The object containing the configuration of this model.
53559  * @cfg {Boolean} enter_is_tab Enter behaves the same as tab. (eg. goes to next cell) default: false
53560  */
53561 Roo.grid.CellSelectionModel = function(config){
53562     Roo.apply(this, config);
53563
53564     this.selection = null;
53565
53566     this.addEvents({
53567         /**
53568              * @event beforerowselect
53569              * Fires before a cell is selected.
53570              * @param {SelectionModel} this
53571              * @param {Number} rowIndex The selected row index
53572              * @param {Number} colIndex The selected cell index
53573              */
53574             "beforecellselect" : true,
53575         /**
53576              * @event cellselect
53577              * Fires when a cell is selected.
53578              * @param {SelectionModel} this
53579              * @param {Number} rowIndex The selected row index
53580              * @param {Number} colIndex The selected cell index
53581              */
53582             "cellselect" : true,
53583         /**
53584              * @event selectionchange
53585              * Fires when the active selection changes.
53586              * @param {SelectionModel} this
53587              * @param {Object} selection null for no selection or an object (o) with two properties
53588                 <ul>
53589                 <li>o.record: the record object for the row the selection is in</li>
53590                 <li>o.cell: An array of [rowIndex, columnIndex]</li>
53591                 </ul>
53592              */
53593             "selectionchange" : true,
53594         /**
53595              * @event tabend
53596              * Fires when the tab (or enter) was pressed on the last editable cell
53597              * You can use this to trigger add new row.
53598              * @param {SelectionModel} this
53599              */
53600             "tabend" : true,
53601          /**
53602              * @event beforeeditnext
53603              * Fires before the next editable sell is made active
53604              * You can use this to skip to another cell or fire the tabend
53605              *    if you set cell to false
53606              * @param {Object} eventdata object : { cell : [ row, col ] } 
53607              */
53608             "beforeeditnext" : true
53609     });
53610     Roo.grid.CellSelectionModel.superclass.constructor.call(this);
53611 };
53612
53613 Roo.extend(Roo.grid.CellSelectionModel, Roo.grid.AbstractSelectionModel,  {
53614     
53615     enter_is_tab: false,
53616
53617     /** @ignore */
53618     initEvents : function(){
53619         this.grid.on("mousedown", this.handleMouseDown, this);
53620         this.grid.getGridEl().on(Roo.isIE ? "keydown" : "keypress", this.handleKeyDown, this);
53621         var view = this.grid.view;
53622         view.on("refresh", this.onViewChange, this);
53623         view.on("rowupdated", this.onRowUpdated, this);
53624         view.on("beforerowremoved", this.clearSelections, this);
53625         view.on("beforerowsinserted", this.clearSelections, this);
53626         if(this.grid.isEditor){
53627             this.grid.on("beforeedit", this.beforeEdit,  this);
53628         }
53629     },
53630
53631         //private
53632     beforeEdit : function(e){
53633         this.select(e.row, e.column, false, true, e.record);
53634     },
53635
53636         //private
53637     onRowUpdated : function(v, index, r){
53638         if(this.selection && this.selection.record == r){
53639             v.onCellSelect(index, this.selection.cell[1]);
53640         }
53641     },
53642
53643         //private
53644     onViewChange : function(){
53645         this.clearSelections(true);
53646     },
53647
53648         /**
53649          * Returns the currently selected cell,.
53650          * @return {Array} The selected cell (row, column) or null if none selected.
53651          */
53652     getSelectedCell : function(){
53653         return this.selection ? this.selection.cell : null;
53654     },
53655
53656     /**
53657      * Clears all selections.
53658      * @param {Boolean} true to prevent the gridview from being notified about the change.
53659      */
53660     clearSelections : function(preventNotify){
53661         var s = this.selection;
53662         if(s){
53663             if(preventNotify !== true){
53664                 this.grid.view.onCellDeselect(s.cell[0], s.cell[1]);
53665             }
53666             this.selection = null;
53667             this.fireEvent("selectionchange", this, null);
53668         }
53669     },
53670
53671     /**
53672      * Returns true if there is a selection.
53673      * @return {Boolean}
53674      */
53675     hasSelection : function(){
53676         return this.selection ? true : false;
53677     },
53678
53679     /** @ignore */
53680     handleMouseDown : function(e, t){
53681         var v = this.grid.getView();
53682         if(this.isLocked()){
53683             return;
53684         };
53685         var row = v.findRowIndex(t);
53686         var cell = v.findCellIndex(t);
53687         if(row !== false && cell !== false){
53688             this.select(row, cell);
53689         }
53690     },
53691
53692     /**
53693      * Selects a cell.
53694      * @param {Number} rowIndex
53695      * @param {Number} collIndex
53696      */
53697     select : function(rowIndex, colIndex, preventViewNotify, preventFocus, /*internal*/ r){
53698         if(this.fireEvent("beforecellselect", this, rowIndex, colIndex) !== false){
53699             this.clearSelections();
53700             r = r || this.grid.dataSource.getAt(rowIndex);
53701             this.selection = {
53702                 record : r,
53703                 cell : [rowIndex, colIndex]
53704             };
53705             if(!preventViewNotify){
53706                 var v = this.grid.getView();
53707                 v.onCellSelect(rowIndex, colIndex);
53708                 if(preventFocus !== true){
53709                     v.focusCell(rowIndex, colIndex);
53710                 }
53711             }
53712             this.fireEvent("cellselect", this, rowIndex, colIndex);
53713             this.fireEvent("selectionchange", this, this.selection);
53714         }
53715     },
53716
53717         //private
53718     isSelectable : function(rowIndex, colIndex, cm){
53719         return !cm.isHidden(colIndex);
53720     },
53721
53722     /** @ignore */
53723     handleKeyDown : function(e){
53724         //Roo.log('Cell Sel Model handleKeyDown');
53725         if(!e.isNavKeyPress()){
53726             return;
53727         }
53728         var g = this.grid, s = this.selection;
53729         if(!s){
53730             e.stopEvent();
53731             var cell = g.walkCells(0, 0, 1, this.isSelectable,  this);
53732             if(cell){
53733                 this.select(cell[0], cell[1]);
53734             }
53735             return;
53736         }
53737         var sm = this;
53738         var walk = function(row, col, step){
53739             return g.walkCells(row, col, step, sm.isSelectable,  sm);
53740         };
53741         var k = e.getKey(), r = s.cell[0], c = s.cell[1];
53742         var newCell;
53743
53744       
53745
53746         switch(k){
53747             case e.TAB:
53748                 // handled by onEditorKey
53749                 if (g.isEditor && g.editing) {
53750                     return;
53751                 }
53752                 if(e.shiftKey) {
53753                     newCell = walk(r, c-1, -1);
53754                 } else {
53755                     newCell = walk(r, c+1, 1);
53756                 }
53757                 break;
53758             
53759             case e.DOWN:
53760                newCell = walk(r+1, c, 1);
53761                 break;
53762             
53763             case e.UP:
53764                 newCell = walk(r-1, c, -1);
53765                 break;
53766             
53767             case e.RIGHT:
53768                 newCell = walk(r, c+1, 1);
53769                 break;
53770             
53771             case e.LEFT:
53772                 newCell = walk(r, c-1, -1);
53773                 break;
53774             
53775             case e.ENTER:
53776                 
53777                 if(g.isEditor && !g.editing){
53778                    g.startEditing(r, c);
53779                    e.stopEvent();
53780                    return;
53781                 }
53782                 
53783                 
53784              break;
53785         };
53786         if(newCell){
53787             this.select(newCell[0], newCell[1]);
53788             e.stopEvent();
53789             
53790         }
53791     },
53792
53793     acceptsNav : function(row, col, cm){
53794         return !cm.isHidden(col) && cm.isCellEditable(col, row);
53795     },
53796     /**
53797      * Selects a cell.
53798      * @param {Number} field (not used) - as it's normally used as a listener
53799      * @param {Number} e - event - fake it by using
53800      *
53801      * var e = Roo.EventObjectImpl.prototype;
53802      * e.keyCode = e.TAB
53803      *
53804      * 
53805      */
53806     onEditorKey : function(field, e){
53807         
53808         var k = e.getKey(),
53809             newCell,
53810             g = this.grid,
53811             ed = g.activeEditor,
53812             forward = false;
53813         ///Roo.log('onEditorKey' + k);
53814         
53815         
53816         if (this.enter_is_tab && k == e.ENTER) {
53817             k = e.TAB;
53818         }
53819         
53820         if(k == e.TAB){
53821             if(e.shiftKey){
53822                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
53823             }else{
53824                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
53825                 forward = true;
53826             }
53827             
53828             e.stopEvent();
53829             
53830         } else if(k == e.ENTER &&  !e.ctrlKey){
53831             ed.completeEdit();
53832             e.stopEvent();
53833             newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
53834         
53835                 } else if(k == e.ESC){
53836             ed.cancelEdit();
53837         }
53838                 
53839         if (newCell) {
53840             var ecall = { cell : newCell, forward : forward };
53841             this.fireEvent('beforeeditnext', ecall );
53842             newCell = ecall.cell;
53843                         forward = ecall.forward;
53844         }
53845                 
53846         if(newCell){
53847             //Roo.log('next cell after edit');
53848             g.startEditing.defer(100, g, [newCell[0], newCell[1]]);
53849         } else if (forward) {
53850             // tabbed past last
53851             this.fireEvent.defer(100, this, ['tabend',this]);
53852         }
53853     }
53854 });/*
53855  * Based on:
53856  * Ext JS Library 1.1.1
53857  * Copyright(c) 2006-2007, Ext JS, LLC.
53858  *
53859  * Originally Released Under LGPL - original licence link has changed is not relivant.
53860  *
53861  * Fork - LGPL
53862  * <script type="text/javascript">
53863  */
53864  
53865 /**
53866  * @class Roo.grid.EditorGrid
53867  * @extends Roo.grid.Grid
53868  * Class for creating and editable grid.
53869  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered - 
53870  * The container MUST have some type of size defined for the grid to fill. The container will be 
53871  * automatically set to position relative if it isn't already.
53872  * @param {Object} dataSource The data model to bind to
53873  * @param {Object} colModel The column model with info about this grid's columns
53874  */
53875 Roo.grid.EditorGrid = function(container, config){
53876     Roo.grid.EditorGrid.superclass.constructor.call(this, container, config);
53877     this.getGridEl().addClass("xedit-grid");
53878
53879     if(!this.selModel){
53880         this.selModel = new Roo.grid.CellSelectionModel();
53881     }
53882
53883     this.activeEditor = null;
53884
53885         this.addEvents({
53886             /**
53887              * @event beforeedit
53888              * Fires before cell editing is triggered. The edit event object has the following properties <br />
53889              * <ul style="padding:5px;padding-left:16px;">
53890              * <li>grid - This grid</li>
53891              * <li>record - The record being edited</li>
53892              * <li>field - The field name being edited</li>
53893              * <li>value - The value for the field being edited.</li>
53894              * <li>row - The grid row index</li>
53895              * <li>column - The grid column index</li>
53896              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
53897              * </ul>
53898              * @param {Object} e An edit event (see above for description)
53899              */
53900             "beforeedit" : true,
53901             /**
53902              * @event afteredit
53903              * Fires after a cell is edited. <br />
53904              * <ul style="padding:5px;padding-left:16px;">
53905              * <li>grid - This grid</li>
53906              * <li>record - The record being edited</li>
53907              * <li>field - The field name being edited</li>
53908              * <li>value - The value being set</li>
53909              * <li>originalValue - The original value for the field, before the edit.</li>
53910              * <li>row - The grid row index</li>
53911              * <li>column - The grid column index</li>
53912              * </ul>
53913              * @param {Object} e An edit event (see above for description)
53914              */
53915             "afteredit" : true,
53916             /**
53917              * @event validateedit
53918              * Fires after a cell is edited, but before the value is set in the record. 
53919          * You can use this to modify the value being set in the field, Return false
53920              * to cancel the change. The edit event object has the following properties <br />
53921              * <ul style="padding:5px;padding-left:16px;">
53922          * <li>editor - This editor</li>
53923              * <li>grid - This grid</li>
53924              * <li>record - The record being edited</li>
53925              * <li>field - The field name being edited</li>
53926              * <li>value - The value being set</li>
53927              * <li>originalValue - The original value for the field, before the edit.</li>
53928              * <li>row - The grid row index</li>
53929              * <li>column - The grid column index</li>
53930              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
53931              * </ul>
53932              * @param {Object} e An edit event (see above for description)
53933              */
53934             "validateedit" : true
53935         });
53936     this.on("bodyscroll", this.stopEditing,  this);
53937     this.on(this.clicksToEdit == 1 ? "cellclick" : "celldblclick", this.onCellDblClick,  this);
53938 };
53939
53940 Roo.extend(Roo.grid.EditorGrid, Roo.grid.Grid, {
53941     /**
53942      * @cfg {Number} clicksToEdit
53943      * The number of clicks on a cell required to display the cell's editor (defaults to 2)
53944      */
53945     clicksToEdit: 2,
53946
53947     // private
53948     isEditor : true,
53949     // private
53950     trackMouseOver: false, // causes very odd FF errors
53951
53952     onCellDblClick : function(g, row, col){
53953         this.startEditing(row, col);
53954     },
53955
53956     onEditComplete : function(ed, value, startValue){
53957         this.editing = false;
53958         this.activeEditor = null;
53959         ed.un("specialkey", this.selModel.onEditorKey, this.selModel);
53960         var r = ed.record;
53961         var field = this.colModel.getDataIndex(ed.col);
53962         var e = {
53963             grid: this,
53964             record: r,
53965             field: field,
53966             originalValue: startValue,
53967             value: value,
53968             row: ed.row,
53969             column: ed.col,
53970             cancel:false,
53971             editor: ed
53972         };
53973         var cell = Roo.get(this.view.getCell(ed.row,ed.col))
53974         cell.show();
53975           
53976         if(String(value) !== String(startValue)){
53977             
53978             if(this.fireEvent("validateedit", e) !== false && !e.cancel){
53979                 r.set(field, e.value);
53980                 // if we are dealing with a combo box..
53981                 // then we also set the 'name' colum to be the displayField
53982                 if (ed.field.displayField && ed.field.name) {
53983                     r.set(ed.field.name, ed.field.el.dom.value);
53984                 }
53985                 
53986                 delete e.cancel; //?? why!!!
53987                 this.fireEvent("afteredit", e);
53988             }
53989         } else {
53990             this.fireEvent("afteredit", e); // always fire it!
53991         }
53992         this.view.focusCell(ed.row, ed.col);
53993     },
53994
53995     /**
53996      * Starts editing the specified for the specified row/column
53997      * @param {Number} rowIndex
53998      * @param {Number} colIndex
53999      */
54000     startEditing : function(row, col){
54001         this.stopEditing();
54002         if(this.colModel.isCellEditable(col, row)){
54003             this.view.ensureVisible(row, col, true);
54004           
54005             var r = this.dataSource.getAt(row);
54006             var field = this.colModel.getDataIndex(col);
54007             var cell = Roo.get(this.view.getCell(row,col));
54008             var e = {
54009                 grid: this,
54010                 record: r,
54011                 field: field,
54012                 value: r.data[field],
54013                 row: row,
54014                 column: col,
54015                 cancel:false 
54016             };
54017             if(this.fireEvent("beforeedit", e) !== false && !e.cancel){
54018                 this.editing = true;
54019                 var ed = this.colModel.getCellEditor(col, row);
54020                 
54021                 if (!ed) {
54022                     return;
54023                 }
54024                 if(!ed.rendered){
54025                     ed.render(ed.parentEl || document.body);
54026                 }
54027                 ed.field.reset();
54028                
54029                 cell.hide();
54030                 
54031                 (function(){ // complex but required for focus issues in safari, ie and opera
54032                     ed.row = row;
54033                     ed.col = col;
54034                     ed.record = r;
54035                     ed.on("complete",   this.onEditComplete,        this,       {single: true});
54036                     ed.on("specialkey", this.selModel.onEditorKey,  this.selModel);
54037                     this.activeEditor = ed;
54038                     var v = r.data[field];
54039                     ed.startEdit(this.view.getCell(row, col), v);
54040                     // combo's with 'displayField and name set
54041                     if (ed.field.displayField && ed.field.name) {
54042                         ed.field.el.dom.value = r.data[ed.field.name];
54043                     }
54044                     
54045                     
54046                 }).defer(50, this);
54047             }
54048         }
54049     },
54050         
54051     /**
54052      * Stops any active editing
54053      */
54054     stopEditing : function(){
54055         if(this.activeEditor){
54056             this.activeEditor.completeEdit();
54057         }
54058         this.activeEditor = null;
54059     },
54060         
54061          /**
54062      * Called to get grid's drag proxy text, by default returns this.ddText.
54063      * @return {String}
54064      */
54065     getDragDropText : function(){
54066         var count = this.selModel.getSelectedCell() ? 1 : 0;
54067         return String.format(this.ddText, count, count == 1 ? '' : 's');
54068     }
54069         
54070 });/*
54071  * Based on:
54072  * Ext JS Library 1.1.1
54073  * Copyright(c) 2006-2007, Ext JS, LLC.
54074  *
54075  * Originally Released Under LGPL - original licence link has changed is not relivant.
54076  *
54077  * Fork - LGPL
54078  * <script type="text/javascript">
54079  */
54080
54081 // private - not really -- you end up using it !
54082 // This is a support class used internally by the Grid components
54083
54084 /**
54085  * @class Roo.grid.GridEditor
54086  * @extends Roo.Editor
54087  * Class for creating and editable grid elements.
54088  * @param {Object} config any settings (must include field)
54089  */
54090 Roo.grid.GridEditor = function(field, config){
54091     if (!config && field.field) {
54092         config = field;
54093         field = Roo.factory(config.field, Roo.form);
54094     }
54095     Roo.grid.GridEditor.superclass.constructor.call(this, field, config);
54096     field.monitorTab = false;
54097 };
54098
54099 Roo.extend(Roo.grid.GridEditor, Roo.Editor, {
54100     
54101     /**
54102      * @cfg {Roo.form.Field} field Field to wrap (or xtyped)
54103      */
54104     
54105     alignment: "tl-tl",
54106     autoSize: "width",
54107     hideEl : false,
54108     cls: "x-small-editor x-grid-editor",
54109     shim:false,
54110     shadow:"frame"
54111 });/*
54112  * Based on:
54113  * Ext JS Library 1.1.1
54114  * Copyright(c) 2006-2007, Ext JS, LLC.
54115  *
54116  * Originally Released Under LGPL - original licence link has changed is not relivant.
54117  *
54118  * Fork - LGPL
54119  * <script type="text/javascript">
54120  */
54121   
54122
54123   
54124 Roo.grid.PropertyRecord = Roo.data.Record.create([
54125     {name:'name',type:'string'},  'value'
54126 ]);
54127
54128
54129 Roo.grid.PropertyStore = function(grid, source){
54130     this.grid = grid;
54131     this.store = new Roo.data.Store({
54132         recordType : Roo.grid.PropertyRecord
54133     });
54134     this.store.on('update', this.onUpdate,  this);
54135     if(source){
54136         this.setSource(source);
54137     }
54138     Roo.grid.PropertyStore.superclass.constructor.call(this);
54139 };
54140
54141
54142
54143 Roo.extend(Roo.grid.PropertyStore, Roo.util.Observable, {
54144     setSource : function(o){
54145         this.source = o;
54146         this.store.removeAll();
54147         var data = [];
54148         for(var k in o){
54149             if(this.isEditableValue(o[k])){
54150                 data.push(new Roo.grid.PropertyRecord({name: k, value: o[k]}, k));
54151             }
54152         }
54153         this.store.loadRecords({records: data}, {}, true);
54154     },
54155
54156     onUpdate : function(ds, record, type){
54157         if(type == Roo.data.Record.EDIT){
54158             var v = record.data['value'];
54159             var oldValue = record.modified['value'];
54160             if(this.grid.fireEvent('beforepropertychange', this.source, record.id, v, oldValue) !== false){
54161                 this.source[record.id] = v;
54162                 record.commit();
54163                 this.grid.fireEvent('propertychange', this.source, record.id, v, oldValue);
54164             }else{
54165                 record.reject();
54166             }
54167         }
54168     },
54169
54170     getProperty : function(row){
54171        return this.store.getAt(row);
54172     },
54173
54174     isEditableValue: function(val){
54175         if(val && val instanceof Date){
54176             return true;
54177         }else if(typeof val == 'object' || typeof val == 'function'){
54178             return false;
54179         }
54180         return true;
54181     },
54182
54183     setValue : function(prop, value){
54184         this.source[prop] = value;
54185         this.store.getById(prop).set('value', value);
54186     },
54187
54188     getSource : function(){
54189         return this.source;
54190     }
54191 });
54192
54193 Roo.grid.PropertyColumnModel = function(grid, store){
54194     this.grid = grid;
54195     var g = Roo.grid;
54196     g.PropertyColumnModel.superclass.constructor.call(this, [
54197         {header: this.nameText, sortable: true, dataIndex:'name', id: 'name'},
54198         {header: this.valueText, resizable:false, dataIndex: 'value', id: 'value'}
54199     ]);
54200     this.store = store;
54201     this.bselect = Roo.DomHelper.append(document.body, {
54202         tag: 'select', style:'display:none', cls: 'x-grid-editor', children: [
54203             {tag: 'option', value: 'true', html: 'true'},
54204             {tag: 'option', value: 'false', html: 'false'}
54205         ]
54206     });
54207     Roo.id(this.bselect);
54208     var f = Roo.form;
54209     this.editors = {
54210         'date' : new g.GridEditor(new f.DateField({selectOnFocus:true})),
54211         'string' : new g.GridEditor(new f.TextField({selectOnFocus:true})),
54212         'number' : new g.GridEditor(new f.NumberField({selectOnFocus:true, style:'text-align:left;'})),
54213         'int' : new g.GridEditor(new f.NumberField({selectOnFocus:true, allowDecimals:false, style:'text-align:left;'})),
54214         'boolean' : new g.GridEditor(new f.Field({el:this.bselect,selectOnFocus:true}))
54215     };
54216     this.renderCellDelegate = this.renderCell.createDelegate(this);
54217     this.renderPropDelegate = this.renderProp.createDelegate(this);
54218 };
54219
54220 Roo.extend(Roo.grid.PropertyColumnModel, Roo.grid.ColumnModel, {
54221     
54222     
54223     nameText : 'Name',
54224     valueText : 'Value',
54225     
54226     dateFormat : 'm/j/Y',
54227     
54228     
54229     renderDate : function(dateVal){
54230         return dateVal.dateFormat(this.dateFormat);
54231     },
54232
54233     renderBool : function(bVal){
54234         return bVal ? 'true' : 'false';
54235     },
54236
54237     isCellEditable : function(colIndex, rowIndex){
54238         return colIndex == 1;
54239     },
54240
54241     getRenderer : function(col){
54242         return col == 1 ?
54243             this.renderCellDelegate : this.renderPropDelegate;
54244     },
54245
54246     renderProp : function(v){
54247         return this.getPropertyName(v);
54248     },
54249
54250     renderCell : function(val){
54251         var rv = val;
54252         if(val instanceof Date){
54253             rv = this.renderDate(val);
54254         }else if(typeof val == 'boolean'){
54255             rv = this.renderBool(val);
54256         }
54257         return Roo.util.Format.htmlEncode(rv);
54258     },
54259
54260     getPropertyName : function(name){
54261         var pn = this.grid.propertyNames;
54262         return pn && pn[name] ? pn[name] : name;
54263     },
54264
54265     getCellEditor : function(colIndex, rowIndex){
54266         var p = this.store.getProperty(rowIndex);
54267         var n = p.data['name'], val = p.data['value'];
54268         
54269         if(typeof(this.grid.customEditors[n]) == 'string'){
54270             return this.editors[this.grid.customEditors[n]];
54271         }
54272         if(typeof(this.grid.customEditors[n]) != 'undefined'){
54273             return this.grid.customEditors[n];
54274         }
54275         if(val instanceof Date){
54276             return this.editors['date'];
54277         }else if(typeof val == 'number'){
54278             return this.editors['number'];
54279         }else if(typeof val == 'boolean'){
54280             return this.editors['boolean'];
54281         }else{
54282             return this.editors['string'];
54283         }
54284     }
54285 });
54286
54287 /**
54288  * @class Roo.grid.PropertyGrid
54289  * @extends Roo.grid.EditorGrid
54290  * This class represents the  interface of a component based property grid control.
54291  * <br><br>Usage:<pre><code>
54292  var grid = new Roo.grid.PropertyGrid("my-container-id", {
54293       
54294  });
54295  // set any options
54296  grid.render();
54297  * </code></pre>
54298   
54299  * @constructor
54300  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
54301  * The container MUST have some type of size defined for the grid to fill. The container will be
54302  * automatically set to position relative if it isn't already.
54303  * @param {Object} config A config object that sets properties on this grid.
54304  */
54305 Roo.grid.PropertyGrid = function(container, config){
54306     config = config || {};
54307     var store = new Roo.grid.PropertyStore(this);
54308     this.store = store;
54309     var cm = new Roo.grid.PropertyColumnModel(this, store);
54310     store.store.sort('name', 'ASC');
54311     Roo.grid.PropertyGrid.superclass.constructor.call(this, container, Roo.apply({
54312         ds: store.store,
54313         cm: cm,
54314         enableColLock:false,
54315         enableColumnMove:false,
54316         stripeRows:false,
54317         trackMouseOver: false,
54318         clicksToEdit:1
54319     }, config));
54320     this.getGridEl().addClass('x-props-grid');
54321     this.lastEditRow = null;
54322     this.on('columnresize', this.onColumnResize, this);
54323     this.addEvents({
54324          /**
54325              * @event beforepropertychange
54326              * Fires before a property changes (return false to stop?)
54327              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
54328              * @param {String} id Record Id
54329              * @param {String} newval New Value
54330          * @param {String} oldval Old Value
54331              */
54332         "beforepropertychange": true,
54333         /**
54334              * @event propertychange
54335              * Fires after a property changes
54336              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
54337              * @param {String} id Record Id
54338              * @param {String} newval New Value
54339          * @param {String} oldval Old Value
54340              */
54341         "propertychange": true
54342     });
54343     this.customEditors = this.customEditors || {};
54344 };
54345 Roo.extend(Roo.grid.PropertyGrid, Roo.grid.EditorGrid, {
54346     
54347      /**
54348      * @cfg {Object} customEditors map of colnames=> custom editors.
54349      * the custom editor can be one of the standard ones (date|string|number|int|boolean), or a
54350      * grid editor eg. Roo.grid.GridEditor(new Roo.form.TextArea({selectOnFocus:true})),
54351      * false disables editing of the field.
54352          */
54353     
54354       /**
54355      * @cfg {Object} propertyNames map of property Names to their displayed value
54356          */
54357     
54358     render : function(){
54359         Roo.grid.PropertyGrid.superclass.render.call(this);
54360         this.autoSize.defer(100, this);
54361     },
54362
54363     autoSize : function(){
54364         Roo.grid.PropertyGrid.superclass.autoSize.call(this);
54365         if(this.view){
54366             this.view.fitColumns();
54367         }
54368     },
54369
54370     onColumnResize : function(){
54371         this.colModel.setColumnWidth(1, this.container.getWidth(true)-this.colModel.getColumnWidth(0));
54372         this.autoSize();
54373     },
54374     /**
54375      * Sets the data for the Grid
54376      * accepts a Key => Value object of all the elements avaiable.
54377      * @param {Object} data  to appear in grid.
54378      */
54379     setSource : function(source){
54380         this.store.setSource(source);
54381         //this.autoSize();
54382     },
54383     /**
54384      * Gets all the data from the grid.
54385      * @return {Object} data  data stored in grid
54386      */
54387     getSource : function(){
54388         return this.store.getSource();
54389     }
54390 });/*
54391  * Based on:
54392  * Ext JS Library 1.1.1
54393  * Copyright(c) 2006-2007, Ext JS, LLC.
54394  *
54395  * Originally Released Under LGPL - original licence link has changed is not relivant.
54396  *
54397  * Fork - LGPL
54398  * <script type="text/javascript">
54399  */
54400  
54401 /**
54402  * @class Roo.LoadMask
54403  * A simple utility class for generically masking elements while loading data.  If the element being masked has
54404  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
54405  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
54406  * element's UpdateManager load indicator and will be destroyed after the initial load.
54407  * @constructor
54408  * Create a new LoadMask
54409  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
54410  * @param {Object} config The config object
54411  */
54412 Roo.LoadMask = function(el, config){
54413     this.el = Roo.get(el);
54414     Roo.apply(this, config);
54415     if(this.store){
54416         this.store.on('beforeload', this.onBeforeLoad, this);
54417         this.store.on('load', this.onLoad, this);
54418         this.store.on('loadexception', this.onLoadException, this);
54419         this.removeMask = false;
54420     }else{
54421         var um = this.el.getUpdateManager();
54422         um.showLoadIndicator = false; // disable the default indicator
54423         um.on('beforeupdate', this.onBeforeLoad, this);
54424         um.on('update', this.onLoad, this);
54425         um.on('failure', this.onLoad, this);
54426         this.removeMask = true;
54427     }
54428 };
54429
54430 Roo.LoadMask.prototype = {
54431     /**
54432      * @cfg {Boolean} removeMask
54433      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
54434      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
54435      */
54436     /**
54437      * @cfg {String} msg
54438      * The text to display in a centered loading message box (defaults to 'Loading...')
54439      */
54440     msg : 'Loading...',
54441     /**
54442      * @cfg {String} msgCls
54443      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
54444      */
54445     msgCls : 'x-mask-loading',
54446
54447     /**
54448      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
54449      * @type Boolean
54450      */
54451     disabled: false,
54452
54453     /**
54454      * Disables the mask to prevent it from being displayed
54455      */
54456     disable : function(){
54457        this.disabled = true;
54458     },
54459
54460     /**
54461      * Enables the mask so that it can be displayed
54462      */
54463     enable : function(){
54464         this.disabled = false;
54465     },
54466     
54467     onLoadException : function()
54468     {
54469         Roo.log(arguments);
54470         
54471         if (typeof(arguments[3]) != 'undefined') {
54472             Roo.MessageBox.alert("Error loading",arguments[3]);
54473         } 
54474         /*
54475         try {
54476             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
54477                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
54478             }   
54479         } catch(e) {
54480             
54481         }
54482         */
54483     
54484         
54485         
54486         this.el.unmask(this.removeMask);
54487     },
54488     // private
54489     onLoad : function()
54490     {
54491         this.el.unmask(this.removeMask);
54492     },
54493
54494     // private
54495     onBeforeLoad : function(){
54496         if(!this.disabled){
54497             this.el.mask(this.msg, this.msgCls);
54498         }
54499     },
54500
54501     // private
54502     destroy : function(){
54503         if(this.store){
54504             this.store.un('beforeload', this.onBeforeLoad, this);
54505             this.store.un('load', this.onLoad, this);
54506             this.store.un('loadexception', this.onLoadException, this);
54507         }else{
54508             var um = this.el.getUpdateManager();
54509             um.un('beforeupdate', this.onBeforeLoad, this);
54510             um.un('update', this.onLoad, this);
54511             um.un('failure', this.onLoad, this);
54512         }
54513     }
54514 };/*
54515  * Based on:
54516  * Ext JS Library 1.1.1
54517  * Copyright(c) 2006-2007, Ext JS, LLC.
54518  *
54519  * Originally Released Under LGPL - original licence link has changed is not relivant.
54520  *
54521  * Fork - LGPL
54522  * <script type="text/javascript">
54523  */
54524
54525
54526 /**
54527  * @class Roo.XTemplate
54528  * @extends Roo.Template
54529  * Provides a template that can have nested templates for loops or conditionals. The syntax is:
54530 <pre><code>
54531 var t = new Roo.XTemplate(
54532         '&lt;select name="{name}"&gt;',
54533                 '&lt;tpl for="options"&gt;&lt;option value="{value:trim}"&gt;{text:ellipsis(10)}&lt;/option&gt;&lt;/tpl&gt;',
54534         '&lt;/select&gt;'
54535 );
54536  
54537 // then append, applying the master template values
54538  </code></pre>
54539  *
54540  * Supported features:
54541  *
54542  *  Tags:
54543
54544 <pre><code>
54545       {a_variable} - output encoded.
54546       {a_variable.format:("Y-m-d")} - call a method on the variable
54547       {a_variable:raw} - unencoded output
54548       {a_variable:toFixed(1,2)} - Roo.util.Format."toFixed"
54549       {a_variable:this.method_on_template(...)} - call a method on the template object.
54550  
54551 </code></pre>
54552  *  The tpl tag:
54553 <pre><code>
54554         &lt;tpl for="a_variable or condition.."&gt;&lt;/tpl&gt;
54555         &lt;tpl if="a_variable or condition"&gt;&lt;/tpl&gt;
54556         &lt;tpl exec="some javascript"&gt;&lt;/tpl&gt;
54557         &lt;tpl name="named_template"&gt;&lt;/tpl&gt; (experimental)
54558   
54559         &lt;tpl for="."&gt;&lt;/tpl&gt; - just iterate the property..
54560         &lt;tpl for=".."&gt;&lt;/tpl&gt; - iterates with the parent (probably the template) 
54561 </code></pre>
54562  *      
54563  */
54564 Roo.XTemplate = function()
54565 {
54566     Roo.XTemplate.superclass.constructor.apply(this, arguments);
54567     if (this.html) {
54568         this.compile();
54569     }
54570 };
54571
54572
54573 Roo.extend(Roo.XTemplate, Roo.Template, {
54574
54575     /**
54576      * The various sub templates
54577      */
54578     tpls : false,
54579     /**
54580      *
54581      * basic tag replacing syntax
54582      * WORD:WORD()
54583      *
54584      * // you can fake an object call by doing this
54585      *  x.t:(test,tesT) 
54586      * 
54587      */
54588     re : /\{([\w-\.]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
54589
54590     /**
54591      * compile the template
54592      *
54593      * This is not recursive, so I'm not sure how nested templates are really going to be handled..
54594      *
54595      */
54596     compile: function()
54597     {
54598         var s = this.html;
54599      
54600         s = ['<tpl>', s, '</tpl>'].join('');
54601     
54602         var re     = /<tpl\b[^>]*>((?:(?=([^<]+))\2|<(?!tpl\b[^>]*>))*?)<\/tpl>/,
54603             nameRe = /^<tpl\b[^>]*?for="(.*?)"/,
54604             ifRe   = /^<tpl\b[^>]*?if="(.*?)"/,
54605             execRe = /^<tpl\b[^>]*?exec="(.*?)"/,
54606             namedRe = /^<tpl\b[^>]*?name="(\w+)"/,  // named templates..
54607             m,
54608             id     = 0,
54609             tpls   = [];
54610     
54611         while(true == !!(m = s.match(re))){
54612             var forMatch   = m[0].match(nameRe),
54613                 ifMatch   = m[0].match(ifRe),
54614                 execMatch   = m[0].match(execRe),
54615                 namedMatch   = m[0].match(namedRe),
54616                 
54617                 exp  = null, 
54618                 fn   = null,
54619                 exec = null,
54620                 name = forMatch && forMatch[1] ? forMatch[1] : '';
54621                 
54622             if (ifMatch) {
54623                 // if - puts fn into test..
54624                 exp = ifMatch && ifMatch[1] ? ifMatch[1] : null;
54625                 if(exp){
54626                    fn = new Function('values', 'parent', 'with(values){ return '+(Roo.util.Format.htmlDecode(exp))+'; }');
54627                 }
54628             }
54629             
54630             if (execMatch) {
54631                 // exec - calls a function... returns empty if true is  returned.
54632                 exp = execMatch && execMatch[1] ? execMatch[1] : null;
54633                 if(exp){
54634                    exec = new Function('values', 'parent', 'with(values){ '+(Roo.util.Format.htmlDecode(exp))+'; }');
54635                 }
54636             }
54637             
54638             
54639             if (name) {
54640                 // for = 
54641                 switch(name){
54642                     case '.':  name = new Function('values', 'parent', 'with(values){ return values; }'); break;
54643                     case '..': name = new Function('values', 'parent', 'with(values){ return parent; }'); break;
54644                     default:   name = new Function('values', 'parent', 'with(values){ return '+name+'; }');
54645                 }
54646             }
54647             var uid = namedMatch ? namedMatch[1] : id;
54648             
54649             
54650             tpls.push({
54651                 id:     namedMatch ? namedMatch[1] : id,
54652                 target: name,
54653                 exec:   exec,
54654                 test:   fn,
54655                 body:   m[1] || ''
54656             });
54657             if (namedMatch) {
54658                 s = s.replace(m[0], '');
54659             } else { 
54660                 s = s.replace(m[0], '{xtpl'+ id + '}');
54661             }
54662             ++id;
54663         }
54664         this.tpls = [];
54665         for(var i = tpls.length-1; i >= 0; --i){
54666             this.compileTpl(tpls[i]);
54667             this.tpls[tpls[i].id] = tpls[i];
54668         }
54669         this.master = tpls[tpls.length-1];
54670         return this;
54671     },
54672     /**
54673      * same as applyTemplate, except it's done to one of the subTemplates
54674      * when using named templates, you can do:
54675      *
54676      * var str = pl.applySubTemplate('your-name', values);
54677      *
54678      * 
54679      * @param {Number} id of the template
54680      * @param {Object} values to apply to template
54681      * @param {Object} parent (normaly the instance of this object)
54682      */
54683     applySubTemplate : function(id, values, parent)
54684     {
54685         
54686         
54687         var t = this.tpls[id];
54688         
54689         
54690         try { 
54691             if(t.test && !t.test.call(this, values, parent)){
54692                 return '';
54693             }
54694         } catch(e) {
54695             Roo.log("Xtemplate.applySubTemplate 'test': Exception thrown");
54696             Roo.log(e.toString());
54697             Roo.log(t.test);
54698             return ''
54699         }
54700         try { 
54701             
54702             if(t.exec && t.exec.call(this, values, parent)){
54703                 return '';
54704             }
54705         } catch(e) {
54706             Roo.log("Xtemplate.applySubTemplate 'exec': Exception thrown");
54707             Roo.log(e.toString());
54708             Roo.log(t.exec);
54709             return ''
54710         }
54711         try {
54712             var vs = t.target ? t.target.call(this, values, parent) : values;
54713             parent = t.target ? values : parent;
54714             if(t.target && vs instanceof Array){
54715                 var buf = [];
54716                 for(var i = 0, len = vs.length; i < len; i++){
54717                     buf[buf.length] = t.compiled.call(this, vs[i], parent);
54718                 }
54719                 return buf.join('');
54720             }
54721             return t.compiled.call(this, vs, parent);
54722         } catch (e) {
54723             Roo.log("Xtemplate.applySubTemplate : Exception thrown");
54724             Roo.log(e.toString());
54725             Roo.log(t.compiled);
54726             return '';
54727         }
54728     },
54729
54730     compileTpl : function(tpl)
54731     {
54732         var fm = Roo.util.Format;
54733         var useF = this.disableFormats !== true;
54734         var sep = Roo.isGecko ? "+" : ",";
54735         var undef = function(str) {
54736             Roo.log("Property not found :"  + str);
54737             return '';
54738         };
54739         
54740         var fn = function(m, name, format, args)
54741         {
54742             //Roo.log(arguments);
54743             args = args ? args.replace(/\\'/g,"'") : args;
54744             //["{TEST:(a,b,c)}", "TEST", "", "a,b,c", 0, "{TEST:(a,b,c)}"]
54745             if (typeof(format) == 'undefined') {
54746                 format= 'htmlEncode';
54747             }
54748             if (format == 'raw' ) {
54749                 format = false;
54750             }
54751             
54752             if(name.substr(0, 4) == 'xtpl'){
54753                 return "'"+ sep +'this.applySubTemplate('+name.substr(4)+', values, parent)'+sep+"'";
54754             }
54755             
54756             // build an array of options to determine if value is undefined..
54757             
54758             // basically get 'xxxx.yyyy' then do
54759             // (typeof(xxxx) == 'undefined' || typeof(xxx.yyyy) == 'undefined') ?
54760             //    (function () { Roo.log("Property not found"); return ''; })() :
54761             //    ......
54762             
54763             var udef_ar = [];
54764             var lookfor = '';
54765             Roo.each(name.split('.'), function(st) {
54766                 lookfor += (lookfor.length ? '.': '') + st;
54767                 udef_ar.push(  "(typeof(" + lookfor + ") == 'undefined')"  );
54768             });
54769             
54770             var udef_st = '((' + udef_ar.join(" || ") +") ? undef('" + name + "') : "; // .. needs )
54771             
54772             
54773             if(format && useF){
54774                 
54775                 args = args ? ',' + args : "";
54776                  
54777                 if(format.substr(0, 5) != "this."){
54778                     format = "fm." + format + '(';
54779                 }else{
54780                     format = 'this.call("'+ format.substr(5) + '", ';
54781                     args = ", values";
54782                 }
54783                 
54784                 return "'"+ sep +   udef_st   +    format + name + args + "))"+sep+"'";
54785             }
54786              
54787             if (args.length) {
54788                 // called with xxyx.yuu:(test,test)
54789                 // change to ()
54790                 return "'"+ sep + udef_st  + name + '(' +  args + "))"+sep+"'";
54791             }
54792             // raw.. - :raw modifier..
54793             return "'"+ sep + udef_st  + name + ")"+sep+"'";
54794             
54795         };
54796         var body;
54797         // branched to use + in gecko and [].join() in others
54798         if(Roo.isGecko){
54799             body = "tpl.compiled = function(values, parent){  with(values) { return '" +
54800                    tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
54801                     "';};};";
54802         }else{
54803             body = ["tpl.compiled = function(values, parent){  with (values) { return ['"];
54804             body.push(tpl.body.replace(/(\r\n|\n)/g,
54805                             '\\n').replace(/'/g, "\\'").replace(this.re, fn));
54806             body.push("'].join('');};};");
54807             body = body.join('');
54808         }
54809         
54810         Roo.debug && Roo.log(body.replace(/\\n/,'\n'));
54811        
54812         /** eval:var:tpl eval:var:fm eval:var:useF eval:var:undef  */
54813         eval(body);
54814         
54815         return this;
54816     },
54817
54818     applyTemplate : function(values){
54819         return this.master.compiled.call(this, values, {});
54820         //var s = this.subs;
54821     },
54822
54823     apply : function(){
54824         return this.applyTemplate.apply(this, arguments);
54825     }
54826
54827  });
54828
54829 Roo.XTemplate.from = function(el){
54830     el = Roo.getDom(el);
54831     return new Roo.XTemplate(el.value || el.innerHTML);
54832 };/*
54833  * Original code for Roojs - LGPL
54834  * <script type="text/javascript">
54835  */
54836  
54837 /**
54838  * @class Roo.XComponent
54839  * A delayed Element creator...
54840  * Or a way to group chunks of interface together.
54841  * 
54842  * Mypart.xyx = new Roo.XComponent({
54843
54844     parent : 'Mypart.xyz', // empty == document.element.!!
54845     order : '001',
54846     name : 'xxxx'
54847     region : 'xxxx'
54848     disabled : function() {} 
54849      
54850     tree : function() { // return an tree of xtype declared components
54851         var MODULE = this;
54852         return 
54853         {
54854             xtype : 'NestedLayoutPanel',
54855             // technicall
54856         }
54857      ]
54858  *})
54859  *
54860  *
54861  * It can be used to build a big heiracy, with parent etc.
54862  * or you can just use this to render a single compoent to a dom element
54863  * MYPART.render(Roo.Element | String(id) | dom_element )
54864  * 
54865  * @extends Roo.util.Observable
54866  * @constructor
54867  * @param cfg {Object} configuration of component
54868  * 
54869  */
54870 Roo.XComponent = function(cfg) {
54871     Roo.apply(this, cfg);
54872     this.addEvents({ 
54873         /**
54874              * @event built
54875              * Fires when this the componnt is built
54876              * @param {Roo.XComponent} c the component
54877              */
54878         'built' : true
54879         
54880     });
54881     this.region = this.region || 'center'; // default..
54882     Roo.XComponent.register(this);
54883     this.modules = false;
54884     this.el = false; // where the layout goes..
54885     
54886     
54887 }
54888 Roo.extend(Roo.XComponent, Roo.util.Observable, {
54889     /**
54890      * @property el
54891      * The created element (with Roo.factory())
54892      * @type {Roo.Layout}
54893      */
54894     el  : false,
54895     
54896     /**
54897      * @property el
54898      * for BC  - use el in new code
54899      * @type {Roo.Layout}
54900      */
54901     panel : false,
54902     
54903     /**
54904      * @property layout
54905      * for BC  - use el in new code
54906      * @type {Roo.Layout}
54907      */
54908     layout : false,
54909     
54910      /**
54911      * @cfg {Function|boolean} disabled
54912      * If this module is disabled by some rule, return true from the funtion
54913      */
54914     disabled : false,
54915     
54916     /**
54917      * @cfg {String} parent 
54918      * Name of parent element which it get xtype added to..
54919      */
54920     parent: false,
54921     
54922     /**
54923      * @cfg {String} order
54924      * Used to set the order in which elements are created (usefull for multiple tabs)
54925      */
54926     
54927     order : false,
54928     /**
54929      * @cfg {String} name
54930      * String to display while loading.
54931      */
54932     name : false,
54933     /**
54934      * @cfg {String} region
54935      * Region to render component to (defaults to center)
54936      */
54937     region : 'center',
54938     
54939     /**
54940      * @cfg {Array} items
54941      * A single item array - the first element is the root of the tree..
54942      * It's done this way to stay compatible with the Xtype system...
54943      */
54944     items : false,
54945     
54946     /**
54947      * @property _tree
54948      * The method that retuns the tree of parts that make up this compoennt 
54949      * @type {function}
54950      */
54951     _tree  : false,
54952     
54953      /**
54954      * render
54955      * render element to dom or tree
54956      * @param {Roo.Element|String|DomElement} optional render to if parent is not set.
54957      */
54958     
54959     render : function(el)
54960     {
54961         
54962         el = el || false;
54963         var hp = this.parent ? 1 : 0;
54964         
54965         if (!el && typeof(this.parent) == 'string' && this.parent.substring(0,1) == '#') {
54966             // if parent is a '#.....' string, then let's use that..
54967             var ename = this.parent.substr(1)
54968             this.parent = false;
54969             el = Roo.get(ename);
54970             if (!el) {
54971                 Roo.log("Warning - element can not be found :#" + ename );
54972                 return;
54973             }
54974         }
54975         
54976         
54977         if (!this.parent) {
54978             
54979             el = el ? Roo.get(el) : false;      
54980             
54981             // it's a top level one..
54982             this.parent =  {
54983                 el : new Roo.BorderLayout(el || document.body, {
54984                 
54985                      center: {
54986                          titlebar: false,
54987                          autoScroll:false,
54988                          closeOnTab: true,
54989                          tabPosition: 'top',
54990                           //resizeTabs: true,
54991                          alwaysShowTabs: el && hp? false :  true,
54992                          hideTabs: el || !hp ? true :  false,
54993                          minTabWidth: 140
54994                      }
54995                  })
54996             }
54997         }
54998         
54999                 if (!this.parent.el) {
55000                         // probably an old style ctor, which has been disabled.
55001                         return;
55002                         
55003                 }
55004                 // The 'tree' method is  '_tree now' 
55005             
55006         var tree = this._tree ? this._tree() : this.tree();
55007         tree.region = tree.region || this.region;
55008         this.el = this.parent.el.addxtype(tree);
55009         this.fireEvent('built', this);
55010         
55011         this.panel = this.el;
55012         this.layout = this.panel.layout;
55013                 this.parentLayout = this.parent.layout  || false;  
55014          
55015     }
55016     
55017 });
55018
55019 Roo.apply(Roo.XComponent, {
55020     /**
55021      * @property  hideProgress
55022      * true to disable the building progress bar.. usefull on single page renders.
55023      * @type Boolean
55024      */
55025     hideProgress : false,
55026     /**
55027      * @property  buildCompleted
55028      * True when the builder has completed building the interface.
55029      * @type Boolean
55030      */
55031     buildCompleted : false,
55032      
55033     /**
55034      * @property  topModule
55035      * the upper most module - uses document.element as it's constructor.
55036      * @type Object
55037      */
55038      
55039     topModule  : false,
55040       
55041     /**
55042      * @property  modules
55043      * array of modules to be created by registration system.
55044      * @type {Array} of Roo.XComponent
55045      */
55046     
55047     modules : [],
55048     /**
55049      * @property  elmodules
55050      * array of modules to be created by which use #ID 
55051      * @type {Array} of Roo.XComponent
55052      */
55053      
55054     elmodules : [],
55055
55056     
55057     /**
55058      * Register components to be built later.
55059      *
55060      * This solves the following issues
55061      * - Building is not done on page load, but after an authentication process has occured.
55062      * - Interface elements are registered on page load
55063      * - Parent Interface elements may not be loaded before child, so this handles that..
55064      * 
55065      *
55066      * example:
55067      * 
55068      * MyApp.register({
55069           order : '000001',
55070           module : 'Pman.Tab.projectMgr',
55071           region : 'center',
55072           parent : 'Pman.layout',
55073           disabled : false,  // or use a function..
55074         })
55075      
55076      * * @param {Object} details about module
55077      */
55078     register : function(obj) {
55079                 
55080         Roo.XComponent.event.fireEvent('register', obj);
55081         switch(typeof(obj.disabled) ) {
55082                 
55083             case 'undefined':
55084                 break;
55085             
55086             case 'function':
55087                 if ( obj.disabled() ) {
55088                         return;
55089                 }
55090                 break;
55091             
55092             default:
55093                 if (obj.disabled) {
55094                         return;
55095                 }
55096                 break;
55097         }
55098                 
55099         this.modules.push(obj);
55100          
55101     },
55102     /**
55103      * convert a string to an object..
55104      * eg. 'AAA.BBB' -> finds AAA.BBB
55105
55106      */
55107     
55108     toObject : function(str)
55109     {
55110         if (!str || typeof(str) == 'object') {
55111             return str;
55112         }
55113         if (str.substring(0,1) == '#') {
55114             return str;
55115         }
55116
55117         var ar = str.split('.');
55118         var rt, o;
55119         rt = ar.shift();
55120             /** eval:var:o */
55121         try {
55122             eval('if (typeof ' + rt + ' == "undefined"){ o = false;} o = ' + rt + ';');
55123         } catch (e) {
55124             throw "Module not found : " + str;
55125         }
55126         
55127         if (o === false) {
55128             throw "Module not found : " + str;
55129         }
55130         Roo.each(ar, function(e) {
55131             if (typeof(o[e]) == 'undefined') {
55132                 throw "Module not found : " + str;
55133             }
55134             o = o[e];
55135         });
55136         
55137         return o;
55138         
55139     },
55140     
55141     
55142     /**
55143      * move modules into their correct place in the tree..
55144      * 
55145      */
55146     preBuild : function ()
55147     {
55148         var _t = this;
55149         Roo.each(this.modules , function (obj)
55150         {
55151             Roo.XComponent.event.fireEvent('beforebuild', obj);
55152             
55153             var opar = obj.parent;
55154             try { 
55155                 obj.parent = this.toObject(opar);
55156             } catch(e) {
55157                 Roo.log("parent:toObject failed: " + e.toString());
55158                 return;
55159             }
55160             
55161             if (!obj.parent) {
55162                 Roo.debug && Roo.log("GOT top level module");
55163                 Roo.debug && Roo.log(obj);
55164                 obj.modules = new Roo.util.MixedCollection(false, 
55165                     function(o) { return o.order + '' }
55166                 );
55167                 this.topModule = obj;
55168                 return;
55169             }
55170                         // parent is a string (usually a dom element name..)
55171             if (typeof(obj.parent) == 'string') {
55172                 this.elmodules.push(obj);
55173                 return;
55174             }
55175             if (obj.parent.constructor != Roo.XComponent) {
55176                 Roo.log("Warning : Object Parent is not instance of XComponent:" + obj.name)
55177             }
55178             if (!obj.parent.modules) {
55179                 obj.parent.modules = new Roo.util.MixedCollection(false, 
55180                     function(o) { return o.order + '' }
55181                 );
55182             }
55183             if (obj.parent.disabled) {
55184                 obj.disabled = true;
55185             }
55186             obj.parent.modules.add(obj);
55187         }, this);
55188     },
55189     
55190      /**
55191      * make a list of modules to build.
55192      * @return {Array} list of modules. 
55193      */ 
55194     
55195     buildOrder : function()
55196     {
55197         var _this = this;
55198         var cmp = function(a,b) {   
55199             return String(a).toUpperCase() > String(b).toUpperCase() ? 1 : -1;
55200         };
55201         if ((!this.topModule || !this.topModule.modules) && !this.elmodules.length) {
55202             throw "No top level modules to build";
55203         }
55204         
55205         // make a flat list in order of modules to build.
55206         var mods = this.topModule ? [ this.topModule ] : [];
55207                 
55208         
55209         // elmodules (is a list of DOM based modules )
55210         Roo.each(this.elmodules, function(e) {
55211             mods.push(e);
55212             if (!this.topModule &&
55213                 typeof(e.parent) == 'string' &&
55214                 e.parent.substring(0,1) == '#' &&
55215                 Roo.get(e.parent.substr(1))
55216                ) {
55217                 
55218                 _this.topModule = e;
55219             }
55220             
55221         });
55222
55223         
55224         // add modules to their parents..
55225         var addMod = function(m) {
55226             Roo.debug && Roo.log("build Order: add: " + m.name);
55227                 
55228             mods.push(m);
55229             if (m.modules && !m.disabled) {
55230                 Roo.debug && Roo.log("build Order: " + m.modules.length + " child modules");
55231                 m.modules.keySort('ASC',  cmp );
55232                 Roo.debug && Roo.log("build Order: " + m.modules.length + " child modules (after sort)");
55233     
55234                 m.modules.each(addMod);
55235             } else {
55236                 Roo.debug && Roo.log("build Order: no child modules");
55237             }
55238             // not sure if this is used any more..
55239             if (m.finalize) {
55240                 m.finalize.name = m.name + " (clean up) ";
55241                 mods.push(m.finalize);
55242             }
55243             
55244         }
55245         if (this.topModule && this.topModule.modules) { 
55246             this.topModule.modules.keySort('ASC',  cmp );
55247             this.topModule.modules.each(addMod);
55248         } 
55249         return mods;
55250     },
55251     
55252      /**
55253      * Build the registered modules.
55254      * @param {Object} parent element.
55255      * @param {Function} optional method to call after module has been added.
55256      * 
55257      */ 
55258    
55259     build : function() 
55260     {
55261         
55262         this.preBuild();
55263         var mods = this.buildOrder();
55264       
55265         //this.allmods = mods;
55266         //Roo.debug && Roo.log(mods);
55267         //return;
55268         if (!mods.length) { // should not happen
55269             throw "NO modules!!!";
55270         }
55271         
55272         
55273         var msg = "Building Interface...";
55274         // flash it up as modal - so we store the mask!?
55275         if (!this.hideProgress) {
55276             Roo.MessageBox.show({ title: 'loading' });
55277             Roo.MessageBox.show({
55278                title: "Please wait...",
55279                msg: msg,
55280                width:450,
55281                progress:true,
55282                closable:false,
55283                modal: false
55284               
55285             });
55286         }
55287         var total = mods.length;
55288         
55289         var _this = this;
55290         var progressRun = function() {
55291             if (!mods.length) {
55292                 Roo.debug && Roo.log('hide?');
55293                 if (!this.hideProgress) {
55294                     Roo.MessageBox.hide();
55295                 }
55296                 Roo.XComponent.event.fireEvent('buildcomplete', _this.topModule);
55297                 
55298                 // THE END...
55299                 return false;   
55300             }
55301             
55302             var m = mods.shift();
55303             
55304             
55305             Roo.debug && Roo.log(m);
55306             // not sure if this is supported any more.. - modules that are are just function
55307             if (typeof(m) == 'function') { 
55308                 m.call(this);
55309                 return progressRun.defer(10, _this);
55310             } 
55311             
55312             
55313             msg = "Building Interface " + (total  - mods.length) + 
55314                     " of " + total + 
55315                     (m.name ? (' - ' + m.name) : '');
55316                         Roo.debug && Roo.log(msg);
55317             if (!this.hideProgress) { 
55318                 Roo.MessageBox.updateProgress(  (total  - mods.length)/total, msg  );
55319             }
55320             
55321          
55322             // is the module disabled?
55323             var disabled = (typeof(m.disabled) == 'function') ?
55324                 m.disabled.call(m.module.disabled) : m.disabled;    
55325             
55326             
55327             if (disabled) {
55328                 return progressRun(); // we do not update the display!
55329             }
55330             
55331             // now build 
55332             
55333                         
55334                         
55335             m.render();
55336             // it's 10 on top level, and 1 on others??? why...
55337             return progressRun.defer(10, _this);
55338              
55339         }
55340         progressRun.defer(1, _this);
55341      
55342         
55343         
55344     },
55345         
55346         
55347         /**
55348          * Event Object.
55349          *
55350          *
55351          */
55352         event: false, 
55353     /**
55354          * wrapper for event.on - aliased later..  
55355          * Typically use to register a event handler for register:
55356          *
55357          * eg. Roo.XComponent.on('register', function(comp) { comp.disable = true } );
55358          *
55359          */
55360     on : false
55361    
55362     
55363     
55364 });
55365
55366 Roo.XComponent.event = new Roo.util.Observable({
55367                 events : { 
55368                         /**
55369                          * @event register
55370                          * Fires when an Component is registered,
55371                          * set the disable property on the Component to stop registration.
55372                          * @param {Roo.XComponent} c the component being registerd.
55373                          * 
55374                          */
55375                         'register' : true,
55376             /**
55377                          * @event beforebuild
55378                          * Fires before each Component is built
55379                          * can be used to apply permissions.
55380                          * @param {Roo.XComponent} c the component being registerd.
55381                          * 
55382                          */
55383                         'beforebuild' : true,
55384                         /**
55385                          * @event buildcomplete
55386                          * Fires on the top level element when all elements have been built
55387                          * @param {Roo.XComponent} the top level component.
55388                          */
55389                         'buildcomplete' : true
55390                         
55391                 }
55392 });
55393
55394 Roo.XComponent.on = Roo.XComponent.event.on.createDelegate(Roo.XComponent.event); 
55395