sync
[roojs1] / roojs-debug.js
1 /*
2  * Based on:
3  * Ext JS Library 1.1.1
4  * Copyright(c) 2006-2007, Ext JS, LLC.
5  *
6  * Originally Released Under LGPL - original licence link has changed is not relivant.
7  *
8  * Fork - LGPL
9  * <script type="text/javascript">
10  */
11  
12
13
14
15
16 // for old browsers
17 window["undefined"] = window["undefined"];
18
19 /**
20  * @class Roo
21  * Roo core utilities and functions.
22  * @singleton
23  */
24 var Roo = {}; 
25 /**
26  * Copies all the properties of config to obj.
27  * @param {Object} obj The receiver of the properties
28  * @param {Object} config The source of the properties
29  * @param {Object} defaults A different object that will also be applied for default values
30  * @return {Object} returns obj
31  * @member Roo apply
32  */
33
34  
35 Roo.apply = function(o, c, defaults){
36     if(defaults){
37         // no "this" reference for friendly out of scope calls
38         Roo.apply(o, defaults);
39     }
40     if(o && c && typeof c == 'object'){
41         for(var p in c){
42             o[p] = c[p];
43         }
44     }
45     return o;
46 };
47
48
49 (function(){
50     var idSeed = 0;
51     var ua = navigator.userAgent.toLowerCase();
52
53     var isStrict = document.compatMode == "CSS1Compat",
54         isOpera = ua.indexOf("opera") > -1,
55         isSafari = (/webkit|khtml/).test(ua),
56         isIE = ua.indexOf("msie") > -1,
57         isIE7 = ua.indexOf("msie 7") > -1,
58         isGecko = !isSafari && ua.indexOf("gecko") > -1,
59         isBorderBox = isIE && !isStrict,
60         isWindows = (ua.indexOf("windows") != -1 || ua.indexOf("win32") != -1),
61         isMac = (ua.indexOf("macintosh") != -1 || ua.indexOf("mac os x") != -1),
62         isLinux = (ua.indexOf("linux") != -1),
63         isSecure = window.location.href.toLowerCase().indexOf("https") === 0,
64         isTouch =  'ontouchstart' in window || window.DocumentTouch && document instanceof DocumentTouch;
65     // remove css image flicker
66         if(isIE && !isIE7){
67         try{
68             document.execCommand("BackgroundImageCache", false, true);
69         }catch(e){}
70     }
71     
72     Roo.apply(Roo, {
73         /**
74          * True if the browser is in strict mode
75          * @type Boolean
76          */
77         isStrict : isStrict,
78         /**
79          * True if the page is running over SSL
80          * @type Boolean
81          */
82         isSecure : isSecure,
83         /**
84          * True when the document is fully initialized and ready for action
85          * @type Boolean
86          */
87         isReady : false,
88         /**
89          * Turn on debugging output (currently only the factory uses this)
90          * @type Boolean
91          */
92         
93         debug: false,
94
95         /**
96          * True to automatically uncache orphaned Roo.Elements periodically (defaults to true)
97          * @type Boolean
98          */
99         enableGarbageCollector : true,
100
101         /**
102          * True to automatically purge event listeners after uncaching an element (defaults to false).
103          * Note: this only happens if enableGarbageCollector is true.
104          * @type Boolean
105          */
106         enableListenerCollection:false,
107
108         /**
109          * URL to a blank file used by Roo when in secure mode for iframe src and onReady src to prevent
110          * the IE insecure content warning (defaults to javascript:false).
111          * @type String
112          */
113         SSL_SECURE_URL : "javascript:false",
114
115         /**
116          * URL to a 1x1 transparent gif image used by Roo to create inline icons with CSS background images. (Defaults to
117          * "http://Roojs.com/s.gif" and you should change this to a URL on your server).
118          * @type String
119          */
120         BLANK_IMAGE_URL : "http:/"+"/localhost/s.gif",
121
122         emptyFn : function(){},
123         
124         /**
125          * Copies all the properties of config to obj if they don't already exist.
126          * @param {Object} obj The receiver of the properties
127          * @param {Object} config The source of the properties
128          * @return {Object} returns obj
129          */
130         applyIf : function(o, c){
131             if(o && c){
132                 for(var p in c){
133                     if(typeof o[p] == "undefined"){ o[p] = c[p]; }
134                 }
135             }
136             return o;
137         },
138
139         /**
140          * Applies event listeners to elements by selectors when the document is ready.
141          * The event name is specified with an @ suffix.
142 <pre><code>
143 Roo.addBehaviors({
144    // add a listener for click on all anchors in element with id foo
145    '#foo a@click' : function(e, t){
146        // do something
147    },
148
149    // add the same listener to multiple selectors (separated by comma BEFORE the @)
150    '#foo a, #bar span.some-class@mouseover' : function(){
151        // do something
152    }
153 });
154 </code></pre>
155          * @param {Object} obj The list of behaviors to apply
156          */
157         addBehaviors : function(o){
158             if(!Roo.isReady){
159                 Roo.onReady(function(){
160                     Roo.addBehaviors(o);
161                 });
162                 return;
163             }
164             var cache = {}; // simple cache for applying multiple behaviors to same selector does query multiple times
165             for(var b in o){
166                 var parts = b.split('@');
167                 if(parts[1]){ // for Object prototype breakers
168                     var s = parts[0];
169                     if(!cache[s]){
170                         cache[s] = Roo.select(s);
171                     }
172                     cache[s].on(parts[1], o[b]);
173                 }
174             }
175             cache = null;
176         },
177
178         /**
179          * Generates unique ids. If the element already has an id, it is unchanged
180          * @param {String/HTMLElement/Element} el (optional) The element to generate an id for
181          * @param {String} prefix (optional) Id prefix (defaults "Roo-gen")
182          * @return {String} The generated Id.
183          */
184         id : function(el, prefix){
185             prefix = prefix || "roo-gen";
186             el = Roo.getDom(el);
187             var id = prefix + (++idSeed);
188             return el ? (el.id ? el.id : (el.id = id)) : id;
189         },
190          
191        
192         /**
193          * Extends one class with another class and optionally overrides members with the passed literal. This class
194          * also adds the function "override()" to the class that can be used to override
195          * members on an instance.
196          * @param {Object} subclass The class inheriting the functionality
197          * @param {Object} superclass The class being extended
198          * @param {Object} overrides (optional) A literal with members
199          * @method extend
200          */
201         extend : function(){
202             // inline overrides
203             var io = function(o){
204                 for(var m in o){
205                     this[m] = o[m];
206                 }
207             };
208             return function(sb, sp, overrides){
209                 if(typeof sp == 'object'){ // eg. prototype, rather than function constructor..
210                     overrides = sp;
211                     sp = sb;
212                     sb = function(){sp.apply(this, arguments);};
213                 }
214                 var F = function(){}, sbp, spp = sp.prototype;
215                 F.prototype = spp;
216                 sbp = sb.prototype = new F();
217                 sbp.constructor=sb;
218                 sb.superclass=spp;
219                 
220                 if(spp.constructor == Object.prototype.constructor){
221                     spp.constructor=sp;
222                    
223                 }
224                 
225                 sb.override = function(o){
226                     Roo.override(sb, o);
227                 };
228                 sbp.override = io;
229                 Roo.override(sb, overrides);
230                 return sb;
231             };
232         }(),
233
234         /**
235          * Adds a list of functions to the prototype of an existing class, overwriting any existing methods with the same name.
236          * Usage:<pre><code>
237 Roo.override(MyClass, {
238     newMethod1: function(){
239         // etc.
240     },
241     newMethod2: function(foo){
242         // etc.
243     }
244 });
245  </code></pre>
246          * @param {Object} origclass The class to override
247          * @param {Object} overrides The list of functions to add to origClass.  This should be specified as an object literal
248          * containing one or more methods.
249          * @method override
250          */
251         override : function(origclass, overrides){
252             if(overrides){
253                 var p = origclass.prototype;
254                 for(var method in overrides){
255                     p[method] = overrides[method];
256                 }
257             }
258         },
259         /**
260          * Creates namespaces to be used for scoping variables and classes so that they are not global.  Usage:
261          * <pre><code>
262 Roo.namespace('Company', 'Company.data');
263 Company.Widget = function() { ... }
264 Company.data.CustomStore = function(config) { ... }
265 </code></pre>
266          * @param {String} namespace1
267          * @param {String} namespace2
268          * @param {String} etc
269          * @method namespace
270          */
271         namespace : function(){
272             var a=arguments, o=null, i, j, d, rt;
273             for (i=0; i<a.length; ++i) {
274                 d=a[i].split(".");
275                 rt = d[0];
276                 /** eval:var:o */
277                 eval('if (typeof ' + rt + ' == "undefined"){' + rt + ' = {};} o = ' + rt + ';');
278                 for (j=1; j<d.length; ++j) {
279                     o[d[j]]=o[d[j]] || {};
280                     o=o[d[j]];
281                 }
282             }
283         },
284         /**
285          * Creates namespaces to be used for scoping variables and classes so that they are not global.  Usage:
286          * <pre><code>
287 Roo.factory({ xns: Roo.data, xtype : 'Store', .....});
288 Roo.factory(conf, Roo.data);
289 </code></pre>
290          * @param {String} classname
291          * @param {String} namespace (optional)
292          * @method factory
293          */
294          
295         factory : function(c, ns)
296         {
297             // no xtype, no ns or c.xns - or forced off by c.xns
298             if (!c.xtype   || (!ns && !c.xns) ||  (c.xns === false)) { // not enough info...
299                 return c;
300             }
301             ns = c.xns ? c.xns : ns; // if c.xns is set, then use that..
302             if (c.constructor == ns[c.xtype]) {// already created...
303                 return c;
304             }
305             if (ns[c.xtype]) {
306                 if (Roo.debug) Roo.log("Roo.Factory(" + c.xtype + ")");
307                 var ret = new ns[c.xtype](c);
308                 ret.xns = false;
309                 return ret;
310             }
311             c.xns = false; // prevent recursion..
312             return c;
313         },
314          /**
315          * Logs to console if it can.
316          *
317          * @param {String|Object} string
318          * @method log
319          */
320         log : function(s)
321         {
322             if ((typeof(console) == 'undefined') || (typeof(console.log) == 'undefined')) {
323                 return; // alerT?
324             }
325             console.log(s);
326             
327         },
328         /**
329          * Takes an object and converts it to an encoded URL. e.g. Roo.urlEncode({foo: 1, bar: 2}); would return "foo=1&bar=2".  Optionally, property values can be arrays, instead of keys and the resulting string that's returned will contain a name/value pair for each array value.
330          * @param {Object} o
331          * @return {String}
332          */
333         urlEncode : function(o){
334             if(!o){
335                 return "";
336             }
337             var buf = [];
338             for(var key in o){
339                 var ov = o[key], k = Roo.encodeURIComponent(key);
340                 var type = typeof ov;
341                 if(type == 'undefined'){
342                     buf.push(k, "=&");
343                 }else if(type != "function" && type != "object"){
344                     buf.push(k, "=", Roo.encodeURIComponent(ov), "&");
345                 }else if(ov instanceof Array){
346                     if (ov.length) {
347                             for(var i = 0, len = ov.length; i < len; i++) {
348                                 buf.push(k, "=", Roo.encodeURIComponent(ov[i] === undefined ? '' : ov[i]), "&");
349                             }
350                         } else {
351                             buf.push(k, "=&");
352                         }
353                 }
354             }
355             buf.pop();
356             return buf.join("");
357         },
358          /**
359          * Safe version of encodeURIComponent
360          * @param {String} data 
361          * @return {String} 
362          */
363         
364         encodeURIComponent : function (data)
365         {
366             try {
367                 return encodeURIComponent(data);
368             } catch(e) {} // should be an uri encode error.
369             
370             if (data == '' || data == null){
371                return '';
372             }
373             // http://stackoverflow.com/questions/2596483/unicode-and-uri-encoding-decoding-and-escaping-in-javascript
374             function nibble_to_hex(nibble){
375                 var chars = '0123456789ABCDEF';
376                 return chars.charAt(nibble);
377             }
378             data = data.toString();
379             var buffer = '';
380             for(var i=0; i<data.length; i++){
381                 var c = data.charCodeAt(i);
382                 var bs = new Array();
383                 if (c > 0x10000){
384                         // 4 bytes
385                     bs[0] = 0xF0 | ((c & 0x1C0000) >>> 18);
386                     bs[1] = 0x80 | ((c & 0x3F000) >>> 12);
387                     bs[2] = 0x80 | ((c & 0xFC0) >>> 6);
388                     bs[3] = 0x80 | (c & 0x3F);
389                 }else if (c > 0x800){
390                          // 3 bytes
391                     bs[0] = 0xE0 | ((c & 0xF000) >>> 12);
392                     bs[1] = 0x80 | ((c & 0xFC0) >>> 6);
393                     bs[2] = 0x80 | (c & 0x3F);
394                 }else if (c > 0x80){
395                        // 2 bytes
396                     bs[0] = 0xC0 | ((c & 0x7C0) >>> 6);
397                     bs[1] = 0x80 | (c & 0x3F);
398                 }else{
399                         // 1 byte
400                     bs[0] = c;
401                 }
402                 for(var j=0; j<bs.length; j++){
403                     var b = bs[j];
404                     var hex = nibble_to_hex((b & 0xF0) >>> 4) 
405                             + nibble_to_hex(b &0x0F);
406                     buffer += '%'+hex;
407                }
408             }
409             return buffer;    
410              
411         },
412
413         /**
414          * Takes an encoded URL and and converts it to an object. e.g. Roo.urlDecode("foo=1&bar=2"); would return {foo: 1, bar: 2} or Roo.urlDecode("foo=1&bar=2&bar=3&bar=4", true); would return {foo: 1, bar: [2, 3, 4]}.
415          * @param {String} string
416          * @param {Boolean} overwrite (optional) Items of the same name will overwrite previous values instead of creating an an array (Defaults to false).
417          * @return {Object} A literal with members
418          */
419         urlDecode : function(string, overwrite){
420             if(!string || !string.length){
421                 return {};
422             }
423             var obj = {};
424             var pairs = string.split('&');
425             var pair, name, value;
426             for(var i = 0, len = pairs.length; i < len; i++){
427                 pair = pairs[i].split('=');
428                 name = decodeURIComponent(pair[0]);
429                 value = decodeURIComponent(pair[1]);
430                 if(overwrite !== true){
431                     if(typeof obj[name] == "undefined"){
432                         obj[name] = value;
433                     }else if(typeof obj[name] == "string"){
434                         obj[name] = [obj[name]];
435                         obj[name].push(value);
436                     }else{
437                         obj[name].push(value);
438                     }
439                 }else{
440                     obj[name] = value;
441                 }
442             }
443             return obj;
444         },
445
446         /**
447          * Iterates an array calling the passed function with each item, stopping if your function returns false. If the
448          * passed array is not really an array, your function is called once with it.
449          * The supplied function is called with (Object item, Number index, Array allItems).
450          * @param {Array/NodeList/Mixed} array
451          * @param {Function} fn
452          * @param {Object} scope
453          */
454         each : function(array, fn, scope){
455             if(typeof array.length == "undefined" || typeof array == "string"){
456                 array = [array];
457             }
458             for(var i = 0, len = array.length; i < len; i++){
459                 if(fn.call(scope || array[i], array[i], i, array) === false){ return i; };
460             }
461         },
462
463         // deprecated
464         combine : function(){
465             var as = arguments, l = as.length, r = [];
466             for(var i = 0; i < l; i++){
467                 var a = as[i];
468                 if(a instanceof Array){
469                     r = r.concat(a);
470                 }else if(a.length !== undefined && !a.substr){
471                     r = r.concat(Array.prototype.slice.call(a, 0));
472                 }else{
473                     r.push(a);
474                 }
475             }
476             return r;
477         },
478
479         /**
480          * Escapes the passed string for use in a regular expression
481          * @param {String} str
482          * @return {String}
483          */
484         escapeRe : function(s) {
485             return s.replace(/([.*+?^${}()|[\]\/\\])/g, "\\$1");
486         },
487
488         // internal
489         callback : function(cb, scope, args, delay){
490             if(typeof cb == "function"){
491                 if(delay){
492                     cb.defer(delay, scope, args || []);
493                 }else{
494                     cb.apply(scope, args || []);
495                 }
496             }
497         },
498
499         /**
500          * Return the dom node for the passed string (id), dom node, or Roo.Element
501          * @param {String/HTMLElement/Roo.Element} el
502          * @return HTMLElement
503          */
504         getDom : function(el){
505             if(!el){
506                 return null;
507             }
508             return el.dom ? el.dom : (typeof el == 'string' ? document.getElementById(el) : el);
509         },
510
511         /**
512         * Shorthand for {@link Roo.ComponentMgr#get}
513         * @param {String} id
514         * @return Roo.Component
515         */
516         getCmp : function(id){
517             return Roo.ComponentMgr.get(id);
518         },
519          
520         num : function(v, defaultValue){
521             if(typeof v != 'number'){
522                 return defaultValue;
523             }
524             return v;
525         },
526
527         destroy : function(){
528             for(var i = 0, a = arguments, len = a.length; i < len; i++) {
529                 var as = a[i];
530                 if(as){
531                     if(as.dom){
532                         as.removeAllListeners();
533                         as.remove();
534                         continue;
535                     }
536                     if(typeof as.purgeListeners == 'function'){
537                         as.purgeListeners();
538                     }
539                     if(typeof as.destroy == 'function'){
540                         as.destroy();
541                     }
542                 }
543             }
544         },
545
546         // inpired by a similar function in mootools library
547         /**
548          * Returns the type of object that is passed in. If the object passed in is null or undefined it
549          * return false otherwise it returns one of the following values:<ul>
550          * <li><b>string</b>: If the object passed is a string</li>
551          * <li><b>number</b>: If the object passed is a number</li>
552          * <li><b>boolean</b>: If the object passed is a boolean value</li>
553          * <li><b>function</b>: If the object passed is a function reference</li>
554          * <li><b>object</b>: If the object passed is an object</li>
555          * <li><b>array</b>: If the object passed is an array</li>
556          * <li><b>regexp</b>: If the object passed is a regular expression</li>
557          * <li><b>element</b>: If the object passed is a DOM Element</li>
558          * <li><b>nodelist</b>: If the object passed is a DOM NodeList</li>
559          * <li><b>textnode</b>: If the object passed is a DOM text node and contains something other than whitespace</li>
560          * <li><b>whitespace</b>: If the object passed is a DOM text node and contains only whitespace</li>
561          * @param {Mixed} object
562          * @return {String}
563          */
564         type : function(o){
565             if(o === undefined || o === null){
566                 return false;
567             }
568             if(o.htmlElement){
569                 return 'element';
570             }
571             var t = typeof o;
572             if(t == 'object' && o.nodeName) {
573                 switch(o.nodeType) {
574                     case 1: return 'element';
575                     case 3: return (/\S/).test(o.nodeValue) ? 'textnode' : 'whitespace';
576                 }
577             }
578             if(t == 'object' || t == 'function') {
579                 switch(o.constructor) {
580                     case Array: return 'array';
581                     case RegExp: return 'regexp';
582                 }
583                 if(typeof o.length == 'number' && typeof o.item == 'function') {
584                     return 'nodelist';
585                 }
586             }
587             return t;
588         },
589
590         /**
591          * Returns true if the passed value is null, undefined or an empty string (optional).
592          * @param {Mixed} value The value to test
593          * @param {Boolean} allowBlank (optional) Pass true if an empty string is not considered empty
594          * @return {Boolean}
595          */
596         isEmpty : function(v, allowBlank){
597             return v === null || v === undefined || (!allowBlank ? v === '' : false);
598         },
599         
600         /** @type Boolean */
601         isOpera : isOpera,
602         /** @type Boolean */
603         isSafari : isSafari,
604         /** @type Boolean */
605         isIE : isIE,
606         /** @type Boolean */
607         isIE7 : isIE7,
608         /** @type Boolean */
609         isGecko : isGecko,
610         /** @type Boolean */
611         isBorderBox : isBorderBox,
612         /** @type Boolean */
613         isWindows : isWindows,
614         /** @type Boolean */
615         isLinux : isLinux,
616         /** @type Boolean */
617         isMac : isMac,
618         /** @type Boolean */
619         isTouch : isTouch,
620
621         /**
622          * By default, Ext intelligently decides whether floating elements should be shimmed. If you are using flash,
623          * you may want to set this to true.
624          * @type Boolean
625          */
626         useShims : ((isIE && !isIE7) || (isGecko && isMac)),
627         
628         
629                 
630         /**
631          * Selects a single element as a Roo Element
632          * This is about as close as you can get to jQuery's $('do crazy stuff')
633          * @param {String} selector The selector/xpath query
634          * @param {Node} root (optional) The start of the query (defaults to document).
635          * @return {Roo.Element}
636          */
637         selectNode : function(selector, root) 
638         {
639             var node = Roo.DomQuery.selectNode(selector,root);
640             return node ? Roo.get(node) : new Roo.Element(false);
641         }
642         
643     });
644
645
646 })();
647
648 Roo.namespace("Roo", "Roo.util", "Roo.grid", "Roo.dd", "Roo.tree", "Roo.data",
649                 "Roo.form", "Roo.menu", "Roo.state", "Roo.lib", "Roo.layout",
650                 "Roo.app", "Roo.ux",
651                 "Roo.bootstrap",
652                 "Roo.bootstrap.dash");
653 /*
654  * Based on:
655  * Ext JS Library 1.1.1
656  * Copyright(c) 2006-2007, Ext JS, LLC.
657  *
658  * Originally Released Under LGPL - original licence link has changed is not relivant.
659  *
660  * Fork - LGPL
661  * <script type="text/javascript">
662  */
663
664 (function() {    
665     // wrappedn so fnCleanup is not in global scope...
666     if(Roo.isIE) {
667         function fnCleanUp() {
668             var p = Function.prototype;
669             delete p.createSequence;
670             delete p.defer;
671             delete p.createDelegate;
672             delete p.createCallback;
673             delete p.createInterceptor;
674
675             window.detachEvent("onunload", fnCleanUp);
676         }
677         window.attachEvent("onunload", fnCleanUp);
678     }
679 })();
680
681
682 /**
683  * @class Function
684  * These functions are available on every Function object (any JavaScript function).
685  */
686 Roo.apply(Function.prototype, {
687      /**
688      * Creates a callback that passes arguments[0], arguments[1], arguments[2], ...
689      * Call directly on any function. Example: <code>myFunction.createCallback(myarg, myarg2)</code>
690      * Will create a function that is bound to those 2 args.
691      * @return {Function} The new function
692     */
693     createCallback : function(/*args...*/){
694         // make args available, in function below
695         var args = arguments;
696         var method = this;
697         return function() {
698             return method.apply(window, args);
699         };
700     },
701
702     /**
703      * Creates a delegate (callback) that sets the scope to obj.
704      * Call directly on any function. Example: <code>this.myFunction.createDelegate(this)</code>
705      * Will create a function that is automatically scoped to this.
706      * @param {Object} obj (optional) The object for which the scope is set
707      * @param {Array} args (optional) Overrides arguments for the call. (Defaults to the arguments passed by the caller)
708      * @param {Boolean/Number} appendArgs (optional) if True args are appended to call args instead of overriding,
709      *                                             if a number the args are inserted at the specified position
710      * @return {Function} The new function
711      */
712     createDelegate : function(obj, args, appendArgs){
713         var method = this;
714         return function() {
715             var callArgs = args || arguments;
716             if(appendArgs === true){
717                 callArgs = Array.prototype.slice.call(arguments, 0);
718                 callArgs = callArgs.concat(args);
719             }else if(typeof appendArgs == "number"){
720                 callArgs = Array.prototype.slice.call(arguments, 0); // copy arguments first
721                 var applyArgs = [appendArgs, 0].concat(args); // create method call params
722                 Array.prototype.splice.apply(callArgs, applyArgs); // splice them in
723             }
724             return method.apply(obj || window, callArgs);
725         };
726     },
727
728     /**
729      * Calls this function after the number of millseconds specified.
730      * @param {Number} millis The number of milliseconds for the setTimeout call (if 0 the function is executed immediately)
731      * @param {Object} obj (optional) The object for which the scope is set
732      * @param {Array} args (optional) Overrides arguments for the call. (Defaults to the arguments passed by the caller)
733      * @param {Boolean/Number} appendArgs (optional) if True args are appended to call args instead of overriding,
734      *                                             if a number the args are inserted at the specified position
735      * @return {Number} The timeout id that can be used with clearTimeout
736      */
737     defer : function(millis, obj, args, appendArgs){
738         var fn = this.createDelegate(obj, args, appendArgs);
739         if(millis){
740             return setTimeout(fn, millis);
741         }
742         fn();
743         return 0;
744     },
745     /**
746      * Create a combined function call sequence of the original function + the passed function.
747      * The resulting function returns the results of the original function.
748      * The passed fcn is called with the parameters of the original function
749      * @param {Function} fcn The function to sequence
750      * @param {Object} scope (optional) The scope of the passed fcn (Defaults to scope of original function or window)
751      * @return {Function} The new function
752      */
753     createSequence : function(fcn, scope){
754         if(typeof fcn != "function"){
755             return this;
756         }
757         var method = this;
758         return function() {
759             var retval = method.apply(this || window, arguments);
760             fcn.apply(scope || this || window, arguments);
761             return retval;
762         };
763     },
764
765     /**
766      * Creates an interceptor function. The passed fcn is called before the original one. If it returns false, the original one is not called.
767      * The resulting function returns the results of the original function.
768      * The passed fcn is called with the parameters of the original function.
769      * @addon
770      * @param {Function} fcn The function to call before the original
771      * @param {Object} scope (optional) The scope of the passed fcn (Defaults to scope of original function or window)
772      * @return {Function} The new function
773      */
774     createInterceptor : function(fcn, scope){
775         if(typeof fcn != "function"){
776             return this;
777         }
778         var method = this;
779         return function() {
780             fcn.target = this;
781             fcn.method = method;
782             if(fcn.apply(scope || this || window, arguments) === false){
783                 return;
784             }
785             return method.apply(this || window, arguments);
786         };
787     }
788 });
789 /*
790  * Based on:
791  * Ext JS Library 1.1.1
792  * Copyright(c) 2006-2007, Ext JS, LLC.
793  *
794  * Originally Released Under LGPL - original licence link has changed is not relivant.
795  *
796  * Fork - LGPL
797  * <script type="text/javascript">
798  */
799
800 Roo.applyIf(String, {
801     
802     /** @scope String */
803     
804     /**
805      * Escapes the passed string for ' and \
806      * @param {String} string The string to escape
807      * @return {String} The escaped string
808      * @static
809      */
810     escape : function(string) {
811         return string.replace(/('|\\)/g, "\\$1");
812     },
813
814     /**
815      * Pads the left side of a string with a specified character.  This is especially useful
816      * for normalizing number and date strings.  Example usage:
817      * <pre><code>
818 var s = String.leftPad('123', 5, '0');
819 // s now contains the string: '00123'
820 </code></pre>
821      * @param {String} string The original string
822      * @param {Number} size The total length of the output string
823      * @param {String} char (optional) The character with which to pad the original string (defaults to empty string " ")
824      * @return {String} The padded string
825      * @static
826      */
827     leftPad : function (val, size, ch) {
828         var result = new String(val);
829         if(ch === null || ch === undefined || ch === '') {
830             ch = " ";
831         }
832         while (result.length < size) {
833             result = ch + result;
834         }
835         return result;
836     },
837
838     /**
839      * Allows you to define a tokenized string and pass an arbitrary number of arguments to replace the tokens.  Each
840      * token must be unique, and must increment in the format {0}, {1}, etc.  Example usage:
841      * <pre><code>
842 var cls = 'my-class', text = 'Some text';
843 var s = String.format('<div class="{0}">{1}</div>', cls, text);
844 // s now contains the string: '<div class="my-class">Some text</div>'
845 </code></pre>
846      * @param {String} string The tokenized string to be formatted
847      * @param {String} value1 The value to replace token {0}
848      * @param {String} value2 Etc...
849      * @return {String} The formatted string
850      * @static
851      */
852     format : function(format){
853         var args = Array.prototype.slice.call(arguments, 1);
854         return format.replace(/\{(\d+)\}/g, function(m, i){
855             return Roo.util.Format.htmlEncode(args[i]);
856         });
857     }
858 });
859
860 /**
861  * Utility function that allows you to easily switch a string between two alternating values.  The passed value
862  * is compared to the current string, and if they are equal, the other value that was passed in is returned.  If
863  * they are already different, the first value passed in is returned.  Note that this method returns the new value
864  * but does not change the current string.
865  * <pre><code>
866 // alternate sort directions
867 sort = sort.toggle('ASC', 'DESC');
868
869 // instead of conditional logic:
870 sort = (sort == 'ASC' ? 'DESC' : 'ASC');
871 </code></pre>
872  * @param {String} value The value to compare to the current string
873  * @param {String} other The new value to use if the string already equals the first value passed in
874  * @return {String} The new value
875  */
876  
877 String.prototype.toggle = function(value, other){
878     return this == value ? other : value;
879 };/*
880  * Based on:
881  * Ext JS Library 1.1.1
882  * Copyright(c) 2006-2007, Ext JS, LLC.
883  *
884  * Originally Released Under LGPL - original licence link has changed is not relivant.
885  *
886  * Fork - LGPL
887  * <script type="text/javascript">
888  */
889
890  /**
891  * @class Number
892  */
893 Roo.applyIf(Number.prototype, {
894     /**
895      * Checks whether or not the current number is within a desired range.  If the number is already within the
896      * range it is returned, otherwise the min or max value is returned depending on which side of the range is
897      * exceeded.  Note that this method returns the constrained value but does not change the current number.
898      * @param {Number} min The minimum number in the range
899      * @param {Number} max The maximum number in the range
900      * @return {Number} The constrained value if outside the range, otherwise the current value
901      */
902     constrain : function(min, max){
903         return Math.min(Math.max(this, min), max);
904     }
905 });/*
906  * Based on:
907  * Ext JS Library 1.1.1
908  * Copyright(c) 2006-2007, Ext JS, LLC.
909  *
910  * Originally Released Under LGPL - original licence link has changed is not relivant.
911  *
912  * Fork - LGPL
913  * <script type="text/javascript">
914  */
915  /**
916  * @class Array
917  */
918 Roo.applyIf(Array.prototype, {
919     /**
920      * Checks whether or not the specified object exists in the array.
921      * @param {Object} o The object to check for
922      * @return {Number} The index of o in the array (or -1 if it is not found)
923      */
924     indexOf : function(o){
925        for (var i = 0, len = this.length; i < len; i++){
926               if(this[i] == o) return i;
927        }
928            return -1;
929     },
930
931     /**
932      * Removes the specified object from the array.  If the object is not found nothing happens.
933      * @param {Object} o The object to remove
934      */
935     remove : function(o){
936        var index = this.indexOf(o);
937        if(index != -1){
938            this.splice(index, 1);
939        }
940     },
941     /**
942      * Map (JS 1.6 compatibility)
943      * @param {Function} function  to call
944      */
945     map : function(fun )
946     {
947         var len = this.length >>> 0;
948         if (typeof fun != "function")
949             throw new TypeError();
950
951         var res = new Array(len);
952         var thisp = arguments[1];
953         for (var i = 0; i < len; i++)
954         {
955             if (i in this)
956                 res[i] = fun.call(thisp, this[i], i, this);
957         }
958
959         return res;
960     }
961     
962 });
963
964
965  /*
966  * Based on:
967  * Ext JS Library 1.1.1
968  * Copyright(c) 2006-2007, Ext JS, LLC.
969  *
970  * Originally Released Under LGPL - original licence link has changed is not relivant.
971  *
972  * Fork - LGPL
973  * <script type="text/javascript">
974  */
975
976 /**
977  * @class Date
978  *
979  * The date parsing and format syntax is a subset of
980  * <a href="http://www.php.net/date">PHP's date() function</a>, and the formats that are
981  * supported will provide results equivalent to their PHP versions.
982  *
983  * Following is the list of all currently supported formats:
984  *<pre>
985 Sample date:
986 'Wed Jan 10 2007 15:05:01 GMT-0600 (Central Standard Time)'
987
988 Format  Output      Description
989 ------  ----------  --------------------------------------------------------------
990   d      10         Day of the month, 2 digits with leading zeros
991   D      Wed        A textual representation of a day, three letters
992   j      10         Day of the month without leading zeros
993   l      Wednesday  A full textual representation of the day of the week
994   S      th         English ordinal day of month suffix, 2 chars (use with j)
995   w      3          Numeric representation of the day of the week
996   z      9          The julian date, or day of the year (0-365)
997   W      01         ISO-8601 2-digit week number of year, weeks starting on Monday (00-52)
998   F      January    A full textual representation of the month
999   m      01         Numeric representation of a month, with leading zeros
1000   M      Jan        Month name abbreviation, three letters
1001   n      1          Numeric representation of a month, without leading zeros
1002   t      31         Number of days in the given month
1003   L      0          Whether it's a leap year (1 if it is a leap year, else 0)
1004   Y      2007       A full numeric representation of a year, 4 digits
1005   y      07         A two digit representation of a year
1006   a      pm         Lowercase Ante meridiem and Post meridiem
1007   A      PM         Uppercase Ante meridiem and Post meridiem
1008   g      3          12-hour format of an hour without leading zeros
1009   G      15         24-hour format of an hour without leading zeros
1010   h      03         12-hour format of an hour with leading zeros
1011   H      15         24-hour format of an hour with leading zeros
1012   i      05         Minutes with leading zeros
1013   s      01         Seconds, with leading zeros
1014   O      -0600      Difference to Greenwich time (GMT) in hours (Allows +08, without minutes)
1015   P      -06:00     Difference to Greenwich time (GMT) with colon between hours and minutes
1016   T      CST        Timezone setting of the machine running the code
1017   Z      -21600     Timezone offset in seconds (negative if west of UTC, positive if east)
1018 </pre>
1019  *
1020  * Example usage (note that you must escape format specifiers with '\\' to render them as character literals):
1021  * <pre><code>
1022 var dt = new Date('1/10/2007 03:05:01 PM GMT-0600');
1023 document.write(dt.format('Y-m-d'));                         //2007-01-10
1024 document.write(dt.format('F j, Y, g:i a'));                 //January 10, 2007, 3:05 pm
1025 document.write(dt.format('l, \\t\\he dS of F Y h:i:s A'));  //Wednesday, the 10th of January 2007 03:05:01 PM
1026  </code></pre>
1027  *
1028  * Here are some standard date/time patterns that you might find helpful.  They
1029  * are not part of the source of Date.js, but to use them you can simply copy this
1030  * block of code into any script that is included after Date.js and they will also become
1031  * globally available on the Date object.  Feel free to add or remove patterns as needed in your code.
1032  * <pre><code>
1033 Date.patterns = {
1034     ISO8601Long:"Y-m-d H:i:s",
1035     ISO8601Short:"Y-m-d",
1036     ShortDate: "n/j/Y",
1037     LongDate: "l, F d, Y",
1038     FullDateTime: "l, F d, Y g:i:s A",
1039     MonthDay: "F d",
1040     ShortTime: "g:i A",
1041     LongTime: "g:i:s A",
1042     SortableDateTime: "Y-m-d\\TH:i:s",
1043     UniversalSortableDateTime: "Y-m-d H:i:sO",
1044     YearMonth: "F, Y"
1045 };
1046 </code></pre>
1047  *
1048  * Example usage:
1049  * <pre><code>
1050 var dt = new Date();
1051 document.write(dt.format(Date.patterns.ShortDate));
1052  </code></pre>
1053  */
1054
1055 /*
1056  * Most of the date-formatting functions below are the excellent work of Baron Schwartz.
1057  * They generate precompiled functions from date formats instead of parsing and
1058  * processing the pattern every time you format a date.  These functions are available
1059  * on every Date object (any javascript function).
1060  *
1061  * The original article and download are here:
1062  * http://www.xaprb.com/blog/2005/12/12/javascript-closures-for-runtime-efficiency/
1063  *
1064  */
1065  
1066  
1067  // was in core
1068 /**
1069  Returns the number of milliseconds between this date and date
1070  @param {Date} date (optional) Defaults to now
1071  @return {Number} The diff in milliseconds
1072  @member Date getElapsed
1073  */
1074 Date.prototype.getElapsed = function(date) {
1075         return Math.abs((date || new Date()).getTime()-this.getTime());
1076 };
1077 // was in date file..
1078
1079
1080 // private
1081 Date.parseFunctions = {count:0};
1082 // private
1083 Date.parseRegexes = [];
1084 // private
1085 Date.formatFunctions = {count:0};
1086
1087 // private
1088 Date.prototype.dateFormat = function(format) {
1089     if (Date.formatFunctions[format] == null) {
1090         Date.createNewFormat(format);
1091     }
1092     var func = Date.formatFunctions[format];
1093     return this[func]();
1094 };
1095
1096
1097 /**
1098  * Formats a date given the supplied format string
1099  * @param {String} format The format string
1100  * @return {String} The formatted date
1101  * @method
1102  */
1103 Date.prototype.format = Date.prototype.dateFormat;
1104
1105 // private
1106 Date.createNewFormat = function(format) {
1107     var funcName = "format" + Date.formatFunctions.count++;
1108     Date.formatFunctions[format] = funcName;
1109     var code = "Date.prototype." + funcName + " = function(){return ";
1110     var special = false;
1111     var ch = '';
1112     for (var i = 0; i < format.length; ++i) {
1113         ch = format.charAt(i);
1114         if (!special && ch == "\\") {
1115             special = true;
1116         }
1117         else if (special) {
1118             special = false;
1119             code += "'" + String.escape(ch) + "' + ";
1120         }
1121         else {
1122             code += Date.getFormatCode(ch);
1123         }
1124     }
1125     /** eval:var:zzzzzzzzzzzzz */
1126     eval(code.substring(0, code.length - 3) + ";}");
1127 };
1128
1129 // private
1130 Date.getFormatCode = function(character) {
1131     switch (character) {
1132     case "d":
1133         return "String.leftPad(this.getDate(), 2, '0') + ";
1134     case "D":
1135         return "Date.dayNames[this.getDay()].substring(0, 3) + ";
1136     case "j":
1137         return "this.getDate() + ";
1138     case "l":
1139         return "Date.dayNames[this.getDay()] + ";
1140     case "S":
1141         return "this.getSuffix() + ";
1142     case "w":
1143         return "this.getDay() + ";
1144     case "z":
1145         return "this.getDayOfYear() + ";
1146     case "W":
1147         return "this.getWeekOfYear() + ";
1148     case "F":
1149         return "Date.monthNames[this.getMonth()] + ";
1150     case "m":
1151         return "String.leftPad(this.getMonth() + 1, 2, '0') + ";
1152     case "M":
1153         return "Date.monthNames[this.getMonth()].substring(0, 3) + ";
1154     case "n":
1155         return "(this.getMonth() + 1) + ";
1156     case "t":
1157         return "this.getDaysInMonth() + ";
1158     case "L":
1159         return "(this.isLeapYear() ? 1 : 0) + ";
1160     case "Y":
1161         return "this.getFullYear() + ";
1162     case "y":
1163         return "('' + this.getFullYear()).substring(2, 4) + ";
1164     case "a":
1165         return "(this.getHours() < 12 ? 'am' : 'pm') + ";
1166     case "A":
1167         return "(this.getHours() < 12 ? 'AM' : 'PM') + ";
1168     case "g":
1169         return "((this.getHours() % 12) ? this.getHours() % 12 : 12) + ";
1170     case "G":
1171         return "this.getHours() + ";
1172     case "h":
1173         return "String.leftPad((this.getHours() % 12) ? this.getHours() % 12 : 12, 2, '0') + ";
1174     case "H":
1175         return "String.leftPad(this.getHours(), 2, '0') + ";
1176     case "i":
1177         return "String.leftPad(this.getMinutes(), 2, '0') + ";
1178     case "s":
1179         return "String.leftPad(this.getSeconds(), 2, '0') + ";
1180     case "O":
1181         return "this.getGMTOffset() + ";
1182     case "P":
1183         return "this.getGMTColonOffset() + ";
1184     case "T":
1185         return "this.getTimezone() + ";
1186     case "Z":
1187         return "(this.getTimezoneOffset() * -60) + ";
1188     default:
1189         return "'" + String.escape(character) + "' + ";
1190     }
1191 };
1192
1193 /**
1194  * Parses the passed string using the specified format. Note that this function expects dates in normal calendar
1195  * format, meaning that months are 1-based (1 = January) and not zero-based like in JavaScript dates.  Any part of
1196  * the date format that is not specified will default to the current date value for that part.  Time parts can also
1197  * be specified, but default to 0.  Keep in mind that the input date string must precisely match the specified format
1198  * string or the parse operation will fail.
1199  * Example Usage:
1200 <pre><code>
1201 //dt = Fri May 25 2007 (current date)
1202 var dt = new Date();
1203
1204 //dt = Thu May 25 2006 (today's month/day in 2006)
1205 dt = Date.parseDate("2006", "Y");
1206
1207 //dt = Sun Jan 15 2006 (all date parts specified)
1208 dt = Date.parseDate("2006-1-15", "Y-m-d");
1209
1210 //dt = Sun Jan 15 2006 15:20:01 GMT-0600 (CST)
1211 dt = Date.parseDate("2006-1-15 3:20:01 PM", "Y-m-d h:i:s A" );
1212 </code></pre>
1213  * @param {String} input The unparsed date as a string
1214  * @param {String} format The format the date is in
1215  * @return {Date} The parsed date
1216  * @static
1217  */
1218 Date.parseDate = function(input, format) {
1219     if (Date.parseFunctions[format] == null) {
1220         Date.createParser(format);
1221     }
1222     var func = Date.parseFunctions[format];
1223     return Date[func](input);
1224 };
1225 /**
1226  * @private
1227  */
1228 Date.createParser = function(format) {
1229     var funcName = "parse" + Date.parseFunctions.count++;
1230     var regexNum = Date.parseRegexes.length;
1231     var currentGroup = 1;
1232     Date.parseFunctions[format] = funcName;
1233
1234     var code = "Date." + funcName + " = function(input){\n"
1235         + "var y = -1, m = -1, d = -1, h = -1, i = -1, s = -1, o, z, v;\n"
1236         + "var d = new Date();\n"
1237         + "y = d.getFullYear();\n"
1238         + "m = d.getMonth();\n"
1239         + "d = d.getDate();\n"
1240         + "var results = input.match(Date.parseRegexes[" + regexNum + "]);\n"
1241         + "if (results && results.length > 0) {";
1242     var regex = "";
1243
1244     var special = false;
1245     var ch = '';
1246     for (var i = 0; i < format.length; ++i) {
1247         ch = format.charAt(i);
1248         if (!special && ch == "\\") {
1249             special = true;
1250         }
1251         else if (special) {
1252             special = false;
1253             regex += String.escape(ch);
1254         }
1255         else {
1256             var obj = Date.formatCodeToRegex(ch, currentGroup);
1257             currentGroup += obj.g;
1258             regex += obj.s;
1259             if (obj.g && obj.c) {
1260                 code += obj.c;
1261             }
1262         }
1263     }
1264
1265     code += "if (y >= 0 && m >= 0 && d > 0 && h >= 0 && i >= 0 && s >= 0)\n"
1266         + "{v = new Date(y, m, d, h, i, s);}\n"
1267         + "else if (y >= 0 && m >= 0 && d > 0 && h >= 0 && i >= 0)\n"
1268         + "{v = new Date(y, m, d, h, i);}\n"
1269         + "else if (y >= 0 && m >= 0 && d > 0 && h >= 0)\n"
1270         + "{v = new Date(y, m, d, h);}\n"
1271         + "else if (y >= 0 && m >= 0 && d > 0)\n"
1272         + "{v = new Date(y, m, d);}\n"
1273         + "else if (y >= 0 && m >= 0)\n"
1274         + "{v = new Date(y, m);}\n"
1275         + "else if (y >= 0)\n"
1276         + "{v = new Date(y);}\n"
1277         + "}return (v && (z || o))?\n" // favour UTC offset over GMT offset
1278         + "    ((z)? v.add(Date.SECOND, (v.getTimezoneOffset() * 60) + (z*1)) :\n" // reset to UTC, then add offset
1279         + "        v.add(Date.HOUR, (v.getGMTOffset() / 100) + (o / -100))) : v\n" // reset to GMT, then add offset
1280         + ";}";
1281
1282     Date.parseRegexes[regexNum] = new RegExp("^" + regex + "$");
1283     /** eval:var:zzzzzzzzzzzzz */
1284     eval(code);
1285 };
1286
1287 // private
1288 Date.formatCodeToRegex = function(character, currentGroup) {
1289     switch (character) {
1290     case "D":
1291         return {g:0,
1292         c:null,
1293         s:"(?:Sun|Mon|Tue|Wed|Thu|Fri|Sat)"};
1294     case "j":
1295         return {g:1,
1296             c:"d = parseInt(results[" + currentGroup + "], 10);\n",
1297             s:"(\\d{1,2})"}; // day of month without leading zeroes
1298     case "d":
1299         return {g:1,
1300             c:"d = parseInt(results[" + currentGroup + "], 10);\n",
1301             s:"(\\d{2})"}; // day of month with leading zeroes
1302     case "l":
1303         return {g:0,
1304             c:null,
1305             s:"(?:" + Date.dayNames.join("|") + ")"};
1306     case "S":
1307         return {g:0,
1308             c:null,
1309             s:"(?:st|nd|rd|th)"};
1310     case "w":
1311         return {g:0,
1312             c:null,
1313             s:"\\d"};
1314     case "z":
1315         return {g:0,
1316             c:null,
1317             s:"(?:\\d{1,3})"};
1318     case "W":
1319         return {g:0,
1320             c:null,
1321             s:"(?:\\d{2})"};
1322     case "F":
1323         return {g:1,
1324             c:"m = parseInt(Date.monthNumbers[results[" + currentGroup + "].substring(0, 3)], 10);\n",
1325             s:"(" + Date.monthNames.join("|") + ")"};
1326     case "M":
1327         return {g:1,
1328             c:"m = parseInt(Date.monthNumbers[results[" + currentGroup + "]], 10);\n",
1329             s:"(Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)"};
1330     case "n":
1331         return {g:1,
1332             c:"m = parseInt(results[" + currentGroup + "], 10) - 1;\n",
1333             s:"(\\d{1,2})"}; // Numeric representation of a month, without leading zeros
1334     case "m":
1335         return {g:1,
1336             c:"m = parseInt(results[" + currentGroup + "], 10) - 1;\n",
1337             s:"(\\d{2})"}; // Numeric representation of a month, with leading zeros
1338     case "t":
1339         return {g:0,
1340             c:null,
1341             s:"\\d{1,2}"};
1342     case "L":
1343         return {g:0,
1344             c:null,
1345             s:"(?:1|0)"};
1346     case "Y":
1347         return {g:1,
1348             c:"y = parseInt(results[" + currentGroup + "], 10);\n",
1349             s:"(\\d{4})"};
1350     case "y":
1351         return {g:1,
1352             c:"var ty = parseInt(results[" + currentGroup + "], 10);\n"
1353                 + "y = ty > Date.y2kYear ? 1900 + ty : 2000 + ty;\n",
1354             s:"(\\d{1,2})"};
1355     case "a":
1356         return {g:1,
1357             c:"if (results[" + currentGroup + "] == 'am') {\n"
1358                 + "if (h == 12) { h = 0; }\n"
1359                 + "} else { if (h < 12) { h += 12; }}",
1360             s:"(am|pm)"};
1361     case "A":
1362         return {g:1,
1363             c:"if (results[" + currentGroup + "] == 'AM') {\n"
1364                 + "if (h == 12) { h = 0; }\n"
1365                 + "} else { if (h < 12) { h += 12; }}",
1366             s:"(AM|PM)"};
1367     case "g":
1368     case "G":
1369         return {g:1,
1370             c:"h = parseInt(results[" + currentGroup + "], 10);\n",
1371             s:"(\\d{1,2})"}; // 12/24-hr format  format of an hour without leading zeroes
1372     case "h":
1373     case "H":
1374         return {g:1,
1375             c:"h = parseInt(results[" + currentGroup + "], 10);\n",
1376             s:"(\\d{2})"}; //  12/24-hr format  format of an hour with leading zeroes
1377     case "i":
1378         return {g:1,
1379             c:"i = parseInt(results[" + currentGroup + "], 10);\n",
1380             s:"(\\d{2})"};
1381     case "s":
1382         return {g:1,
1383             c:"s = parseInt(results[" + currentGroup + "], 10);\n",
1384             s:"(\\d{2})"};
1385     case "O":
1386         return {g:1,
1387             c:[
1388                 "o = results[", currentGroup, "];\n",
1389                 "var sn = o.substring(0,1);\n", // get + / - sign
1390                 "var hr = o.substring(1,3)*1 + Math.floor(o.substring(3,5) / 60);\n", // get hours (performs minutes-to-hour conversion also)
1391                 "var mn = o.substring(3,5) % 60;\n", // get minutes
1392                 "o = ((-12 <= (hr*60 + mn)/60) && ((hr*60 + mn)/60 <= 14))?\n", // -12hrs <= GMT offset <= 14hrs
1393                 "    (sn + String.leftPad(hr, 2, 0) + String.leftPad(mn, 2, 0)) : null;\n"
1394             ].join(""),
1395             s:"([+\-]\\d{2,4})"};
1396     
1397     
1398     case "P":
1399         return {g:1,
1400                 c:[
1401                    "o = results[", currentGroup, "];\n",
1402                    "var sn = o.substring(0,1);\n",
1403                    "var hr = o.substring(1,3)*1 + Math.floor(o.substring(4,6) / 60);\n",
1404                    "var mn = o.substring(4,6) % 60;\n",
1405                    "o = ((-12 <= (hr*60 + mn)/60) && ((hr*60 + mn)/60 <= 14))?\n",
1406                         "    (sn + String.leftPad(hr, 2, 0) + String.leftPad(mn, 2, 0)) : null;\n"
1407             ].join(""),
1408             s:"([+\-]\\d{4})"};
1409     case "T":
1410         return {g:0,
1411             c:null,
1412             s:"[A-Z]{1,4}"}; // timezone abbrev. may be between 1 - 4 chars
1413     case "Z":
1414         return {g:1,
1415             c:"z = results[" + currentGroup + "];\n" // -43200 <= UTC offset <= 50400
1416                   + "z = (-43200 <= z*1 && z*1 <= 50400)? z : null;\n",
1417             s:"([+\-]?\\d{1,5})"}; // leading '+' sign is optional for UTC offset
1418     default:
1419         return {g:0,
1420             c:null,
1421             s:String.escape(character)};
1422     }
1423 };
1424
1425 /**
1426  * Get the timezone abbreviation of the current date (equivalent to the format specifier 'T').
1427  * @return {String} The abbreviated timezone name (e.g. 'CST')
1428  */
1429 Date.prototype.getTimezone = function() {
1430     return this.toString().replace(/^.*? ([A-Z]{1,4})[\-+][0-9]{4} .*$/, "$1");
1431 };
1432
1433 /**
1434  * Get the offset from GMT of the current date (equivalent to the format specifier 'O').
1435  * @return {String} The 4-character offset string prefixed with + or - (e.g. '-0600')
1436  */
1437 Date.prototype.getGMTOffset = function() {
1438     return (this.getTimezoneOffset() > 0 ? "-" : "+")
1439         + String.leftPad(Math.abs(Math.floor(this.getTimezoneOffset() / 60)), 2, "0")
1440         + String.leftPad(this.getTimezoneOffset() % 60, 2, "0");
1441 };
1442
1443 /**
1444  * Get the offset from GMT of the current date (equivalent to the format specifier 'P').
1445  * @return {String} 2-characters representing hours and 2-characters representing minutes
1446  * seperated by a colon and prefixed with + or - (e.g. '-06:00')
1447  */
1448 Date.prototype.getGMTColonOffset = function() {
1449         return (this.getTimezoneOffset() > 0 ? "-" : "+")
1450                 + String.leftPad(Math.abs(Math.floor(this.getTimezoneOffset() / 60)), 2, "0")
1451                 + ":"
1452                 + String.leftPad(this.getTimezoneOffset() %60, 2, "0");
1453 }
1454
1455 /**
1456  * Get the numeric day number of the year, adjusted for leap year.
1457  * @return {Number} 0 through 364 (365 in leap years)
1458  */
1459 Date.prototype.getDayOfYear = function() {
1460     var num = 0;
1461     Date.daysInMonth[1] = this.isLeapYear() ? 29 : 28;
1462     for (var i = 0; i < this.getMonth(); ++i) {
1463         num += Date.daysInMonth[i];
1464     }
1465     return num + this.getDate() - 1;
1466 };
1467
1468 /**
1469  * Get the string representation of the numeric week number of the year
1470  * (equivalent to the format specifier 'W').
1471  * @return {String} '00' through '52'
1472  */
1473 Date.prototype.getWeekOfYear = function() {
1474     // Skip to Thursday of this week
1475     var now = this.getDayOfYear() + (4 - this.getDay());
1476     // Find the first Thursday of the year
1477     var jan1 = new Date(this.getFullYear(), 0, 1);
1478     var then = (7 - jan1.getDay() + 4);
1479     return String.leftPad(((now - then) / 7) + 1, 2, "0");
1480 };
1481
1482 /**
1483  * Whether or not the current date is in a leap year.
1484  * @return {Boolean} True if the current date is in a leap year, else false
1485  */
1486 Date.prototype.isLeapYear = function() {
1487     var year = this.getFullYear();
1488     return ((year & 3) == 0 && (year % 100 || (year % 400 == 0 && year)));
1489 };
1490
1491 /**
1492  * Get the first day of the current month, adjusted for leap year.  The returned value
1493  * is the numeric day index within the week (0-6) which can be used in conjunction with
1494  * the {@link #monthNames} array to retrieve the textual day name.
1495  * Example:
1496  *<pre><code>
1497 var dt = new Date('1/10/2007');
1498 document.write(Date.dayNames[dt.getFirstDayOfMonth()]); //output: 'Monday'
1499 </code></pre>
1500  * @return {Number} The day number (0-6)
1501  */
1502 Date.prototype.getFirstDayOfMonth = function() {
1503     var day = (this.getDay() - (this.getDate() - 1)) % 7;
1504     return (day < 0) ? (day + 7) : day;
1505 };
1506
1507 /**
1508  * Get the last day of the current month, adjusted for leap year.  The returned value
1509  * is the numeric day index within the week (0-6) which can be used in conjunction with
1510  * the {@link #monthNames} array to retrieve the textual day name.
1511  * Example:
1512  *<pre><code>
1513 var dt = new Date('1/10/2007');
1514 document.write(Date.dayNames[dt.getLastDayOfMonth()]); //output: 'Wednesday'
1515 </code></pre>
1516  * @return {Number} The day number (0-6)
1517  */
1518 Date.prototype.getLastDayOfMonth = function() {
1519     var day = (this.getDay() + (Date.daysInMonth[this.getMonth()] - this.getDate())) % 7;
1520     return (day < 0) ? (day + 7) : day;
1521 };
1522
1523
1524 /**
1525  * Get the first date of this date's month
1526  * @return {Date}
1527  */
1528 Date.prototype.getFirstDateOfMonth = function() {
1529     return new Date(this.getFullYear(), this.getMonth(), 1);
1530 };
1531
1532 /**
1533  * Get the last date of this date's month
1534  * @return {Date}
1535  */
1536 Date.prototype.getLastDateOfMonth = function() {
1537     return new Date(this.getFullYear(), this.getMonth(), this.getDaysInMonth());
1538 };
1539 /**
1540  * Get the number of days in the current month, adjusted for leap year.
1541  * @return {Number} The number of days in the month
1542  */
1543 Date.prototype.getDaysInMonth = function() {
1544     Date.daysInMonth[1] = this.isLeapYear() ? 29 : 28;
1545     return Date.daysInMonth[this.getMonth()];
1546 };
1547
1548 /**
1549  * Get the English ordinal suffix of the current day (equivalent to the format specifier 'S').
1550  * @return {String} 'st, 'nd', 'rd' or 'th'
1551  */
1552 Date.prototype.getSuffix = function() {
1553     switch (this.getDate()) {
1554         case 1:
1555         case 21:
1556         case 31:
1557             return "st";
1558         case 2:
1559         case 22:
1560             return "nd";
1561         case 3:
1562         case 23:
1563             return "rd";
1564         default:
1565             return "th";
1566     }
1567 };
1568
1569 // private
1570 Date.daysInMonth = [31,28,31,30,31,30,31,31,30,31,30,31];
1571
1572 /**
1573  * An array of textual month names.
1574  * Override these values for international dates, for example...
1575  * Date.monthNames = ['JanInYourLang', 'FebInYourLang', ...];
1576  * @type Array
1577  * @static
1578  */
1579 Date.monthNames =
1580    ["January",
1581     "February",
1582     "March",
1583     "April",
1584     "May",
1585     "June",
1586     "July",
1587     "August",
1588     "September",
1589     "October",
1590     "November",
1591     "December"];
1592
1593 /**
1594  * An array of textual day names.
1595  * Override these values for international dates, for example...
1596  * Date.dayNames = ['SundayInYourLang', 'MondayInYourLang', ...];
1597  * @type Array
1598  * @static
1599  */
1600 Date.dayNames =
1601    ["Sunday",
1602     "Monday",
1603     "Tuesday",
1604     "Wednesday",
1605     "Thursday",
1606     "Friday",
1607     "Saturday"];
1608
1609 // private
1610 Date.y2kYear = 50;
1611 // private
1612 Date.monthNumbers = {
1613     Jan:0,
1614     Feb:1,
1615     Mar:2,
1616     Apr:3,
1617     May:4,
1618     Jun:5,
1619     Jul:6,
1620     Aug:7,
1621     Sep:8,
1622     Oct:9,
1623     Nov:10,
1624     Dec:11};
1625
1626 /**
1627  * Creates and returns a new Date instance with the exact same date value as the called instance.
1628  * Dates are copied and passed by reference, so if a copied date variable is modified later, the original
1629  * variable will also be changed.  When the intention is to create a new variable that will not
1630  * modify the original instance, you should create a clone.
1631  *
1632  * Example of correctly cloning a date:
1633  * <pre><code>
1634 //wrong way:
1635 var orig = new Date('10/1/2006');
1636 var copy = orig;
1637 copy.setDate(5);
1638 document.write(orig);  //returns 'Thu Oct 05 2006'!
1639
1640 //correct way:
1641 var orig = new Date('10/1/2006');
1642 var copy = orig.clone();
1643 copy.setDate(5);
1644 document.write(orig);  //returns 'Thu Oct 01 2006'
1645 </code></pre>
1646  * @return {Date} The new Date instance
1647  */
1648 Date.prototype.clone = function() {
1649         return new Date(this.getTime());
1650 };
1651
1652 /**
1653  * Clears any time information from this date
1654  @param {Boolean} clone true to create a clone of this date, clear the time and return it
1655  @return {Date} this or the clone
1656  */
1657 Date.prototype.clearTime = function(clone){
1658     if(clone){
1659         return this.clone().clearTime();
1660     }
1661     this.setHours(0);
1662     this.setMinutes(0);
1663     this.setSeconds(0);
1664     this.setMilliseconds(0);
1665     return this;
1666 };
1667
1668 // private
1669 // safari setMonth is broken
1670 if(Roo.isSafari){
1671     Date.brokenSetMonth = Date.prototype.setMonth;
1672         Date.prototype.setMonth = function(num){
1673                 if(num <= -1){
1674                         var n = Math.ceil(-num);
1675                         var back_year = Math.ceil(n/12);
1676                         var month = (n % 12) ? 12 - n % 12 : 0 ;
1677                         this.setFullYear(this.getFullYear() - back_year);
1678                         return Date.brokenSetMonth.call(this, month);
1679                 } else {
1680                         return Date.brokenSetMonth.apply(this, arguments);
1681                 }
1682         };
1683 }
1684
1685 /** Date interval constant 
1686 * @static 
1687 * @type String */
1688 Date.MILLI = "ms";
1689 /** Date interval constant 
1690 * @static 
1691 * @type String */
1692 Date.SECOND = "s";
1693 /** Date interval constant 
1694 * @static 
1695 * @type String */
1696 Date.MINUTE = "mi";
1697 /** Date interval constant 
1698 * @static 
1699 * @type String */
1700 Date.HOUR = "h";
1701 /** Date interval constant 
1702 * @static 
1703 * @type String */
1704 Date.DAY = "d";
1705 /** Date interval constant 
1706 * @static 
1707 * @type String */
1708 Date.MONTH = "mo";
1709 /** Date interval constant 
1710 * @static 
1711 * @type String */
1712 Date.YEAR = "y";
1713
1714 /**
1715  * Provides a convenient method of performing basic date arithmetic.  This method
1716  * does not modify the Date instance being called - it creates and returns
1717  * a new Date instance containing the resulting date value.
1718  *
1719  * Examples:
1720  * <pre><code>
1721 //Basic usage:
1722 var dt = new Date('10/29/2006').add(Date.DAY, 5);
1723 document.write(dt); //returns 'Fri Oct 06 2006 00:00:00'
1724
1725 //Negative values will subtract correctly:
1726 var dt2 = new Date('10/1/2006').add(Date.DAY, -5);
1727 document.write(dt2); //returns 'Tue Sep 26 2006 00:00:00'
1728
1729 //You can even chain several calls together in one line!
1730 var dt3 = new Date('10/1/2006').add(Date.DAY, 5).add(Date.HOUR, 8).add(Date.MINUTE, -30);
1731 document.write(dt3); //returns 'Fri Oct 06 2006 07:30:00'
1732  </code></pre>
1733  *
1734  * @param {String} interval   A valid date interval enum value
1735  * @param {Number} value      The amount to add to the current date
1736  * @return {Date} The new Date instance
1737  */
1738 Date.prototype.add = function(interval, value){
1739   var d = this.clone();
1740   if (!interval || value === 0) return d;
1741   switch(interval.toLowerCase()){
1742     case Date.MILLI:
1743       d.setMilliseconds(this.getMilliseconds() + value);
1744       break;
1745     case Date.SECOND:
1746       d.setSeconds(this.getSeconds() + value);
1747       break;
1748     case Date.MINUTE:
1749       d.setMinutes(this.getMinutes() + value);
1750       break;
1751     case Date.HOUR:
1752       d.setHours(this.getHours() + value);
1753       break;
1754     case Date.DAY:
1755       d.setDate(this.getDate() + value);
1756       break;
1757     case Date.MONTH:
1758       var day = this.getDate();
1759       if(day > 28){
1760           day = Math.min(day, this.getFirstDateOfMonth().add('mo', value).getLastDateOfMonth().getDate());
1761       }
1762       d.setDate(day);
1763       d.setMonth(this.getMonth() + value);
1764       break;
1765     case Date.YEAR:
1766       d.setFullYear(this.getFullYear() + value);
1767       break;
1768   }
1769   return d;
1770 };
1771 /*
1772  * Based on:
1773  * Ext JS Library 1.1.1
1774  * Copyright(c) 2006-2007, Ext JS, LLC.
1775  *
1776  * Originally Released Under LGPL - original licence link has changed is not relivant.
1777  *
1778  * Fork - LGPL
1779  * <script type="text/javascript">
1780  */
1781
1782 /**
1783  * @class Roo.lib.Dom
1784  * @static
1785  * 
1786  * Dom utils (from YIU afaik)
1787  * 
1788  **/
1789 Roo.lib.Dom = {
1790     /**
1791      * Get the view width
1792      * @param {Boolean} full True will get the full document, otherwise it's the view width
1793      * @return {Number} The width
1794      */
1795      
1796     getViewWidth : function(full) {
1797         return full ? this.getDocumentWidth() : this.getViewportWidth();
1798     },
1799     /**
1800      * Get the view height
1801      * @param {Boolean} full True will get the full document, otherwise it's the view height
1802      * @return {Number} The height
1803      */
1804     getViewHeight : function(full) {
1805         return full ? this.getDocumentHeight() : this.getViewportHeight();
1806     },
1807
1808     getDocumentHeight: function() {
1809         var scrollHeight = (document.compatMode != "CSS1Compat") ? document.body.scrollHeight : document.documentElement.scrollHeight;
1810         return Math.max(scrollHeight, this.getViewportHeight());
1811     },
1812
1813     getDocumentWidth: function() {
1814         var scrollWidth = (document.compatMode != "CSS1Compat") ? document.body.scrollWidth : document.documentElement.scrollWidth;
1815         return Math.max(scrollWidth, this.getViewportWidth());
1816     },
1817
1818     getViewportHeight: function() {
1819         var height = self.innerHeight;
1820         var mode = document.compatMode;
1821
1822         if ((mode || Roo.isIE) && !Roo.isOpera) {
1823             height = (mode == "CSS1Compat") ?
1824                      document.documentElement.clientHeight :
1825                      document.body.clientHeight;
1826         }
1827
1828         return height;
1829     },
1830
1831     getViewportWidth: function() {
1832         var width = self.innerWidth;
1833         var mode = document.compatMode;
1834
1835         if (mode || Roo.isIE) {
1836             width = (mode == "CSS1Compat") ?
1837                     document.documentElement.clientWidth :
1838                     document.body.clientWidth;
1839         }
1840         return width;
1841     },
1842
1843     isAncestor : function(p, c) {
1844         p = Roo.getDom(p);
1845         c = Roo.getDom(c);
1846         if (!p || !c) {
1847             return false;
1848         }
1849
1850         if (p.contains && !Roo.isSafari) {
1851             return p.contains(c);
1852         } else if (p.compareDocumentPosition) {
1853             return !!(p.compareDocumentPosition(c) & 16);
1854         } else {
1855             var parent = c.parentNode;
1856             while (parent) {
1857                 if (parent == p) {
1858                     return true;
1859                 }
1860                 else if (!parent.tagName || parent.tagName.toUpperCase() == "HTML") {
1861                     return false;
1862                 }
1863                 parent = parent.parentNode;
1864             }
1865             return false;
1866         }
1867     },
1868
1869     getRegion : function(el) {
1870         return Roo.lib.Region.getRegion(el);
1871     },
1872
1873     getY : function(el) {
1874         return this.getXY(el)[1];
1875     },
1876
1877     getX : function(el) {
1878         return this.getXY(el)[0];
1879     },
1880
1881     getXY : function(el) {
1882         var p, pe, b, scroll, bd = document.body;
1883         el = Roo.getDom(el);
1884         var fly = Roo.lib.AnimBase.fly;
1885         if (el.getBoundingClientRect) {
1886             b = el.getBoundingClientRect();
1887             scroll = fly(document).getScroll();
1888             return [b.left + scroll.left, b.top + scroll.top];
1889         }
1890         var x = 0, y = 0;
1891
1892         p = el;
1893
1894         var hasAbsolute = fly(el).getStyle("position") == "absolute";
1895
1896         while (p) {
1897
1898             x += p.offsetLeft;
1899             y += p.offsetTop;
1900
1901             if (!hasAbsolute && fly(p).getStyle("position") == "absolute") {
1902                 hasAbsolute = true;
1903             }
1904
1905             if (Roo.isGecko) {
1906                 pe = fly(p);
1907
1908                 var bt = parseInt(pe.getStyle("borderTopWidth"), 10) || 0;
1909                 var bl = parseInt(pe.getStyle("borderLeftWidth"), 10) || 0;
1910
1911
1912                 x += bl;
1913                 y += bt;
1914
1915
1916                 if (p != el && pe.getStyle('overflow') != 'visible') {
1917                     x += bl;
1918                     y += bt;
1919                 }
1920             }
1921             p = p.offsetParent;
1922         }
1923
1924         if (Roo.isSafari && hasAbsolute) {
1925             x -= bd.offsetLeft;
1926             y -= bd.offsetTop;
1927         }
1928
1929         if (Roo.isGecko && !hasAbsolute) {
1930             var dbd = fly(bd);
1931             x += parseInt(dbd.getStyle("borderLeftWidth"), 10) || 0;
1932             y += parseInt(dbd.getStyle("borderTopWidth"), 10) || 0;
1933         }
1934
1935         p = el.parentNode;
1936         while (p && p != bd) {
1937             if (!Roo.isOpera || (p.tagName != 'TR' && fly(p).getStyle("display") != "inline")) {
1938                 x -= p.scrollLeft;
1939                 y -= p.scrollTop;
1940             }
1941             p = p.parentNode;
1942         }
1943         return [x, y];
1944     },
1945  
1946   
1947
1948
1949     setXY : function(el, xy) {
1950         el = Roo.fly(el, '_setXY');
1951         el.position();
1952         var pts = el.translatePoints(xy);
1953         if (xy[0] !== false) {
1954             el.dom.style.left = pts.left + "px";
1955         }
1956         if (xy[1] !== false) {
1957             el.dom.style.top = pts.top + "px";
1958         }
1959     },
1960
1961     setX : function(el, x) {
1962         this.setXY(el, [x, false]);
1963     },
1964
1965     setY : function(el, y) {
1966         this.setXY(el, [false, y]);
1967     }
1968 };
1969 /*
1970  * Portions of this file are based on pieces of Yahoo User Interface Library
1971  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
1972  * YUI licensed under the BSD License:
1973  * http://developer.yahoo.net/yui/license.txt
1974  * <script type="text/javascript">
1975  *
1976  */
1977
1978 Roo.lib.Event = function() {
1979     var loadComplete = false;
1980     var listeners = [];
1981     var unloadListeners = [];
1982     var retryCount = 0;
1983     var onAvailStack = [];
1984     var counter = 0;
1985     var lastError = null;
1986
1987     return {
1988         POLL_RETRYS: 200,
1989         POLL_INTERVAL: 20,
1990         EL: 0,
1991         TYPE: 1,
1992         FN: 2,
1993         WFN: 3,
1994         OBJ: 3,
1995         ADJ_SCOPE: 4,
1996         _interval: null,
1997
1998         startInterval: function() {
1999             if (!this._interval) {
2000                 var self = this;
2001                 var callback = function() {
2002                     self._tryPreloadAttach();
2003                 };
2004                 this._interval = setInterval(callback, this.POLL_INTERVAL);
2005
2006             }
2007         },
2008
2009         onAvailable: function(p_id, p_fn, p_obj, p_override) {
2010             onAvailStack.push({ id:         p_id,
2011                 fn:         p_fn,
2012                 obj:        p_obj,
2013                 override:   p_override,
2014                 checkReady: false    });
2015
2016             retryCount = this.POLL_RETRYS;
2017             this.startInterval();
2018         },
2019
2020
2021         addListener: function(el, eventName, fn) {
2022             el = Roo.getDom(el);
2023             if (!el || !fn) {
2024                 return false;
2025             }
2026
2027             if ("unload" == eventName) {
2028                 unloadListeners[unloadListeners.length] =
2029                 [el, eventName, fn];
2030                 return true;
2031             }
2032
2033             var wrappedFn = function(e) {
2034                 return fn(Roo.lib.Event.getEvent(e));
2035             };
2036
2037             var li = [el, eventName, fn, wrappedFn];
2038
2039             var index = listeners.length;
2040             listeners[index] = li;
2041
2042             this.doAdd(el, eventName, wrappedFn, false);
2043             return true;
2044
2045         },
2046
2047
2048         removeListener: function(el, eventName, fn) {
2049             var i, len;
2050
2051             el = Roo.getDom(el);
2052
2053             if(!fn) {
2054                 return this.purgeElement(el, false, eventName);
2055             }
2056
2057
2058             if ("unload" == eventName) {
2059
2060                 for (i = 0,len = unloadListeners.length; i < len; i++) {
2061                     var li = unloadListeners[i];
2062                     if (li &&
2063                         li[0] == el &&
2064                         li[1] == eventName &&
2065                         li[2] == fn) {
2066                         unloadListeners.splice(i, 1);
2067                         return true;
2068                     }
2069                 }
2070
2071                 return false;
2072             }
2073
2074             var cacheItem = null;
2075
2076
2077             var index = arguments[3];
2078
2079             if ("undefined" == typeof index) {
2080                 index = this._getCacheIndex(el, eventName, fn);
2081             }
2082
2083             if (index >= 0) {
2084                 cacheItem = listeners[index];
2085             }
2086
2087             if (!el || !cacheItem) {
2088                 return false;
2089             }
2090
2091             this.doRemove(el, eventName, cacheItem[this.WFN], false);
2092
2093             delete listeners[index][this.WFN];
2094             delete listeners[index][this.FN];
2095             listeners.splice(index, 1);
2096
2097             return true;
2098
2099         },
2100
2101
2102         getTarget: function(ev, resolveTextNode) {
2103             ev = ev.browserEvent || ev;
2104             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2105             var t = ev.target || ev.srcElement;
2106             return this.resolveTextNode(t);
2107         },
2108
2109
2110         resolveTextNode: function(node) {
2111             if (Roo.isSafari && node && 3 == node.nodeType) {
2112                 return node.parentNode;
2113             } else {
2114                 return node;
2115             }
2116         },
2117
2118
2119         getPageX: function(ev) {
2120             ev = ev.browserEvent || ev;
2121             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2122             var x = ev.pageX;
2123             if (!x && 0 !== x) {
2124                 x = ev.clientX || 0;
2125
2126                 if (Roo.isIE) {
2127                     x += this.getScroll()[1];
2128                 }
2129             }
2130
2131             return x;
2132         },
2133
2134
2135         getPageY: function(ev) {
2136             ev = ev.browserEvent || ev;
2137             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2138             var y = ev.pageY;
2139             if (!y && 0 !== y) {
2140                 y = ev.clientY || 0;
2141
2142                 if (Roo.isIE) {
2143                     y += this.getScroll()[0];
2144                 }
2145             }
2146
2147
2148             return y;
2149         },
2150
2151
2152         getXY: function(ev) {
2153             ev = ev.browserEvent || ev;
2154             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2155             return [this.getPageX(ev), this.getPageY(ev)];
2156         },
2157
2158
2159         getRelatedTarget: function(ev) {
2160             ev = ev.browserEvent || ev;
2161             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2162             var t = ev.relatedTarget;
2163             if (!t) {
2164                 if (ev.type == "mouseout") {
2165                     t = ev.toElement;
2166                 } else if (ev.type == "mouseover") {
2167                     t = ev.fromElement;
2168                 }
2169             }
2170
2171             return this.resolveTextNode(t);
2172         },
2173
2174
2175         getTime: function(ev) {
2176             ev = ev.browserEvent || ev;
2177             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2178             if (!ev.time) {
2179                 var t = new Date().getTime();
2180                 try {
2181                     ev.time = t;
2182                 } catch(ex) {
2183                     this.lastError = ex;
2184                     return t;
2185                 }
2186             }
2187
2188             return ev.time;
2189         },
2190
2191
2192         stopEvent: function(ev) {
2193             this.stopPropagation(ev);
2194             this.preventDefault(ev);
2195         },
2196
2197
2198         stopPropagation: function(ev) {
2199             ev = ev.browserEvent || ev;
2200             if (ev.stopPropagation) {
2201                 ev.stopPropagation();
2202             } else {
2203                 ev.cancelBubble = true;
2204             }
2205         },
2206
2207
2208         preventDefault: function(ev) {
2209             ev = ev.browserEvent || ev;
2210             if(ev.preventDefault) {
2211                 ev.preventDefault();
2212             } else {
2213                 ev.returnValue = false;
2214             }
2215         },
2216
2217
2218         getEvent: function(e) {
2219             var ev = e || window.event;
2220             if (!ev) {
2221                 var c = this.getEvent.caller;
2222                 while (c) {
2223                     ev = c.arguments[0];
2224                     if (ev && Event == ev.constructor) {
2225                         break;
2226                     }
2227                     c = c.caller;
2228                 }
2229             }
2230             return ev;
2231         },
2232
2233
2234         getCharCode: function(ev) {
2235             ev = ev.browserEvent || ev;
2236             return ev.charCode || ev.keyCode || 0;
2237         },
2238
2239
2240         _getCacheIndex: function(el, eventName, fn) {
2241             for (var i = 0,len = listeners.length; i < len; ++i) {
2242                 var li = listeners[i];
2243                 if (li &&
2244                     li[this.FN] == fn &&
2245                     li[this.EL] == el &&
2246                     li[this.TYPE] == eventName) {
2247                     return i;
2248                 }
2249             }
2250
2251             return -1;
2252         },
2253
2254
2255         elCache: {},
2256
2257
2258         getEl: function(id) {
2259             return document.getElementById(id);
2260         },
2261
2262
2263         clearCache: function() {
2264         },
2265
2266
2267         _load: function(e) {
2268             loadComplete = true;
2269             var EU = Roo.lib.Event;
2270
2271
2272             if (Roo.isIE) {
2273                 EU.doRemove(window, "load", EU._load);
2274             }
2275         },
2276
2277
2278         _tryPreloadAttach: function() {
2279
2280             if (this.locked) {
2281                 return false;
2282             }
2283
2284             this.locked = true;
2285
2286
2287             var tryAgain = !loadComplete;
2288             if (!tryAgain) {
2289                 tryAgain = (retryCount > 0);
2290             }
2291
2292
2293             var notAvail = [];
2294             for (var i = 0,len = onAvailStack.length; i < len; ++i) {
2295                 var item = onAvailStack[i];
2296                 if (item) {
2297                     var el = this.getEl(item.id);
2298
2299                     if (el) {
2300                         if (!item.checkReady ||
2301                             loadComplete ||
2302                             el.nextSibling ||
2303                             (document && document.body)) {
2304
2305                             var scope = el;
2306                             if (item.override) {
2307                                 if (item.override === true) {
2308                                     scope = item.obj;
2309                                 } else {
2310                                     scope = item.override;
2311                                 }
2312                             }
2313                             item.fn.call(scope, item.obj);
2314                             onAvailStack[i] = null;
2315                         }
2316                     } else {
2317                         notAvail.push(item);
2318                     }
2319                 }
2320             }
2321
2322             retryCount = (notAvail.length === 0) ? 0 : retryCount - 1;
2323
2324             if (tryAgain) {
2325
2326                 this.startInterval();
2327             } else {
2328                 clearInterval(this._interval);
2329                 this._interval = null;
2330             }
2331
2332             this.locked = false;
2333
2334             return true;
2335
2336         },
2337
2338
2339         purgeElement: function(el, recurse, eventName) {
2340             var elListeners = this.getListeners(el, eventName);
2341             if (elListeners) {
2342                 for (var i = 0,len = elListeners.length; i < len; ++i) {
2343                     var l = elListeners[i];
2344                     this.removeListener(el, l.type, l.fn);
2345                 }
2346             }
2347
2348             if (recurse && el && el.childNodes) {
2349                 for (i = 0,len = el.childNodes.length; i < len; ++i) {
2350                     this.purgeElement(el.childNodes[i], recurse, eventName);
2351                 }
2352             }
2353         },
2354
2355
2356         getListeners: function(el, eventName) {
2357             var results = [], searchLists;
2358             if (!eventName) {
2359                 searchLists = [listeners, unloadListeners];
2360             } else if (eventName == "unload") {
2361                 searchLists = [unloadListeners];
2362             } else {
2363                 searchLists = [listeners];
2364             }
2365
2366             for (var j = 0; j < searchLists.length; ++j) {
2367                 var searchList = searchLists[j];
2368                 if (searchList && searchList.length > 0) {
2369                     for (var i = 0,len = searchList.length; i < len; ++i) {
2370                         var l = searchList[i];
2371                         if (l && l[this.EL] === el &&
2372                             (!eventName || eventName === l[this.TYPE])) {
2373                             results.push({
2374                                 type:   l[this.TYPE],
2375                                 fn:     l[this.FN],
2376                                 obj:    l[this.OBJ],
2377                                 adjust: l[this.ADJ_SCOPE],
2378                                 index:  i
2379                             });
2380                         }
2381                     }
2382                 }
2383             }
2384
2385             return (results.length) ? results : null;
2386         },
2387
2388
2389         _unload: function(e) {
2390
2391             var EU = Roo.lib.Event, i, j, l, len, index;
2392
2393             for (i = 0,len = unloadListeners.length; i < len; ++i) {
2394                 l = unloadListeners[i];
2395                 if (l) {
2396                     var scope = window;
2397                     if (l[EU.ADJ_SCOPE]) {
2398                         if (l[EU.ADJ_SCOPE] === true) {
2399                             scope = l[EU.OBJ];
2400                         } else {
2401                             scope = l[EU.ADJ_SCOPE];
2402                         }
2403                     }
2404                     l[EU.FN].call(scope, EU.getEvent(e), l[EU.OBJ]);
2405                     unloadListeners[i] = null;
2406                     l = null;
2407                     scope = null;
2408                 }
2409             }
2410
2411             unloadListeners = null;
2412
2413             if (listeners && listeners.length > 0) {
2414                 j = listeners.length;
2415                 while (j) {
2416                     index = j - 1;
2417                     l = listeners[index];
2418                     if (l) {
2419                         EU.removeListener(l[EU.EL], l[EU.TYPE],
2420                                 l[EU.FN], index);
2421                     }
2422                     j = j - 1;
2423                 }
2424                 l = null;
2425
2426                 EU.clearCache();
2427             }
2428
2429             EU.doRemove(window, "unload", EU._unload);
2430
2431         },
2432
2433
2434         getScroll: function() {
2435             var dd = document.documentElement, db = document.body;
2436             if (dd && (dd.scrollTop || dd.scrollLeft)) {
2437                 return [dd.scrollTop, dd.scrollLeft];
2438             } else if (db) {
2439                 return [db.scrollTop, db.scrollLeft];
2440             } else {
2441                 return [0, 0];
2442             }
2443         },
2444
2445
2446         doAdd: function () {
2447             if (window.addEventListener) {
2448                 return function(el, eventName, fn, capture) {
2449                     el.addEventListener(eventName, fn, (capture));
2450                 };
2451             } else if (window.attachEvent) {
2452                 return function(el, eventName, fn, capture) {
2453                     el.attachEvent("on" + eventName, fn);
2454                 };
2455             } else {
2456                 return function() {
2457                 };
2458             }
2459         }(),
2460
2461
2462         doRemove: function() {
2463             if (window.removeEventListener) {
2464                 return function (el, eventName, fn, capture) {
2465                     el.removeEventListener(eventName, fn, (capture));
2466                 };
2467             } else if (window.detachEvent) {
2468                 return function (el, eventName, fn) {
2469                     el.detachEvent("on" + eventName, fn);
2470                 };
2471             } else {
2472                 return function() {
2473                 };
2474             }
2475         }()
2476     };
2477     
2478 }();
2479 (function() {     
2480    
2481     var E = Roo.lib.Event;
2482     E.on = E.addListener;
2483     E.un = E.removeListener;
2484
2485     if (document && document.body) {
2486         E._load();
2487     } else {
2488         E.doAdd(window, "load", E._load);
2489     }
2490     E.doAdd(window, "unload", E._unload);
2491     E._tryPreloadAttach();
2492 })();
2493
2494 /*
2495  * Portions of this file are based on pieces of Yahoo User Interface Library
2496  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
2497  * YUI licensed under the BSD License:
2498  * http://developer.yahoo.net/yui/license.txt
2499  * <script type="text/javascript">
2500  *
2501  */
2502
2503 (function() {
2504     /**
2505      * @class Roo.lib.Ajax
2506      *
2507      */
2508     Roo.lib.Ajax = {
2509         /**
2510          * @static 
2511          */
2512         request : function(method, uri, cb, data, options) {
2513             if(options){
2514                 var hs = options.headers;
2515                 if(hs){
2516                     for(var h in hs){
2517                         if(hs.hasOwnProperty(h)){
2518                             this.initHeader(h, hs[h], false);
2519                         }
2520                     }
2521                 }
2522                 if(options.xmlData){
2523                     this.initHeader('Content-Type', 'text/xml', false);
2524                     method = 'POST';
2525                     data = options.xmlData;
2526                 }
2527             }
2528
2529             return this.asyncRequest(method, uri, cb, data);
2530         },
2531
2532         serializeForm : function(form) {
2533             if(typeof form == 'string') {
2534                 form = (document.getElementById(form) || document.forms[form]);
2535             }
2536
2537             var el, name, val, disabled, data = '', hasSubmit = false;
2538             for (var i = 0; i < form.elements.length; i++) {
2539                 el = form.elements[i];
2540                 disabled = form.elements[i].disabled;
2541                 name = form.elements[i].name;
2542                 val = form.elements[i].value;
2543
2544                 if (!disabled && name){
2545                     switch (el.type)
2546                             {
2547                         case 'select-one':
2548                         case 'select-multiple':
2549                             for (var j = 0; j < el.options.length; j++) {
2550                                 if (el.options[j].selected) {
2551                                     if (Roo.isIE) {
2552                                         data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(el.options[j].attributes['value'].specified ? el.options[j].value : el.options[j].text) + '&';
2553                                     }
2554                                     else {
2555                                         data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(el.options[j].hasAttribute('value') ? el.options[j].value : el.options[j].text) + '&';
2556                                     }
2557                                 }
2558                             }
2559                             break;
2560                         case 'radio':
2561                         case 'checkbox':
2562                             if (el.checked) {
2563                                 data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(val) + '&';
2564                             }
2565                             break;
2566                         case 'file':
2567
2568                         case undefined:
2569
2570                         case 'reset':
2571
2572                         case 'button':
2573
2574                             break;
2575                         case 'submit':
2576                             if(hasSubmit == false) {
2577                                 data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(val) + '&';
2578                                 hasSubmit = true;
2579                             }
2580                             break;
2581                         default:
2582                             data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(val) + '&';
2583                             break;
2584                     }
2585                 }
2586             }
2587             data = data.substr(0, data.length - 1);
2588             return data;
2589         },
2590
2591         headers:{},
2592
2593         hasHeaders:false,
2594
2595         useDefaultHeader:true,
2596
2597         defaultPostHeader:'application/x-www-form-urlencoded',
2598
2599         useDefaultXhrHeader:true,
2600
2601         defaultXhrHeader:'XMLHttpRequest',
2602
2603         hasDefaultHeaders:true,
2604
2605         defaultHeaders:{},
2606
2607         poll:{},
2608
2609         timeout:{},
2610
2611         pollInterval:50,
2612
2613         transactionId:0,
2614
2615         setProgId:function(id)
2616         {
2617             this.activeX.unshift(id);
2618         },
2619
2620         setDefaultPostHeader:function(b)
2621         {
2622             this.useDefaultHeader = b;
2623         },
2624
2625         setDefaultXhrHeader:function(b)
2626         {
2627             this.useDefaultXhrHeader = b;
2628         },
2629
2630         setPollingInterval:function(i)
2631         {
2632             if (typeof i == 'number' && isFinite(i)) {
2633                 this.pollInterval = i;
2634             }
2635         },
2636
2637         createXhrObject:function(transactionId)
2638         {
2639             var obj,http;
2640             try
2641             {
2642
2643                 http = new XMLHttpRequest();
2644
2645                 obj = { conn:http, tId:transactionId };
2646             }
2647             catch(e)
2648             {
2649                 for (var i = 0; i < this.activeX.length; ++i) {
2650                     try
2651                     {
2652
2653                         http = new ActiveXObject(this.activeX[i]);
2654
2655                         obj = { conn:http, tId:transactionId };
2656                         break;
2657                     }
2658                     catch(e) {
2659                     }
2660                 }
2661             }
2662             finally
2663             {
2664                 return obj;
2665             }
2666         },
2667
2668         getConnectionObject:function()
2669         {
2670             var o;
2671             var tId = this.transactionId;
2672
2673             try
2674             {
2675                 o = this.createXhrObject(tId);
2676                 if (o) {
2677                     this.transactionId++;
2678                 }
2679             }
2680             catch(e) {
2681             }
2682             finally
2683             {
2684                 return o;
2685             }
2686         },
2687
2688         asyncRequest:function(method, uri, callback, postData)
2689         {
2690             var o = this.getConnectionObject();
2691
2692             if (!o) {
2693                 return null;
2694             }
2695             else {
2696                 o.conn.open(method, uri, true);
2697
2698                 if (this.useDefaultXhrHeader) {
2699                     if (!this.defaultHeaders['X-Requested-With']) {
2700                         this.initHeader('X-Requested-With', this.defaultXhrHeader, true);
2701                     }
2702                 }
2703
2704                 if(postData && this.useDefaultHeader){
2705                     this.initHeader('Content-Type', this.defaultPostHeader);
2706                 }
2707
2708                  if (this.hasDefaultHeaders || this.hasHeaders) {
2709                     this.setHeader(o);
2710                 }
2711
2712                 this.handleReadyState(o, callback);
2713                 o.conn.send(postData || null);
2714
2715                 return o;
2716             }
2717         },
2718
2719         handleReadyState:function(o, callback)
2720         {
2721             var oConn = this;
2722
2723             if (callback && callback.timeout) {
2724                 
2725                 this.timeout[o.tId] = window.setTimeout(function() {
2726                     oConn.abort(o, callback, true);
2727                 }, callback.timeout);
2728             }
2729
2730             this.poll[o.tId] = window.setInterval(
2731                     function() {
2732                         if (o.conn && o.conn.readyState == 4) {
2733                             window.clearInterval(oConn.poll[o.tId]);
2734                             delete oConn.poll[o.tId];
2735
2736                             if(callback && callback.timeout) {
2737                                 window.clearTimeout(oConn.timeout[o.tId]);
2738                                 delete oConn.timeout[o.tId];
2739                             }
2740
2741                             oConn.handleTransactionResponse(o, callback);
2742                         }
2743                     }
2744                     , this.pollInterval);
2745         },
2746
2747         handleTransactionResponse:function(o, callback, isAbort)
2748         {
2749
2750             if (!callback) {
2751                 this.releaseObject(o);
2752                 return;
2753             }
2754
2755             var httpStatus, responseObject;
2756
2757             try
2758             {
2759                 if (o.conn.status !== undefined && o.conn.status != 0) {
2760                     httpStatus = o.conn.status;
2761                 }
2762                 else {
2763                     httpStatus = 13030;
2764                 }
2765             }
2766             catch(e) {
2767
2768
2769                 httpStatus = 13030;
2770             }
2771
2772             if (httpStatus >= 200 && httpStatus < 300) {
2773                 responseObject = this.createResponseObject(o, callback.argument);
2774                 if (callback.success) {
2775                     if (!callback.scope) {
2776                         callback.success(responseObject);
2777                     }
2778                     else {
2779
2780
2781                         callback.success.apply(callback.scope, [responseObject]);
2782                     }
2783                 }
2784             }
2785             else {
2786                 switch (httpStatus) {
2787
2788                     case 12002:
2789                     case 12029:
2790                     case 12030:
2791                     case 12031:
2792                     case 12152:
2793                     case 13030:
2794                         responseObject = this.createExceptionObject(o.tId, callback.argument, (isAbort ? isAbort : false));
2795                         if (callback.failure) {
2796                             if (!callback.scope) {
2797                                 callback.failure(responseObject);
2798                             }
2799                             else {
2800                                 callback.failure.apply(callback.scope, [responseObject]);
2801                             }
2802                         }
2803                         break;
2804                     default:
2805                         responseObject = this.createResponseObject(o, callback.argument);
2806                         if (callback.failure) {
2807                             if (!callback.scope) {
2808                                 callback.failure(responseObject);
2809                             }
2810                             else {
2811                                 callback.failure.apply(callback.scope, [responseObject]);
2812                             }
2813                         }
2814                 }
2815             }
2816
2817             this.releaseObject(o);
2818             responseObject = null;
2819         },
2820
2821         createResponseObject:function(o, callbackArg)
2822         {
2823             var obj = {};
2824             var headerObj = {};
2825
2826             try
2827             {
2828                 var headerStr = o.conn.getAllResponseHeaders();
2829                 var header = headerStr.split('\n');
2830                 for (var i = 0; i < header.length; i++) {
2831                     var delimitPos = header[i].indexOf(':');
2832                     if (delimitPos != -1) {
2833                         headerObj[header[i].substring(0, delimitPos)] = header[i].substring(delimitPos + 2);
2834                     }
2835                 }
2836             }
2837             catch(e) {
2838             }
2839
2840             obj.tId = o.tId;
2841             obj.status = o.conn.status;
2842             obj.statusText = o.conn.statusText;
2843             obj.getResponseHeader = headerObj;
2844             obj.getAllResponseHeaders = headerStr;
2845             obj.responseText = o.conn.responseText;
2846             obj.responseXML = o.conn.responseXML;
2847
2848             if (typeof callbackArg !== undefined) {
2849                 obj.argument = callbackArg;
2850             }
2851
2852             return obj;
2853         },
2854
2855         createExceptionObject:function(tId, callbackArg, isAbort)
2856         {
2857             var COMM_CODE = 0;
2858             var COMM_ERROR = 'communication failure';
2859             var ABORT_CODE = -1;
2860             var ABORT_ERROR = 'transaction aborted';
2861
2862             var obj = {};
2863
2864             obj.tId = tId;
2865             if (isAbort) {
2866                 obj.status = ABORT_CODE;
2867                 obj.statusText = ABORT_ERROR;
2868             }
2869             else {
2870                 obj.status = COMM_CODE;
2871                 obj.statusText = COMM_ERROR;
2872             }
2873
2874             if (callbackArg) {
2875                 obj.argument = callbackArg;
2876             }
2877
2878             return obj;
2879         },
2880
2881         initHeader:function(label, value, isDefault)
2882         {
2883             var headerObj = (isDefault) ? this.defaultHeaders : this.headers;
2884
2885             if (headerObj[label] === undefined) {
2886                 headerObj[label] = value;
2887             }
2888             else {
2889
2890
2891                 headerObj[label] = value + "," + headerObj[label];
2892             }
2893
2894             if (isDefault) {
2895                 this.hasDefaultHeaders = true;
2896             }
2897             else {
2898                 this.hasHeaders = true;
2899             }
2900         },
2901
2902
2903         setHeader:function(o)
2904         {
2905             if (this.hasDefaultHeaders) {
2906                 for (var prop in this.defaultHeaders) {
2907                     if (this.defaultHeaders.hasOwnProperty(prop)) {
2908                         o.conn.setRequestHeader(prop, this.defaultHeaders[prop]);
2909                     }
2910                 }
2911             }
2912
2913             if (this.hasHeaders) {
2914                 for (var prop in this.headers) {
2915                     if (this.headers.hasOwnProperty(prop)) {
2916                         o.conn.setRequestHeader(prop, this.headers[prop]);
2917                     }
2918                 }
2919                 this.headers = {};
2920                 this.hasHeaders = false;
2921             }
2922         },
2923
2924         resetDefaultHeaders:function() {
2925             delete this.defaultHeaders;
2926             this.defaultHeaders = {};
2927             this.hasDefaultHeaders = false;
2928         },
2929
2930         abort:function(o, callback, isTimeout)
2931         {
2932             if(this.isCallInProgress(o)) {
2933                 o.conn.abort();
2934                 window.clearInterval(this.poll[o.tId]);
2935                 delete this.poll[o.tId];
2936                 if (isTimeout) {
2937                     delete this.timeout[o.tId];
2938                 }
2939
2940                 this.handleTransactionResponse(o, callback, true);
2941
2942                 return true;
2943             }
2944             else {
2945                 return false;
2946             }
2947         },
2948
2949
2950         isCallInProgress:function(o)
2951         {
2952             if (o && o.conn) {
2953                 return o.conn.readyState != 4 && o.conn.readyState != 0;
2954             }
2955             else {
2956
2957                 return false;
2958             }
2959         },
2960
2961
2962         releaseObject:function(o)
2963         {
2964
2965             o.conn = null;
2966
2967             o = null;
2968         },
2969
2970         activeX:[
2971         'MSXML2.XMLHTTP.3.0',
2972         'MSXML2.XMLHTTP',
2973         'Microsoft.XMLHTTP'
2974         ]
2975
2976
2977     };
2978 })();/*
2979  * Portions of this file are based on pieces of Yahoo User Interface Library
2980  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
2981  * YUI licensed under the BSD License:
2982  * http://developer.yahoo.net/yui/license.txt
2983  * <script type="text/javascript">
2984  *
2985  */
2986
2987 Roo.lib.Region = function(t, r, b, l) {
2988     this.top = t;
2989     this[1] = t;
2990     this.right = r;
2991     this.bottom = b;
2992     this.left = l;
2993     this[0] = l;
2994 };
2995
2996
2997 Roo.lib.Region.prototype = {
2998     contains : function(region) {
2999         return ( region.left >= this.left &&
3000                  region.right <= this.right &&
3001                  region.top >= this.top &&
3002                  region.bottom <= this.bottom    );
3003
3004     },
3005
3006     getArea : function() {
3007         return ( (this.bottom - this.top) * (this.right - this.left) );
3008     },
3009
3010     intersect : function(region) {
3011         var t = Math.max(this.top, region.top);
3012         var r = Math.min(this.right, region.right);
3013         var b = Math.min(this.bottom, region.bottom);
3014         var l = Math.max(this.left, region.left);
3015
3016         if (b >= t && r >= l) {
3017             return new Roo.lib.Region(t, r, b, l);
3018         } else {
3019             return null;
3020         }
3021     },
3022     union : function(region) {
3023         var t = Math.min(this.top, region.top);
3024         var r = Math.max(this.right, region.right);
3025         var b = Math.max(this.bottom, region.bottom);
3026         var l = Math.min(this.left, region.left);
3027
3028         return new Roo.lib.Region(t, r, b, l);
3029     },
3030
3031     adjust : function(t, l, b, r) {
3032         this.top += t;
3033         this.left += l;
3034         this.right += r;
3035         this.bottom += b;
3036         return this;
3037     }
3038 };
3039
3040 Roo.lib.Region.getRegion = function(el) {
3041     var p = Roo.lib.Dom.getXY(el);
3042
3043     var t = p[1];
3044     var r = p[0] + el.offsetWidth;
3045     var b = p[1] + el.offsetHeight;
3046     var l = p[0];
3047
3048     return new Roo.lib.Region(t, r, b, l);
3049 };
3050 /*
3051  * Portions of this file are based on pieces of Yahoo User Interface Library
3052  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3053  * YUI licensed under the BSD License:
3054  * http://developer.yahoo.net/yui/license.txt
3055  * <script type="text/javascript">
3056  *
3057  */
3058 //@@dep Roo.lib.Region
3059
3060
3061 Roo.lib.Point = function(x, y) {
3062     if (x instanceof Array) {
3063         y = x[1];
3064         x = x[0];
3065     }
3066     this.x = this.right = this.left = this[0] = x;
3067     this.y = this.top = this.bottom = this[1] = y;
3068 };
3069
3070 Roo.lib.Point.prototype = new Roo.lib.Region();
3071 /*
3072  * Portions of this file are based on pieces of Yahoo User Interface Library
3073  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3074  * YUI licensed under the BSD License:
3075  * http://developer.yahoo.net/yui/license.txt
3076  * <script type="text/javascript">
3077  *
3078  */
3079  
3080 (function() {   
3081
3082     Roo.lib.Anim = {
3083         scroll : function(el, args, duration, easing, cb, scope) {
3084             this.run(el, args, duration, easing, cb, scope, Roo.lib.Scroll);
3085         },
3086
3087         motion : function(el, args, duration, easing, cb, scope) {
3088             this.run(el, args, duration, easing, cb, scope, Roo.lib.Motion);
3089         },
3090
3091         color : function(el, args, duration, easing, cb, scope) {
3092             this.run(el, args, duration, easing, cb, scope, Roo.lib.ColorAnim);
3093         },
3094
3095         run : function(el, args, duration, easing, cb, scope, type) {
3096             type = type || Roo.lib.AnimBase;
3097             if (typeof easing == "string") {
3098                 easing = Roo.lib.Easing[easing];
3099             }
3100             var anim = new type(el, args, duration, easing);
3101             anim.animateX(function() {
3102                 Roo.callback(cb, scope);
3103             });
3104             return anim;
3105         }
3106     };
3107 })();/*
3108  * Portions of this file are based on pieces of Yahoo User Interface Library
3109  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3110  * YUI licensed under the BSD License:
3111  * http://developer.yahoo.net/yui/license.txt
3112  * <script type="text/javascript">
3113  *
3114  */
3115
3116 (function() {    
3117     var libFlyweight;
3118     
3119     function fly(el) {
3120         if (!libFlyweight) {
3121             libFlyweight = new Roo.Element.Flyweight();
3122         }
3123         libFlyweight.dom = el;
3124         return libFlyweight;
3125     }
3126
3127     // since this uses fly! - it cant be in DOM (which does not have fly yet..)
3128     
3129    
3130     
3131     Roo.lib.AnimBase = function(el, attributes, duration, method) {
3132         if (el) {
3133             this.init(el, attributes, duration, method);
3134         }
3135     };
3136
3137     Roo.lib.AnimBase.fly = fly;
3138     
3139     
3140     
3141     Roo.lib.AnimBase.prototype = {
3142
3143         toString: function() {
3144             var el = this.getEl();
3145             var id = el.id || el.tagName;
3146             return ("Anim " + id);
3147         },
3148
3149         patterns: {
3150             noNegatives:        /width|height|opacity|padding/i,
3151             offsetAttribute:  /^((width|height)|(top|left))$/,
3152             defaultUnit:        /width|height|top$|bottom$|left$|right$/i,
3153             offsetUnit:         /\d+(em|%|en|ex|pt|in|cm|mm|pc)$/i
3154         },
3155
3156
3157         doMethod: function(attr, start, end) {
3158             return this.method(this.currentFrame, start, end - start, this.totalFrames);
3159         },
3160
3161
3162         setAttribute: function(attr, val, unit) {
3163             if (this.patterns.noNegatives.test(attr)) {
3164                 val = (val > 0) ? val : 0;
3165             }
3166
3167             Roo.fly(this.getEl(), '_anim').setStyle(attr, val + unit);
3168         },
3169
3170
3171         getAttribute: function(attr) {
3172             var el = this.getEl();
3173             var val = fly(el).getStyle(attr);
3174
3175             if (val !== 'auto' && !this.patterns.offsetUnit.test(val)) {
3176                 return parseFloat(val);
3177             }
3178
3179             var a = this.patterns.offsetAttribute.exec(attr) || [];
3180             var pos = !!( a[3] );
3181             var box = !!( a[2] );
3182
3183
3184             if (box || (fly(el).getStyle('position') == 'absolute' && pos)) {
3185                 val = el['offset' + a[0].charAt(0).toUpperCase() + a[0].substr(1)];
3186             } else {
3187                 val = 0;
3188             }
3189
3190             return val;
3191         },
3192
3193
3194         getDefaultUnit: function(attr) {
3195             if (this.patterns.defaultUnit.test(attr)) {
3196                 return 'px';
3197             }
3198
3199             return '';
3200         },
3201
3202         animateX : function(callback, scope) {
3203             var f = function() {
3204                 this.onComplete.removeListener(f);
3205                 if (typeof callback == "function") {
3206                     callback.call(scope || this, this);
3207                 }
3208             };
3209             this.onComplete.addListener(f, this);
3210             this.animate();
3211         },
3212
3213
3214         setRuntimeAttribute: function(attr) {
3215             var start;
3216             var end;
3217             var attributes = this.attributes;
3218
3219             this.runtimeAttributes[attr] = {};
3220
3221             var isset = function(prop) {
3222                 return (typeof prop !== 'undefined');
3223             };
3224
3225             if (!isset(attributes[attr]['to']) && !isset(attributes[attr]['by'])) {
3226                 return false;
3227             }
3228
3229             start = ( isset(attributes[attr]['from']) ) ? attributes[attr]['from'] : this.getAttribute(attr);
3230
3231
3232             if (isset(attributes[attr]['to'])) {
3233                 end = attributes[attr]['to'];
3234             } else if (isset(attributes[attr]['by'])) {
3235                 if (start.constructor == Array) {
3236                     end = [];
3237                     for (var i = 0, len = start.length; i < len; ++i) {
3238                         end[i] = start[i] + attributes[attr]['by'][i];
3239                     }
3240                 } else {
3241                     end = start + attributes[attr]['by'];
3242                 }
3243             }
3244
3245             this.runtimeAttributes[attr].start = start;
3246             this.runtimeAttributes[attr].end = end;
3247
3248
3249             this.runtimeAttributes[attr].unit = ( isset(attributes[attr].unit) ) ? attributes[attr]['unit'] : this.getDefaultUnit(attr);
3250         },
3251
3252
3253         init: function(el, attributes, duration, method) {
3254
3255             var isAnimated = false;
3256
3257
3258             var startTime = null;
3259
3260
3261             var actualFrames = 0;
3262
3263
3264             el = Roo.getDom(el);
3265
3266
3267             this.attributes = attributes || {};
3268
3269
3270             this.duration = duration || 1;
3271
3272
3273             this.method = method || Roo.lib.Easing.easeNone;
3274
3275
3276             this.useSeconds = true;
3277
3278
3279             this.currentFrame = 0;
3280
3281
3282             this.totalFrames = Roo.lib.AnimMgr.fps;
3283
3284
3285             this.getEl = function() {
3286                 return el;
3287             };
3288
3289
3290             this.isAnimated = function() {
3291                 return isAnimated;
3292             };
3293
3294
3295             this.getStartTime = function() {
3296                 return startTime;
3297             };
3298
3299             this.runtimeAttributes = {};
3300
3301
3302             this.animate = function() {
3303                 if (this.isAnimated()) {
3304                     return false;
3305                 }
3306
3307                 this.currentFrame = 0;
3308
3309                 this.totalFrames = ( this.useSeconds ) ? Math.ceil(Roo.lib.AnimMgr.fps * this.duration) : this.duration;
3310
3311                 Roo.lib.AnimMgr.registerElement(this);
3312             };
3313
3314
3315             this.stop = function(finish) {
3316                 if (finish) {
3317                     this.currentFrame = this.totalFrames;
3318                     this._onTween.fire();
3319                 }
3320                 Roo.lib.AnimMgr.stop(this);
3321             };
3322
3323             var onStart = function() {
3324                 this.onStart.fire();
3325
3326                 this.runtimeAttributes = {};
3327                 for (var attr in this.attributes) {
3328                     this.setRuntimeAttribute(attr);
3329                 }
3330
3331                 isAnimated = true;
3332                 actualFrames = 0;
3333                 startTime = new Date();
3334             };
3335
3336
3337             var onTween = function() {
3338                 var data = {
3339                     duration: new Date() - this.getStartTime(),
3340                     currentFrame: this.currentFrame
3341                 };
3342
3343                 data.toString = function() {
3344                     return (
3345                             'duration: ' + data.duration +
3346                             ', currentFrame: ' + data.currentFrame
3347                             );
3348                 };
3349
3350                 this.onTween.fire(data);
3351
3352                 var runtimeAttributes = this.runtimeAttributes;
3353
3354                 for (var attr in runtimeAttributes) {
3355                     this.setAttribute(attr, this.doMethod(attr, runtimeAttributes[attr].start, runtimeAttributes[attr].end), runtimeAttributes[attr].unit);
3356                 }
3357
3358                 actualFrames += 1;
3359             };
3360
3361             var onComplete = function() {
3362                 var actual_duration = (new Date() - startTime) / 1000 ;
3363
3364                 var data = {
3365                     duration: actual_duration,
3366                     frames: actualFrames,
3367                     fps: actualFrames / actual_duration
3368                 };
3369
3370                 data.toString = function() {
3371                     return (
3372                             'duration: ' + data.duration +
3373                             ', frames: ' + data.frames +
3374                             ', fps: ' + data.fps
3375                             );
3376                 };
3377
3378                 isAnimated = false;
3379                 actualFrames = 0;
3380                 this.onComplete.fire(data);
3381             };
3382
3383
3384             this._onStart = new Roo.util.Event(this);
3385             this.onStart = new Roo.util.Event(this);
3386             this.onTween = new Roo.util.Event(this);
3387             this._onTween = new Roo.util.Event(this);
3388             this.onComplete = new Roo.util.Event(this);
3389             this._onComplete = new Roo.util.Event(this);
3390             this._onStart.addListener(onStart);
3391             this._onTween.addListener(onTween);
3392             this._onComplete.addListener(onComplete);
3393         }
3394     };
3395 })();
3396 /*
3397  * Portions of this file are based on pieces of Yahoo User Interface Library
3398  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3399  * YUI licensed under the BSD License:
3400  * http://developer.yahoo.net/yui/license.txt
3401  * <script type="text/javascript">
3402  *
3403  */
3404
3405 Roo.lib.AnimMgr = new function() {
3406
3407     var thread = null;
3408
3409
3410     var queue = [];
3411
3412
3413     var tweenCount = 0;
3414
3415
3416     this.fps = 1000;
3417
3418
3419     this.delay = 1;
3420
3421
3422     this.registerElement = function(tween) {
3423         queue[queue.length] = tween;
3424         tweenCount += 1;
3425         tween._onStart.fire();
3426         this.start();
3427     };
3428
3429
3430     this.unRegister = function(tween, index) {
3431         tween._onComplete.fire();
3432         index = index || getIndex(tween);
3433         if (index != -1) {
3434             queue.splice(index, 1);
3435         }
3436
3437         tweenCount -= 1;
3438         if (tweenCount <= 0) {
3439             this.stop();
3440         }
3441     };
3442
3443
3444     this.start = function() {
3445         if (thread === null) {
3446             thread = setInterval(this.run, this.delay);
3447         }
3448     };
3449
3450
3451     this.stop = function(tween) {
3452         if (!tween) {
3453             clearInterval(thread);
3454
3455             for (var i = 0, len = queue.length; i < len; ++i) {
3456                 if (queue[0].isAnimated()) {
3457                     this.unRegister(queue[0], 0);
3458                 }
3459             }
3460
3461             queue = [];
3462             thread = null;
3463             tweenCount = 0;
3464         }
3465         else {
3466             this.unRegister(tween);
3467         }
3468     };
3469
3470
3471     this.run = function() {
3472         for (var i = 0, len = queue.length; i < len; ++i) {
3473             var tween = queue[i];
3474             if (!tween || !tween.isAnimated()) {
3475                 continue;
3476             }
3477
3478             if (tween.currentFrame < tween.totalFrames || tween.totalFrames === null)
3479             {
3480                 tween.currentFrame += 1;
3481
3482                 if (tween.useSeconds) {
3483                     correctFrame(tween);
3484                 }
3485                 tween._onTween.fire();
3486             }
3487             else {
3488                 Roo.lib.AnimMgr.stop(tween, i);
3489             }
3490         }
3491     };
3492
3493     var getIndex = function(anim) {
3494         for (var i = 0, len = queue.length; i < len; ++i) {
3495             if (queue[i] == anim) {
3496                 return i;
3497             }
3498         }
3499         return -1;
3500     };
3501
3502
3503     var correctFrame = function(tween) {
3504         var frames = tween.totalFrames;
3505         var frame = tween.currentFrame;
3506         var expected = (tween.currentFrame * tween.duration * 1000 / tween.totalFrames);
3507         var elapsed = (new Date() - tween.getStartTime());
3508         var tweak = 0;
3509
3510         if (elapsed < tween.duration * 1000) {
3511             tweak = Math.round((elapsed / expected - 1) * tween.currentFrame);
3512         } else {
3513             tweak = frames - (frame + 1);
3514         }
3515         if (tweak > 0 && isFinite(tweak)) {
3516             if (tween.currentFrame + tweak >= frames) {
3517                 tweak = frames - (frame + 1);
3518             }
3519
3520             tween.currentFrame += tweak;
3521         }
3522     };
3523 };
3524
3525     /*
3526  * Portions of this file are based on pieces of Yahoo User Interface Library
3527  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3528  * YUI licensed under the BSD License:
3529  * http://developer.yahoo.net/yui/license.txt
3530  * <script type="text/javascript">
3531  *
3532  */
3533 Roo.lib.Bezier = new function() {
3534
3535         this.getPosition = function(points, t) {
3536             var n = points.length;
3537             var tmp = [];
3538
3539             for (var i = 0; i < n; ++i) {
3540                 tmp[i] = [points[i][0], points[i][1]];
3541             }
3542
3543             for (var j = 1; j < n; ++j) {
3544                 for (i = 0; i < n - j; ++i) {
3545                     tmp[i][0] = (1 - t) * tmp[i][0] + t * tmp[parseInt(i + 1, 10)][0];
3546                     tmp[i][1] = (1 - t) * tmp[i][1] + t * tmp[parseInt(i + 1, 10)][1];
3547                 }
3548             }
3549
3550             return [ tmp[0][0], tmp[0][1] ];
3551
3552         };
3553     };/*
3554  * Portions of this file are based on pieces of Yahoo User Interface Library
3555  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3556  * YUI licensed under the BSD License:
3557  * http://developer.yahoo.net/yui/license.txt
3558  * <script type="text/javascript">
3559  *
3560  */
3561 (function() {
3562
3563     Roo.lib.ColorAnim = function(el, attributes, duration, method) {
3564         Roo.lib.ColorAnim.superclass.constructor.call(this, el, attributes, duration, method);
3565     };
3566
3567     Roo.extend(Roo.lib.ColorAnim, Roo.lib.AnimBase);
3568
3569     var fly = Roo.lib.AnimBase.fly;
3570     var Y = Roo.lib;
3571     var superclass = Y.ColorAnim.superclass;
3572     var proto = Y.ColorAnim.prototype;
3573
3574     proto.toString = function() {
3575         var el = this.getEl();
3576         var id = el.id || el.tagName;
3577         return ("ColorAnim " + id);
3578     };
3579
3580     proto.patterns.color = /color$/i;
3581     proto.patterns.rgb = /^rgb\(([0-9]+)\s*,\s*([0-9]+)\s*,\s*([0-9]+)\)$/i;
3582     proto.patterns.hex = /^#?([0-9A-F]{2})([0-9A-F]{2})([0-9A-F]{2})$/i;
3583     proto.patterns.hex3 = /^#?([0-9A-F]{1})([0-9A-F]{1})([0-9A-F]{1})$/i;
3584     proto.patterns.transparent = /^transparent|rgba\(0, 0, 0, 0\)$/;
3585
3586
3587     proto.parseColor = function(s) {
3588         if (s.length == 3) {
3589             return s;
3590         }
3591
3592         var c = this.patterns.hex.exec(s);
3593         if (c && c.length == 4) {
3594             return [ parseInt(c[1], 16), parseInt(c[2], 16), parseInt(c[3], 16) ];
3595         }
3596
3597         c = this.patterns.rgb.exec(s);
3598         if (c && c.length == 4) {
3599             return [ parseInt(c[1], 10), parseInt(c[2], 10), parseInt(c[3], 10) ];
3600         }
3601
3602         c = this.patterns.hex3.exec(s);
3603         if (c && c.length == 4) {
3604             return [ parseInt(c[1] + c[1], 16), parseInt(c[2] + c[2], 16), parseInt(c[3] + c[3], 16) ];
3605         }
3606
3607         return null;
3608     };
3609     // since this uses fly! - it cant be in ColorAnim (which does not have fly yet..)
3610     proto.getAttribute = function(attr) {
3611         var el = this.getEl();
3612         if (this.patterns.color.test(attr)) {
3613             var val = fly(el).getStyle(attr);
3614
3615             if (this.patterns.transparent.test(val)) {
3616                 var parent = el.parentNode;
3617                 val = fly(parent).getStyle(attr);
3618
3619                 while (parent && this.patterns.transparent.test(val)) {
3620                     parent = parent.parentNode;
3621                     val = fly(parent).getStyle(attr);
3622                     if (parent.tagName.toUpperCase() == 'HTML') {
3623                         val = '#fff';
3624                     }
3625                 }
3626             }
3627         } else {
3628             val = superclass.getAttribute.call(this, attr);
3629         }
3630
3631         return val;
3632     };
3633     proto.getAttribute = function(attr) {
3634         var el = this.getEl();
3635         if (this.patterns.color.test(attr)) {
3636             var val = fly(el).getStyle(attr);
3637
3638             if (this.patterns.transparent.test(val)) {
3639                 var parent = el.parentNode;
3640                 val = fly(parent).getStyle(attr);
3641
3642                 while (parent && this.patterns.transparent.test(val)) {
3643                     parent = parent.parentNode;
3644                     val = fly(parent).getStyle(attr);
3645                     if (parent.tagName.toUpperCase() == 'HTML') {
3646                         val = '#fff';
3647                     }
3648                 }
3649             }
3650         } else {
3651             val = superclass.getAttribute.call(this, attr);
3652         }
3653
3654         return val;
3655     };
3656
3657     proto.doMethod = function(attr, start, end) {
3658         var val;
3659
3660         if (this.patterns.color.test(attr)) {
3661             val = [];
3662             for (var i = 0, len = start.length; i < len; ++i) {
3663                 val[i] = superclass.doMethod.call(this, attr, start[i], end[i]);
3664             }
3665
3666             val = 'rgb(' + Math.floor(val[0]) + ',' + Math.floor(val[1]) + ',' + Math.floor(val[2]) + ')';
3667         }
3668         else {
3669             val = superclass.doMethod.call(this, attr, start, end);
3670         }
3671
3672         return val;
3673     };
3674
3675     proto.setRuntimeAttribute = function(attr) {
3676         superclass.setRuntimeAttribute.call(this, attr);
3677
3678         if (this.patterns.color.test(attr)) {
3679             var attributes = this.attributes;
3680             var start = this.parseColor(this.runtimeAttributes[attr].start);
3681             var end = this.parseColor(this.runtimeAttributes[attr].end);
3682
3683             if (typeof attributes[attr]['to'] === 'undefined' && typeof attributes[attr]['by'] !== 'undefined') {
3684                 end = this.parseColor(attributes[attr].by);
3685
3686                 for (var i = 0, len = start.length; i < len; ++i) {
3687                     end[i] = start[i] + end[i];
3688                 }
3689             }
3690
3691             this.runtimeAttributes[attr].start = start;
3692             this.runtimeAttributes[attr].end = end;
3693         }
3694     };
3695 })();
3696
3697 /*
3698  * Portions of this file are based on pieces of Yahoo User Interface Library
3699  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3700  * YUI licensed under the BSD License:
3701  * http://developer.yahoo.net/yui/license.txt
3702  * <script type="text/javascript">
3703  *
3704  */
3705 Roo.lib.Easing = {
3706
3707
3708     easeNone: function (t, b, c, d) {
3709         return c * t / d + b;
3710     },
3711
3712
3713     easeIn: function (t, b, c, d) {
3714         return c * (t /= d) * t + b;
3715     },
3716
3717
3718     easeOut: function (t, b, c, d) {
3719         return -c * (t /= d) * (t - 2) + b;
3720     },
3721
3722
3723     easeBoth: function (t, b, c, d) {
3724         if ((t /= d / 2) < 1) {
3725             return c / 2 * t * t + b;
3726         }
3727
3728         return -c / 2 * ((--t) * (t - 2) - 1) + b;
3729     },
3730
3731
3732     easeInStrong: function (t, b, c, d) {
3733         return c * (t /= d) * t * t * t + b;
3734     },
3735
3736
3737     easeOutStrong: function (t, b, c, d) {
3738         return -c * ((t = t / d - 1) * t * t * t - 1) + b;
3739     },
3740
3741
3742     easeBothStrong: function (t, b, c, d) {
3743         if ((t /= d / 2) < 1) {
3744             return c / 2 * t * t * t * t + b;
3745         }
3746
3747         return -c / 2 * ((t -= 2) * t * t * t - 2) + b;
3748     },
3749
3750
3751
3752     elasticIn: function (t, b, c, d, a, p) {
3753         if (t == 0) {
3754             return b;
3755         }
3756         if ((t /= d) == 1) {
3757             return b + c;
3758         }
3759         if (!p) {
3760             p = d * .3;
3761         }
3762
3763         if (!a || a < Math.abs(c)) {
3764             a = c;
3765             var s = p / 4;
3766         }
3767         else {
3768             var s = p / (2 * Math.PI) * Math.asin(c / a);
3769         }
3770
3771         return -(a * Math.pow(2, 10 * (t -= 1)) * Math.sin((t * d - s) * (2 * Math.PI) / p)) + b;
3772     },
3773
3774
3775     elasticOut: function (t, b, c, d, a, p) {
3776         if (t == 0) {
3777             return b;
3778         }
3779         if ((t /= d) == 1) {
3780             return b + c;
3781         }
3782         if (!p) {
3783             p = d * .3;
3784         }
3785
3786         if (!a || a < Math.abs(c)) {
3787             a = c;
3788             var s = p / 4;
3789         }
3790         else {
3791             var s = p / (2 * Math.PI) * Math.asin(c / a);
3792         }
3793
3794         return a * Math.pow(2, -10 * t) * Math.sin((t * d - s) * (2 * Math.PI) / p) + c + b;
3795     },
3796
3797
3798     elasticBoth: function (t, b, c, d, a, p) {
3799         if (t == 0) {
3800             return b;
3801         }
3802
3803         if ((t /= d / 2) == 2) {
3804             return b + c;
3805         }
3806
3807         if (!p) {
3808             p = d * (.3 * 1.5);
3809         }
3810
3811         if (!a || a < Math.abs(c)) {
3812             a = c;
3813             var s = p / 4;
3814         }
3815         else {
3816             var s = p / (2 * Math.PI) * Math.asin(c / a);
3817         }
3818
3819         if (t < 1) {
3820             return -.5 * (a * Math.pow(2, 10 * (t -= 1)) *
3821                           Math.sin((t * d - s) * (2 * Math.PI) / p)) + b;
3822         }
3823         return a * Math.pow(2, -10 * (t -= 1)) *
3824                Math.sin((t * d - s) * (2 * Math.PI) / p) * .5 + c + b;
3825     },
3826
3827
3828
3829     backIn: function (t, b, c, d, s) {
3830         if (typeof s == 'undefined') {
3831             s = 1.70158;
3832         }
3833         return c * (t /= d) * t * ((s + 1) * t - s) + b;
3834     },
3835
3836
3837     backOut: function (t, b, c, d, s) {
3838         if (typeof s == 'undefined') {
3839             s = 1.70158;
3840         }
3841         return c * ((t = t / d - 1) * t * ((s + 1) * t + s) + 1) + b;
3842     },
3843
3844
3845     backBoth: function (t, b, c, d, s) {
3846         if (typeof s == 'undefined') {
3847             s = 1.70158;
3848         }
3849
3850         if ((t /= d / 2 ) < 1) {
3851             return c / 2 * (t * t * (((s *= (1.525)) + 1) * t - s)) + b;
3852         }
3853         return c / 2 * ((t -= 2) * t * (((s *= (1.525)) + 1) * t + s) + 2) + b;
3854     },
3855
3856
3857     bounceIn: function (t, b, c, d) {
3858         return c - Roo.lib.Easing.bounceOut(d - t, 0, c, d) + b;
3859     },
3860
3861
3862     bounceOut: function (t, b, c, d) {
3863         if ((t /= d) < (1 / 2.75)) {
3864             return c * (7.5625 * t * t) + b;
3865         } else if (t < (2 / 2.75)) {
3866             return c * (7.5625 * (t -= (1.5 / 2.75)) * t + .75) + b;
3867         } else if (t < (2.5 / 2.75)) {
3868             return c * (7.5625 * (t -= (2.25 / 2.75)) * t + .9375) + b;
3869         }
3870         return c * (7.5625 * (t -= (2.625 / 2.75)) * t + .984375) + b;
3871     },
3872
3873
3874     bounceBoth: function (t, b, c, d) {
3875         if (t < d / 2) {
3876             return Roo.lib.Easing.bounceIn(t * 2, 0, c, d) * .5 + b;
3877         }
3878         return Roo.lib.Easing.bounceOut(t * 2 - d, 0, c, d) * .5 + c * .5 + b;
3879     }
3880 };/*
3881  * Portions of this file are based on pieces of Yahoo User Interface Library
3882  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3883  * YUI licensed under the BSD License:
3884  * http://developer.yahoo.net/yui/license.txt
3885  * <script type="text/javascript">
3886  *
3887  */
3888     (function() {
3889         Roo.lib.Motion = function(el, attributes, duration, method) {
3890             if (el) {
3891                 Roo.lib.Motion.superclass.constructor.call(this, el, attributes, duration, method);
3892             }
3893         };
3894
3895         Roo.extend(Roo.lib.Motion, Roo.lib.ColorAnim);
3896
3897
3898         var Y = Roo.lib;
3899         var superclass = Y.Motion.superclass;
3900         var proto = Y.Motion.prototype;
3901
3902         proto.toString = function() {
3903             var el = this.getEl();
3904             var id = el.id || el.tagName;
3905             return ("Motion " + id);
3906         };
3907
3908         proto.patterns.points = /^points$/i;
3909
3910         proto.setAttribute = function(attr, val, unit) {
3911             if (this.patterns.points.test(attr)) {
3912                 unit = unit || 'px';
3913                 superclass.setAttribute.call(this, 'left', val[0], unit);
3914                 superclass.setAttribute.call(this, 'top', val[1], unit);
3915             } else {
3916                 superclass.setAttribute.call(this, attr, val, unit);
3917             }
3918         };
3919
3920         proto.getAttribute = function(attr) {
3921             if (this.patterns.points.test(attr)) {
3922                 var val = [
3923                         superclass.getAttribute.call(this, 'left'),
3924                         superclass.getAttribute.call(this, 'top')
3925                         ];
3926             } else {
3927                 val = superclass.getAttribute.call(this, attr);
3928             }
3929
3930             return val;
3931         };
3932
3933         proto.doMethod = function(attr, start, end) {
3934             var val = null;
3935
3936             if (this.patterns.points.test(attr)) {
3937                 var t = this.method(this.currentFrame, 0, 100, this.totalFrames) / 100;
3938                 val = Y.Bezier.getPosition(this.runtimeAttributes[attr], t);
3939             } else {
3940                 val = superclass.doMethod.call(this, attr, start, end);
3941             }
3942             return val;
3943         };
3944
3945         proto.setRuntimeAttribute = function(attr) {
3946             if (this.patterns.points.test(attr)) {
3947                 var el = this.getEl();
3948                 var attributes = this.attributes;
3949                 var start;
3950                 var control = attributes['points']['control'] || [];
3951                 var end;
3952                 var i, len;
3953
3954                 if (control.length > 0 && !(control[0] instanceof Array)) {
3955                     control = [control];
3956                 } else {
3957                     var tmp = [];
3958                     for (i = 0,len = control.length; i < len; ++i) {
3959                         tmp[i] = control[i];
3960                     }
3961                     control = tmp;
3962                 }
3963
3964                 Roo.fly(el).position();
3965
3966                 if (isset(attributes['points']['from'])) {
3967                     Roo.lib.Dom.setXY(el, attributes['points']['from']);
3968                 }
3969                 else {
3970                     Roo.lib.Dom.setXY(el, Roo.lib.Dom.getXY(el));
3971                 }
3972
3973                 start = this.getAttribute('points');
3974
3975
3976                 if (isset(attributes['points']['to'])) {
3977                     end = translateValues.call(this, attributes['points']['to'], start);
3978
3979                     var pageXY = Roo.lib.Dom.getXY(this.getEl());
3980                     for (i = 0,len = control.length; i < len; ++i) {
3981                         control[i] = translateValues.call(this, control[i], start);
3982                     }
3983
3984
3985                 } else if (isset(attributes['points']['by'])) {
3986                     end = [ start[0] + attributes['points']['by'][0], start[1] + attributes['points']['by'][1] ];
3987
3988                     for (i = 0,len = control.length; i < len; ++i) {
3989                         control[i] = [ start[0] + control[i][0], start[1] + control[i][1] ];
3990                     }
3991                 }
3992
3993                 this.runtimeAttributes[attr] = [start];
3994
3995                 if (control.length > 0) {
3996                     this.runtimeAttributes[attr] = this.runtimeAttributes[attr].concat(control);
3997                 }
3998
3999                 this.runtimeAttributes[attr][this.runtimeAttributes[attr].length] = end;
4000             }
4001             else {
4002                 superclass.setRuntimeAttribute.call(this, attr);
4003             }
4004         };
4005
4006         var translateValues = function(val, start) {
4007             var pageXY = Roo.lib.Dom.getXY(this.getEl());
4008             val = [ val[0] - pageXY[0] + start[0], val[1] - pageXY[1] + start[1] ];
4009
4010             return val;
4011         };
4012
4013         var isset = function(prop) {
4014             return (typeof prop !== 'undefined');
4015         };
4016     })();
4017 /*
4018  * Portions of this file are based on pieces of Yahoo User Interface Library
4019  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
4020  * YUI licensed under the BSD License:
4021  * http://developer.yahoo.net/yui/license.txt
4022  * <script type="text/javascript">
4023  *
4024  */
4025     (function() {
4026         Roo.lib.Scroll = function(el, attributes, duration, method) {
4027             if (el) {
4028                 Roo.lib.Scroll.superclass.constructor.call(this, el, attributes, duration, method);
4029             }
4030         };
4031
4032         Roo.extend(Roo.lib.Scroll, Roo.lib.ColorAnim);
4033
4034
4035         var Y = Roo.lib;
4036         var superclass = Y.Scroll.superclass;
4037         var proto = Y.Scroll.prototype;
4038
4039         proto.toString = function() {
4040             var el = this.getEl();
4041             var id = el.id || el.tagName;
4042             return ("Scroll " + id);
4043         };
4044
4045         proto.doMethod = function(attr, start, end) {
4046             var val = null;
4047
4048             if (attr == 'scroll') {
4049                 val = [
4050                         this.method(this.currentFrame, start[0], end[0] - start[0], this.totalFrames),
4051                         this.method(this.currentFrame, start[1], end[1] - start[1], this.totalFrames)
4052                         ];
4053
4054             } else {
4055                 val = superclass.doMethod.call(this, attr, start, end);
4056             }
4057             return val;
4058         };
4059
4060         proto.getAttribute = function(attr) {
4061             var val = null;
4062             var el = this.getEl();
4063
4064             if (attr == 'scroll') {
4065                 val = [ el.scrollLeft, el.scrollTop ];
4066             } else {
4067                 val = superclass.getAttribute.call(this, attr);
4068             }
4069
4070             return val;
4071         };
4072
4073         proto.setAttribute = function(attr, val, unit) {
4074             var el = this.getEl();
4075
4076             if (attr == 'scroll') {
4077                 el.scrollLeft = val[0];
4078                 el.scrollTop = val[1];
4079             } else {
4080                 superclass.setAttribute.call(this, attr, val, unit);
4081             }
4082         };
4083     })();
4084 /*
4085  * Based on:
4086  * Ext JS Library 1.1.1
4087  * Copyright(c) 2006-2007, Ext JS, LLC.
4088  *
4089  * Originally Released Under LGPL - original licence link has changed is not relivant.
4090  *
4091  * Fork - LGPL
4092  * <script type="text/javascript">
4093  */
4094
4095
4096 // nasty IE9 hack - what a pile of crap that is..
4097
4098  if (typeof Range != "undefined" && typeof Range.prototype.createContextualFragment == "undefined") {
4099     Range.prototype.createContextualFragment = function (html) {
4100         var doc = window.document;
4101         var container = doc.createElement("div");
4102         container.innerHTML = html;
4103         var frag = doc.createDocumentFragment(), n;
4104         while ((n = container.firstChild)) {
4105             frag.appendChild(n);
4106         }
4107         return frag;
4108     };
4109 }
4110
4111 /**
4112  * @class Roo.DomHelper
4113  * Utility class for working with DOM and/or Templates. It transparently supports using HTML fragments or DOM.
4114  * For more information see <a href="http://web.archive.org/web/20071221063734/http://www.jackslocum.com/blog/2006/10/06/domhelper-create-elements-using-dom-html-fragments-or-templates/">this blog post with examples</a>.
4115  * @singleton
4116  */
4117 Roo.DomHelper = function(){
4118     var tempTableEl = null;
4119     var emptyTags = /^(?:br|frame|hr|img|input|link|meta|range|spacer|wbr|area|param|col)$/i;
4120     var tableRe = /^table|tbody|tr|td$/i;
4121     var xmlns = {};
4122     // build as innerHTML where available
4123     /** @ignore */
4124     var createHtml = function(o){
4125         if(typeof o == 'string'){
4126             return o;
4127         }
4128         var b = "";
4129         if(!o.tag){
4130             o.tag = "div";
4131         }
4132         b += "<" + o.tag;
4133         for(var attr in o){
4134             if(attr == "tag" || attr == "children" || attr == "cn" || attr == "html" || typeof o[attr] == "function") continue;
4135             if(attr == "style"){
4136                 var s = o["style"];
4137                 if(typeof s == "function"){
4138                     s = s.call();
4139                 }
4140                 if(typeof s == "string"){
4141                     b += ' style="' + s + '"';
4142                 }else if(typeof s == "object"){
4143                     b += ' style="';
4144                     for(var key in s){
4145                         if(typeof s[key] != "function"){
4146                             b += key + ":" + s[key] + ";";
4147                         }
4148                     }
4149                     b += '"';
4150                 }
4151             }else{
4152                 if(attr == "cls"){
4153                     b += ' class="' + o["cls"] + '"';
4154                 }else if(attr == "htmlFor"){
4155                     b += ' for="' + o["htmlFor"] + '"';
4156                 }else{
4157                     b += " " + attr + '="' + o[attr] + '"';
4158                 }
4159             }
4160         }
4161         if(emptyTags.test(o.tag)){
4162             b += "/>";
4163         }else{
4164             b += ">";
4165             var cn = o.children || o.cn;
4166             if(cn){
4167                 //http://bugs.kde.org/show_bug.cgi?id=71506
4168                 if((cn instanceof Array) || (Roo.isSafari && typeof(cn.join) == "function")){
4169                     for(var i = 0, len = cn.length; i < len; i++) {
4170                         b += createHtml(cn[i], b);
4171                     }
4172                 }else{
4173                     b += createHtml(cn, b);
4174                 }
4175             }
4176             if(o.html){
4177                 b += o.html;
4178             }
4179             b += "</" + o.tag + ">";
4180         }
4181         return b;
4182     };
4183
4184     // build as dom
4185     /** @ignore */
4186     var createDom = function(o, parentNode){
4187          
4188         // defininition craeted..
4189         var ns = false;
4190         if (o.ns && o.ns != 'html') {
4191                
4192             if (o.xmlns && typeof(xmlns[o.ns]) == 'undefined') {
4193                 xmlns[o.ns] = o.xmlns;
4194                 ns = o.xmlns;
4195             }
4196             if (typeof(xmlns[o.ns]) == 'undefined') {
4197                 console.log("Trying to create namespace element " + o.ns + ", however no xmlns was sent to builder previously");
4198             }
4199             ns = xmlns[o.ns];
4200         }
4201         
4202         
4203         if (typeof(o) == 'string') {
4204             return parentNode.appendChild(document.createTextNode(o));
4205         }
4206         o.tag = o.tag || div;
4207         if (o.ns && Roo.isIE) {
4208             ns = false;
4209             o.tag = o.ns + ':' + o.tag;
4210             
4211         }
4212         var el = ns ? document.createElementNS( ns, o.tag||'div') :  document.createElement(o.tag||'div');
4213         var useSet = el.setAttribute ? true : false; // In IE some elements don't have setAttribute
4214         for(var attr in o){
4215             
4216             if(attr == "tag" || attr == "ns" ||attr == "xmlns" ||attr == "children" || attr == "cn" || attr == "html" || 
4217                     attr == "style" || typeof o[attr] == "function") continue;
4218                     
4219             if(attr=="cls" && Roo.isIE){
4220                 el.className = o["cls"];
4221             }else{
4222                 if(useSet) el.setAttribute(attr=="cls" ? 'class' : attr, o[attr]);
4223                 else el[attr] = o[attr];
4224             }
4225         }
4226         Roo.DomHelper.applyStyles(el, o.style);
4227         var cn = o.children || o.cn;
4228         if(cn){
4229             //http://bugs.kde.org/show_bug.cgi?id=71506
4230              if((cn instanceof Array) || (Roo.isSafari && typeof(cn.join) == "function")){
4231                 for(var i = 0, len = cn.length; i < len; i++) {
4232                     createDom(cn[i], el);
4233                 }
4234             }else{
4235                 createDom(cn, el);
4236             }
4237         }
4238         if(o.html){
4239             el.innerHTML = o.html;
4240         }
4241         if(parentNode){
4242            parentNode.appendChild(el);
4243         }
4244         return el;
4245     };
4246
4247     var ieTable = function(depth, s, h, e){
4248         tempTableEl.innerHTML = [s, h, e].join('');
4249         var i = -1, el = tempTableEl;
4250         while(++i < depth){
4251             el = el.firstChild;
4252         }
4253         return el;
4254     };
4255
4256     // kill repeat to save bytes
4257     var ts = '<table>',
4258         te = '</table>',
4259         tbs = ts+'<tbody>',
4260         tbe = '</tbody>'+te,
4261         trs = tbs + '<tr>',
4262         tre = '</tr>'+tbe;
4263
4264     /**
4265      * @ignore
4266      * Nasty code for IE's broken table implementation
4267      */
4268     var insertIntoTable = function(tag, where, el, html){
4269         if(!tempTableEl){
4270             tempTableEl = document.createElement('div');
4271         }
4272         var node;
4273         var before = null;
4274         if(tag == 'td'){
4275             if(where == 'afterbegin' || where == 'beforeend'){ // INTO a TD
4276                 return;
4277             }
4278             if(where == 'beforebegin'){
4279                 before = el;
4280                 el = el.parentNode;
4281             } else{
4282                 before = el.nextSibling;
4283                 el = el.parentNode;
4284             }
4285             node = ieTable(4, trs, html, tre);
4286         }
4287         else if(tag == 'tr'){
4288             if(where == 'beforebegin'){
4289                 before = el;
4290                 el = el.parentNode;
4291                 node = ieTable(3, tbs, html, tbe);
4292             } else if(where == 'afterend'){
4293                 before = el.nextSibling;
4294                 el = el.parentNode;
4295                 node = ieTable(3, tbs, html, tbe);
4296             } else{ // INTO a TR
4297                 if(where == 'afterbegin'){
4298                     before = el.firstChild;
4299                 }
4300                 node = ieTable(4, trs, html, tre);
4301             }
4302         } else if(tag == 'tbody'){
4303             if(where == 'beforebegin'){
4304                 before = el;
4305                 el = el.parentNode;
4306                 node = ieTable(2, ts, html, te);
4307             } else if(where == 'afterend'){
4308                 before = el.nextSibling;
4309                 el = el.parentNode;
4310                 node = ieTable(2, ts, html, te);
4311             } else{
4312                 if(where == 'afterbegin'){
4313                     before = el.firstChild;
4314                 }
4315                 node = ieTable(3, tbs, html, tbe);
4316             }
4317         } else{ // TABLE
4318             if(where == 'beforebegin' || where == 'afterend'){ // OUTSIDE the table
4319                 return;
4320             }
4321             if(where == 'afterbegin'){
4322                 before = el.firstChild;
4323             }
4324             node = ieTable(2, ts, html, te);
4325         }
4326         el.insertBefore(node, before);
4327         return node;
4328     };
4329
4330     return {
4331     /** True to force the use of DOM instead of html fragments @type Boolean */
4332     useDom : false,
4333
4334     /**
4335      * Returns the markup for the passed Element(s) config
4336      * @param {Object} o The Dom object spec (and children)
4337      * @return {String}
4338      */
4339     markup : function(o){
4340         return createHtml(o);
4341     },
4342
4343     /**
4344      * Applies a style specification to an element
4345      * @param {String/HTMLElement} el The element to apply styles to
4346      * @param {String/Object/Function} styles A style specification string eg "width:100px", or object in the form {width:"100px"}, or
4347      * a function which returns such a specification.
4348      */
4349     applyStyles : function(el, styles){
4350         if(styles){
4351            el = Roo.fly(el);
4352            if(typeof styles == "string"){
4353                var re = /\s?([a-z\-]*)\:\s?([^;]*);?/gi;
4354                var matches;
4355                while ((matches = re.exec(styles)) != null){
4356                    el.setStyle(matches[1], matches[2]);
4357                }
4358            }else if (typeof styles == "object"){
4359                for (var style in styles){
4360                   el.setStyle(style, styles[style]);
4361                }
4362            }else if (typeof styles == "function"){
4363                 Roo.DomHelper.applyStyles(el, styles.call());
4364            }
4365         }
4366     },
4367
4368     /**
4369      * Inserts an HTML fragment into the Dom
4370      * @param {String} where Where to insert the html in relation to el - beforeBegin, afterBegin, beforeEnd, afterEnd.
4371      * @param {HTMLElement} el The context element
4372      * @param {String} html The HTML fragmenet
4373      * @return {HTMLElement} The new node
4374      */
4375     insertHtml : function(where, el, html){
4376         where = where.toLowerCase();
4377         if(el.insertAdjacentHTML){
4378             if(tableRe.test(el.tagName)){
4379                 var rs;
4380                 if(rs = insertIntoTable(el.tagName.toLowerCase(), where, el, html)){
4381                     return rs;
4382                 }
4383             }
4384             switch(where){
4385                 case "beforebegin":
4386                     el.insertAdjacentHTML('BeforeBegin', html);
4387                     return el.previousSibling;
4388                 case "afterbegin":
4389                     el.insertAdjacentHTML('AfterBegin', html);
4390                     return el.firstChild;
4391                 case "beforeend":
4392                     el.insertAdjacentHTML('BeforeEnd', html);
4393                     return el.lastChild;
4394                 case "afterend":
4395                     el.insertAdjacentHTML('AfterEnd', html);
4396                     return el.nextSibling;
4397             }
4398             throw 'Illegal insertion point -> "' + where + '"';
4399         }
4400         var range = el.ownerDocument.createRange();
4401         var frag;
4402         switch(where){
4403              case "beforebegin":
4404                 range.setStartBefore(el);
4405                 frag = range.createContextualFragment(html);
4406                 el.parentNode.insertBefore(frag, el);
4407                 return el.previousSibling;
4408              case "afterbegin":
4409                 if(el.firstChild){
4410                     range.setStartBefore(el.firstChild);
4411                     frag = range.createContextualFragment(html);
4412                     el.insertBefore(frag, el.firstChild);
4413                     return el.firstChild;
4414                 }else{
4415                     el.innerHTML = html;
4416                     return el.firstChild;
4417                 }
4418             case "beforeend":
4419                 if(el.lastChild){
4420                     range.setStartAfter(el.lastChild);
4421                     frag = range.createContextualFragment(html);
4422                     el.appendChild(frag);
4423                     return el.lastChild;
4424                 }else{
4425                     el.innerHTML = html;
4426                     return el.lastChild;
4427                 }
4428             case "afterend":
4429                 range.setStartAfter(el);
4430                 frag = range.createContextualFragment(html);
4431                 el.parentNode.insertBefore(frag, el.nextSibling);
4432                 return el.nextSibling;
4433             }
4434             throw 'Illegal insertion point -> "' + where + '"';
4435     },
4436
4437     /**
4438      * Creates new Dom element(s) and inserts them before el
4439      * @param {String/HTMLElement/Element} el The context element
4440      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
4441      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4442      * @return {HTMLElement/Roo.Element} The new node
4443      */
4444     insertBefore : function(el, o, returnElement){
4445         return this.doInsert(el, o, returnElement, "beforeBegin");
4446     },
4447
4448     /**
4449      * Creates new Dom element(s) and inserts them after el
4450      * @param {String/HTMLElement/Element} el The context element
4451      * @param {Object} o The Dom object spec (and children)
4452      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4453      * @return {HTMLElement/Roo.Element} The new node
4454      */
4455     insertAfter : function(el, o, returnElement){
4456         return this.doInsert(el, o, returnElement, "afterEnd", "nextSibling");
4457     },
4458
4459     /**
4460      * Creates new Dom element(s) and inserts them as the first child of el
4461      * @param {String/HTMLElement/Element} el The context element
4462      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
4463      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4464      * @return {HTMLElement/Roo.Element} The new node
4465      */
4466     insertFirst : function(el, o, returnElement){
4467         return this.doInsert(el, o, returnElement, "afterBegin");
4468     },
4469
4470     // private
4471     doInsert : function(el, o, returnElement, pos, sibling){
4472         el = Roo.getDom(el);
4473         var newNode;
4474         if(this.useDom || o.ns){
4475             newNode = createDom(o, null);
4476             el.parentNode.insertBefore(newNode, sibling ? el[sibling] : el);
4477         }else{
4478             var html = createHtml(o);
4479             newNode = this.insertHtml(pos, el, html);
4480         }
4481         return returnElement ? Roo.get(newNode, true) : newNode;
4482     },
4483
4484     /**
4485      * Creates new Dom element(s) and appends them to el
4486      * @param {String/HTMLElement/Element} el The context element
4487      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
4488      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4489      * @return {HTMLElement/Roo.Element} The new node
4490      */
4491     append : function(el, o, returnElement){
4492         el = Roo.getDom(el);
4493         var newNode;
4494         if(this.useDom || o.ns){
4495             newNode = createDom(o, null);
4496             el.appendChild(newNode);
4497         }else{
4498             var html = createHtml(o);
4499             newNode = this.insertHtml("beforeEnd", el, html);
4500         }
4501         return returnElement ? Roo.get(newNode, true) : newNode;
4502     },
4503
4504     /**
4505      * Creates new Dom element(s) and overwrites the contents of el with them
4506      * @param {String/HTMLElement/Element} el The context element
4507      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
4508      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4509      * @return {HTMLElement/Roo.Element} The new node
4510      */
4511     overwrite : function(el, o, returnElement){
4512         el = Roo.getDom(el);
4513         if (o.ns) {
4514           
4515             while (el.childNodes.length) {
4516                 el.removeChild(el.firstChild);
4517             }
4518             createDom(o, el);
4519         } else {
4520             el.innerHTML = createHtml(o);   
4521         }
4522         
4523         return returnElement ? Roo.get(el.firstChild, true) : el.firstChild;
4524     },
4525
4526     /**
4527      * Creates a new Roo.DomHelper.Template from the Dom object spec
4528      * @param {Object} o The Dom object spec (and children)
4529      * @return {Roo.DomHelper.Template} The new template
4530      */
4531     createTemplate : function(o){
4532         var html = createHtml(o);
4533         return new Roo.Template(html);
4534     }
4535     };
4536 }();
4537 /*
4538  * Based on:
4539  * Ext JS Library 1.1.1
4540  * Copyright(c) 2006-2007, Ext JS, LLC.
4541  *
4542  * Originally Released Under LGPL - original licence link has changed is not relivant.
4543  *
4544  * Fork - LGPL
4545  * <script type="text/javascript">
4546  */
4547  
4548 /**
4549 * @class Roo.Template
4550 * Represents an HTML fragment template. Templates can be precompiled for greater performance.
4551 * For a list of available format functions, see {@link Roo.util.Format}.<br />
4552 * Usage:
4553 <pre><code>
4554 var t = new Roo.Template({
4555     html :  '&lt;div name="{id}"&gt;' + 
4556         '&lt;span class="{cls}"&gt;{name:trim} {someval:this.myformat}{value:ellipsis(10)}&lt;/span&gt;' +
4557         '&lt;/div&gt;',
4558     myformat: function (value, allValues) {
4559         return 'XX' + value;
4560     }
4561 });
4562 t.append('some-element', {id: 'myid', cls: 'myclass', name: 'foo', value: 'bar'});
4563 </code></pre>
4564 * For more information see this blog post with examples:
4565 *  <a href="http://www.cnitblog.com/seeyeah/archive/2011/12/30/38728.html/">DomHelper
4566      - Create Elements using DOM, HTML fragments and Templates</a>. 
4567 * @constructor
4568 * @param {Object} cfg - Configuration object.
4569 */
4570 Roo.Template = function(cfg){
4571     // BC!
4572     if(cfg instanceof Array){
4573         cfg = cfg.join("");
4574     }else if(arguments.length > 1){
4575         cfg = Array.prototype.join.call(arguments, "");
4576     }
4577     
4578     
4579     if (typeof(cfg) == 'object') {
4580         Roo.apply(this,cfg)
4581     } else {
4582         // bc
4583         this.html = cfg;
4584     }
4585     if (this.url) {
4586         this.load();
4587     }
4588     
4589 };
4590 Roo.Template.prototype = {
4591     
4592     /**
4593      * @cfg {String} url  The Url to load the template from. beware if you are loading from a url, the data may not be ready if you use it instantly..
4594      *                    it should be fixed so that template is observable...
4595      */
4596     url : false,
4597     /**
4598      * @cfg {String} html  The HTML fragment or an array of fragments to join("") or multiple arguments to join("")
4599      */
4600     html : '',
4601     /**
4602      * Returns an HTML fragment of this template with the specified values applied.
4603      * @param {Object} values The template values. Can be an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'})
4604      * @return {String} The HTML fragment
4605      */
4606     applyTemplate : function(values){
4607         try {
4608            
4609             if(this.compiled){
4610                 return this.compiled(values);
4611             }
4612             var useF = this.disableFormats !== true;
4613             var fm = Roo.util.Format, tpl = this;
4614             var fn = function(m, name, format, args){
4615                 if(format && useF){
4616                     if(format.substr(0, 5) == "this."){
4617                         return tpl.call(format.substr(5), values[name], values);
4618                     }else{
4619                         if(args){
4620                             // quoted values are required for strings in compiled templates, 
4621                             // but for non compiled we need to strip them
4622                             // quoted reversed for jsmin
4623                             var re = /^\s*['"](.*)["']\s*$/;
4624                             args = args.split(',');
4625                             for(var i = 0, len = args.length; i < len; i++){
4626                                 args[i] = args[i].replace(re, "$1");
4627                             }
4628                             args = [values[name]].concat(args);
4629                         }else{
4630                             args = [values[name]];
4631                         }
4632                         return fm[format].apply(fm, args);
4633                     }
4634                 }else{
4635                     return values[name] !== undefined ? values[name] : "";
4636                 }
4637             };
4638             return this.html.replace(this.re, fn);
4639         } catch (e) {
4640             Roo.log(e);
4641             throw e;
4642         }
4643          
4644     },
4645     
4646     loading : false,
4647       
4648     load : function ()
4649     {
4650          
4651         if (this.loading) {
4652             return;
4653         }
4654         var _t = this;
4655         
4656         this.loading = true;
4657         this.compiled = false;
4658         
4659         var cx = new Roo.data.Connection();
4660         cx.request({
4661             url : this.url,
4662             method : 'GET',
4663             success : function (response) {
4664                 _t.loading = false;
4665                 _t.html = response.responseText;
4666                 _t.url = false;
4667                 _t.compile();
4668              },
4669             failure : function(response) {
4670                 Roo.log("Template failed to load from " + _t.url);
4671                 _t.loading = false;
4672             }
4673         });
4674     },
4675
4676     /**
4677      * Sets the HTML used as the template and optionally compiles it.
4678      * @param {String} html
4679      * @param {Boolean} compile (optional) True to compile the template (defaults to undefined)
4680      * @return {Roo.Template} this
4681      */
4682     set : function(html, compile){
4683         this.html = html;
4684         this.compiled = null;
4685         if(compile){
4686             this.compile();
4687         }
4688         return this;
4689     },
4690     
4691     /**
4692      * True to disable format functions (defaults to false)
4693      * @type Boolean
4694      */
4695     disableFormats : false,
4696     
4697     /**
4698     * The regular expression used to match template variables 
4699     * @type RegExp
4700     * @property 
4701     */
4702     re : /\{([\w-]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
4703     
4704     /**
4705      * Compiles the template into an internal function, eliminating the RegEx overhead.
4706      * @return {Roo.Template} this
4707      */
4708     compile : function(){
4709         var fm = Roo.util.Format;
4710         var useF = this.disableFormats !== true;
4711         var sep = Roo.isGecko ? "+" : ",";
4712         var fn = function(m, name, format, args){
4713             if(format && useF){
4714                 args = args ? ',' + args : "";
4715                 if(format.substr(0, 5) != "this."){
4716                     format = "fm." + format + '(';
4717                 }else{
4718                     format = 'this.call("'+ format.substr(5) + '", ';
4719                     args = ", values";
4720                 }
4721             }else{
4722                 args= ''; format = "(values['" + name + "'] == undefined ? '' : ";
4723             }
4724             return "'"+ sep + format + "values['" + name + "']" + args + ")"+sep+"'";
4725         };
4726         var body;
4727         // branched to use + in gecko and [].join() in others
4728         if(Roo.isGecko){
4729             body = "this.compiled = function(values){ return '" +
4730                    this.html.replace(/\\/g, '\\\\').replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
4731                     "';};";
4732         }else{
4733             body = ["this.compiled = function(values){ return ['"];
4734             body.push(this.html.replace(/\\/g, '\\\\').replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn));
4735             body.push("'].join('');};");
4736             body = body.join('');
4737         }
4738         /**
4739          * eval:var:values
4740          * eval:var:fm
4741          */
4742         eval(body);
4743         return this;
4744     },
4745     
4746     // private function used to call members
4747     call : function(fnName, value, allValues){
4748         return this[fnName](value, allValues);
4749     },
4750     
4751     /**
4752      * Applies the supplied values to the template and inserts the new node(s) as the first child of el.
4753      * @param {String/HTMLElement/Roo.Element} el The context element
4754      * @param {Object} values The template values. Can be an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'})
4755      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4756      * @return {HTMLElement/Roo.Element} The new node or Element
4757      */
4758     insertFirst: function(el, values, returnElement){
4759         return this.doInsert('afterBegin', el, values, returnElement);
4760     },
4761
4762     /**
4763      * Applies the supplied values to the template and inserts the new node(s) before el.
4764      * @param {String/HTMLElement/Roo.Element} el The context element
4765      * @param {Object} values The template values. Can be an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'})
4766      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4767      * @return {HTMLElement/Roo.Element} The new node or Element
4768      */
4769     insertBefore: function(el, values, returnElement){
4770         return this.doInsert('beforeBegin', el, values, returnElement);
4771     },
4772
4773     /**
4774      * Applies the supplied values to the template and inserts the new node(s) after el.
4775      * @param {String/HTMLElement/Roo.Element} el The context element
4776      * @param {Object} values The template values. Can be an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'})
4777      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4778      * @return {HTMLElement/Roo.Element} The new node or Element
4779      */
4780     insertAfter : function(el, values, returnElement){
4781         return this.doInsert('afterEnd', el, values, returnElement);
4782     },
4783     
4784     /**
4785      * Applies the supplied values to the template and appends the new node(s) to el.
4786      * @param {String/HTMLElement/Roo.Element} el The context element
4787      * @param {Object} values The template values. Can be an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'})
4788      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4789      * @return {HTMLElement/Roo.Element} The new node or Element
4790      */
4791     append : function(el, values, returnElement){
4792         return this.doInsert('beforeEnd', el, values, returnElement);
4793     },
4794
4795     doInsert : function(where, el, values, returnEl){
4796         el = Roo.getDom(el);
4797         var newNode = Roo.DomHelper.insertHtml(where, el, this.applyTemplate(values));
4798         return returnEl ? Roo.get(newNode, true) : newNode;
4799     },
4800
4801     /**
4802      * Applies the supplied values to the template and overwrites the content of el with the new node(s).
4803      * @param {String/HTMLElement/Roo.Element} el The context element
4804      * @param {Object} values The template values. Can be an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'})
4805      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4806      * @return {HTMLElement/Roo.Element} The new node or Element
4807      */
4808     overwrite : function(el, values, returnElement){
4809         el = Roo.getDom(el);
4810         el.innerHTML = this.applyTemplate(values);
4811         return returnElement ? Roo.get(el.firstChild, true) : el.firstChild;
4812     }
4813 };
4814 /**
4815  * Alias for {@link #applyTemplate}
4816  * @method
4817  */
4818 Roo.Template.prototype.apply = Roo.Template.prototype.applyTemplate;
4819
4820 // backwards compat
4821 Roo.DomHelper.Template = Roo.Template;
4822
4823 /**
4824  * Creates a template from the passed element's value (<i>display:none</i> textarea, preferred) or innerHTML.
4825  * @param {String/HTMLElement} el A DOM element or its id
4826  * @returns {Roo.Template} The created template
4827  * @static
4828  */
4829 Roo.Template.from = function(el){
4830     el = Roo.getDom(el);
4831     return new Roo.Template(el.value || el.innerHTML);
4832 };/*
4833  * Based on:
4834  * Ext JS Library 1.1.1
4835  * Copyright(c) 2006-2007, Ext JS, LLC.
4836  *
4837  * Originally Released Under LGPL - original licence link has changed is not relivant.
4838  *
4839  * Fork - LGPL
4840  * <script type="text/javascript">
4841  */
4842  
4843
4844 /*
4845  * This is code is also distributed under MIT license for use
4846  * with jQuery and prototype JavaScript libraries.
4847  */
4848 /**
4849  * @class Roo.DomQuery
4850 Provides high performance selector/xpath processing by compiling queries into reusable functions. New pseudo classes and matchers can be plugged. It works on HTML and XML documents (if a content node is passed in).
4851 <p>
4852 DomQuery supports most of the <a href="http://www.w3.org/TR/2005/WD-css3-selectors-20051215/">CSS3 selectors spec</a>, along with some custom selectors and basic XPath.</p>
4853
4854 <p>
4855 All selectors, attribute filters and pseudos below can be combined infinitely in any order. For example "div.foo:nth-child(odd)[@foo=bar].bar:first" would be a perfectly valid selector. Node filters are processed in the order in which they appear, which allows you to optimize your queries for your document structure.
4856 </p>
4857 <h4>Element Selectors:</h4>
4858 <ul class="list">
4859     <li> <b>*</b> any element</li>
4860     <li> <b>E</b> an element with the tag E</li>
4861     <li> <b>E F</b> All descendent elements of E that have the tag F</li>
4862     <li> <b>E > F</b> or <b>E/F</b> all direct children elements of E that have the tag F</li>
4863     <li> <b>E + F</b> all elements with the tag F that are immediately preceded by an element with the tag E</li>
4864     <li> <b>E ~ F</b> all elements with the tag F that are preceded by a sibling element with the tag E</li>
4865 </ul>
4866 <h4>Attribute Selectors:</h4>
4867 <p>The use of @ and quotes are optional. For example, div[@foo='bar'] is also a valid attribute selector.</p>
4868 <ul class="list">
4869     <li> <b>E[foo]</b> has an attribute "foo"</li>
4870     <li> <b>E[foo=bar]</b> has an attribute "foo" that equals "bar"</li>
4871     <li> <b>E[foo^=bar]</b> has an attribute "foo" that starts with "bar"</li>
4872     <li> <b>E[foo$=bar]</b> has an attribute "foo" that ends with "bar"</li>
4873     <li> <b>E[foo*=bar]</b> has an attribute "foo" that contains the substring "bar"</li>
4874     <li> <b>E[foo%=2]</b> has an attribute "foo" that is evenly divisible by 2</li>
4875     <li> <b>E[foo!=bar]</b> has an attribute "foo" that does not equal "bar"</li>
4876 </ul>
4877 <h4>Pseudo Classes:</h4>
4878 <ul class="list">
4879     <li> <b>E:first-child</b> E is the first child of its parent</li>
4880     <li> <b>E:last-child</b> E is the last child of its parent</li>
4881     <li> <b>E:nth-child(<i>n</i>)</b> E is the <i>n</i>th child of its parent (1 based as per the spec)</li>
4882     <li> <b>E:nth-child(odd)</b> E is an odd child of its parent</li>
4883     <li> <b>E:nth-child(even)</b> E is an even child of its parent</li>
4884     <li> <b>E:only-child</b> E is the only child of its parent</li>
4885     <li> <b>E:checked</b> E is an element that is has a checked attribute that is true (e.g. a radio or checkbox) </li>
4886     <li> <b>E:first</b> the first E in the resultset</li>
4887     <li> <b>E:last</b> the last E in the resultset</li>
4888     <li> <b>E:nth(<i>n</i>)</b> the <i>n</i>th E in the resultset (1 based)</li>
4889     <li> <b>E:odd</b> shortcut for :nth-child(odd)</li>
4890     <li> <b>E:even</b> shortcut for :nth-child(even)</li>
4891     <li> <b>E:contains(foo)</b> E's innerHTML contains the substring "foo"</li>
4892     <li> <b>E:nodeValue(foo)</b> E contains a textNode with a nodeValue that equals "foo"</li>
4893     <li> <b>E:not(S)</b> an E element that does not match simple selector S</li>
4894     <li> <b>E:has(S)</b> an E element that has a descendent that matches simple selector S</li>
4895     <li> <b>E:next(S)</b> an E element whose next sibling matches simple selector S</li>
4896     <li> <b>E:prev(S)</b> an E element whose previous sibling matches simple selector S</li>
4897 </ul>
4898 <h4>CSS Value Selectors:</h4>
4899 <ul class="list">
4900     <li> <b>E{display=none}</b> css value "display" that equals "none"</li>
4901     <li> <b>E{display^=none}</b> css value "display" that starts with "none"</li>
4902     <li> <b>E{display$=none}</b> css value "display" that ends with "none"</li>
4903     <li> <b>E{display*=none}</b> css value "display" that contains the substring "none"</li>
4904     <li> <b>E{display%=2}</b> css value "display" that is evenly divisible by 2</li>
4905     <li> <b>E{display!=none}</b> css value "display" that does not equal "none"</li>
4906 </ul>
4907  * @singleton
4908  */
4909 Roo.DomQuery = function(){
4910     var cache = {}, simpleCache = {}, valueCache = {};
4911     var nonSpace = /\S/;
4912     var trimRe = /^\s+|\s+$/g;
4913     var tplRe = /\{(\d+)\}/g;
4914     var modeRe = /^(\s?[\/>+~]\s?|\s|$)/;
4915     var tagTokenRe = /^(#)?([\w-\*]+)/;
4916     var nthRe = /(\d*)n\+?(\d*)/, nthRe2 = /\D/;
4917
4918     function child(p, index){
4919         var i = 0;
4920         var n = p.firstChild;
4921         while(n){
4922             if(n.nodeType == 1){
4923                if(++i == index){
4924                    return n;
4925                }
4926             }
4927             n = n.nextSibling;
4928         }
4929         return null;
4930     };
4931
4932     function next(n){
4933         while((n = n.nextSibling) && n.nodeType != 1);
4934         return n;
4935     };
4936
4937     function prev(n){
4938         while((n = n.previousSibling) && n.nodeType != 1);
4939         return n;
4940     };
4941
4942     function children(d){
4943         var n = d.firstChild, ni = -1;
4944             while(n){
4945                 var nx = n.nextSibling;
4946                 if(n.nodeType == 3 && !nonSpace.test(n.nodeValue)){
4947                     d.removeChild(n);
4948                 }else{
4949                     n.nodeIndex = ++ni;
4950                 }
4951                 n = nx;
4952             }
4953             return this;
4954         };
4955
4956     function byClassName(c, a, v){
4957         if(!v){
4958             return c;
4959         }
4960         var r = [], ri = -1, cn;
4961         for(var i = 0, ci; ci = c[i]; i++){
4962             if((' '+ci.className+' ').indexOf(v) != -1){
4963                 r[++ri] = ci;
4964             }
4965         }
4966         return r;
4967     };
4968
4969     function attrValue(n, attr){
4970         if(!n.tagName && typeof n.length != "undefined"){
4971             n = n[0];
4972         }
4973         if(!n){
4974             return null;
4975         }
4976         if(attr == "for"){
4977             return n.htmlFor;
4978         }
4979         if(attr == "class" || attr == "className"){
4980             return n.className;
4981         }
4982         return n.getAttribute(attr) || n[attr];
4983
4984     };
4985
4986     function getNodes(ns, mode, tagName){
4987         var result = [], ri = -1, cs;
4988         if(!ns){
4989             return result;
4990         }
4991         tagName = tagName || "*";
4992         if(typeof ns.getElementsByTagName != "undefined"){
4993             ns = [ns];
4994         }
4995         if(!mode){
4996             for(var i = 0, ni; ni = ns[i]; i++){
4997                 cs = ni.getElementsByTagName(tagName);
4998                 for(var j = 0, ci; ci = cs[j]; j++){
4999                     result[++ri] = ci;
5000                 }
5001             }
5002         }else if(mode == "/" || mode == ">"){
5003             var utag = tagName.toUpperCase();
5004             for(var i = 0, ni, cn; ni = ns[i]; i++){
5005                 cn = ni.children || ni.childNodes;
5006                 for(var j = 0, cj; cj = cn[j]; j++){
5007                     if(cj.nodeName == utag || cj.nodeName == tagName  || tagName == '*'){
5008                         result[++ri] = cj;
5009                     }
5010                 }
5011             }
5012         }else if(mode == "+"){
5013             var utag = tagName.toUpperCase();
5014             for(var i = 0, n; n = ns[i]; i++){
5015                 while((n = n.nextSibling) && n.nodeType != 1);
5016                 if(n && (n.nodeName == utag || n.nodeName == tagName || tagName == '*')){
5017                     result[++ri] = n;
5018                 }
5019             }
5020         }else if(mode == "~"){
5021             for(var i = 0, n; n = ns[i]; i++){
5022                 while((n = n.nextSibling) && (n.nodeType != 1 || (tagName == '*' || n.tagName.toLowerCase()!=tagName)));
5023                 if(n){
5024                     result[++ri] = n;
5025                 }
5026             }
5027         }
5028         return result;
5029     };
5030
5031     function concat(a, b){
5032         if(b.slice){
5033             return a.concat(b);
5034         }
5035         for(var i = 0, l = b.length; i < l; i++){
5036             a[a.length] = b[i];
5037         }
5038         return a;
5039     }
5040
5041     function byTag(cs, tagName){
5042         if(cs.tagName || cs == document){
5043             cs = [cs];
5044         }
5045         if(!tagName){
5046             return cs;
5047         }
5048         var r = [], ri = -1;
5049         tagName = tagName.toLowerCase();
5050         for(var i = 0, ci; ci = cs[i]; i++){
5051             if(ci.nodeType == 1 && ci.tagName.toLowerCase()==tagName){
5052                 r[++ri] = ci;
5053             }
5054         }
5055         return r;
5056     };
5057
5058     function byId(cs, attr, id){
5059         if(cs.tagName || cs == document){
5060             cs = [cs];
5061         }
5062         if(!id){
5063             return cs;
5064         }
5065         var r = [], ri = -1;
5066         for(var i = 0,ci; ci = cs[i]; i++){
5067             if(ci && ci.id == id){
5068                 r[++ri] = ci;
5069                 return r;
5070             }
5071         }
5072         return r;
5073     };
5074
5075     function byAttribute(cs, attr, value, op, custom){
5076         var r = [], ri = -1, st = custom=="{";
5077         var f = Roo.DomQuery.operators[op];
5078         for(var i = 0, ci; ci = cs[i]; i++){
5079             var a;
5080             if(st){
5081                 a = Roo.DomQuery.getStyle(ci, attr);
5082             }
5083             else if(attr == "class" || attr == "className"){
5084                 a = ci.className;
5085             }else if(attr == "for"){
5086                 a = ci.htmlFor;
5087             }else if(attr == "href"){
5088                 a = ci.getAttribute("href", 2);
5089             }else{
5090                 a = ci.getAttribute(attr);
5091             }
5092             if((f && f(a, value)) || (!f && a)){
5093                 r[++ri] = ci;
5094             }
5095         }
5096         return r;
5097     };
5098
5099     function byPseudo(cs, name, value){
5100         return Roo.DomQuery.pseudos[name](cs, value);
5101     };
5102
5103     // This is for IE MSXML which does not support expandos.
5104     // IE runs the same speed using setAttribute, however FF slows way down
5105     // and Safari completely fails so they need to continue to use expandos.
5106     var isIE = window.ActiveXObject ? true : false;
5107
5108     // this eval is stop the compressor from
5109     // renaming the variable to something shorter
5110     
5111     /** eval:var:batch */
5112     var batch = 30803; 
5113
5114     var key = 30803;
5115
5116     function nodupIEXml(cs){
5117         var d = ++key;
5118         cs[0].setAttribute("_nodup", d);
5119         var r = [cs[0]];
5120         for(var i = 1, len = cs.length; i < len; i++){
5121             var c = cs[i];
5122             if(!c.getAttribute("_nodup") != d){
5123                 c.setAttribute("_nodup", d);
5124                 r[r.length] = c;
5125             }
5126         }
5127         for(var i = 0, len = cs.length; i < len; i++){
5128             cs[i].removeAttribute("_nodup");
5129         }
5130         return r;
5131     }
5132
5133     function nodup(cs){
5134         if(!cs){
5135             return [];
5136         }
5137         var len = cs.length, c, i, r = cs, cj, ri = -1;
5138         if(!len || typeof cs.nodeType != "undefined" || len == 1){
5139             return cs;
5140         }
5141         if(isIE && typeof cs[0].selectSingleNode != "undefined"){
5142             return nodupIEXml(cs);
5143         }
5144         var d = ++key;
5145         cs[0]._nodup = d;
5146         for(i = 1; c = cs[i]; i++){
5147             if(c._nodup != d){
5148                 c._nodup = d;
5149             }else{
5150                 r = [];
5151                 for(var j = 0; j < i; j++){
5152                     r[++ri] = cs[j];
5153                 }
5154                 for(j = i+1; cj = cs[j]; j++){
5155                     if(cj._nodup != d){
5156                         cj._nodup = d;
5157                         r[++ri] = cj;
5158                     }
5159                 }
5160                 return r;
5161             }
5162         }
5163         return r;
5164     }
5165
5166     function quickDiffIEXml(c1, c2){
5167         var d = ++key;
5168         for(var i = 0, len = c1.length; i < len; i++){
5169             c1[i].setAttribute("_qdiff", d);
5170         }
5171         var r = [];
5172         for(var i = 0, len = c2.length; i < len; i++){
5173             if(c2[i].getAttribute("_qdiff") != d){
5174                 r[r.length] = c2[i];
5175             }
5176         }
5177         for(var i = 0, len = c1.length; i < len; i++){
5178            c1[i].removeAttribute("_qdiff");
5179         }
5180         return r;
5181     }
5182
5183     function quickDiff(c1, c2){
5184         var len1 = c1.length;
5185         if(!len1){
5186             return c2;
5187         }
5188         if(isIE && c1[0].selectSingleNode){
5189             return quickDiffIEXml(c1, c2);
5190         }
5191         var d = ++key;
5192         for(var i = 0; i < len1; i++){
5193             c1[i]._qdiff = d;
5194         }
5195         var r = [];
5196         for(var i = 0, len = c2.length; i < len; i++){
5197             if(c2[i]._qdiff != d){
5198                 r[r.length] = c2[i];
5199             }
5200         }
5201         return r;
5202     }
5203
5204     function quickId(ns, mode, root, id){
5205         if(ns == root){
5206            var d = root.ownerDocument || root;
5207            return d.getElementById(id);
5208         }
5209         ns = getNodes(ns, mode, "*");
5210         return byId(ns, null, id);
5211     }
5212
5213     return {
5214         getStyle : function(el, name){
5215             return Roo.fly(el).getStyle(name);
5216         },
5217         /**
5218          * Compiles a selector/xpath query into a reusable function. The returned function
5219          * takes one parameter "root" (optional), which is the context node from where the query should start.
5220          * @param {String} selector The selector/xpath query
5221          * @param {String} type (optional) Either "select" (the default) or "simple" for a simple selector match
5222          * @return {Function}
5223          */
5224         compile : function(path, type){
5225             type = type || "select";
5226             
5227             var fn = ["var f = function(root){\n var mode; ++batch; var n = root || document;\n"];
5228             var q = path, mode, lq;
5229             var tk = Roo.DomQuery.matchers;
5230             var tklen = tk.length;
5231             var mm;
5232
5233             // accept leading mode switch
5234             var lmode = q.match(modeRe);
5235             if(lmode && lmode[1]){
5236                 fn[fn.length] = 'mode="'+lmode[1].replace(trimRe, "")+'";';
5237                 q = q.replace(lmode[1], "");
5238             }
5239             // strip leading slashes
5240             while(path.substr(0, 1)=="/"){
5241                 path = path.substr(1);
5242             }
5243
5244             while(q && lq != q){
5245                 lq = q;
5246                 var tm = q.match(tagTokenRe);
5247                 if(type == "select"){
5248                     if(tm){
5249                         if(tm[1] == "#"){
5250                             fn[fn.length] = 'n = quickId(n, mode, root, "'+tm[2]+'");';
5251                         }else{
5252                             fn[fn.length] = 'n = getNodes(n, mode, "'+tm[2]+'");';
5253                         }
5254                         q = q.replace(tm[0], "");
5255                     }else if(q.substr(0, 1) != '@'){
5256                         fn[fn.length] = 'n = getNodes(n, mode, "*");';
5257                     }
5258                 }else{
5259                     if(tm){
5260                         if(tm[1] == "#"){
5261                             fn[fn.length] = 'n = byId(n, null, "'+tm[2]+'");';
5262                         }else{
5263                             fn[fn.length] = 'n = byTag(n, "'+tm[2]+'");';
5264                         }
5265                         q = q.replace(tm[0], "");
5266                     }
5267                 }
5268                 while(!(mm = q.match(modeRe))){
5269                     var matched = false;
5270                     for(var j = 0; j < tklen; j++){
5271                         var t = tk[j];
5272                         var m = q.match(t.re);
5273                         if(m){
5274                             fn[fn.length] = t.select.replace(tplRe, function(x, i){
5275                                                     return m[i];
5276                                                 });
5277                             q = q.replace(m[0], "");
5278                             matched = true;
5279                             break;
5280                         }
5281                     }
5282                     // prevent infinite loop on bad selector
5283                     if(!matched){
5284                         throw 'Error parsing selector, parsing failed at "' + q + '"';
5285                     }
5286                 }
5287                 if(mm[1]){
5288                     fn[fn.length] = 'mode="'+mm[1].replace(trimRe, "")+'";';
5289                     q = q.replace(mm[1], "");
5290                 }
5291             }
5292             fn[fn.length] = "return nodup(n);\n}";
5293             
5294              /** 
5295               * list of variables that need from compression as they are used by eval.
5296              *  eval:var:batch 
5297              *  eval:var:nodup
5298              *  eval:var:byTag
5299              *  eval:var:ById
5300              *  eval:var:getNodes
5301              *  eval:var:quickId
5302              *  eval:var:mode
5303              *  eval:var:root
5304              *  eval:var:n
5305              *  eval:var:byClassName
5306              *  eval:var:byPseudo
5307              *  eval:var:byAttribute
5308              *  eval:var:attrValue
5309              * 
5310              **/ 
5311             eval(fn.join(""));
5312             return f;
5313         },
5314
5315         /**
5316          * Selects a group of elements.
5317          * @param {String} selector The selector/xpath query (can be a comma separated list of selectors)
5318          * @param {Node} root (optional) The start of the query (defaults to document).
5319          * @return {Array}
5320          */
5321         select : function(path, root, type){
5322             if(!root || root == document){
5323                 root = document;
5324             }
5325             if(typeof root == "string"){
5326                 root = document.getElementById(root);
5327             }
5328             var paths = path.split(",");
5329             var results = [];
5330             for(var i = 0, len = paths.length; i < len; i++){
5331                 var p = paths[i].replace(trimRe, "");
5332                 if(!cache[p]){
5333                     cache[p] = Roo.DomQuery.compile(p);
5334                     if(!cache[p]){
5335                         throw p + " is not a valid selector";
5336                     }
5337                 }
5338                 var result = cache[p](root);
5339                 if(result && result != document){
5340                     results = results.concat(result);
5341                 }
5342             }
5343             if(paths.length > 1){
5344                 return nodup(results);
5345             }
5346             return results;
5347         },
5348
5349         /**
5350          * Selects a single element.
5351          * @param {String} selector The selector/xpath query
5352          * @param {Node} root (optional) The start of the query (defaults to document).
5353          * @return {Element}
5354          */
5355         selectNode : function(path, root){
5356             return Roo.DomQuery.select(path, root)[0];
5357         },
5358
5359         /**
5360          * Selects the value of a node, optionally replacing null with the defaultValue.
5361          * @param {String} selector The selector/xpath query
5362          * @param {Node} root (optional) The start of the query (defaults to document).
5363          * @param {String} defaultValue
5364          */
5365         selectValue : function(path, root, defaultValue){
5366             path = path.replace(trimRe, "");
5367             if(!valueCache[path]){
5368                 valueCache[path] = Roo.DomQuery.compile(path, "select");
5369             }
5370             var n = valueCache[path](root);
5371             n = n[0] ? n[0] : n;
5372             var v = (n && n.firstChild ? n.firstChild.nodeValue : null);
5373             return ((v === null||v === undefined||v==='') ? defaultValue : v);
5374         },
5375
5376         /**
5377          * Selects the value of a node, parsing integers and floats.
5378          * @param {String} selector The selector/xpath query
5379          * @param {Node} root (optional) The start of the query (defaults to document).
5380          * @param {Number} defaultValue
5381          * @return {Number}
5382          */
5383         selectNumber : function(path, root, defaultValue){
5384             var v = Roo.DomQuery.selectValue(path, root, defaultValue || 0);
5385             return parseFloat(v);
5386         },
5387
5388         /**
5389          * Returns true if the passed element(s) match the passed simple selector (e.g. div.some-class or span:first-child)
5390          * @param {String/HTMLElement/Array} el An element id, element or array of elements
5391          * @param {String} selector The simple selector to test
5392          * @return {Boolean}
5393          */
5394         is : function(el, ss){
5395             if(typeof el == "string"){
5396                 el = document.getElementById(el);
5397             }
5398             var isArray = (el instanceof Array);
5399             var result = Roo.DomQuery.filter(isArray ? el : [el], ss);
5400             return isArray ? (result.length == el.length) : (result.length > 0);
5401         },
5402
5403         /**
5404          * Filters an array of elements to only include matches of a simple selector (e.g. div.some-class or span:first-child)
5405          * @param {Array} el An array of elements to filter
5406          * @param {String} selector The simple selector to test
5407          * @param {Boolean} nonMatches If true, it returns the elements that DON'T match
5408          * the selector instead of the ones that match
5409          * @return {Array}
5410          */
5411         filter : function(els, ss, nonMatches){
5412             ss = ss.replace(trimRe, "");
5413             if(!simpleCache[ss]){
5414                 simpleCache[ss] = Roo.DomQuery.compile(ss, "simple");
5415             }
5416             var result = simpleCache[ss](els);
5417             return nonMatches ? quickDiff(result, els) : result;
5418         },
5419
5420         /**
5421          * Collection of matching regular expressions and code snippets.
5422          */
5423         matchers : [{
5424                 re: /^\.([\w-]+)/,
5425                 select: 'n = byClassName(n, null, " {1} ");'
5426             }, {
5427                 re: /^\:([\w-]+)(?:\(((?:[^\s>\/]*|.*?))\))?/,
5428                 select: 'n = byPseudo(n, "{1}", "{2}");'
5429             },{
5430                 re: /^(?:([\[\{])(?:@)?([\w-]+)\s?(?:(=|.=)\s?['"]?(.*?)["']?)?[\]\}])/,
5431                 select: 'n = byAttribute(n, "{2}", "{4}", "{3}", "{1}");'
5432             }, {
5433                 re: /^#([\w-]+)/,
5434                 select: 'n = byId(n, null, "{1}");'
5435             },{
5436                 re: /^@([\w-]+)/,
5437                 select: 'return {firstChild:{nodeValue:attrValue(n, "{1}")}};'
5438             }
5439         ],
5440
5441         /**
5442          * Collection of operator comparison functions. The default operators are =, !=, ^=, $=, *=, %=, |= and ~=.
5443          * New operators can be added as long as the match the format <i>c</i>= where <i>c</i> is any character other than space, &gt; &lt;.
5444          */
5445         operators : {
5446             "=" : function(a, v){
5447                 return a == v;
5448             },
5449             "!=" : function(a, v){
5450                 return a != v;
5451             },
5452             "^=" : function(a, v){
5453                 return a && a.substr(0, v.length) == v;
5454             },
5455             "$=" : function(a, v){
5456                 return a && a.substr(a.length-v.length) == v;
5457             },
5458             "*=" : function(a, v){
5459                 return a && a.indexOf(v) !== -1;
5460             },
5461             "%=" : function(a, v){
5462                 return (a % v) == 0;
5463             },
5464             "|=" : function(a, v){
5465                 return a && (a == v || a.substr(0, v.length+1) == v+'-');
5466             },
5467             "~=" : function(a, v){
5468                 return a && (' '+a+' ').indexOf(' '+v+' ') != -1;
5469             }
5470         },
5471
5472         /**
5473          * Collection of "pseudo class" processors. Each processor is passed the current nodeset (array)
5474          * and the argument (if any) supplied in the selector.
5475          */
5476         pseudos : {
5477             "first-child" : function(c){
5478                 var r = [], ri = -1, n;
5479                 for(var i = 0, ci; ci = n = c[i]; i++){
5480                     while((n = n.previousSibling) && n.nodeType != 1);
5481                     if(!n){
5482                         r[++ri] = ci;
5483                     }
5484                 }
5485                 return r;
5486             },
5487
5488             "last-child" : function(c){
5489                 var r = [], ri = -1, n;
5490                 for(var i = 0, ci; ci = n = c[i]; i++){
5491                     while((n = n.nextSibling) && n.nodeType != 1);
5492                     if(!n){
5493                         r[++ri] = ci;
5494                     }
5495                 }
5496                 return r;
5497             },
5498
5499             "nth-child" : function(c, a) {
5500                 var r = [], ri = -1;
5501                 var m = nthRe.exec(a == "even" && "2n" || a == "odd" && "2n+1" || !nthRe2.test(a) && "n+" + a || a);
5502                 var f = (m[1] || 1) - 0, l = m[2] - 0;
5503                 for(var i = 0, n; n = c[i]; i++){
5504                     var pn = n.parentNode;
5505                     if (batch != pn._batch) {
5506                         var j = 0;
5507                         for(var cn = pn.firstChild; cn; cn = cn.nextSibling){
5508                             if(cn.nodeType == 1){
5509                                cn.nodeIndex = ++j;
5510                             }
5511                         }
5512                         pn._batch = batch;
5513                     }
5514                     if (f == 1) {
5515                         if (l == 0 || n.nodeIndex == l){
5516                             r[++ri] = n;
5517                         }
5518                     } else if ((n.nodeIndex + l) % f == 0){
5519                         r[++ri] = n;
5520                     }
5521                 }
5522
5523                 return r;
5524             },
5525
5526             "only-child" : function(c){
5527                 var r = [], ri = -1;;
5528                 for(var i = 0, ci; ci = c[i]; i++){
5529                     if(!prev(ci) && !next(ci)){
5530                         r[++ri] = ci;
5531                     }
5532                 }
5533                 return r;
5534             },
5535
5536             "empty" : function(c){
5537                 var r = [], ri = -1;
5538                 for(var i = 0, ci; ci = c[i]; i++){
5539                     var cns = ci.childNodes, j = 0, cn, empty = true;
5540                     while(cn = cns[j]){
5541                         ++j;
5542                         if(cn.nodeType == 1 || cn.nodeType == 3){
5543                             empty = false;
5544                             break;
5545                         }
5546                     }
5547                     if(empty){
5548                         r[++ri] = ci;
5549                     }
5550                 }
5551                 return r;
5552             },
5553
5554             "contains" : function(c, v){
5555                 var r = [], ri = -1;
5556                 for(var i = 0, ci; ci = c[i]; i++){
5557                     if((ci.textContent||ci.innerText||'').indexOf(v) != -1){
5558                         r[++ri] = ci;
5559                     }
5560                 }
5561                 return r;
5562             },
5563
5564             "nodeValue" : function(c, v){
5565                 var r = [], ri = -1;
5566                 for(var i = 0, ci; ci = c[i]; i++){
5567                     if(ci.firstChild && ci.firstChild.nodeValue == v){
5568                         r[++ri] = ci;
5569                     }
5570                 }
5571                 return r;
5572             },
5573
5574             "checked" : function(c){
5575                 var r = [], ri = -1;
5576                 for(var i = 0, ci; ci = c[i]; i++){
5577                     if(ci.checked == true){
5578                         r[++ri] = ci;
5579                     }
5580                 }
5581                 return r;
5582             },
5583
5584             "not" : function(c, ss){
5585                 return Roo.DomQuery.filter(c, ss, true);
5586             },
5587
5588             "odd" : function(c){
5589                 return this["nth-child"](c, "odd");
5590             },
5591
5592             "even" : function(c){
5593                 return this["nth-child"](c, "even");
5594             },
5595
5596             "nth" : function(c, a){
5597                 return c[a-1] || [];
5598             },
5599
5600             "first" : function(c){
5601                 return c[0] || [];
5602             },
5603
5604             "last" : function(c){
5605                 return c[c.length-1] || [];
5606             },
5607
5608             "has" : function(c, ss){
5609                 var s = Roo.DomQuery.select;
5610                 var r = [], ri = -1;
5611                 for(var i = 0, ci; ci = c[i]; i++){
5612                     if(s(ss, ci).length > 0){
5613                         r[++ri] = ci;
5614                     }
5615                 }
5616                 return r;
5617             },
5618
5619             "next" : function(c, ss){
5620                 var is = Roo.DomQuery.is;
5621                 var r = [], ri = -1;
5622                 for(var i = 0, ci; ci = c[i]; i++){
5623                     var n = next(ci);
5624                     if(n && is(n, ss)){
5625                         r[++ri] = ci;
5626                     }
5627                 }
5628                 return r;
5629             },
5630
5631             "prev" : function(c, ss){
5632                 var is = Roo.DomQuery.is;
5633                 var r = [], ri = -1;
5634                 for(var i = 0, ci; ci = c[i]; i++){
5635                     var n = prev(ci);
5636                     if(n && is(n, ss)){
5637                         r[++ri] = ci;
5638                     }
5639                 }
5640                 return r;
5641             }
5642         }
5643     };
5644 }();
5645
5646 /**
5647  * Selects an array of DOM nodes by CSS/XPath selector. Shorthand of {@link Roo.DomQuery#select}
5648  * @param {String} path The selector/xpath query
5649  * @param {Node} root (optional) The start of the query (defaults to document).
5650  * @return {Array}
5651  * @member Roo
5652  * @method query
5653  */
5654 Roo.query = Roo.DomQuery.select;
5655 /*
5656  * Based on:
5657  * Ext JS Library 1.1.1
5658  * Copyright(c) 2006-2007, Ext JS, LLC.
5659  *
5660  * Originally Released Under LGPL - original licence link has changed is not relivant.
5661  *
5662  * Fork - LGPL
5663  * <script type="text/javascript">
5664  */
5665
5666 /**
5667  * @class Roo.util.Observable
5668  * Base class that provides a common interface for publishing events. Subclasses are expected to
5669  * to have a property "events" with all the events defined.<br>
5670  * For example:
5671  * <pre><code>
5672  Employee = function(name){
5673     this.name = name;
5674     this.addEvents({
5675         "fired" : true,
5676         "quit" : true
5677     });
5678  }
5679  Roo.extend(Employee, Roo.util.Observable);
5680 </code></pre>
5681  * @param {Object} config properties to use (incuding events / listeners)
5682  */
5683
5684 Roo.util.Observable = function(cfg){
5685     
5686     cfg = cfg|| {};
5687     this.addEvents(cfg.events || {});
5688     if (cfg.events) {
5689         delete cfg.events; // make sure
5690     }
5691      
5692     Roo.apply(this, cfg);
5693     
5694     if(this.listeners){
5695         this.on(this.listeners);
5696         delete this.listeners;
5697     }
5698 };
5699 Roo.util.Observable.prototype = {
5700     /** 
5701  * @cfg {Object} listeners  list of events and functions to call for this object, 
5702  * For example :
5703  * <pre><code>
5704     listeners :  { 
5705        'click' : function(e) {
5706            ..... 
5707         } ,
5708         .... 
5709     } 
5710   </code></pre>
5711  */
5712     
5713     
5714     /**
5715      * Fires the specified event with the passed parameters (minus the event name).
5716      * @param {String} eventName
5717      * @param {Object...} args Variable number of parameters are passed to handlers
5718      * @return {Boolean} returns false if any of the handlers return false otherwise it returns true
5719      */
5720     fireEvent : function(){
5721         var ce = this.events[arguments[0].toLowerCase()];
5722         if(typeof ce == "object"){
5723             return ce.fire.apply(ce, Array.prototype.slice.call(arguments, 1));
5724         }else{
5725             return true;
5726         }
5727     },
5728
5729     // private
5730     filterOptRe : /^(?:scope|delay|buffer|single)$/,
5731
5732     /**
5733      * Appends an event handler to this component
5734      * @param {String}   eventName The type of event to listen for
5735      * @param {Function} handler The method the event invokes
5736      * @param {Object}   scope (optional) The scope in which to execute the handler
5737      * function. The handler function's "this" context.
5738      * @param {Object}   options (optional) An object containing handler configuration
5739      * properties. This may contain any of the following properties:<ul>
5740      * <li>scope {Object} The scope in which to execute the handler function. The handler function's "this" context.</li>
5741      * <li>delay {Number} The number of milliseconds to delay the invocation of the handler after te event fires.</li>
5742      * <li>single {Boolean} True to add a handler to handle just the next firing of the event, and then remove itself.</li>
5743      * <li>buffer {Number} Causes the handler to be scheduled to run in an {@link Roo.util.DelayedTask} delayed
5744      * by the specified number of milliseconds. If the event fires again within that time, the original
5745      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>
5746      * </ul><br>
5747      * <p>
5748      * <b>Combining Options</b><br>
5749      * Using the options argument, it is possible to combine different types of listeners:<br>
5750      * <br>
5751      * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)
5752                 <pre><code>
5753                 el.on('click', this.onClick, this, {
5754                         single: true,
5755                 delay: 100,
5756                 forumId: 4
5757                 });
5758                 </code></pre>
5759      * <p>
5760      * <b>Attaching multiple handlers in 1 call</b><br>
5761      * The method also allows for a single argument to be passed which is a config object containing properties
5762      * which specify multiple handlers.
5763      * <pre><code>
5764                 el.on({
5765                         'click': {
5766                         fn: this.onClick,
5767                         scope: this,
5768                         delay: 100
5769                 }, 
5770                 'mouseover': {
5771                         fn: this.onMouseOver,
5772                         scope: this
5773                 },
5774                 'mouseout': {
5775                         fn: this.onMouseOut,
5776                         scope: this
5777                 }
5778                 });
5779                 </code></pre>
5780      * <p>
5781      * Or a shorthand syntax which passes the same scope object to all handlers:
5782         <pre><code>
5783                 el.on({
5784                         'click': this.onClick,
5785                 'mouseover': this.onMouseOver,
5786                 'mouseout': this.onMouseOut,
5787                 scope: this
5788                 });
5789                 </code></pre>
5790      */
5791     addListener : function(eventName, fn, scope, o){
5792         if(typeof eventName == "object"){
5793             o = eventName;
5794             for(var e in o){
5795                 if(this.filterOptRe.test(e)){
5796                     continue;
5797                 }
5798                 if(typeof o[e] == "function"){
5799                     // shared options
5800                     this.addListener(e, o[e], o.scope,  o);
5801                 }else{
5802                     // individual options
5803                     this.addListener(e, o[e].fn, o[e].scope, o[e]);
5804                 }
5805             }
5806             return;
5807         }
5808         o = (!o || typeof o == "boolean") ? {} : o;
5809         eventName = eventName.toLowerCase();
5810         var ce = this.events[eventName] || true;
5811         if(typeof ce == "boolean"){
5812             ce = new Roo.util.Event(this, eventName);
5813             this.events[eventName] = ce;
5814         }
5815         ce.addListener(fn, scope, o);
5816     },
5817
5818     /**
5819      * Removes a listener
5820      * @param {String}   eventName     The type of event to listen for
5821      * @param {Function} handler        The handler to remove
5822      * @param {Object}   scope  (optional) The scope (this object) for the handler
5823      */
5824     removeListener : function(eventName, fn, scope){
5825         var ce = this.events[eventName.toLowerCase()];
5826         if(typeof ce == "object"){
5827             ce.removeListener(fn, scope);
5828         }
5829     },
5830
5831     /**
5832      * Removes all listeners for this object
5833      */
5834     purgeListeners : function(){
5835         for(var evt in this.events){
5836             if(typeof this.events[evt] == "object"){
5837                  this.events[evt].clearListeners();
5838             }
5839         }
5840     },
5841
5842     relayEvents : function(o, events){
5843         var createHandler = function(ename){
5844             return function(){
5845                 return this.fireEvent.apply(this, Roo.combine(ename, Array.prototype.slice.call(arguments, 0)));
5846             };
5847         };
5848         for(var i = 0, len = events.length; i < len; i++){
5849             var ename = events[i];
5850             if(!this.events[ename]){ this.events[ename] = true; };
5851             o.on(ename, createHandler(ename), this);
5852         }
5853     },
5854
5855     /**
5856      * Used to define events on this Observable
5857      * @param {Object} object The object with the events defined
5858      */
5859     addEvents : function(o){
5860         if(!this.events){
5861             this.events = {};
5862         }
5863         Roo.applyIf(this.events, o);
5864     },
5865
5866     /**
5867      * Checks to see if this object has any listeners for a specified event
5868      * @param {String} eventName The name of the event to check for
5869      * @return {Boolean} True if the event is being listened for, else false
5870      */
5871     hasListener : function(eventName){
5872         var e = this.events[eventName];
5873         return typeof e == "object" && e.listeners.length > 0;
5874     }
5875 };
5876 /**
5877  * Appends an event handler to this element (shorthand for addListener)
5878  * @param {String}   eventName     The type of event to listen for
5879  * @param {Function} handler        The method the event invokes
5880  * @param {Object}   scope (optional) The scope in which to execute the handler
5881  * function. The handler function's "this" context.
5882  * @param {Object}   options  (optional)
5883  * @method
5884  */
5885 Roo.util.Observable.prototype.on = Roo.util.Observable.prototype.addListener;
5886 /**
5887  * Removes a listener (shorthand for removeListener)
5888  * @param {String}   eventName     The type of event to listen for
5889  * @param {Function} handler        The handler to remove
5890  * @param {Object}   scope  (optional) The scope (this object) for the handler
5891  * @method
5892  */
5893 Roo.util.Observable.prototype.un = Roo.util.Observable.prototype.removeListener;
5894
5895 /**
5896  * Starts capture on the specified Observable. All events will be passed
5897  * to the supplied function with the event name + standard signature of the event
5898  * <b>before</b> the event is fired. If the supplied function returns false,
5899  * the event will not fire.
5900  * @param {Observable} o The Observable to capture
5901  * @param {Function} fn The function to call
5902  * @param {Object} scope (optional) The scope (this object) for the fn
5903  * @static
5904  */
5905 Roo.util.Observable.capture = function(o, fn, scope){
5906     o.fireEvent = o.fireEvent.createInterceptor(fn, scope);
5907 };
5908
5909 /**
5910  * Removes <b>all</b> added captures from the Observable.
5911  * @param {Observable} o The Observable to release
5912  * @static
5913  */
5914 Roo.util.Observable.releaseCapture = function(o){
5915     o.fireEvent = Roo.util.Observable.prototype.fireEvent;
5916 };
5917
5918 (function(){
5919
5920     var createBuffered = function(h, o, scope){
5921         var task = new Roo.util.DelayedTask();
5922         return function(){
5923             task.delay(o.buffer, h, scope, Array.prototype.slice.call(arguments, 0));
5924         };
5925     };
5926
5927     var createSingle = function(h, e, fn, scope){
5928         return function(){
5929             e.removeListener(fn, scope);
5930             return h.apply(scope, arguments);
5931         };
5932     };
5933
5934     var createDelayed = function(h, o, scope){
5935         return function(){
5936             var args = Array.prototype.slice.call(arguments, 0);
5937             setTimeout(function(){
5938                 h.apply(scope, args);
5939             }, o.delay || 10);
5940         };
5941     };
5942
5943     Roo.util.Event = function(obj, name){
5944         this.name = name;
5945         this.obj = obj;
5946         this.listeners = [];
5947     };
5948
5949     Roo.util.Event.prototype = {
5950         addListener : function(fn, scope, options){
5951             var o = options || {};
5952             scope = scope || this.obj;
5953             if(!this.isListening(fn, scope)){
5954                 var l = {fn: fn, scope: scope, options: o};
5955                 var h = fn;
5956                 if(o.delay){
5957                     h = createDelayed(h, o, scope);
5958                 }
5959                 if(o.single){
5960                     h = createSingle(h, this, fn, scope);
5961                 }
5962                 if(o.buffer){
5963                     h = createBuffered(h, o, scope);
5964                 }
5965                 l.fireFn = h;
5966                 if(!this.firing){ // if we are currently firing this event, don't disturb the listener loop
5967                     this.listeners.push(l);
5968                 }else{
5969                     this.listeners = this.listeners.slice(0);
5970                     this.listeners.push(l);
5971                 }
5972             }
5973         },
5974
5975         findListener : function(fn, scope){
5976             scope = scope || this.obj;
5977             var ls = this.listeners;
5978             for(var i = 0, len = ls.length; i < len; i++){
5979                 var l = ls[i];
5980                 if(l.fn == fn && l.scope == scope){
5981                     return i;
5982                 }
5983             }
5984             return -1;
5985         },
5986
5987         isListening : function(fn, scope){
5988             return this.findListener(fn, scope) != -1;
5989         },
5990
5991         removeListener : function(fn, scope){
5992             var index;
5993             if((index = this.findListener(fn, scope)) != -1){
5994                 if(!this.firing){
5995                     this.listeners.splice(index, 1);
5996                 }else{
5997                     this.listeners = this.listeners.slice(0);
5998                     this.listeners.splice(index, 1);
5999                 }
6000                 return true;
6001             }
6002             return false;
6003         },
6004
6005         clearListeners : function(){
6006             this.listeners = [];
6007         },
6008
6009         fire : function(){
6010             var ls = this.listeners, scope, len = ls.length;
6011             if(len > 0){
6012                 this.firing = true;
6013                 var args = Array.prototype.slice.call(arguments, 0);
6014                 for(var i = 0; i < len; i++){
6015                     var l = ls[i];
6016                     if(l.fireFn.apply(l.scope||this.obj||window, arguments) === false){
6017                         this.firing = false;
6018                         return false;
6019                     }
6020                 }
6021                 this.firing = false;
6022             }
6023             return true;
6024         }
6025     };
6026 })();/*
6027  * Based on:
6028  * Ext JS Library 1.1.1
6029  * Copyright(c) 2006-2007, Ext JS, LLC.
6030  *
6031  * Originally Released Under LGPL - original licence link has changed is not relivant.
6032  *
6033  * Fork - LGPL
6034  * <script type="text/javascript">
6035  */
6036
6037 /**
6038  * @class Roo.EventManager
6039  * Registers event handlers that want to receive a normalized EventObject instead of the standard browser event and provides 
6040  * several useful events directly.
6041  * See {@link Roo.EventObject} for more details on normalized event objects.
6042  * @singleton
6043  */
6044 Roo.EventManager = function(){
6045     var docReadyEvent, docReadyProcId, docReadyState = false;
6046     var resizeEvent, resizeTask, textEvent, textSize;
6047     var E = Roo.lib.Event;
6048     var D = Roo.lib.Dom;
6049
6050     
6051     
6052
6053     var fireDocReady = function(){
6054         if(!docReadyState){
6055             docReadyState = true;
6056             Roo.isReady = true;
6057             if(docReadyProcId){
6058                 clearInterval(docReadyProcId);
6059             }
6060             if(Roo.isGecko || Roo.isOpera) {
6061                 document.removeEventListener("DOMContentLoaded", fireDocReady, false);
6062             }
6063             if(Roo.isIE){
6064                 var defer = document.getElementById("ie-deferred-loader");
6065                 if(defer){
6066                     defer.onreadystatechange = null;
6067                     defer.parentNode.removeChild(defer);
6068                 }
6069             }
6070             if(docReadyEvent){
6071                 docReadyEvent.fire();
6072                 docReadyEvent.clearListeners();
6073             }
6074         }
6075     };
6076     
6077     var initDocReady = function(){
6078         docReadyEvent = new Roo.util.Event();
6079         if(Roo.isGecko || Roo.isOpera) {
6080             document.addEventListener("DOMContentLoaded", fireDocReady, false);
6081         }else if(Roo.isIE){
6082             document.write("<s"+'cript id="ie-deferred-loader" defer="defer" src="/'+'/:"></s'+"cript>");
6083             var defer = document.getElementById("ie-deferred-loader");
6084             defer.onreadystatechange = function(){
6085                 if(this.readyState == "complete"){
6086                     fireDocReady();
6087                 }
6088             };
6089         }else if(Roo.isSafari){ 
6090             docReadyProcId = setInterval(function(){
6091                 var rs = document.readyState;
6092                 if(rs == "complete") {
6093                     fireDocReady();     
6094                  }
6095             }, 10);
6096         }
6097         // no matter what, make sure it fires on load
6098         E.on(window, "load", fireDocReady);
6099     };
6100
6101     var createBuffered = function(h, o){
6102         var task = new Roo.util.DelayedTask(h);
6103         return function(e){
6104             // create new event object impl so new events don't wipe out properties
6105             e = new Roo.EventObjectImpl(e);
6106             task.delay(o.buffer, h, null, [e]);
6107         };
6108     };
6109
6110     var createSingle = function(h, el, ename, fn){
6111         return function(e){
6112             Roo.EventManager.removeListener(el, ename, fn);
6113             h(e);
6114         };
6115     };
6116
6117     var createDelayed = function(h, o){
6118         return function(e){
6119             // create new event object impl so new events don't wipe out properties
6120             e = new Roo.EventObjectImpl(e);
6121             setTimeout(function(){
6122                 h(e);
6123             }, o.delay || 10);
6124         };
6125     };
6126     var transitionEndVal = false;
6127     
6128     var transitionEnd = function()
6129     {
6130         if (transitionEndVal) {
6131             return transitionEndVal;
6132         }
6133         var el = document.createElement('div');
6134
6135         var transEndEventNames = {
6136             WebkitTransition : 'webkitTransitionEnd',
6137             MozTransition    : 'transitionend',
6138             OTransition      : 'oTransitionEnd otransitionend',
6139             transition       : 'transitionend'
6140         };
6141     
6142         for (var name in transEndEventNames) {
6143             if (el.style[name] !== undefined) {
6144                 transitionEndVal = transEndEventNames[name];
6145                 return  transitionEndVal ;
6146             }
6147         }
6148     }
6149     
6150
6151     var listen = function(element, ename, opt, fn, scope){
6152         var o = (!opt || typeof opt == "boolean") ? {} : opt;
6153         fn = fn || o.fn; scope = scope || o.scope;
6154         var el = Roo.getDom(element);
6155         
6156         
6157         if(!el){
6158             throw "Error listening for \"" + ename + '\". Element "' + element + '" doesn\'t exist.';
6159         }
6160         
6161         if (ename == 'transitionend') {
6162             ename = transitionEnd();
6163         }
6164         var h = function(e){
6165             e = Roo.EventObject.setEvent(e);
6166             var t;
6167             if(o.delegate){
6168                 t = e.getTarget(o.delegate, el);
6169                 if(!t){
6170                     return;
6171                 }
6172             }else{
6173                 t = e.target;
6174             }
6175             if(o.stopEvent === true){
6176                 e.stopEvent();
6177             }
6178             if(o.preventDefault === true){
6179                e.preventDefault();
6180             }
6181             if(o.stopPropagation === true){
6182                 e.stopPropagation();
6183             }
6184
6185             if(o.normalized === false){
6186                 e = e.browserEvent;
6187             }
6188
6189             fn.call(scope || el, e, t, o);
6190         };
6191         if(o.delay){
6192             h = createDelayed(h, o);
6193         }
6194         if(o.single){
6195             h = createSingle(h, el, ename, fn);
6196         }
6197         if(o.buffer){
6198             h = createBuffered(h, o);
6199         }
6200         fn._handlers = fn._handlers || [];
6201         
6202         
6203         fn._handlers.push([Roo.id(el), ename, h]);
6204         
6205         
6206          
6207         E.on(el, ename, h);
6208         if(ename == "mousewheel" && el.addEventListener){ // workaround for jQuery
6209             el.addEventListener("DOMMouseScroll", h, false);
6210             E.on(window, 'unload', function(){
6211                 el.removeEventListener("DOMMouseScroll", h, false);
6212             });
6213         }
6214         if(ename == "mousedown" && el == document){ // fix stopped mousedowns on the document
6215             Roo.EventManager.stoppedMouseDownEvent.addListener(h);
6216         }
6217         return h;
6218     };
6219
6220     var stopListening = function(el, ename, fn){
6221         var id = Roo.id(el), hds = fn._handlers, hd = fn;
6222         if(hds){
6223             for(var i = 0, len = hds.length; i < len; i++){
6224                 var h = hds[i];
6225                 if(h[0] == id && h[1] == ename){
6226                     hd = h[2];
6227                     hds.splice(i, 1);
6228                     break;
6229                 }
6230             }
6231         }
6232         E.un(el, ename, hd);
6233         el = Roo.getDom(el);
6234         if(ename == "mousewheel" && el.addEventListener){
6235             el.removeEventListener("DOMMouseScroll", hd, false);
6236         }
6237         if(ename == "mousedown" && el == document){ // fix stopped mousedowns on the document
6238             Roo.EventManager.stoppedMouseDownEvent.removeListener(hd);
6239         }
6240     };
6241
6242     var propRe = /^(?:scope|delay|buffer|single|stopEvent|preventDefault|stopPropagation|normalized|args|delegate)$/;
6243     
6244     var pub = {
6245         
6246         
6247         /** 
6248          * Fix for doc tools
6249          * @scope Roo.EventManager
6250          */
6251         
6252         
6253         /** 
6254          * This is no longer needed and is deprecated. Places a simple wrapper around an event handler to override the browser event
6255          * object with a Roo.EventObject
6256          * @param {Function} fn        The method the event invokes
6257          * @param {Object}   scope    An object that becomes the scope of the handler
6258          * @param {boolean}  override If true, the obj passed in becomes
6259          *                             the execution scope of the listener
6260          * @return {Function} The wrapped function
6261          * @deprecated
6262          */
6263         wrap : function(fn, scope, override){
6264             return function(e){
6265                 Roo.EventObject.setEvent(e);
6266                 fn.call(override ? scope || window : window, Roo.EventObject, scope);
6267             };
6268         },
6269         
6270         /**
6271      * Appends an event handler to an element (shorthand for addListener)
6272      * @param {String/HTMLElement}   element        The html element or id to assign the
6273      * @param {String}   eventName The type of event to listen for
6274      * @param {Function} handler The method the event invokes
6275      * @param {Object}   scope (optional) The scope in which to execute the handler
6276      * function. The handler function's "this" context.
6277      * @param {Object}   options (optional) An object containing handler configuration
6278      * properties. This may contain any of the following properties:<ul>
6279      * <li>scope {Object} The scope in which to execute the handler function. The handler function's "this" context.</li>
6280      * <li>delegate {String} A simple selector to filter the target or look for a descendant of the target</li>
6281      * <li>stopEvent {Boolean} True to stop the event. That is stop propagation, and prevent the default action.</li>
6282      * <li>preventDefault {Boolean} True to prevent the default action</li>
6283      * <li>stopPropagation {Boolean} True to prevent event propagation</li>
6284      * <li>normalized {Boolean} False to pass a browser event to the handler function instead of an Roo.EventObject</li>
6285      * <li>delay {Number} The number of milliseconds to delay the invocation of the handler after te event fires.</li>
6286      * <li>single {Boolean} True to add a handler to handle just the next firing of the event, and then remove itself.</li>
6287      * <li>buffer {Number} Causes the handler to be scheduled to run in an {@link Roo.util.DelayedTask} delayed
6288      * by the specified number of milliseconds. If the event fires again within that time, the original
6289      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>
6290      * </ul><br>
6291      * <p>
6292      * <b>Combining Options</b><br>
6293      * Using the options argument, it is possible to combine different types of listeners:<br>
6294      * <br>
6295      * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)<div style="margin: 5px 20px 20px;">
6296      * Code:<pre><code>
6297 el.on('click', this.onClick, this, {
6298     single: true,
6299     delay: 100,
6300     stopEvent : true,
6301     forumId: 4
6302 });</code></pre>
6303      * <p>
6304      * <b>Attaching multiple handlers in 1 call</b><br>
6305       * The method also allows for a single argument to be passed which is a config object containing properties
6306      * which specify multiple handlers.
6307      * <p>
6308      * Code:<pre><code>
6309 el.on({
6310     'click' : {
6311         fn: this.onClick
6312         scope: this,
6313         delay: 100
6314     },
6315     'mouseover' : {
6316         fn: this.onMouseOver
6317         scope: this
6318     },
6319     'mouseout' : {
6320         fn: this.onMouseOut
6321         scope: this
6322     }
6323 });</code></pre>
6324      * <p>
6325      * Or a shorthand syntax:<br>
6326      * Code:<pre><code>
6327 el.on({
6328     'click' : this.onClick,
6329     'mouseover' : this.onMouseOver,
6330     'mouseout' : this.onMouseOut
6331     scope: this
6332 });</code></pre>
6333      */
6334         addListener : function(element, eventName, fn, scope, options){
6335             if(typeof eventName == "object"){
6336                 var o = eventName;
6337                 for(var e in o){
6338                     if(propRe.test(e)){
6339                         continue;
6340                     }
6341                     if(typeof o[e] == "function"){
6342                         // shared options
6343                         listen(element, e, o, o[e], o.scope);
6344                     }else{
6345                         // individual options
6346                         listen(element, e, o[e]);
6347                     }
6348                 }
6349                 return;
6350             }
6351             return listen(element, eventName, options, fn, scope);
6352         },
6353         
6354         /**
6355          * Removes an event handler
6356          *
6357          * @param {String/HTMLElement}   element        The id or html element to remove the 
6358          *                             event from
6359          * @param {String}   eventName     The type of event
6360          * @param {Function} fn
6361          * @return {Boolean} True if a listener was actually removed
6362          */
6363         removeListener : function(element, eventName, fn){
6364             return stopListening(element, eventName, fn);
6365         },
6366         
6367         /**
6368          * Fires when the document is ready (before onload and before images are loaded). Can be 
6369          * accessed shorthanded Roo.onReady().
6370          * @param {Function} fn        The method the event invokes
6371          * @param {Object}   scope    An  object that becomes the scope of the handler
6372          * @param {boolean}  options
6373          */
6374         onDocumentReady : function(fn, scope, options){
6375             if(docReadyState){ // if it already fired
6376                 docReadyEvent.addListener(fn, scope, options);
6377                 docReadyEvent.fire();
6378                 docReadyEvent.clearListeners();
6379                 return;
6380             }
6381             if(!docReadyEvent){
6382                 initDocReady();
6383             }
6384             docReadyEvent.addListener(fn, scope, options);
6385         },
6386         
6387         /**
6388          * Fires when the window is resized and provides resize event buffering (50 milliseconds), passes new viewport width and height to handlers.
6389          * @param {Function} fn        The method the event invokes
6390          * @param {Object}   scope    An object that becomes the scope of the handler
6391          * @param {boolean}  options
6392          */
6393         onWindowResize : function(fn, scope, options){
6394             if(!resizeEvent){
6395                 resizeEvent = new Roo.util.Event();
6396                 resizeTask = new Roo.util.DelayedTask(function(){
6397                     resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
6398                 });
6399                 E.on(window, "resize", function(){
6400                     if(Roo.isIE){
6401                         resizeTask.delay(50);
6402                     }else{
6403                         resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
6404                     }
6405                 });
6406             }
6407             resizeEvent.addListener(fn, scope, options);
6408         },
6409
6410         /**
6411          * Fires when the user changes the active text size. Handler gets called with 2 params, the old size and the new size.
6412          * @param {Function} fn        The method the event invokes
6413          * @param {Object}   scope    An object that becomes the scope of the handler
6414          * @param {boolean}  options
6415          */
6416         onTextResize : function(fn, scope, options){
6417             if(!textEvent){
6418                 textEvent = new Roo.util.Event();
6419                 var textEl = new Roo.Element(document.createElement('div'));
6420                 textEl.dom.className = 'x-text-resize';
6421                 textEl.dom.innerHTML = 'X';
6422                 textEl.appendTo(document.body);
6423                 textSize = textEl.dom.offsetHeight;
6424                 setInterval(function(){
6425                     if(textEl.dom.offsetHeight != textSize){
6426                         textEvent.fire(textSize, textSize = textEl.dom.offsetHeight);
6427                     }
6428                 }, this.textResizeInterval);
6429             }
6430             textEvent.addListener(fn, scope, options);
6431         },
6432
6433         /**
6434          * Removes the passed window resize listener.
6435          * @param {Function} fn        The method the event invokes
6436          * @param {Object}   scope    The scope of handler
6437          */
6438         removeResizeListener : function(fn, scope){
6439             if(resizeEvent){
6440                 resizeEvent.removeListener(fn, scope);
6441             }
6442         },
6443
6444         // private
6445         fireResize : function(){
6446             if(resizeEvent){
6447                 resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
6448             }   
6449         },
6450         /**
6451          * Url used for onDocumentReady with using SSL (defaults to Roo.SSL_SECURE_URL)
6452          */
6453         ieDeferSrc : false,
6454         /**
6455          * The frequency, in milliseconds, to check for text resize events (defaults to 50)
6456          */
6457         textResizeInterval : 50
6458     };
6459     
6460     /**
6461      * Fix for doc tools
6462      * @scopeAlias pub=Roo.EventManager
6463      */
6464     
6465      /**
6466      * Appends an event handler to an element (shorthand for addListener)
6467      * @param {String/HTMLElement}   element        The html element or id to assign the
6468      * @param {String}   eventName The type of event to listen for
6469      * @param {Function} handler The method the event invokes
6470      * @param {Object}   scope (optional) The scope in which to execute the handler
6471      * function. The handler function's "this" context.
6472      * @param {Object}   options (optional) An object containing handler configuration
6473      * properties. This may contain any of the following properties:<ul>
6474      * <li>scope {Object} The scope in which to execute the handler function. The handler function's "this" context.</li>
6475      * <li>delegate {String} A simple selector to filter the target or look for a descendant of the target</li>
6476      * <li>stopEvent {Boolean} True to stop the event. That is stop propagation, and prevent the default action.</li>
6477      * <li>preventDefault {Boolean} True to prevent the default action</li>
6478      * <li>stopPropagation {Boolean} True to prevent event propagation</li>
6479      * <li>normalized {Boolean} False to pass a browser event to the handler function instead of an Roo.EventObject</li>
6480      * <li>delay {Number} The number of milliseconds to delay the invocation of the handler after te event fires.</li>
6481      * <li>single {Boolean} True to add a handler to handle just the next firing of the event, and then remove itself.</li>
6482      * <li>buffer {Number} Causes the handler to be scheduled to run in an {@link Roo.util.DelayedTask} delayed
6483      * by the specified number of milliseconds. If the event fires again within that time, the original
6484      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>
6485      * </ul><br>
6486      * <p>
6487      * <b>Combining Options</b><br>
6488      * Using the options argument, it is possible to combine different types of listeners:<br>
6489      * <br>
6490      * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)<div style="margin: 5px 20px 20px;">
6491      * Code:<pre><code>
6492 el.on('click', this.onClick, this, {
6493     single: true,
6494     delay: 100,
6495     stopEvent : true,
6496     forumId: 4
6497 });</code></pre>
6498      * <p>
6499      * <b>Attaching multiple handlers in 1 call</b><br>
6500       * The method also allows for a single argument to be passed which is a config object containing properties
6501      * which specify multiple handlers.
6502      * <p>
6503      * Code:<pre><code>
6504 el.on({
6505     'click' : {
6506         fn: this.onClick
6507         scope: this,
6508         delay: 100
6509     },
6510     'mouseover' : {
6511         fn: this.onMouseOver
6512         scope: this
6513     },
6514     'mouseout' : {
6515         fn: this.onMouseOut
6516         scope: this
6517     }
6518 });</code></pre>
6519      * <p>
6520      * Or a shorthand syntax:<br>
6521      * Code:<pre><code>
6522 el.on({
6523     'click' : this.onClick,
6524     'mouseover' : this.onMouseOver,
6525     'mouseout' : this.onMouseOut
6526     scope: this
6527 });</code></pre>
6528      */
6529     pub.on = pub.addListener;
6530     pub.un = pub.removeListener;
6531
6532     pub.stoppedMouseDownEvent = new Roo.util.Event();
6533     return pub;
6534 }();
6535 /**
6536   * Fires when the document is ready (before onload and before images are loaded).  Shorthand of {@link Roo.EventManager#onDocumentReady}.
6537   * @param {Function} fn        The method the event invokes
6538   * @param {Object}   scope    An  object that becomes the scope of the handler
6539   * @param {boolean}  override If true, the obj passed in becomes
6540   *                             the execution scope of the listener
6541   * @member Roo
6542   * @method onReady
6543  */
6544 Roo.onReady = Roo.EventManager.onDocumentReady;
6545
6546 Roo.onReady(function(){
6547     var bd = Roo.get(document.body);
6548     if(!bd){ return; }
6549
6550     var cls = [
6551             Roo.isIE ? "roo-ie"
6552             : Roo.isGecko ? "roo-gecko"
6553             : Roo.isOpera ? "roo-opera"
6554             : Roo.isSafari ? "roo-safari" : ""];
6555
6556     if(Roo.isMac){
6557         cls.push("roo-mac");
6558     }
6559     if(Roo.isLinux){
6560         cls.push("roo-linux");
6561     }
6562     if(Roo.isBorderBox){
6563         cls.push('roo-border-box');
6564     }
6565     if(Roo.isStrict){ // add to the parent to allow for selectors like ".ext-strict .ext-ie"
6566         var p = bd.dom.parentNode;
6567         if(p){
6568             p.className += ' roo-strict';
6569         }
6570     }
6571     bd.addClass(cls.join(' '));
6572 });
6573
6574 /**
6575  * @class Roo.EventObject
6576  * EventObject exposes the Yahoo! UI Event functionality directly on the object
6577  * passed to your event handler. It exists mostly for convenience. It also fixes the annoying null checks automatically to cleanup your code 
6578  * Example:
6579  * <pre><code>
6580  function handleClick(e){ // e is not a standard event object, it is a Roo.EventObject
6581     e.preventDefault();
6582     var target = e.getTarget();
6583     ...
6584  }
6585  var myDiv = Roo.get("myDiv");
6586  myDiv.on("click", handleClick);
6587  //or
6588  Roo.EventManager.on("myDiv", 'click', handleClick);
6589  Roo.EventManager.addListener("myDiv", 'click', handleClick);
6590  </code></pre>
6591  * @singleton
6592  */
6593 Roo.EventObject = function(){
6594     
6595     var E = Roo.lib.Event;
6596     
6597     // safari keypress events for special keys return bad keycodes
6598     var safariKeys = {
6599         63234 : 37, // left
6600         63235 : 39, // right
6601         63232 : 38, // up
6602         63233 : 40, // down
6603         63276 : 33, // page up
6604         63277 : 34, // page down
6605         63272 : 46, // delete
6606         63273 : 36, // home
6607         63275 : 35  // end
6608     };
6609
6610     // normalize button clicks
6611     var btnMap = Roo.isIE ? {1:0,4:1,2:2} :
6612                 (Roo.isSafari ? {1:0,2:1,3:2} : {0:0,1:1,2:2});
6613
6614     Roo.EventObjectImpl = function(e){
6615         if(e){
6616             this.setEvent(e.browserEvent || e);
6617         }
6618     };
6619     Roo.EventObjectImpl.prototype = {
6620         /**
6621          * Used to fix doc tools.
6622          * @scope Roo.EventObject.prototype
6623          */
6624             
6625
6626         
6627         
6628         /** The normal browser event */
6629         browserEvent : null,
6630         /** The button pressed in a mouse event */
6631         button : -1,
6632         /** True if the shift key was down during the event */
6633         shiftKey : false,
6634         /** True if the control key was down during the event */
6635         ctrlKey : false,
6636         /** True if the alt key was down during the event */
6637         altKey : false,
6638
6639         /** Key constant 
6640         * @type Number */
6641         BACKSPACE : 8,
6642         /** Key constant 
6643         * @type Number */
6644         TAB : 9,
6645         /** Key constant 
6646         * @type Number */
6647         RETURN : 13,
6648         /** Key constant 
6649         * @type Number */
6650         ENTER : 13,
6651         /** Key constant 
6652         * @type Number */
6653         SHIFT : 16,
6654         /** Key constant 
6655         * @type Number */
6656         CONTROL : 17,
6657         /** Key constant 
6658         * @type Number */
6659         ESC : 27,
6660         /** Key constant 
6661         * @type Number */
6662         SPACE : 32,
6663         /** Key constant 
6664         * @type Number */
6665         PAGEUP : 33,
6666         /** Key constant 
6667         * @type Number */
6668         PAGEDOWN : 34,
6669         /** Key constant 
6670         * @type Number */
6671         END : 35,
6672         /** Key constant 
6673         * @type Number */
6674         HOME : 36,
6675         /** Key constant 
6676         * @type Number */
6677         LEFT : 37,
6678         /** Key constant 
6679         * @type Number */
6680         UP : 38,
6681         /** Key constant 
6682         * @type Number */
6683         RIGHT : 39,
6684         /** Key constant 
6685         * @type Number */
6686         DOWN : 40,
6687         /** Key constant 
6688         * @type Number */
6689         DELETE : 46,
6690         /** Key constant 
6691         * @type Number */
6692         F5 : 116,
6693
6694            /** @private */
6695         setEvent : function(e){
6696             if(e == this || (e && e.browserEvent)){ // already wrapped
6697                 return e;
6698             }
6699             this.browserEvent = e;
6700             if(e){
6701                 // normalize buttons
6702                 this.button = e.button ? btnMap[e.button] : (e.which ? e.which-1 : -1);
6703                 if(e.type == 'click' && this.button == -1){
6704                     this.button = 0;
6705                 }
6706                 this.type = e.type;
6707                 this.shiftKey = e.shiftKey;
6708                 // mac metaKey behaves like ctrlKey
6709                 this.ctrlKey = e.ctrlKey || e.metaKey;
6710                 this.altKey = e.altKey;
6711                 // in getKey these will be normalized for the mac
6712                 this.keyCode = e.keyCode;
6713                 // keyup warnings on firefox.
6714                 this.charCode = (e.type == 'keyup' || e.type == 'keydown') ? 0 : e.charCode;
6715                 // cache the target for the delayed and or buffered events
6716                 this.target = E.getTarget(e);
6717                 // same for XY
6718                 this.xy = E.getXY(e);
6719             }else{
6720                 this.button = -1;
6721                 this.shiftKey = false;
6722                 this.ctrlKey = false;
6723                 this.altKey = false;
6724                 this.keyCode = 0;
6725                 this.charCode =0;
6726                 this.target = null;
6727                 this.xy = [0, 0];
6728             }
6729             return this;
6730         },
6731
6732         /**
6733          * Stop the event (preventDefault and stopPropagation)
6734          */
6735         stopEvent : function(){
6736             if(this.browserEvent){
6737                 if(this.browserEvent.type == 'mousedown'){
6738                     Roo.EventManager.stoppedMouseDownEvent.fire(this);
6739                 }
6740                 E.stopEvent(this.browserEvent);
6741             }
6742         },
6743
6744         /**
6745          * Prevents the browsers default handling of the event.
6746          */
6747         preventDefault : function(){
6748             if(this.browserEvent){
6749                 E.preventDefault(this.browserEvent);
6750             }
6751         },
6752
6753         /** @private */
6754         isNavKeyPress : function(){
6755             var k = this.keyCode;
6756             k = Roo.isSafari ? (safariKeys[k] || k) : k;
6757             return (k >= 33 && k <= 40) || k == this.RETURN || k == this.TAB || k == this.ESC;
6758         },
6759
6760         isSpecialKey : function(){
6761             var k = this.keyCode;
6762             return (this.type == 'keypress' && this.ctrlKey) || k == 9 || k == 13  || k == 40 || k == 27 ||
6763             (k == 16) || (k == 17) ||
6764             (k >= 18 && k <= 20) ||
6765             (k >= 33 && k <= 35) ||
6766             (k >= 36 && k <= 39) ||
6767             (k >= 44 && k <= 45);
6768         },
6769         /**
6770          * Cancels bubbling of the event.
6771          */
6772         stopPropagation : function(){
6773             if(this.browserEvent){
6774                 if(this.type == 'mousedown'){
6775                     Roo.EventManager.stoppedMouseDownEvent.fire(this);
6776                 }
6777                 E.stopPropagation(this.browserEvent);
6778             }
6779         },
6780
6781         /**
6782          * Gets the key code for the event.
6783          * @return {Number}
6784          */
6785         getCharCode : function(){
6786             return this.charCode || this.keyCode;
6787         },
6788
6789         /**
6790          * Returns a normalized keyCode for the event.
6791          * @return {Number} The key code
6792          */
6793         getKey : function(){
6794             var k = this.keyCode || this.charCode;
6795             return Roo.isSafari ? (safariKeys[k] || k) : k;
6796         },
6797
6798         /**
6799          * Gets the x coordinate of the event.
6800          * @return {Number}
6801          */
6802         getPageX : function(){
6803             return this.xy[0];
6804         },
6805
6806         /**
6807          * Gets the y coordinate of the event.
6808          * @return {Number}
6809          */
6810         getPageY : function(){
6811             return this.xy[1];
6812         },
6813
6814         /**
6815          * Gets the time of the event.
6816          * @return {Number}
6817          */
6818         getTime : function(){
6819             if(this.browserEvent){
6820                 return E.getTime(this.browserEvent);
6821             }
6822             return null;
6823         },
6824
6825         /**
6826          * Gets the page coordinates of the event.
6827          * @return {Array} The xy values like [x, y]
6828          */
6829         getXY : function(){
6830             return this.xy;
6831         },
6832
6833         /**
6834          * Gets the target for the event.
6835          * @param {String} selector (optional) A simple selector to filter the target or look for an ancestor of the target
6836          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
6837                 search as a number or element (defaults to 10 || document.body)
6838          * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
6839          * @return {HTMLelement}
6840          */
6841         getTarget : function(selector, maxDepth, returnEl){
6842             return selector ? Roo.fly(this.target).findParent(selector, maxDepth, returnEl) : this.target;
6843         },
6844         /**
6845          * Gets the related target.
6846          * @return {HTMLElement}
6847          */
6848         getRelatedTarget : function(){
6849             if(this.browserEvent){
6850                 return E.getRelatedTarget(this.browserEvent);
6851             }
6852             return null;
6853         },
6854
6855         /**
6856          * Normalizes mouse wheel delta across browsers
6857          * @return {Number} The delta
6858          */
6859         getWheelDelta : function(){
6860             var e = this.browserEvent;
6861             var delta = 0;
6862             if(e.wheelDelta){ /* IE/Opera. */
6863                 delta = e.wheelDelta/120;
6864             }else if(e.detail){ /* Mozilla case. */
6865                 delta = -e.detail/3;
6866             }
6867             return delta;
6868         },
6869
6870         /**
6871          * Returns true if the control, meta, shift or alt key was pressed during this event.
6872          * @return {Boolean}
6873          */
6874         hasModifier : function(){
6875             return !!((this.ctrlKey || this.altKey) || this.shiftKey);
6876         },
6877
6878         /**
6879          * Returns true if the target of this event equals el or is a child of el
6880          * @param {String/HTMLElement/Element} el
6881          * @param {Boolean} related (optional) true to test if the related target is within el instead of the target
6882          * @return {Boolean}
6883          */
6884         within : function(el, related){
6885             var t = this[related ? "getRelatedTarget" : "getTarget"]();
6886             return t && Roo.fly(el).contains(t);
6887         },
6888
6889         getPoint : function(){
6890             return new Roo.lib.Point(this.xy[0], this.xy[1]);
6891         }
6892     };
6893
6894     return new Roo.EventObjectImpl();
6895 }();
6896             
6897     /*
6898  * Based on:
6899  * Ext JS Library 1.1.1
6900  * Copyright(c) 2006-2007, Ext JS, LLC.
6901  *
6902  * Originally Released Under LGPL - original licence link has changed is not relivant.
6903  *
6904  * Fork - LGPL
6905  * <script type="text/javascript">
6906  */
6907
6908  
6909 // was in Composite Element!??!?!
6910  
6911 (function(){
6912     var D = Roo.lib.Dom;
6913     var E = Roo.lib.Event;
6914     var A = Roo.lib.Anim;
6915
6916     // local style camelizing for speed
6917     var propCache = {};
6918     var camelRe = /(-[a-z])/gi;
6919     var camelFn = function(m, a){ return a.charAt(1).toUpperCase(); };
6920     var view = document.defaultView;
6921
6922 /**
6923  * @class Roo.Element
6924  * Represents an Element in the DOM.<br><br>
6925  * Usage:<br>
6926 <pre><code>
6927 var el = Roo.get("my-div");
6928
6929 // or with getEl
6930 var el = getEl("my-div");
6931
6932 // or with a DOM element
6933 var el = Roo.get(myDivElement);
6934 </code></pre>
6935  * Using Roo.get() or getEl() instead of calling the constructor directly ensures you get the same object
6936  * each call instead of constructing a new one.<br><br>
6937  * <b>Animations</b><br />
6938  * Many of the functions for manipulating an element have an optional "animate" parameter. The animate parameter
6939  * should either be a boolean (true) or an object literal with animation options. The animation options are:
6940 <pre>
6941 Option    Default   Description
6942 --------- --------  ---------------------------------------------
6943 duration  .35       The duration of the animation in seconds
6944 easing    easeOut   The YUI easing method
6945 callback  none      A function to execute when the anim completes
6946 scope     this      The scope (this) of the callback function
6947 </pre>
6948 * Also, the Anim object being used for the animation will be set on your options object as "anim", which allows you to stop or
6949 * manipulate the animation. Here's an example:
6950 <pre><code>
6951 var el = Roo.get("my-div");
6952
6953 // no animation
6954 el.setWidth(100);
6955
6956 // default animation
6957 el.setWidth(100, true);
6958
6959 // animation with some options set
6960 el.setWidth(100, {
6961     duration: 1,
6962     callback: this.foo,
6963     scope: this
6964 });
6965
6966 // using the "anim" property to get the Anim object
6967 var opt = {
6968     duration: 1,
6969     callback: this.foo,
6970     scope: this
6971 };
6972 el.setWidth(100, opt);
6973 ...
6974 if(opt.anim.isAnimated()){
6975     opt.anim.stop();
6976 }
6977 </code></pre>
6978 * <b> Composite (Collections of) Elements</b><br />
6979  * For working with collections of Elements, see <a href="Roo.CompositeElement.html">Roo.CompositeElement</a>
6980  * @constructor Create a new Element directly.
6981  * @param {String/HTMLElement} element
6982  * @param {Boolean} forceNew (optional) By default the constructor checks to see if there is already an instance of this element in the cache and if there is it returns the same instance. This will skip that check (useful for extending this class).
6983  */
6984     Roo.Element = function(element, forceNew){
6985         var dom = typeof element == "string" ?
6986                 document.getElementById(element) : element;
6987         if(!dom){ // invalid id/element
6988             return null;
6989         }
6990         var id = dom.id;
6991         if(forceNew !== true && id && Roo.Element.cache[id]){ // element object already exists
6992             return Roo.Element.cache[id];
6993         }
6994
6995         /**
6996          * The DOM element
6997          * @type HTMLElement
6998          */
6999         this.dom = dom;
7000
7001         /**
7002          * The DOM element ID
7003          * @type String
7004          */
7005         this.id = id || Roo.id(dom);
7006     };
7007
7008     var El = Roo.Element;
7009
7010     El.prototype = {
7011         /**
7012          * The element's default display mode  (defaults to "")
7013          * @type String
7014          */
7015         originalDisplay : "",
7016
7017         visibilityMode : 1,
7018         /**
7019          * The default unit to append to CSS values where a unit isn't provided (defaults to px).
7020          * @type String
7021          */
7022         defaultUnit : "px",
7023         /**
7024          * Sets the element's visibility mode. When setVisible() is called it
7025          * will use this to determine whether to set the visibility or the display property.
7026          * @param visMode Element.VISIBILITY or Element.DISPLAY
7027          * @return {Roo.Element} this
7028          */
7029         setVisibilityMode : function(visMode){
7030             this.visibilityMode = visMode;
7031             return this;
7032         },
7033         /**
7034          * Convenience method for setVisibilityMode(Element.DISPLAY)
7035          * @param {String} display (optional) What to set display to when visible
7036          * @return {Roo.Element} this
7037          */
7038         enableDisplayMode : function(display){
7039             this.setVisibilityMode(El.DISPLAY);
7040             if(typeof display != "undefined") this.originalDisplay = display;
7041             return this;
7042         },
7043
7044         /**
7045          * Looks at this node and then at parent nodes for a match of the passed simple selector (e.g. div.some-class or span:first-child)
7046          * @param {String} selector The simple selector to test
7047          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
7048                 search as a number or element (defaults to 10 || document.body)
7049          * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
7050          * @return {HTMLElement} The matching DOM node (or null if no match was found)
7051          */
7052         findParent : function(simpleSelector, maxDepth, returnEl){
7053             var p = this.dom, b = document.body, depth = 0, dq = Roo.DomQuery, stopEl;
7054             maxDepth = maxDepth || 50;
7055             if(typeof maxDepth != "number"){
7056                 stopEl = Roo.getDom(maxDepth);
7057                 maxDepth = 10;
7058             }
7059             while(p && p.nodeType == 1 && depth < maxDepth && p != b && p != stopEl){
7060                 if(dq.is(p, simpleSelector)){
7061                     return returnEl ? Roo.get(p) : p;
7062                 }
7063                 depth++;
7064                 p = p.parentNode;
7065             }
7066             return null;
7067         },
7068
7069
7070         /**
7071          * Looks at parent nodes for a match of the passed simple selector (e.g. div.some-class or span:first-child)
7072          * @param {String} selector The simple selector to test
7073          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
7074                 search as a number or element (defaults to 10 || document.body)
7075          * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
7076          * @return {HTMLElement} The matching DOM node (or null if no match was found)
7077          */
7078         findParentNode : function(simpleSelector, maxDepth, returnEl){
7079             var p = Roo.fly(this.dom.parentNode, '_internal');
7080             return p ? p.findParent(simpleSelector, maxDepth, returnEl) : null;
7081         },
7082
7083         /**
7084          * Walks up the dom looking for a parent node that matches the passed simple selector (e.g. div.some-class or span:first-child).
7085          * This is a shortcut for findParentNode() that always returns an Roo.Element.
7086          * @param {String} selector The simple selector to test
7087          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
7088                 search as a number or element (defaults to 10 || document.body)
7089          * @return {Roo.Element} The matching DOM node (or null if no match was found)
7090          */
7091         up : function(simpleSelector, maxDepth){
7092             return this.findParentNode(simpleSelector, maxDepth, true);
7093         },
7094
7095
7096
7097         /**
7098          * Returns true if this element matches the passed simple selector (e.g. div.some-class or span:first-child)
7099          * @param {String} selector The simple selector to test
7100          * @return {Boolean} True if this element matches the selector, else false
7101          */
7102         is : function(simpleSelector){
7103             return Roo.DomQuery.is(this.dom, simpleSelector);
7104         },
7105
7106         /**
7107          * Perform animation on this element.
7108          * @param {Object} args The YUI animation control args
7109          * @param {Float} duration (optional) How long the animation lasts in seconds (defaults to .35)
7110          * @param {Function} onComplete (optional) Function to call when animation completes
7111          * @param {String} easing (optional) Easing method to use (defaults to 'easeOut')
7112          * @param {String} animType (optional) 'run' is the default. Can also be 'color', 'motion', or 'scroll'
7113          * @return {Roo.Element} this
7114          */
7115         animate : function(args, duration, onComplete, easing, animType){
7116             this.anim(args, {duration: duration, callback: onComplete, easing: easing}, animType);
7117             return this;
7118         },
7119
7120         /*
7121          * @private Internal animation call
7122          */
7123         anim : function(args, opt, animType, defaultDur, defaultEase, cb){
7124             animType = animType || 'run';
7125             opt = opt || {};
7126             var anim = Roo.lib.Anim[animType](
7127                 this.dom, args,
7128                 (opt.duration || defaultDur) || .35,
7129                 (opt.easing || defaultEase) || 'easeOut',
7130                 function(){
7131                     Roo.callback(cb, this);
7132                     Roo.callback(opt.callback, opt.scope || this, [this, opt]);
7133                 },
7134                 this
7135             );
7136             opt.anim = anim;
7137             return anim;
7138         },
7139
7140         // private legacy anim prep
7141         preanim : function(a, i){
7142             return !a[i] ? false : (typeof a[i] == "object" ? a[i]: {duration: a[i+1], callback: a[i+2], easing: a[i+3]});
7143         },
7144
7145         /**
7146          * Removes worthless text nodes
7147          * @param {Boolean} forceReclean (optional) By default the element
7148          * keeps track if it has been cleaned already so
7149          * you can call this over and over. However, if you update the element and
7150          * need to force a reclean, you can pass true.
7151          */
7152         clean : function(forceReclean){
7153             if(this.isCleaned && forceReclean !== true){
7154                 return this;
7155             }
7156             var ns = /\S/;
7157             var d = this.dom, n = d.firstChild, ni = -1;
7158             while(n){
7159                 var nx = n.nextSibling;
7160                 if(n.nodeType == 3 && !ns.test(n.nodeValue)){
7161                     d.removeChild(n);
7162                 }else{
7163                     n.nodeIndex = ++ni;
7164                 }
7165                 n = nx;
7166             }
7167             this.isCleaned = true;
7168             return this;
7169         },
7170
7171         // private
7172         calcOffsetsTo : function(el){
7173             el = Roo.get(el);
7174             var d = el.dom;
7175             var restorePos = false;
7176             if(el.getStyle('position') == 'static'){
7177                 el.position('relative');
7178                 restorePos = true;
7179             }
7180             var x = 0, y =0;
7181             var op = this.dom;
7182             while(op && op != d && op.tagName != 'HTML'){
7183                 x+= op.offsetLeft;
7184                 y+= op.offsetTop;
7185                 op = op.offsetParent;
7186             }
7187             if(restorePos){
7188                 el.position('static');
7189             }
7190             return [x, y];
7191         },
7192
7193         /**
7194          * Scrolls this element into view within the passed container.
7195          * @param {String/HTMLElement/Element} container (optional) The container element to scroll (defaults to document.body)
7196          * @param {Boolean} hscroll (optional) False to disable horizontal scroll (defaults to true)
7197          * @return {Roo.Element} this
7198          */
7199         scrollIntoView : function(container, hscroll){
7200             var c = Roo.getDom(container) || document.body;
7201             var el = this.dom;
7202
7203             var o = this.calcOffsetsTo(c),
7204                 l = o[0],
7205                 t = o[1],
7206                 b = t+el.offsetHeight,
7207                 r = l+el.offsetWidth;
7208
7209             var ch = c.clientHeight;
7210             var ct = parseInt(c.scrollTop, 10);
7211             var cl = parseInt(c.scrollLeft, 10);
7212             var cb = ct + ch;
7213             var cr = cl + c.clientWidth;
7214
7215             if(t < ct){
7216                 c.scrollTop = t;
7217             }else if(b > cb){
7218                 c.scrollTop = b-ch;
7219             }
7220
7221             if(hscroll !== false){
7222                 if(l < cl){
7223                     c.scrollLeft = l;
7224                 }else if(r > cr){
7225                     c.scrollLeft = r-c.clientWidth;
7226                 }
7227             }
7228             return this;
7229         },
7230
7231         // private
7232         scrollChildIntoView : function(child, hscroll){
7233             Roo.fly(child, '_scrollChildIntoView').scrollIntoView(this, hscroll);
7234         },
7235
7236         /**
7237          * Measures the element's content height and updates height to match. Note: this function uses setTimeout so
7238          * the new height may not be available immediately.
7239          * @param {Boolean} animate (optional) Animate the transition (defaults to false)
7240          * @param {Float} duration (optional) Length of the animation in seconds (defaults to .35)
7241          * @param {Function} onComplete (optional) Function to call when animation completes
7242          * @param {String} easing (optional) Easing method to use (defaults to easeOut)
7243          * @return {Roo.Element} this
7244          */
7245         autoHeight : function(animate, duration, onComplete, easing){
7246             var oldHeight = this.getHeight();
7247             this.clip();
7248             this.setHeight(1); // force clipping
7249             setTimeout(function(){
7250                 var height = parseInt(this.dom.scrollHeight, 10); // parseInt for Safari
7251                 if(!animate){
7252                     this.setHeight(height);
7253                     this.unclip();
7254                     if(typeof onComplete == "function"){
7255                         onComplete();
7256                     }
7257                 }else{
7258                     this.setHeight(oldHeight); // restore original height
7259                     this.setHeight(height, animate, duration, function(){
7260                         this.unclip();
7261                         if(typeof onComplete == "function") onComplete();
7262                     }.createDelegate(this), easing);
7263                 }
7264             }.createDelegate(this), 0);
7265             return this;
7266         },
7267
7268         /**
7269          * Returns true if this element is an ancestor of the passed element
7270          * @param {HTMLElement/String} el The element to check
7271          * @return {Boolean} True if this element is an ancestor of el, else false
7272          */
7273         contains : function(el){
7274             if(!el){return false;}
7275             return D.isAncestor(this.dom, el.dom ? el.dom : el);
7276         },
7277
7278         /**
7279          * Checks whether the element is currently visible using both visibility and display properties.
7280          * @param {Boolean} deep (optional) True to walk the dom and see if parent elements are hidden (defaults to false)
7281          * @return {Boolean} True if the element is currently visible, else false
7282          */
7283         isVisible : function(deep) {
7284             var vis = !(this.getStyle("visibility") == "hidden" || this.getStyle("display") == "none");
7285             if(deep !== true || !vis){
7286                 return vis;
7287             }
7288             var p = this.dom.parentNode;
7289             while(p && p.tagName.toLowerCase() != "body"){
7290                 if(!Roo.fly(p, '_isVisible').isVisible()){
7291                     return false;
7292                 }
7293                 p = p.parentNode;
7294             }
7295             return true;
7296         },
7297
7298         /**
7299          * Creates a {@link Roo.CompositeElement} for child nodes based on the passed CSS selector (the selector should not contain an id).
7300          * @param {String} selector The CSS selector
7301          * @param {Boolean} unique (optional) True to create a unique Roo.Element for each child (defaults to false, which creates a single shared flyweight object)
7302          * @return {CompositeElement/CompositeElementLite} The composite element
7303          */
7304         select : function(selector, unique){
7305             return El.select(selector, unique, this.dom);
7306         },
7307
7308         /**
7309          * Selects child nodes based on the passed CSS selector (the selector should not contain an id).
7310          * @param {String} selector The CSS selector
7311          * @return {Array} An array of the matched nodes
7312          */
7313         query : function(selector, unique){
7314             return Roo.DomQuery.select(selector, this.dom);
7315         },
7316
7317         /**
7318          * Selects a single child at any depth below this element based on the passed CSS selector (the selector should not contain an id).
7319          * @param {String} selector The CSS selector
7320          * @param {Boolean} returnDom (optional) True to return the DOM node instead of Roo.Element (defaults to false)
7321          * @return {HTMLElement/Roo.Element} The child Roo.Element (or DOM node if returnDom = true)
7322          */
7323         child : function(selector, returnDom){
7324             var n = Roo.DomQuery.selectNode(selector, this.dom);
7325             return returnDom ? n : Roo.get(n);
7326         },
7327
7328         /**
7329          * Selects a single *direct* child based on the passed CSS selector (the selector should not contain an id).
7330          * @param {String} selector The CSS selector
7331          * @param {Boolean} returnDom (optional) True to return the DOM node instead of Roo.Element (defaults to false)
7332          * @return {HTMLElement/Roo.Element} The child Roo.Element (or DOM node if returnDom = true)
7333          */
7334         down : function(selector, returnDom){
7335             var n = Roo.DomQuery.selectNode(" > " + selector, this.dom);
7336             return returnDom ? n : Roo.get(n);
7337         },
7338
7339         /**
7340          * Initializes a {@link Roo.dd.DD} drag drop object for this element.
7341          * @param {String} group The group the DD object is member of
7342          * @param {Object} config The DD config object
7343          * @param {Object} overrides An object containing methods to override/implement on the DD object
7344          * @return {Roo.dd.DD} The DD object
7345          */
7346         initDD : function(group, config, overrides){
7347             var dd = new Roo.dd.DD(Roo.id(this.dom), group, config);
7348             return Roo.apply(dd, overrides);
7349         },
7350
7351         /**
7352          * Initializes a {@link Roo.dd.DDProxy} object for this element.
7353          * @param {String} group The group the DDProxy object is member of
7354          * @param {Object} config The DDProxy config object
7355          * @param {Object} overrides An object containing methods to override/implement on the DDProxy object
7356          * @return {Roo.dd.DDProxy} The DDProxy object
7357          */
7358         initDDProxy : function(group, config, overrides){
7359             var dd = new Roo.dd.DDProxy(Roo.id(this.dom), group, config);
7360             return Roo.apply(dd, overrides);
7361         },
7362
7363         /**
7364          * Initializes a {@link Roo.dd.DDTarget} object for this element.
7365          * @param {String} group The group the DDTarget object is member of
7366          * @param {Object} config The DDTarget config object
7367          * @param {Object} overrides An object containing methods to override/implement on the DDTarget object
7368          * @return {Roo.dd.DDTarget} The DDTarget object
7369          */
7370         initDDTarget : function(group, config, overrides){
7371             var dd = new Roo.dd.DDTarget(Roo.id(this.dom), group, config);
7372             return Roo.apply(dd, overrides);
7373         },
7374
7375         /**
7376          * Sets the visibility of the element (see details). If the visibilityMode is set to Element.DISPLAY, it will use
7377          * the display property to hide the element, otherwise it uses visibility. The default is to hide and show using the visibility property.
7378          * @param {Boolean} visible Whether the element is visible
7379          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7380          * @return {Roo.Element} this
7381          */
7382          setVisible : function(visible, animate){
7383             if(!animate || !A){
7384                 if(this.visibilityMode == El.DISPLAY){
7385                     this.setDisplayed(visible);
7386                 }else{
7387                     this.fixDisplay();
7388                     this.dom.style.visibility = visible ? "visible" : "hidden";
7389                 }
7390             }else{
7391                 // closure for composites
7392                 var dom = this.dom;
7393                 var visMode = this.visibilityMode;
7394                 if(visible){
7395                     this.setOpacity(.01);
7396                     this.setVisible(true);
7397                 }
7398                 this.anim({opacity: { to: (visible?1:0) }},
7399                       this.preanim(arguments, 1),
7400                       null, .35, 'easeIn', function(){
7401                          if(!visible){
7402                              if(visMode == El.DISPLAY){
7403                                  dom.style.display = "none";
7404                              }else{
7405                                  dom.style.visibility = "hidden";
7406                              }
7407                              Roo.get(dom).setOpacity(1);
7408                          }
7409                      });
7410             }
7411             return this;
7412         },
7413
7414         /**
7415          * Returns true if display is not "none"
7416          * @return {Boolean}
7417          */
7418         isDisplayed : function() {
7419             return this.getStyle("display") != "none";
7420         },
7421
7422         /**
7423          * Toggles the element's visibility or display, depending on visibility mode.
7424          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7425          * @return {Roo.Element} this
7426          */
7427         toggle : function(animate){
7428             this.setVisible(!this.isVisible(), this.preanim(arguments, 0));
7429             return this;
7430         },
7431
7432         /**
7433          * Sets the CSS display property. Uses originalDisplay if the specified value is a boolean true.
7434          * @param {Boolean} value Boolean value to display the element using its default display, or a string to set the display directly
7435          * @return {Roo.Element} this
7436          */
7437         setDisplayed : function(value) {
7438             if(typeof value == "boolean"){
7439                value = value ? this.originalDisplay : "none";
7440             }
7441             this.setStyle("display", value);
7442             return this;
7443         },
7444
7445         /**
7446          * Tries to focus the element. Any exceptions are caught and ignored.
7447          * @return {Roo.Element} this
7448          */
7449         focus : function() {
7450             try{
7451                 this.dom.focus();
7452             }catch(e){}
7453             return this;
7454         },
7455
7456         /**
7457          * Tries to blur the element. Any exceptions are caught and ignored.
7458          * @return {Roo.Element} this
7459          */
7460         blur : function() {
7461             try{
7462                 this.dom.blur();
7463             }catch(e){}
7464             return this;
7465         },
7466
7467         /**
7468          * Adds one or more CSS classes to the element. Duplicate classes are automatically filtered out.
7469          * @param {String/Array} className The CSS class to add, or an array of classes
7470          * @return {Roo.Element} this
7471          */
7472         addClass : function(className){
7473             if(className instanceof Array){
7474                 for(var i = 0, len = className.length; i < len; i++) {
7475                     this.addClass(className[i]);
7476                 }
7477             }else{
7478                 if(className && !this.hasClass(className)){
7479                     this.dom.className = this.dom.className + " " + className;
7480                 }
7481             }
7482             return this;
7483         },
7484
7485         /**
7486          * Adds one or more CSS classes to this element and removes the same class(es) from all siblings.
7487          * @param {String/Array} className The CSS class to add, or an array of classes
7488          * @return {Roo.Element} this
7489          */
7490         radioClass : function(className){
7491             var siblings = this.dom.parentNode.childNodes;
7492             for(var i = 0; i < siblings.length; i++) {
7493                 var s = siblings[i];
7494                 if(s.nodeType == 1){
7495                     Roo.get(s).removeClass(className);
7496                 }
7497             }
7498             this.addClass(className);
7499             return this;
7500         },
7501
7502         /**
7503          * Removes one or more CSS classes from the element.
7504          * @param {String/Array} className The CSS class to remove, or an array of classes
7505          * @return {Roo.Element} this
7506          */
7507         removeClass : function(className){
7508             if(!className || !this.dom.className){
7509                 return this;
7510             }
7511             if(className instanceof Array){
7512                 for(var i = 0, len = className.length; i < len; i++) {
7513                     this.removeClass(className[i]);
7514                 }
7515             }else{
7516                 if(this.hasClass(className)){
7517                     var re = this.classReCache[className];
7518                     if (!re) {
7519                        re = new RegExp('(?:^|\\s+)' + className + '(?:\\s+|$)', "g");
7520                        this.classReCache[className] = re;
7521                     }
7522                     this.dom.className =
7523                         this.dom.className.replace(re, " ");
7524                 }
7525             }
7526             return this;
7527         },
7528
7529         // private
7530         classReCache: {},
7531
7532         /**
7533          * Toggles the specified CSS class on this element (removes it if it already exists, otherwise adds it).
7534          * @param {String} className The CSS class to toggle
7535          * @return {Roo.Element} this
7536          */
7537         toggleClass : function(className){
7538             if(this.hasClass(className)){
7539                 this.removeClass(className);
7540             }else{
7541                 this.addClass(className);
7542             }
7543             return this;
7544         },
7545
7546         /**
7547          * Checks if the specified CSS class exists on this element's DOM node.
7548          * @param {String} className The CSS class to check for
7549          * @return {Boolean} True if the class exists, else false
7550          */
7551         hasClass : function(className){
7552             return className && (' '+this.dom.className+' ').indexOf(' '+className+' ') != -1;
7553         },
7554
7555         /**
7556          * Replaces a CSS class on the element with another.  If the old name does not exist, the new name will simply be added.
7557          * @param {String} oldClassName The CSS class to replace
7558          * @param {String} newClassName The replacement CSS class
7559          * @return {Roo.Element} this
7560          */
7561         replaceClass : function(oldClassName, newClassName){
7562             this.removeClass(oldClassName);
7563             this.addClass(newClassName);
7564             return this;
7565         },
7566
7567         /**
7568          * Returns an object with properties matching the styles requested.
7569          * For example, el.getStyles('color', 'font-size', 'width') might return
7570          * {'color': '#FFFFFF', 'font-size': '13px', 'width': '100px'}.
7571          * @param {String} style1 A style name
7572          * @param {String} style2 A style name
7573          * @param {String} etc.
7574          * @return {Object} The style object
7575          */
7576         getStyles : function(){
7577             var a = arguments, len = a.length, r = {};
7578             for(var i = 0; i < len; i++){
7579                 r[a[i]] = this.getStyle(a[i]);
7580             }
7581             return r;
7582         },
7583
7584         /**
7585          * Normalizes currentStyle and computedStyle. This is not YUI getStyle, it is an optimised version.
7586          * @param {String} property The style property whose value is returned.
7587          * @return {String} The current value of the style property for this element.
7588          */
7589         getStyle : function(){
7590             return view && view.getComputedStyle ?
7591                 function(prop){
7592                     var el = this.dom, v, cs, camel;
7593                     if(prop == 'float'){
7594                         prop = "cssFloat";
7595                     }
7596                     if(el.style && (v = el.style[prop])){
7597                         return v;
7598                     }
7599                     if(cs = view.getComputedStyle(el, "")){
7600                         if(!(camel = propCache[prop])){
7601                             camel = propCache[prop] = prop.replace(camelRe, camelFn);
7602                         }
7603                         return cs[camel];
7604                     }
7605                     return null;
7606                 } :
7607                 function(prop){
7608                     var el = this.dom, v, cs, camel;
7609                     if(prop == 'opacity'){
7610                         if(typeof el.style.filter == 'string'){
7611                             var m = el.style.filter.match(/alpha\(opacity=(.*)\)/i);
7612                             if(m){
7613                                 var fv = parseFloat(m[1]);
7614                                 if(!isNaN(fv)){
7615                                     return fv ? fv / 100 : 0;
7616                                 }
7617                             }
7618                         }
7619                         return 1;
7620                     }else if(prop == 'float'){
7621                         prop = "styleFloat";
7622                     }
7623                     if(!(camel = propCache[prop])){
7624                         camel = propCache[prop] = prop.replace(camelRe, camelFn);
7625                     }
7626                     if(v = el.style[camel]){
7627                         return v;
7628                     }
7629                     if(cs = el.currentStyle){
7630                         return cs[camel];
7631                     }
7632                     return null;
7633                 };
7634         }(),
7635
7636         /**
7637          * Wrapper for setting style properties, also takes single object parameter of multiple styles.
7638          * @param {String/Object} property The style property to be set, or an object of multiple styles.
7639          * @param {String} value (optional) The value to apply to the given property, or null if an object was passed.
7640          * @return {Roo.Element} this
7641          */
7642         setStyle : function(prop, value){
7643             if(typeof prop == "string"){
7644                 
7645                 if (prop == 'float') {
7646                     this.setStyle(Roo.isIE ? 'styleFloat'  : 'cssFloat', value);
7647                     return this;
7648                 }
7649                 
7650                 var camel;
7651                 if(!(camel = propCache[prop])){
7652                     camel = propCache[prop] = prop.replace(camelRe, camelFn);
7653                 }
7654                 
7655                 if(camel == 'opacity') {
7656                     this.setOpacity(value);
7657                 }else{
7658                     this.dom.style[camel] = value;
7659                 }
7660             }else{
7661                 for(var style in prop){
7662                     if(typeof prop[style] != "function"){
7663                        this.setStyle(style, prop[style]);
7664                     }
7665                 }
7666             }
7667             return this;
7668         },
7669
7670         /**
7671          * More flexible version of {@link #setStyle} for setting style properties.
7672          * @param {String/Object/Function} styles A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
7673          * a function which returns such a specification.
7674          * @return {Roo.Element} this
7675          */
7676         applyStyles : function(style){
7677             Roo.DomHelper.applyStyles(this.dom, style);
7678             return this;
7679         },
7680
7681         /**
7682           * Gets the current X position of the element based on page coordinates.  Element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7683           * @return {Number} The X position of the element
7684           */
7685         getX : function(){
7686             return D.getX(this.dom);
7687         },
7688
7689         /**
7690           * Gets the current Y position of the element based on page coordinates.  Element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7691           * @return {Number} The Y position of the element
7692           */
7693         getY : function(){
7694             return D.getY(this.dom);
7695         },
7696
7697         /**
7698           * Gets the current position of the element based on page coordinates.  Element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7699           * @return {Array} The XY position of the element
7700           */
7701         getXY : function(){
7702             return D.getXY(this.dom);
7703         },
7704
7705         /**
7706          * Sets the X position of the element based on page coordinates.  Element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7707          * @param {Number} The X position of the element
7708          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7709          * @return {Roo.Element} this
7710          */
7711         setX : function(x, animate){
7712             if(!animate || !A){
7713                 D.setX(this.dom, x);
7714             }else{
7715                 this.setXY([x, this.getY()], this.preanim(arguments, 1));
7716             }
7717             return this;
7718         },
7719
7720         /**
7721          * Sets the Y position of the element based on page coordinates.  Element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7722          * @param {Number} The Y position of the element
7723          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7724          * @return {Roo.Element} this
7725          */
7726         setY : function(y, animate){
7727             if(!animate || !A){
7728                 D.setY(this.dom, y);
7729             }else{
7730                 this.setXY([this.getX(), y], this.preanim(arguments, 1));
7731             }
7732             return this;
7733         },
7734
7735         /**
7736          * Sets the element's left position directly using CSS style (instead of {@link #setX}).
7737          * @param {String} left The left CSS property value
7738          * @return {Roo.Element} this
7739          */
7740         setLeft : function(left){
7741             this.setStyle("left", this.addUnits(left));
7742             return this;
7743         },
7744
7745         /**
7746          * Sets the element's top position directly using CSS style (instead of {@link #setY}).
7747          * @param {String} top The top CSS property value
7748          * @return {Roo.Element} this
7749          */
7750         setTop : function(top){
7751             this.setStyle("top", this.addUnits(top));
7752             return this;
7753         },
7754
7755         /**
7756          * Sets the element's CSS right style.
7757          * @param {String} right The right CSS property value
7758          * @return {Roo.Element} this
7759          */
7760         setRight : function(right){
7761             this.setStyle("right", this.addUnits(right));
7762             return this;
7763         },
7764
7765         /**
7766          * Sets the element's CSS bottom style.
7767          * @param {String} bottom The bottom CSS property value
7768          * @return {Roo.Element} this
7769          */
7770         setBottom : function(bottom){
7771             this.setStyle("bottom", this.addUnits(bottom));
7772             return this;
7773         },
7774
7775         /**
7776          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
7777          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7778          * @param {Array} pos Contains X & Y [x, y] values for new position (coordinates are page-based)
7779          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7780          * @return {Roo.Element} this
7781          */
7782         setXY : function(pos, animate){
7783             if(!animate || !A){
7784                 D.setXY(this.dom, pos);
7785             }else{
7786                 this.anim({points: {to: pos}}, this.preanim(arguments, 1), 'motion');
7787             }
7788             return this;
7789         },
7790
7791         /**
7792          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
7793          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7794          * @param {Number} x X value for new position (coordinates are page-based)
7795          * @param {Number} y Y value for new position (coordinates are page-based)
7796          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7797          * @return {Roo.Element} this
7798          */
7799         setLocation : function(x, y, animate){
7800             this.setXY([x, y], this.preanim(arguments, 2));
7801             return this;
7802         },
7803
7804         /**
7805          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
7806          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7807          * @param {Number} x X value for new position (coordinates are page-based)
7808          * @param {Number} y Y value for new position (coordinates are page-based)
7809          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7810          * @return {Roo.Element} this
7811          */
7812         moveTo : function(x, y, animate){
7813             this.setXY([x, y], this.preanim(arguments, 2));
7814             return this;
7815         },
7816
7817         /**
7818          * Returns the region of the given element.
7819          * The element must be part of the DOM tree to have a region (display:none or elements not appended return false).
7820          * @return {Region} A Roo.lib.Region containing "top, left, bottom, right" member data.
7821          */
7822         getRegion : function(){
7823             return D.getRegion(this.dom);
7824         },
7825
7826         /**
7827          * Returns the offset height of the element
7828          * @param {Boolean} contentHeight (optional) true to get the height minus borders and padding
7829          * @return {Number} The element's height
7830          */
7831         getHeight : function(contentHeight){
7832             var h = this.dom.offsetHeight || 0;
7833             return contentHeight !== true ? h : h-this.getBorderWidth("tb")-this.getPadding("tb");
7834         },
7835
7836         /**
7837          * Returns the offset width of the element
7838          * @param {Boolean} contentWidth (optional) true to get the width minus borders and padding
7839          * @return {Number} The element's width
7840          */
7841         getWidth : function(contentWidth){
7842             var w = this.dom.offsetWidth || 0;
7843             return contentWidth !== true ? w : w-this.getBorderWidth("lr")-this.getPadding("lr");
7844         },
7845
7846         /**
7847          * Returns either the offsetHeight or the height of this element based on CSS height adjusted by padding or borders
7848          * when needed to simulate offsetHeight when offsets aren't available. This may not work on display:none elements
7849          * if a height has not been set using CSS.
7850          * @return {Number}
7851          */
7852         getComputedHeight : function(){
7853             var h = Math.max(this.dom.offsetHeight, this.dom.clientHeight);
7854             if(!h){
7855                 h = parseInt(this.getStyle('height'), 10) || 0;
7856                 if(!this.isBorderBox()){
7857                     h += this.getFrameWidth('tb');
7858                 }
7859             }
7860             return h;
7861         },
7862
7863         /**
7864          * Returns either the offsetWidth or the width of this element based on CSS width adjusted by padding or borders
7865          * when needed to simulate offsetWidth when offsets aren't available. This may not work on display:none elements
7866          * if a width has not been set using CSS.
7867          * @return {Number}
7868          */
7869         getComputedWidth : function(){
7870             var w = Math.max(this.dom.offsetWidth, this.dom.clientWidth);
7871             if(!w){
7872                 w = parseInt(this.getStyle('width'), 10) || 0;
7873                 if(!this.isBorderBox()){
7874                     w += this.getFrameWidth('lr');
7875                 }
7876             }
7877             return w;
7878         },
7879
7880         /**
7881          * Returns the size of the element.
7882          * @param {Boolean} contentSize (optional) true to get the width/size minus borders and padding
7883          * @return {Object} An object containing the element's size {width: (element width), height: (element height)}
7884          */
7885         getSize : function(contentSize){
7886             return {width: this.getWidth(contentSize), height: this.getHeight(contentSize)};
7887         },
7888
7889         /**
7890          * Returns the width and height of the viewport.
7891          * @return {Object} An object containing the viewport's size {width: (viewport width), height: (viewport height)}
7892          */
7893         getViewSize : function(){
7894             var d = this.dom, doc = document, aw = 0, ah = 0;
7895             if(d == doc || d == doc.body){
7896                 return {width : D.getViewWidth(), height: D.getViewHeight()};
7897             }else{
7898                 return {
7899                     width : d.clientWidth,
7900                     height: d.clientHeight
7901                 };
7902             }
7903         },
7904
7905         /**
7906          * Returns the value of the "value" attribute
7907          * @param {Boolean} asNumber true to parse the value as a number
7908          * @return {String/Number}
7909          */
7910         getValue : function(asNumber){
7911             return asNumber ? parseInt(this.dom.value, 10) : this.dom.value;
7912         },
7913
7914         // private
7915         adjustWidth : function(width){
7916             if(typeof width == "number"){
7917                 if(this.autoBoxAdjust && !this.isBorderBox()){
7918                    width -= (this.getBorderWidth("lr") + this.getPadding("lr"));
7919                 }
7920                 if(width < 0){
7921                     width = 0;
7922                 }
7923             }
7924             return width;
7925         },
7926
7927         // private
7928         adjustHeight : function(height){
7929             if(typeof height == "number"){
7930                if(this.autoBoxAdjust && !this.isBorderBox()){
7931                    height -= (this.getBorderWidth("tb") + this.getPadding("tb"));
7932                }
7933                if(height < 0){
7934                    height = 0;
7935                }
7936             }
7937             return height;
7938         },
7939
7940         /**
7941          * Set the width of the element
7942          * @param {Number} width The new width
7943          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7944          * @return {Roo.Element} this
7945          */
7946         setWidth : function(width, animate){
7947             width = this.adjustWidth(width);
7948             if(!animate || !A){
7949                 this.dom.style.width = this.addUnits(width);
7950             }else{
7951                 this.anim({width: {to: width}}, this.preanim(arguments, 1));
7952             }
7953             return this;
7954         },
7955
7956         /**
7957          * Set the height of the element
7958          * @param {Number} height The new height
7959          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7960          * @return {Roo.Element} this
7961          */
7962          setHeight : function(height, animate){
7963             height = this.adjustHeight(height);
7964             if(!animate || !A){
7965                 this.dom.style.height = this.addUnits(height);
7966             }else{
7967                 this.anim({height: {to: height}}, this.preanim(arguments, 1));
7968             }
7969             return this;
7970         },
7971
7972         /**
7973          * Set the size of the element. If animation is true, both width an height will be animated concurrently.
7974          * @param {Number} width The new width
7975          * @param {Number} height The new height
7976          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7977          * @return {Roo.Element} this
7978          */
7979          setSize : function(width, height, animate){
7980             if(typeof width == "object"){ // in case of object from getSize()
7981                 height = width.height; width = width.width;
7982             }
7983             width = this.adjustWidth(width); height = this.adjustHeight(height);
7984             if(!animate || !A){
7985                 this.dom.style.width = this.addUnits(width);
7986                 this.dom.style.height = this.addUnits(height);
7987             }else{
7988                 this.anim({width: {to: width}, height: {to: height}}, this.preanim(arguments, 2));
7989             }
7990             return this;
7991         },
7992
7993         /**
7994          * Sets the element's position and size in one shot. If animation is true then width, height, x and y will be animated concurrently.
7995          * @param {Number} x X value for new position (coordinates are page-based)
7996          * @param {Number} y Y value for new position (coordinates are page-based)
7997          * @param {Number} width The new width
7998          * @param {Number} height The new height
7999          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8000          * @return {Roo.Element} this
8001          */
8002         setBounds : function(x, y, width, height, animate){
8003             if(!animate || !A){
8004                 this.setSize(width, height);
8005                 this.setLocation(x, y);
8006             }else{
8007                 width = this.adjustWidth(width); height = this.adjustHeight(height);
8008                 this.anim({points: {to: [x, y]}, width: {to: width}, height: {to: height}},
8009                               this.preanim(arguments, 4), 'motion');
8010             }
8011             return this;
8012         },
8013
8014         /**
8015          * Sets the element's position and size the the specified region. If animation is true then width, height, x and y will be animated concurrently.
8016          * @param {Roo.lib.Region} region The region to fill
8017          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8018          * @return {Roo.Element} this
8019          */
8020         setRegion : function(region, animate){
8021             this.setBounds(region.left, region.top, region.right-region.left, region.bottom-region.top, this.preanim(arguments, 1));
8022             return this;
8023         },
8024
8025         /**
8026          * Appends an event handler
8027          *
8028          * @param {String}   eventName     The type of event to append
8029          * @param {Function} fn        The method the event invokes
8030          * @param {Object} scope       (optional) The scope (this object) of the fn
8031          * @param {Object}   options   (optional)An object with standard {@link Roo.EventManager#addListener} options
8032          */
8033         addListener : function(eventName, fn, scope, options){
8034             if (this.dom) {
8035                 Roo.EventManager.on(this.dom,  eventName, fn, scope || this, options);
8036             }
8037         },
8038
8039         /**
8040          * Removes an event handler from this element
8041          * @param {String} eventName the type of event to remove
8042          * @param {Function} fn the method the event invokes
8043          * @return {Roo.Element} this
8044          */
8045         removeListener : function(eventName, fn){
8046             Roo.EventManager.removeListener(this.dom,  eventName, fn);
8047             return this;
8048         },
8049
8050         /**
8051          * Removes all previous added listeners from this element
8052          * @return {Roo.Element} this
8053          */
8054         removeAllListeners : function(){
8055             E.purgeElement(this.dom);
8056             return this;
8057         },
8058
8059         relayEvent : function(eventName, observable){
8060             this.on(eventName, function(e){
8061                 observable.fireEvent(eventName, e);
8062             });
8063         },
8064
8065         /**
8066          * Set the opacity of the element
8067          * @param {Float} opacity The new opacity. 0 = transparent, .5 = 50% visibile, 1 = fully visible, etc
8068          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8069          * @return {Roo.Element} this
8070          */
8071          setOpacity : function(opacity, animate){
8072             if(!animate || !A){
8073                 var s = this.dom.style;
8074                 if(Roo.isIE){
8075                     s.zoom = 1;
8076                     s.filter = (s.filter || '').replace(/alpha\([^\)]*\)/gi,"") +
8077                                (opacity == 1 ? "" : "alpha(opacity=" + opacity * 100 + ")");
8078                 }else{
8079                     s.opacity = opacity;
8080                 }
8081             }else{
8082                 this.anim({opacity: {to: opacity}}, this.preanim(arguments, 1), null, .35, 'easeIn');
8083             }
8084             return this;
8085         },
8086
8087         /**
8088          * Gets the left X coordinate
8089          * @param {Boolean} local True to get the local css position instead of page coordinate
8090          * @return {Number}
8091          */
8092         getLeft : function(local){
8093             if(!local){
8094                 return this.getX();
8095             }else{
8096                 return parseInt(this.getStyle("left"), 10) || 0;
8097             }
8098         },
8099
8100         /**
8101          * Gets the right X coordinate of the element (element X position + element width)
8102          * @param {Boolean} local True to get the local css position instead of page coordinate
8103          * @return {Number}
8104          */
8105         getRight : function(local){
8106             if(!local){
8107                 return this.getX() + this.getWidth();
8108             }else{
8109                 return (this.getLeft(true) + this.getWidth()) || 0;
8110             }
8111         },
8112
8113         /**
8114          * Gets the top Y coordinate
8115          * @param {Boolean} local True to get the local css position instead of page coordinate
8116          * @return {Number}
8117          */
8118         getTop : function(local) {
8119             if(!local){
8120                 return this.getY();
8121             }else{
8122                 return parseInt(this.getStyle("top"), 10) || 0;
8123             }
8124         },
8125
8126         /**
8127          * Gets the bottom Y coordinate of the element (element Y position + element height)
8128          * @param {Boolean} local True to get the local css position instead of page coordinate
8129          * @return {Number}
8130          */
8131         getBottom : function(local){
8132             if(!local){
8133                 return this.getY() + this.getHeight();
8134             }else{
8135                 return (this.getTop(true) + this.getHeight()) || 0;
8136             }
8137         },
8138
8139         /**
8140         * Initializes positioning on this element. If a desired position is not passed, it will make the
8141         * the element positioned relative IF it is not already positioned.
8142         * @param {String} pos (optional) Positioning to use "relative", "absolute" or "fixed"
8143         * @param {Number} zIndex (optional) The zIndex to apply
8144         * @param {Number} x (optional) Set the page X position
8145         * @param {Number} y (optional) Set the page Y position
8146         */
8147         position : function(pos, zIndex, x, y){
8148             if(!pos){
8149                if(this.getStyle('position') == 'static'){
8150                    this.setStyle('position', 'relative');
8151                }
8152             }else{
8153                 this.setStyle("position", pos);
8154             }
8155             if(zIndex){
8156                 this.setStyle("z-index", zIndex);
8157             }
8158             if(x !== undefined && y !== undefined){
8159                 this.setXY([x, y]);
8160             }else if(x !== undefined){
8161                 this.setX(x);
8162             }else if(y !== undefined){
8163                 this.setY(y);
8164             }
8165         },
8166
8167         /**
8168         * Clear positioning back to the default when the document was loaded
8169         * @param {String} value (optional) The value to use for the left,right,top,bottom, defaults to '' (empty string). You could use 'auto'.
8170         * @return {Roo.Element} this
8171          */
8172         clearPositioning : function(value){
8173             value = value ||'';
8174             this.setStyle({
8175                 "left": value,
8176                 "right": value,
8177                 "top": value,
8178                 "bottom": value,
8179                 "z-index": "",
8180                 "position" : "static"
8181             });
8182             return this;
8183         },
8184
8185         /**
8186         * Gets an object with all CSS positioning properties. Useful along with setPostioning to get
8187         * snapshot before performing an update and then restoring the element.
8188         * @return {Object}
8189         */
8190         getPositioning : function(){
8191             var l = this.getStyle("left");
8192             var t = this.getStyle("top");
8193             return {
8194                 "position" : this.getStyle("position"),
8195                 "left" : l,
8196                 "right" : l ? "" : this.getStyle("right"),
8197                 "top" : t,
8198                 "bottom" : t ? "" : this.getStyle("bottom"),
8199                 "z-index" : this.getStyle("z-index")
8200             };
8201         },
8202
8203         /**
8204          * Gets the width of the border(s) for the specified side(s)
8205          * @param {String} side Can be t, l, r, b or any combination of those to add multiple values. For example,
8206          * passing lr would get the border (l)eft width + the border (r)ight width.
8207          * @return {Number} The width of the sides passed added together
8208          */
8209         getBorderWidth : function(side){
8210             return this.addStyles(side, El.borders);
8211         },
8212
8213         /**
8214          * Gets the width of the padding(s) for the specified side(s)
8215          * @param {String} side Can be t, l, r, b or any combination of those to add multiple values. For example,
8216          * passing lr would get the padding (l)eft + the padding (r)ight.
8217          * @return {Number} The padding of the sides passed added together
8218          */
8219         getPadding : function(side){
8220             return this.addStyles(side, El.paddings);
8221         },
8222
8223         /**
8224         * Set positioning with an object returned by getPositioning().
8225         * @param {Object} posCfg
8226         * @return {Roo.Element} this
8227          */
8228         setPositioning : function(pc){
8229             this.applyStyles(pc);
8230             if(pc.right == "auto"){
8231                 this.dom.style.right = "";
8232             }
8233             if(pc.bottom == "auto"){
8234                 this.dom.style.bottom = "";
8235             }
8236             return this;
8237         },
8238
8239         // private
8240         fixDisplay : function(){
8241             if(this.getStyle("display") == "none"){
8242                 this.setStyle("visibility", "hidden");
8243                 this.setStyle("display", this.originalDisplay); // first try reverting to default
8244                 if(this.getStyle("display") == "none"){ // if that fails, default to block
8245                     this.setStyle("display", "block");
8246                 }
8247             }
8248         },
8249
8250         /**
8251          * Quick set left and top adding default units
8252          * @param {String} left The left CSS property value
8253          * @param {String} top The top CSS property value
8254          * @return {Roo.Element} this
8255          */
8256          setLeftTop : function(left, top){
8257             this.dom.style.left = this.addUnits(left);
8258             this.dom.style.top = this.addUnits(top);
8259             return this;
8260         },
8261
8262         /**
8263          * Move this element relative to its current position.
8264          * @param {String} direction Possible values are: "l","left" - "r","right" - "t","top","up" - "b","bottom","down".
8265          * @param {Number} distance How far to move the element in pixels
8266          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8267          * @return {Roo.Element} this
8268          */
8269          move : function(direction, distance, animate){
8270             var xy = this.getXY();
8271             direction = direction.toLowerCase();
8272             switch(direction){
8273                 case "l":
8274                 case "left":
8275                     this.moveTo(xy[0]-distance, xy[1], this.preanim(arguments, 2));
8276                     break;
8277                case "r":
8278                case "right":
8279                     this.moveTo(xy[0]+distance, xy[1], this.preanim(arguments, 2));
8280                     break;
8281                case "t":
8282                case "top":
8283                case "up":
8284                     this.moveTo(xy[0], xy[1]-distance, this.preanim(arguments, 2));
8285                     break;
8286                case "b":
8287                case "bottom":
8288                case "down":
8289                     this.moveTo(xy[0], xy[1]+distance, this.preanim(arguments, 2));
8290                     break;
8291             }
8292             return this;
8293         },
8294
8295         /**
8296          *  Store the current overflow setting and clip overflow on the element - use {@link #unclip} to remove
8297          * @return {Roo.Element} this
8298          */
8299         clip : function(){
8300             if(!this.isClipped){
8301                this.isClipped = true;
8302                this.originalClip = {
8303                    "o": this.getStyle("overflow"),
8304                    "x": this.getStyle("overflow-x"),
8305                    "y": this.getStyle("overflow-y")
8306                };
8307                this.setStyle("overflow", "hidden");
8308                this.setStyle("overflow-x", "hidden");
8309                this.setStyle("overflow-y", "hidden");
8310             }
8311             return this;
8312         },
8313
8314         /**
8315          *  Return clipping (overflow) to original clipping before clip() was called
8316          * @return {Roo.Element} this
8317          */
8318         unclip : function(){
8319             if(this.isClipped){
8320                 this.isClipped = false;
8321                 var o = this.originalClip;
8322                 if(o.o){this.setStyle("overflow", o.o);}
8323                 if(o.x){this.setStyle("overflow-x", o.x);}
8324                 if(o.y){this.setStyle("overflow-y", o.y);}
8325             }
8326             return this;
8327         },
8328
8329
8330         /**
8331          * Gets the x,y coordinates specified by the anchor position on the element.
8332          * @param {String} anchor (optional) The specified anchor position (defaults to "c").  See {@link #alignTo} for details on supported anchor positions.
8333          * @param {Object} size (optional) An object containing the size to use for calculating anchor position
8334          *                       {width: (target width), height: (target height)} (defaults to the element's current size)
8335          * @param {Boolean} local (optional) True to get the local (element top/left-relative) anchor position instead of page coordinates
8336          * @return {Array} [x, y] An array containing the element's x and y coordinates
8337          */
8338         getAnchorXY : function(anchor, local, s){
8339             //Passing a different size is useful for pre-calculating anchors,
8340             //especially for anchored animations that change the el size.
8341
8342             var w, h, vp = false;
8343             if(!s){
8344                 var d = this.dom;
8345                 if(d == document.body || d == document){
8346                     vp = true;
8347                     w = D.getViewWidth(); h = D.getViewHeight();
8348                 }else{
8349                     w = this.getWidth(); h = this.getHeight();
8350                 }
8351             }else{
8352                 w = s.width;  h = s.height;
8353             }
8354             var x = 0, y = 0, r = Math.round;
8355             switch((anchor || "tl").toLowerCase()){
8356                 case "c":
8357                     x = r(w*.5);
8358                     y = r(h*.5);
8359                 break;
8360                 case "t":
8361                     x = r(w*.5);
8362                     y = 0;
8363                 break;
8364                 case "l":
8365                     x = 0;
8366                     y = r(h*.5);
8367                 break;
8368                 case "r":
8369                     x = w;
8370                     y = r(h*.5);
8371                 break;
8372                 case "b":
8373                     x = r(w*.5);
8374                     y = h;
8375                 break;
8376                 case "tl":
8377                     x = 0;
8378                     y = 0;
8379                 break;
8380                 case "bl":
8381                     x = 0;
8382                     y = h;
8383                 break;
8384                 case "br":
8385                     x = w;
8386                     y = h;
8387                 break;
8388                 case "tr":
8389                     x = w;
8390                     y = 0;
8391                 break;
8392             }
8393             if(local === true){
8394                 return [x, y];
8395             }
8396             if(vp){
8397                 var sc = this.getScroll();
8398                 return [x + sc.left, y + sc.top];
8399             }
8400             //Add the element's offset xy
8401             var o = this.getXY();
8402             return [x+o[0], y+o[1]];
8403         },
8404
8405         /**
8406          * Gets the x,y coordinates to align this element with another element. See {@link #alignTo} for more info on the
8407          * supported position values.
8408          * @param {String/HTMLElement/Roo.Element} element The element to align to.
8409          * @param {String} position The position to align to.
8410          * @param {Array} offsets (optional) Offset the positioning by [x, y]
8411          * @return {Array} [x, y]
8412          */
8413         getAlignToXY : function(el, p, o){
8414             el = Roo.get(el);
8415             var d = this.dom;
8416             if(!el.dom){
8417                 throw "Element.alignTo with an element that doesn't exist";
8418             }
8419             var c = false; //constrain to viewport
8420             var p1 = "", p2 = "";
8421             o = o || [0,0];
8422
8423             if(!p){
8424                 p = "tl-bl";
8425             }else if(p == "?"){
8426                 p = "tl-bl?";
8427             }else if(p.indexOf("-") == -1){
8428                 p = "tl-" + p;
8429             }
8430             p = p.toLowerCase();
8431             var m = p.match(/^([a-z]+)-([a-z]+)(\?)?$/);
8432             if(!m){
8433                throw "Element.alignTo with an invalid alignment " + p;
8434             }
8435             p1 = m[1]; p2 = m[2]; c = !!m[3];
8436
8437             //Subtract the aligned el's internal xy from the target's offset xy
8438             //plus custom offset to get the aligned el's new offset xy
8439             var a1 = this.getAnchorXY(p1, true);
8440             var a2 = el.getAnchorXY(p2, false);
8441             var x = a2[0] - a1[0] + o[0];
8442             var y = a2[1] - a1[1] + o[1];
8443             if(c){
8444                 //constrain the aligned el to viewport if necessary
8445                 var w = this.getWidth(), h = this.getHeight(), r = el.getRegion();
8446                 // 5px of margin for ie
8447                 var dw = D.getViewWidth()-5, dh = D.getViewHeight()-5;
8448
8449                 //If we are at a viewport boundary and the aligned el is anchored on a target border that is
8450                 //perpendicular to the vp border, allow the aligned el to slide on that border,
8451                 //otherwise swap the aligned el to the opposite border of the target.
8452                 var p1y = p1.charAt(0), p1x = p1.charAt(p1.length-1);
8453                var p2y = p2.charAt(0), p2x = p2.charAt(p2.length-1);
8454                var swapY = ((p1y=="t" && p2y=="b") || (p1y=="b" && p2y=="t"));
8455                var swapX = ((p1x=="r" && p2x=="l") || (p1x=="l" && p2x=="r"));
8456
8457                var doc = document;
8458                var scrollX = (doc.documentElement.scrollLeft || doc.body.scrollLeft || 0)+5;
8459                var scrollY = (doc.documentElement.scrollTop || doc.body.scrollTop || 0)+5;
8460
8461                if((x+w) > dw + scrollX){
8462                     x = swapX ? r.left-w : dw+scrollX-w;
8463                 }
8464                if(x < scrollX){
8465                    x = swapX ? r.right : scrollX;
8466                }
8467                if((y+h) > dh + scrollY){
8468                     y = swapY ? r.top-h : dh+scrollY-h;
8469                 }
8470                if (y < scrollY){
8471                    y = swapY ? r.bottom : scrollY;
8472                }
8473             }
8474             return [x,y];
8475         },
8476
8477         // private
8478         getConstrainToXY : function(){
8479             var os = {top:0, left:0, bottom:0, right: 0};
8480
8481             return function(el, local, offsets, proposedXY){
8482                 el = Roo.get(el);
8483                 offsets = offsets ? Roo.applyIf(offsets, os) : os;
8484
8485                 var vw, vh, vx = 0, vy = 0;
8486                 if(el.dom == document.body || el.dom == document){
8487                     vw = Roo.lib.Dom.getViewWidth();
8488                     vh = Roo.lib.Dom.getViewHeight();
8489                 }else{
8490                     vw = el.dom.clientWidth;
8491                     vh = el.dom.clientHeight;
8492                     if(!local){
8493                         var vxy = el.getXY();
8494                         vx = vxy[0];
8495                         vy = vxy[1];
8496                     }
8497                 }
8498
8499                 var s = el.getScroll();
8500
8501                 vx += offsets.left + s.left;
8502                 vy += offsets.top + s.top;
8503
8504                 vw -= offsets.right;
8505                 vh -= offsets.bottom;
8506
8507                 var vr = vx+vw;
8508                 var vb = vy+vh;
8509
8510                 var xy = proposedXY || (!local ? this.getXY() : [this.getLeft(true), this.getTop(true)]);
8511                 var x = xy[0], y = xy[1];
8512                 var w = this.dom.offsetWidth, h = this.dom.offsetHeight;
8513
8514                 // only move it if it needs it
8515                 var moved = false;
8516
8517                 // first validate right/bottom
8518                 if((x + w) > vr){
8519                     x = vr - w;
8520                     moved = true;
8521                 }
8522                 if((y + h) > vb){
8523                     y = vb - h;
8524                     moved = true;
8525                 }
8526                 // then make sure top/left isn't negative
8527                 if(x < vx){
8528                     x = vx;
8529                     moved = true;
8530                 }
8531                 if(y < vy){
8532                     y = vy;
8533                     moved = true;
8534                 }
8535                 return moved ? [x, y] : false;
8536             };
8537         }(),
8538
8539         // private
8540         adjustForConstraints : function(xy, parent, offsets){
8541             return this.getConstrainToXY(parent || document, false, offsets, xy) ||  xy;
8542         },
8543
8544         /**
8545          * Aligns this element with another element relative to the specified anchor points. If the other element is the
8546          * document it aligns it to the viewport.
8547          * The position parameter is optional, and can be specified in any one of the following formats:
8548          * <ul>
8549          *   <li><b>Blank</b>: Defaults to aligning the element's top-left corner to the target's bottom-left corner ("tl-bl").</li>
8550          *   <li><b>One anchor (deprecated)</b>: The passed anchor position is used as the target element's anchor point.
8551          *       The element being aligned will position its top-left corner (tl) to that point.  <i>This method has been
8552          *       deprecated in favor of the newer two anchor syntax below</i>.</li>
8553          *   <li><b>Two anchors</b>: If two values from the table below are passed separated by a dash, the first value is used as the
8554          *       element's anchor point, and the second value is used as the target's anchor point.</li>
8555          * </ul>
8556          * In addition to the anchor points, the position parameter also supports the "?" character.  If "?" is passed at the end of
8557          * the position string, the element will attempt to align as specified, but the position will be adjusted to constrain to
8558          * the viewport if necessary.  Note that the element being aligned might be swapped to align to a different position than
8559          * that specified in order to enforce the viewport constraints.
8560          * Following are all of the supported anchor positions:
8561     <pre>
8562     Value  Description
8563     -----  -----------------------------
8564     tl     The top left corner (default)
8565     t      The center of the top edge
8566     tr     The top right corner
8567     l      The center of the left edge
8568     c      In the center of the element
8569     r      The center of the right edge
8570     bl     The bottom left corner
8571     b      The center of the bottom edge
8572     br     The bottom right corner
8573     </pre>
8574     Example Usage:
8575     <pre><code>
8576     // align el to other-el using the default positioning ("tl-bl", non-constrained)
8577     el.alignTo("other-el");
8578
8579     // align the top left corner of el with the top right corner of other-el (constrained to viewport)
8580     el.alignTo("other-el", "tr?");
8581
8582     // align the bottom right corner of el with the center left edge of other-el
8583     el.alignTo("other-el", "br-l?");
8584
8585     // align the center of el with the bottom left corner of other-el and
8586     // adjust the x position by -6 pixels (and the y position by 0)
8587     el.alignTo("other-el", "c-bl", [-6, 0]);
8588     </code></pre>
8589          * @param {String/HTMLElement/Roo.Element} element The element to align to.
8590          * @param {String} position The position to align to.
8591          * @param {Array} offsets (optional) Offset the positioning by [x, y]
8592          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8593          * @return {Roo.Element} this
8594          */
8595         alignTo : function(element, position, offsets, animate){
8596             var xy = this.getAlignToXY(element, position, offsets);
8597             this.setXY(xy, this.preanim(arguments, 3));
8598             return this;
8599         },
8600
8601         /**
8602          * Anchors an element to another element and realigns it when the window is resized.
8603          * @param {String/HTMLElement/Roo.Element} element The element to align to.
8604          * @param {String} position The position to align to.
8605          * @param {Array} offsets (optional) Offset the positioning by [x, y]
8606          * @param {Boolean/Object} animate (optional) True for the default animation or a standard Element animation config object
8607          * @param {Boolean/Number} monitorScroll (optional) True to monitor body scroll and reposition. If this parameter
8608          * is a number, it is used as the buffer delay (defaults to 50ms).
8609          * @param {Function} callback The function to call after the animation finishes
8610          * @return {Roo.Element} this
8611          */
8612         anchorTo : function(el, alignment, offsets, animate, monitorScroll, callback){
8613             var action = function(){
8614                 this.alignTo(el, alignment, offsets, animate);
8615                 Roo.callback(callback, this);
8616             };
8617             Roo.EventManager.onWindowResize(action, this);
8618             var tm = typeof monitorScroll;
8619             if(tm != 'undefined'){
8620                 Roo.EventManager.on(window, 'scroll', action, this,
8621                     {buffer: tm == 'number' ? monitorScroll : 50});
8622             }
8623             action.call(this); // align immediately
8624             return this;
8625         },
8626         /**
8627          * Clears any opacity settings from this element. Required in some cases for IE.
8628          * @return {Roo.Element} this
8629          */
8630         clearOpacity : function(){
8631             if (window.ActiveXObject) {
8632                 if(typeof this.dom.style.filter == 'string' && (/alpha/i).test(this.dom.style.filter)){
8633                     this.dom.style.filter = "";
8634                 }
8635             } else {
8636                 this.dom.style.opacity = "";
8637                 this.dom.style["-moz-opacity"] = "";
8638                 this.dom.style["-khtml-opacity"] = "";
8639             }
8640             return this;
8641         },
8642
8643         /**
8644          * Hide this element - Uses display mode to determine whether to use "display" or "visibility". See {@link #setVisible}.
8645          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8646          * @return {Roo.Element} this
8647          */
8648         hide : function(animate){
8649             this.setVisible(false, this.preanim(arguments, 0));
8650             return this;
8651         },
8652
8653         /**
8654         * Show this element - Uses display mode to determine whether to use "display" or "visibility". See {@link #setVisible}.
8655         * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8656          * @return {Roo.Element} this
8657          */
8658         show : function(animate){
8659             this.setVisible(true, this.preanim(arguments, 0));
8660             return this;
8661         },
8662
8663         /**
8664          * @private Test if size has a unit, otherwise appends the default
8665          */
8666         addUnits : function(size){
8667             return Roo.Element.addUnits(size, this.defaultUnit);
8668         },
8669
8670         /**
8671          * Temporarily enables offsets (width,height,x,y) for an element with display:none, use endMeasure() when done.
8672          * @return {Roo.Element} this
8673          */
8674         beginMeasure : function(){
8675             var el = this.dom;
8676             if(el.offsetWidth || el.offsetHeight){
8677                 return this; // offsets work already
8678             }
8679             var changed = [];
8680             var p = this.dom, b = document.body; // start with this element
8681             while((!el.offsetWidth && !el.offsetHeight) && p && p.tagName && p != b){
8682                 var pe = Roo.get(p);
8683                 if(pe.getStyle('display') == 'none'){
8684                     changed.push({el: p, visibility: pe.getStyle("visibility")});
8685                     p.style.visibility = "hidden";
8686                     p.style.display = "block";
8687                 }
8688                 p = p.parentNode;
8689             }
8690             this._measureChanged = changed;
8691             return this;
8692
8693         },
8694
8695         /**
8696          * Restores displays to before beginMeasure was called
8697          * @return {Roo.Element} this
8698          */
8699         endMeasure : function(){
8700             var changed = this._measureChanged;
8701             if(changed){
8702                 for(var i = 0, len = changed.length; i < len; i++) {
8703                     var r = changed[i];
8704                     r.el.style.visibility = r.visibility;
8705                     r.el.style.display = "none";
8706                 }
8707                 this._measureChanged = null;
8708             }
8709             return this;
8710         },
8711
8712         /**
8713         * Update the innerHTML of this element, optionally searching for and processing scripts
8714         * @param {String} html The new HTML
8715         * @param {Boolean} loadScripts (optional) true to look for and process scripts
8716         * @param {Function} callback For async script loading you can be noticed when the update completes
8717         * @return {Roo.Element} this
8718          */
8719         update : function(html, loadScripts, callback){
8720             if(typeof html == "undefined"){
8721                 html = "";
8722             }
8723             if(loadScripts !== true){
8724                 this.dom.innerHTML = html;
8725                 if(typeof callback == "function"){
8726                     callback();
8727                 }
8728                 return this;
8729             }
8730             var id = Roo.id();
8731             var dom = this.dom;
8732
8733             html += '<span id="' + id + '"></span>';
8734
8735             E.onAvailable(id, function(){
8736                 var hd = document.getElementsByTagName("head")[0];
8737                 var re = /(?:<script([^>]*)?>)((\n|\r|.)*?)(?:<\/script>)/ig;
8738                 var srcRe = /\ssrc=([\'\"])(.*?)\1/i;
8739                 var typeRe = /\stype=([\'\"])(.*?)\1/i;
8740
8741                 var match;
8742                 while(match = re.exec(html)){
8743                     var attrs = match[1];
8744                     var srcMatch = attrs ? attrs.match(srcRe) : false;
8745                     if(srcMatch && srcMatch[2]){
8746                        var s = document.createElement("script");
8747                        s.src = srcMatch[2];
8748                        var typeMatch = attrs.match(typeRe);
8749                        if(typeMatch && typeMatch[2]){
8750                            s.type = typeMatch[2];
8751                        }
8752                        hd.appendChild(s);
8753                     }else if(match[2] && match[2].length > 0){
8754                         if(window.execScript) {
8755                            window.execScript(match[2]);
8756                         } else {
8757                             /**
8758                              * eval:var:id
8759                              * eval:var:dom
8760                              * eval:var:html
8761                              * 
8762                              */
8763                            window.eval(match[2]);
8764                         }
8765                     }
8766                 }
8767                 var el = document.getElementById(id);
8768                 if(el){el.parentNode.removeChild(el);}
8769                 if(typeof callback == "function"){
8770                     callback();
8771                 }
8772             });
8773             dom.innerHTML = html.replace(/(?:<script.*?>)((\n|\r|.)*?)(?:<\/script>)/ig, "");
8774             return this;
8775         },
8776
8777         /**
8778          * Direct access to the UpdateManager update() method (takes the same parameters).
8779          * @param {String/Function} url The url for this request or a function to call to get the url
8780          * @param {String/Object} params (optional) The parameters to pass as either a url encoded string "param1=1&amp;param2=2" or an object {param1: 1, param2: 2}
8781          * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
8782          * @param {Boolean} discardUrl (optional) By default when you execute an update the defaultUrl is changed to the last used url. If true, it will not store the url.
8783          * @return {Roo.Element} this
8784          */
8785         load : function(){
8786             var um = this.getUpdateManager();
8787             um.update.apply(um, arguments);
8788             return this;
8789         },
8790
8791         /**
8792         * Gets this element's UpdateManager
8793         * @return {Roo.UpdateManager} The UpdateManager
8794         */
8795         getUpdateManager : function(){
8796             if(!this.updateManager){
8797                 this.updateManager = new Roo.UpdateManager(this);
8798             }
8799             return this.updateManager;
8800         },
8801
8802         /**
8803          * Disables text selection for this element (normalized across browsers)
8804          * @return {Roo.Element} this
8805          */
8806         unselectable : function(){
8807             this.dom.unselectable = "on";
8808             this.swallowEvent("selectstart", true);
8809             this.applyStyles("-moz-user-select:none;-khtml-user-select:none;");
8810             this.addClass("x-unselectable");
8811             return this;
8812         },
8813
8814         /**
8815         * Calculates the x, y to center this element on the screen
8816         * @return {Array} The x, y values [x, y]
8817         */
8818         getCenterXY : function(){
8819             return this.getAlignToXY(document, 'c-c');
8820         },
8821
8822         /**
8823         * Centers the Element in either the viewport, or another Element.
8824         * @param {String/HTMLElement/Roo.Element} centerIn (optional) The element in which to center the element.
8825         */
8826         center : function(centerIn){
8827             this.alignTo(centerIn || document, 'c-c');
8828             return this;
8829         },
8830
8831         /**
8832          * Tests various css rules/browsers to determine if this element uses a border box
8833          * @return {Boolean}
8834          */
8835         isBorderBox : function(){
8836             return noBoxAdjust[this.dom.tagName.toLowerCase()] || Roo.isBorderBox;
8837         },
8838
8839         /**
8840          * Return a box {x, y, width, height} that can be used to set another elements
8841          * size/location to match this element.
8842          * @param {Boolean} contentBox (optional) If true a box for the content of the element is returned.
8843          * @param {Boolean} local (optional) If true the element's left and top are returned instead of page x/y.
8844          * @return {Object} box An object in the format {x, y, width, height}
8845          */
8846         getBox : function(contentBox, local){
8847             var xy;
8848             if(!local){
8849                 xy = this.getXY();
8850             }else{
8851                 var left = parseInt(this.getStyle("left"), 10) || 0;
8852                 var top = parseInt(this.getStyle("top"), 10) || 0;
8853                 xy = [left, top];
8854             }
8855             var el = this.dom, w = el.offsetWidth, h = el.offsetHeight, bx;
8856             if(!contentBox){
8857                 bx = {x: xy[0], y: xy[1], 0: xy[0], 1: xy[1], width: w, height: h};
8858             }else{
8859                 var l = this.getBorderWidth("l")+this.getPadding("l");
8860                 var r = this.getBorderWidth("r")+this.getPadding("r");
8861                 var t = this.getBorderWidth("t")+this.getPadding("t");
8862                 var b = this.getBorderWidth("b")+this.getPadding("b");
8863                 bx = {x: xy[0]+l, y: xy[1]+t, 0: xy[0]+l, 1: xy[1]+t, width: w-(l+r), height: h-(t+b)};
8864             }
8865             bx.right = bx.x + bx.width;
8866             bx.bottom = bx.y + bx.height;
8867             return bx;
8868         },
8869
8870         /**
8871          * Returns the sum width of the padding and borders for the passed "sides". See getBorderWidth()
8872          for more information about the sides.
8873          * @param {String} sides
8874          * @return {Number}
8875          */
8876         getFrameWidth : function(sides, onlyContentBox){
8877             return onlyContentBox && Roo.isBorderBox ? 0 : (this.getPadding(sides) + this.getBorderWidth(sides));
8878         },
8879
8880         /**
8881          * Sets the element's box. Use getBox() on another element to get a box obj. If animate is true then width, height, x and y will be animated concurrently.
8882          * @param {Object} box The box to fill {x, y, width, height}
8883          * @param {Boolean} adjust (optional) Whether to adjust for box-model issues automatically
8884          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8885          * @return {Roo.Element} this
8886          */
8887         setBox : function(box, adjust, animate){
8888             var w = box.width, h = box.height;
8889             if((adjust && !this.autoBoxAdjust) && !this.isBorderBox()){
8890                w -= (this.getBorderWidth("lr") + this.getPadding("lr"));
8891                h -= (this.getBorderWidth("tb") + this.getPadding("tb"));
8892             }
8893             this.setBounds(box.x, box.y, w, h, this.preanim(arguments, 2));
8894             return this;
8895         },
8896
8897         /**
8898          * Forces the browser to repaint this element
8899          * @return {Roo.Element} this
8900          */
8901          repaint : function(){
8902             var dom = this.dom;
8903             this.addClass("x-repaint");
8904             setTimeout(function(){
8905                 Roo.get(dom).removeClass("x-repaint");
8906             }, 1);
8907             return this;
8908         },
8909
8910         /**
8911          * Returns an object with properties top, left, right and bottom representing the margins of this element unless sides is passed,
8912          * then it returns the calculated width of the sides (see getPadding)
8913          * @param {String} sides (optional) Any combination of l, r, t, b to get the sum of those sides
8914          * @return {Object/Number}
8915          */
8916         getMargins : function(side){
8917             if(!side){
8918                 return {
8919                     top: parseInt(this.getStyle("margin-top"), 10) || 0,
8920                     left: parseInt(this.getStyle("margin-left"), 10) || 0,
8921                     bottom: parseInt(this.getStyle("margin-bottom"), 10) || 0,
8922                     right: parseInt(this.getStyle("margin-right"), 10) || 0
8923                 };
8924             }else{
8925                 return this.addStyles(side, El.margins);
8926              }
8927         },
8928
8929         // private
8930         addStyles : function(sides, styles){
8931             var val = 0, v, w;
8932             for(var i = 0, len = sides.length; i < len; i++){
8933                 v = this.getStyle(styles[sides.charAt(i)]);
8934                 if(v){
8935                      w = parseInt(v, 10);
8936                      if(w){ val += w; }
8937                 }
8938             }
8939             return val;
8940         },
8941
8942         /**
8943          * Creates a proxy element of this element
8944          * @param {String/Object} config The class name of the proxy element or a DomHelper config object
8945          * @param {String/HTMLElement} renderTo (optional) The element or element id to render the proxy to (defaults to document.body)
8946          * @param {Boolean} matchBox (optional) True to align and size the proxy to this element now (defaults to false)
8947          * @return {Roo.Element} The new proxy element
8948          */
8949         createProxy : function(config, renderTo, matchBox){
8950             if(renderTo){
8951                 renderTo = Roo.getDom(renderTo);
8952             }else{
8953                 renderTo = document.body;
8954             }
8955             config = typeof config == "object" ?
8956                 config : {tag : "div", cls: config};
8957             var proxy = Roo.DomHelper.append(renderTo, config, true);
8958             if(matchBox){
8959                proxy.setBox(this.getBox());
8960             }
8961             return proxy;
8962         },
8963
8964         /**
8965          * Puts a mask over this element to disable user interaction. Requires core.css.
8966          * This method can only be applied to elements which accept child nodes.
8967          * @param {String} msg (optional) A message to display in the mask
8968          * @param {String} msgCls (optional) A css class to apply to the msg element
8969          * @return {Element} The mask  element
8970          */
8971         mask : function(msg, msgCls)
8972         {
8973             if(this.getStyle("position") == "static" && this.dom.tagName !== 'BODY'){
8974                 this.setStyle("position", "relative");
8975             }
8976             if(!this._mask){
8977                 this._mask = Roo.DomHelper.append(this.dom, {cls:"roo-el-mask"}, true);
8978             }
8979             this.addClass("x-masked");
8980             this._mask.setDisplayed(true);
8981             
8982             // we wander
8983             var z = 0;
8984             var dom = this.dom
8985             while (dom && dom.style) {
8986                 if (!isNaN(parseInt(dom.style.zIndex))) {
8987                     z = Math.max(z, parseInt(dom.style.zIndex));
8988                 }
8989                 dom = dom.parentNode;
8990             }
8991             // if we are masking the body - then it hides everything..
8992             if (this.dom == document.body) {
8993                 z = 1000000;
8994                 this._mask.setWidth(Roo.lib.Dom.getDocumentWidth());
8995                 this._mask.setHeight(Roo.lib.Dom.getDocumentHeight());
8996             }
8997            
8998             if(typeof msg == 'string'){
8999                 if(!this._maskMsg){
9000                     this._maskMsg = Roo.DomHelper.append(this.dom, {cls:"roo-el-mask-msg", cn:{tag:'div'}}, true);
9001                 }
9002                 var mm = this._maskMsg;
9003                 mm.dom.className = msgCls ? "roo-el-mask-msg " + msgCls : "roo-el-mask-msg";
9004                 mm.dom.firstChild.innerHTML = msg;
9005                 mm.setDisplayed(true);
9006                 mm.center(this);
9007                 mm.setStyle('z-index', z + 102);
9008             }
9009             if(Roo.isIE && !(Roo.isIE7 && Roo.isStrict) && this.getStyle('height') == 'auto'){ // ie will not expand full height automatically
9010                 this._mask.setHeight(this.getHeight());
9011             }
9012             this._mask.setStyle('z-index', z + 100);
9013             
9014             return this._mask;
9015         },
9016
9017         /**
9018          * Removes a previously applied mask. If removeEl is true the mask overlay is destroyed, otherwise
9019          * it is cached for reuse.
9020          */
9021         unmask : function(removeEl){
9022             if(this._mask){
9023                 if(removeEl === true){
9024                     this._mask.remove();
9025                     delete this._mask;
9026                     if(this._maskMsg){
9027                         this._maskMsg.remove();
9028                         delete this._maskMsg;
9029                     }
9030                 }else{
9031                     this._mask.setDisplayed(false);
9032                     if(this._maskMsg){
9033                         this._maskMsg.setDisplayed(false);
9034                     }
9035                 }
9036             }
9037             this.removeClass("x-masked");
9038         },
9039
9040         /**
9041          * Returns true if this element is masked
9042          * @return {Boolean}
9043          */
9044         isMasked : function(){
9045             return this._mask && this._mask.isVisible();
9046         },
9047
9048         /**
9049          * Creates an iframe shim for this element to keep selects and other windowed objects from
9050          * showing through.
9051          * @return {Roo.Element} The new shim element
9052          */
9053         createShim : function(){
9054             var el = document.createElement('iframe');
9055             el.frameBorder = 'no';
9056             el.className = 'roo-shim';
9057             if(Roo.isIE && Roo.isSecure){
9058                 el.src = Roo.SSL_SECURE_URL;
9059             }
9060             var shim = Roo.get(this.dom.parentNode.insertBefore(el, this.dom));
9061             shim.autoBoxAdjust = false;
9062             return shim;
9063         },
9064
9065         /**
9066          * Removes this element from the DOM and deletes it from the cache
9067          */
9068         remove : function(){
9069             if(this.dom.parentNode){
9070                 this.dom.parentNode.removeChild(this.dom);
9071             }
9072             delete El.cache[this.dom.id];
9073         },
9074
9075         /**
9076          * Sets up event handlers to add and remove a css class when the mouse is over this element
9077          * @param {String} className
9078          * @param {Boolean} preventFlicker (optional) If set to true, it prevents flickering by filtering
9079          * mouseout events for children elements
9080          * @return {Roo.Element} this
9081          */
9082         addClassOnOver : function(className, preventFlicker){
9083             this.on("mouseover", function(){
9084                 Roo.fly(this, '_internal').addClass(className);
9085             }, this.dom);
9086             var removeFn = function(e){
9087                 if(preventFlicker !== true || !e.within(this, true)){
9088                     Roo.fly(this, '_internal').removeClass(className);
9089                 }
9090             };
9091             this.on("mouseout", removeFn, this.dom);
9092             return this;
9093         },
9094
9095         /**
9096          * Sets up event handlers to add and remove a css class when this element has the focus
9097          * @param {String} className
9098          * @return {Roo.Element} this
9099          */
9100         addClassOnFocus : function(className){
9101             this.on("focus", function(){
9102                 Roo.fly(this, '_internal').addClass(className);
9103             }, this.dom);
9104             this.on("blur", function(){
9105                 Roo.fly(this, '_internal').removeClass(className);
9106             }, this.dom);
9107             return this;
9108         },
9109         /**
9110          * Sets up event handlers to add and remove a css class when the mouse is down and then up on this element (a click effect)
9111          * @param {String} className
9112          * @return {Roo.Element} this
9113          */
9114         addClassOnClick : function(className){
9115             var dom = this.dom;
9116             this.on("mousedown", function(){
9117                 Roo.fly(dom, '_internal').addClass(className);
9118                 var d = Roo.get(document);
9119                 var fn = function(){
9120                     Roo.fly(dom, '_internal').removeClass(className);
9121                     d.removeListener("mouseup", fn);
9122                 };
9123                 d.on("mouseup", fn);
9124             });
9125             return this;
9126         },
9127
9128         /**
9129          * Stops the specified event from bubbling and optionally prevents the default action
9130          * @param {String} eventName
9131          * @param {Boolean} preventDefault (optional) true to prevent the default action too
9132          * @return {Roo.Element} this
9133          */
9134         swallowEvent : function(eventName, preventDefault){
9135             var fn = function(e){
9136                 e.stopPropagation();
9137                 if(preventDefault){
9138                     e.preventDefault();
9139                 }
9140             };
9141             if(eventName instanceof Array){
9142                 for(var i = 0, len = eventName.length; i < len; i++){
9143                      this.on(eventName[i], fn);
9144                 }
9145                 return this;
9146             }
9147             this.on(eventName, fn);
9148             return this;
9149         },
9150
9151         /**
9152          * @private
9153          */
9154       fitToParentDelegate : Roo.emptyFn, // keep a reference to the fitToParent delegate
9155
9156         /**
9157          * Sizes this element to its parent element's dimensions performing
9158          * neccessary box adjustments.
9159          * @param {Boolean} monitorResize (optional) If true maintains the fit when the browser window is resized.
9160          * @param {String/HTMLElment/Element} targetParent (optional) The target parent, default to the parentNode.
9161          * @return {Roo.Element} this
9162          */
9163         fitToParent : function(monitorResize, targetParent) {
9164           Roo.EventManager.removeResizeListener(this.fitToParentDelegate); // always remove previous fitToParent delegate from onWindowResize
9165           this.fitToParentDelegate = Roo.emptyFn; // remove reference to previous delegate
9166           if (monitorResize === true && !this.dom.parentNode) { // check if this Element still exists
9167             return;
9168           }
9169           var p = Roo.get(targetParent || this.dom.parentNode);
9170           this.setSize(p.getComputedWidth() - p.getFrameWidth('lr'), p.getComputedHeight() - p.getFrameWidth('tb'));
9171           if (monitorResize === true) {
9172             this.fitToParentDelegate = this.fitToParent.createDelegate(this, [true, targetParent]);
9173             Roo.EventManager.onWindowResize(this.fitToParentDelegate);
9174           }
9175           return this;
9176         },
9177
9178         /**
9179          * Gets the next sibling, skipping text nodes
9180          * @return {HTMLElement} The next sibling or null
9181          */
9182         getNextSibling : function(){
9183             var n = this.dom.nextSibling;
9184             while(n && n.nodeType != 1){
9185                 n = n.nextSibling;
9186             }
9187             return n;
9188         },
9189
9190         /**
9191          * Gets the previous sibling, skipping text nodes
9192          * @return {HTMLElement} The previous sibling or null
9193          */
9194         getPrevSibling : function(){
9195             var n = this.dom.previousSibling;
9196             while(n && n.nodeType != 1){
9197                 n = n.previousSibling;
9198             }
9199             return n;
9200         },
9201
9202
9203         /**
9204          * Appends the passed element(s) to this element
9205          * @param {String/HTMLElement/Array/Element/CompositeElement} el
9206          * @return {Roo.Element} this
9207          */
9208         appendChild: function(el){
9209             el = Roo.get(el);
9210             el.appendTo(this);
9211             return this;
9212         },
9213
9214         /**
9215          * Creates the passed DomHelper config and appends it to this element or optionally inserts it before the passed child element.
9216          * @param {Object} config DomHelper element config object.  If no tag is specified (e.g., {tag:'input'}) then a div will be
9217          * automatically generated with the specified attributes.
9218          * @param {HTMLElement} insertBefore (optional) a child element of this element
9219          * @param {Boolean} returnDom (optional) true to return the dom node instead of creating an Element
9220          * @return {Roo.Element} The new child element
9221          */
9222         createChild: function(config, insertBefore, returnDom){
9223             config = config || {tag:'div'};
9224             if(insertBefore){
9225                 return Roo.DomHelper.insertBefore(insertBefore, config, returnDom !== true);
9226             }
9227             return Roo.DomHelper[!this.dom.firstChild ? 'overwrite' : 'append'](this.dom, config,  returnDom !== true);
9228         },
9229
9230         /**
9231          * Appends this element to the passed element
9232          * @param {String/HTMLElement/Element} el The new parent element
9233          * @return {Roo.Element} this
9234          */
9235         appendTo: function(el){
9236             el = Roo.getDom(el);
9237             el.appendChild(this.dom);
9238             return this;
9239         },
9240
9241         /**
9242          * Inserts this element before the passed element in the DOM
9243          * @param {String/HTMLElement/Element} el The element to insert before
9244          * @return {Roo.Element} this
9245          */
9246         insertBefore: function(el){
9247             el = Roo.getDom(el);
9248             el.parentNode.insertBefore(this.dom, el);
9249             return this;
9250         },
9251
9252         /**
9253          * Inserts this element after the passed element in the DOM
9254          * @param {String/HTMLElement/Element} el The element to insert after
9255          * @return {Roo.Element} this
9256          */
9257         insertAfter: function(el){
9258             el = Roo.getDom(el);
9259             el.parentNode.insertBefore(this.dom, el.nextSibling);
9260             return this;
9261         },
9262
9263         /**
9264          * Inserts (or creates) an element (or DomHelper config) as the first child of the this element
9265          * @param {String/HTMLElement/Element/Object} el The id or element to insert or a DomHelper config to create and insert
9266          * @return {Roo.Element} The new child
9267          */
9268         insertFirst: function(el, returnDom){
9269             el = el || {};
9270             if(typeof el == 'object' && !el.nodeType){ // dh config
9271                 return this.createChild(el, this.dom.firstChild, returnDom);
9272             }else{
9273                 el = Roo.getDom(el);
9274                 this.dom.insertBefore(el, this.dom.firstChild);
9275                 return !returnDom ? Roo.get(el) : el;
9276             }
9277         },
9278
9279         /**
9280          * Inserts (or creates) the passed element (or DomHelper config) as a sibling of this element
9281          * @param {String/HTMLElement/Element/Object} el The id or element to insert or a DomHelper config to create and insert
9282          * @param {String} where (optional) 'before' or 'after' defaults to before
9283          * @param {Boolean} returnDom (optional) True to return the raw DOM element instead of Roo.Element
9284          * @return {Roo.Element} the inserted Element
9285          */
9286         insertSibling: function(el, where, returnDom){
9287             where = where ? where.toLowerCase() : 'before';
9288             el = el || {};
9289             var rt, refNode = where == 'before' ? this.dom : this.dom.nextSibling;
9290
9291             if(typeof el == 'object' && !el.nodeType){ // dh config
9292                 if(where == 'after' && !this.dom.nextSibling){
9293                     rt = Roo.DomHelper.append(this.dom.parentNode, el, !returnDom);
9294                 }else{
9295                     rt = Roo.DomHelper[where == 'after' ? 'insertAfter' : 'insertBefore'](this.dom, el, !returnDom);
9296                 }
9297
9298             }else{
9299                 rt = this.dom.parentNode.insertBefore(Roo.getDom(el),
9300                             where == 'before' ? this.dom : this.dom.nextSibling);
9301                 if(!returnDom){
9302                     rt = Roo.get(rt);
9303                 }
9304             }
9305             return rt;
9306         },
9307
9308         /**
9309          * Creates and wraps this element with another element
9310          * @param {Object} config (optional) DomHelper element config object for the wrapper element or null for an empty div
9311          * @param {Boolean} returnDom (optional) True to return the raw DOM element instead of Roo.Element
9312          * @return {HTMLElement/Element} The newly created wrapper element
9313          */
9314         wrap: function(config, returnDom){
9315             if(!config){
9316                 config = {tag: "div"};
9317             }
9318             var newEl = Roo.DomHelper.insertBefore(this.dom, config, !returnDom);
9319             newEl.dom ? newEl.dom.appendChild(this.dom) : newEl.appendChild(this.dom);
9320             return newEl;
9321         },
9322
9323         /**
9324          * Replaces the passed element with this element
9325          * @param {String/HTMLElement/Element} el The element to replace
9326          * @return {Roo.Element} this
9327          */
9328         replace: function(el){
9329             el = Roo.get(el);
9330             this.insertBefore(el);
9331             el.remove();
9332             return this;
9333         },
9334
9335         /**
9336          * Inserts an html fragment into this element
9337          * @param {String} where Where to insert the html in relation to the this element - beforeBegin, afterBegin, beforeEnd, afterEnd.
9338          * @param {String} html The HTML fragment
9339          * @param {Boolean} returnEl True to return an Roo.Element
9340          * @return {HTMLElement/Roo.Element} The inserted node (or nearest related if more than 1 inserted)
9341          */
9342         insertHtml : function(where, html, returnEl){
9343             var el = Roo.DomHelper.insertHtml(where, this.dom, html);
9344             return returnEl ? Roo.get(el) : el;
9345         },
9346
9347         /**
9348          * Sets the passed attributes as attributes of this element (a style attribute can be a string, object or function)
9349          * @param {Object} o The object with the attributes
9350          * @param {Boolean} useSet (optional) false to override the default setAttribute to use expandos.
9351          * @return {Roo.Element} this
9352          */
9353         set : function(o, useSet){
9354             var el = this.dom;
9355             useSet = typeof useSet == 'undefined' ? (el.setAttribute ? true : false) : useSet;
9356             for(var attr in o){
9357                 if(attr == "style" || typeof o[attr] == "function") continue;
9358                 if(attr=="cls"){
9359                     el.className = o["cls"];
9360                 }else{
9361                     if(useSet) el.setAttribute(attr, o[attr]);
9362                     else el[attr] = o[attr];
9363                 }
9364             }
9365             if(o.style){
9366                 Roo.DomHelper.applyStyles(el, o.style);
9367             }
9368             return this;
9369         },
9370
9371         /**
9372          * Convenience method for constructing a KeyMap
9373          * @param {Number/Array/Object/String} key Either a string with the keys to listen for, the numeric key code, array of key codes or an object with the following options:
9374          *                                  {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
9375          * @param {Function} fn The function to call
9376          * @param {Object} scope (optional) The scope of the function
9377          * @return {Roo.KeyMap} The KeyMap created
9378          */
9379         addKeyListener : function(key, fn, scope){
9380             var config;
9381             if(typeof key != "object" || key instanceof Array){
9382                 config = {
9383                     key: key,
9384                     fn: fn,
9385                     scope: scope
9386                 };
9387             }else{
9388                 config = {
9389                     key : key.key,
9390                     shift : key.shift,
9391                     ctrl : key.ctrl,
9392                     alt : key.alt,
9393                     fn: fn,
9394                     scope: scope
9395                 };
9396             }
9397             return new Roo.KeyMap(this, config);
9398         },
9399
9400         /**
9401          * Creates a KeyMap for this element
9402          * @param {Object} config The KeyMap config. See {@link Roo.KeyMap} for more details
9403          * @return {Roo.KeyMap} The KeyMap created
9404          */
9405         addKeyMap : function(config){
9406             return new Roo.KeyMap(this, config);
9407         },
9408
9409         /**
9410          * Returns true if this element is scrollable.
9411          * @return {Boolean}
9412          */
9413          isScrollable : function(){
9414             var dom = this.dom;
9415             return dom.scrollHeight > dom.clientHeight || dom.scrollWidth > dom.clientWidth;
9416         },
9417
9418         /**
9419          * Scrolls this element the specified scroll point. It does NOT do bounds checking so if you scroll to a weird value it will try to do it. For auto bounds checking, use scroll().
9420          * @param {String} side Either "left" for scrollLeft values or "top" for scrollTop values.
9421          * @param {Number} value The new scroll value
9422          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9423          * @return {Element} this
9424          */
9425
9426         scrollTo : function(side, value, animate){
9427             var prop = side.toLowerCase() == "left" ? "scrollLeft" : "scrollTop";
9428             if(!animate || !A){
9429                 this.dom[prop] = value;
9430             }else{
9431                 var to = prop == "scrollLeft" ? [value, this.dom.scrollTop] : [this.dom.scrollLeft, value];
9432                 this.anim({scroll: {"to": to}}, this.preanim(arguments, 2), 'scroll');
9433             }
9434             return this;
9435         },
9436
9437         /**
9438          * Scrolls this element the specified direction. Does bounds checking to make sure the scroll is
9439          * within this element's scrollable range.
9440          * @param {String} direction Possible values are: "l","left" - "r","right" - "t","top","up" - "b","bottom","down".
9441          * @param {Number} distance How far to scroll the element in pixels
9442          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9443          * @return {Boolean} Returns true if a scroll was triggered or false if the element
9444          * was scrolled as far as it could go.
9445          */
9446          scroll : function(direction, distance, animate){
9447              if(!this.isScrollable()){
9448                  return;
9449              }
9450              var el = this.dom;
9451              var l = el.scrollLeft, t = el.scrollTop;
9452              var w = el.scrollWidth, h = el.scrollHeight;
9453              var cw = el.clientWidth, ch = el.clientHeight;
9454              direction = direction.toLowerCase();
9455              var scrolled = false;
9456              var a = this.preanim(arguments, 2);
9457              switch(direction){
9458                  case "l":
9459                  case "left":
9460                      if(w - l > cw){
9461                          var v = Math.min(l + distance, w-cw);
9462                          this.scrollTo("left", v, a);
9463                          scrolled = true;
9464                      }
9465                      break;
9466                 case "r":
9467                 case "right":
9468                      if(l > 0){
9469                          var v = Math.max(l - distance, 0);
9470                          this.scrollTo("left", v, a);
9471                          scrolled = true;
9472                      }
9473                      break;
9474                 case "t":
9475                 case "top":
9476                 case "up":
9477                      if(t > 0){
9478                          var v = Math.max(t - distance, 0);
9479                          this.scrollTo("top", v, a);
9480                          scrolled = true;
9481                      }
9482                      break;
9483                 case "b":
9484                 case "bottom":
9485                 case "down":
9486                      if(h - t > ch){
9487                          var v = Math.min(t + distance, h-ch);
9488                          this.scrollTo("top", v, a);
9489                          scrolled = true;
9490                      }
9491                      break;
9492              }
9493              return scrolled;
9494         },
9495
9496         /**
9497          * Translates the passed page coordinates into left/top css values for this element
9498          * @param {Number/Array} x The page x or an array containing [x, y]
9499          * @param {Number} y The page y
9500          * @return {Object} An object with left and top properties. e.g. {left: (value), top: (value)}
9501          */
9502         translatePoints : function(x, y){
9503             if(typeof x == 'object' || x instanceof Array){
9504                 y = x[1]; x = x[0];
9505             }
9506             var p = this.getStyle('position');
9507             var o = this.getXY();
9508
9509             var l = parseInt(this.getStyle('left'), 10);
9510             var t = parseInt(this.getStyle('top'), 10);
9511
9512             if(isNaN(l)){
9513                 l = (p == "relative") ? 0 : this.dom.offsetLeft;
9514             }
9515             if(isNaN(t)){
9516                 t = (p == "relative") ? 0 : this.dom.offsetTop;
9517             }
9518
9519             return {left: (x - o[0] + l), top: (y - o[1] + t)};
9520         },
9521
9522         /**
9523          * Returns the current scroll position of the element.
9524          * @return {Object} An object containing the scroll position in the format {left: (scrollLeft), top: (scrollTop)}
9525          */
9526         getScroll : function(){
9527             var d = this.dom, doc = document;
9528             if(d == doc || d == doc.body){
9529                 var l = window.pageXOffset || doc.documentElement.scrollLeft || doc.body.scrollLeft || 0;
9530                 var t = window.pageYOffset || doc.documentElement.scrollTop || doc.body.scrollTop || 0;
9531                 return {left: l, top: t};
9532             }else{
9533                 return {left: d.scrollLeft, top: d.scrollTop};
9534             }
9535         },
9536
9537         /**
9538          * Return the CSS color for the specified CSS attribute. rgb, 3 digit (like #fff) and valid values
9539          * are convert to standard 6 digit hex color.
9540          * @param {String} attr The css attribute
9541          * @param {String} defaultValue The default value to use when a valid color isn't found
9542          * @param {String} prefix (optional) defaults to #. Use an empty string when working with
9543          * YUI color anims.
9544          */
9545         getColor : function(attr, defaultValue, prefix){
9546             var v = this.getStyle(attr);
9547             if(!v || v == "transparent" || v == "inherit") {
9548                 return defaultValue;
9549             }
9550             var color = typeof prefix == "undefined" ? "#" : prefix;
9551             if(v.substr(0, 4) == "rgb("){
9552                 var rvs = v.slice(4, v.length -1).split(",");
9553                 for(var i = 0; i < 3; i++){
9554                     var h = parseInt(rvs[i]).toString(16);
9555                     if(h < 16){
9556                         h = "0" + h;
9557                     }
9558                     color += h;
9559                 }
9560             } else {
9561                 if(v.substr(0, 1) == "#"){
9562                     if(v.length == 4) {
9563                         for(var i = 1; i < 4; i++){
9564                             var c = v.charAt(i);
9565                             color +=  c + c;
9566                         }
9567                     }else if(v.length == 7){
9568                         color += v.substr(1);
9569                     }
9570                 }
9571             }
9572             return(color.length > 5 ? color.toLowerCase() : defaultValue);
9573         },
9574
9575         /**
9576          * Wraps the specified element with a special markup/CSS block that renders by default as a gray container with a
9577          * gradient background, rounded corners and a 4-way shadow.
9578          * @param {String} class (optional) A base CSS class to apply to the containing wrapper element (defaults to 'x-box').
9579          * Note that there are a number of CSS rules that are dependent on this name to make the overall effect work,
9580          * so if you supply an alternate base class, make sure you also supply all of the necessary rules.
9581          * @return {Roo.Element} this
9582          */
9583         boxWrap : function(cls){
9584             cls = cls || 'x-box';
9585             var el = Roo.get(this.insertHtml('beforeBegin', String.format('<div class="{0}">'+El.boxMarkup+'</div>', cls)));
9586             el.child('.'+cls+'-mc').dom.appendChild(this.dom);
9587             return el;
9588         },
9589
9590         /**
9591          * Returns the value of a namespaced attribute from the element's underlying DOM node.
9592          * @param {String} namespace The namespace in which to look for the attribute
9593          * @param {String} name The attribute name
9594          * @return {String} The attribute value
9595          */
9596         getAttributeNS : Roo.isIE ? function(ns, name){
9597             var d = this.dom;
9598             var type = typeof d[ns+":"+name];
9599             if(type != 'undefined' && type != 'unknown'){
9600                 return d[ns+":"+name];
9601             }
9602             return d[name];
9603         } : function(ns, name){
9604             var d = this.dom;
9605             return d.getAttributeNS(ns, name) || d.getAttribute(ns+":"+name) || d.getAttribute(name) || d[name];
9606         },
9607         
9608         
9609         /**
9610          * Sets or Returns the value the dom attribute value
9611          * @param {String|Object} name The attribute name (or object to set multiple attributes)
9612          * @param {String} value (optional) The value to set the attribute to
9613          * @return {String} The attribute value
9614          */
9615         attr : function(name){
9616             if (arguments.length > 1) {
9617                 this.dom.setAttribute(name, arguments[1]);
9618                 return arguments[1];
9619             }
9620             if (typeof(name) == 'object') {
9621                 for(var i in name) {
9622                     this.attr(i, name[i]);
9623                 }
9624                 return name;
9625             }
9626             
9627             
9628             if (!this.dom.hasAttribute(name)) {
9629                 return undefined;
9630             }
9631             return this.dom.getAttribute(name);
9632         }
9633         
9634         
9635         
9636     };
9637
9638     var ep = El.prototype;
9639
9640     /**
9641      * Appends an event handler (Shorthand for addListener)
9642      * @param {String}   eventName     The type of event to append
9643      * @param {Function} fn        The method the event invokes
9644      * @param {Object} scope       (optional) The scope (this object) of the fn
9645      * @param {Object}   options   (optional)An object with standard {@link Roo.EventManager#addListener} options
9646      * @method
9647      */
9648     ep.on = ep.addListener;
9649         // backwards compat
9650     ep.mon = ep.addListener;
9651
9652     /**
9653      * Removes an event handler from this element (shorthand for removeListener)
9654      * @param {String} eventName the type of event to remove
9655      * @param {Function} fn the method the event invokes
9656      * @return {Roo.Element} this
9657      * @method
9658      */
9659     ep.un = ep.removeListener;
9660
9661     /**
9662      * true to automatically adjust width and height settings for box-model issues (default to true)
9663      */
9664     ep.autoBoxAdjust = true;
9665
9666     // private
9667     El.unitPattern = /\d+(px|em|%|en|ex|pt|in|cm|mm|pc)$/i;
9668
9669     // private
9670     El.addUnits = function(v, defaultUnit){
9671         if(v === "" || v == "auto"){
9672             return v;
9673         }
9674         if(v === undefined){
9675             return '';
9676         }
9677         if(typeof v == "number" || !El.unitPattern.test(v)){
9678             return v + (defaultUnit || 'px');
9679         }
9680         return v;
9681     };
9682
9683     // special markup used throughout Roo when box wrapping elements
9684     El.boxMarkup = '<div class="{0}-tl"><div class="{0}-tr"><div class="{0}-tc"></div></div></div><div class="{0}-ml"><div class="{0}-mr"><div class="{0}-mc"></div></div></div><div class="{0}-bl"><div class="{0}-br"><div class="{0}-bc"></div></div></div>';
9685     /**
9686      * Visibility mode constant - Use visibility to hide element
9687      * @static
9688      * @type Number
9689      */
9690     El.VISIBILITY = 1;
9691     /**
9692      * Visibility mode constant - Use display to hide element
9693      * @static
9694      * @type Number
9695      */
9696     El.DISPLAY = 2;
9697
9698     El.borders = {l: "border-left-width", r: "border-right-width", t: "border-top-width", b: "border-bottom-width"};
9699     El.paddings = {l: "padding-left", r: "padding-right", t: "padding-top", b: "padding-bottom"};
9700     El.margins = {l: "margin-left", r: "margin-right", t: "margin-top", b: "margin-bottom"};
9701
9702
9703
9704     /**
9705      * @private
9706      */
9707     El.cache = {};
9708
9709     var docEl;
9710
9711     /**
9712      * Static method to retrieve Element objects. Uses simple caching to consistently return the same object.
9713      * Automatically fixes if an object was recreated with the same id via AJAX or DOM.
9714      * @param {String/HTMLElement/Element} el The id of the node, a DOM Node or an existing Element.
9715      * @return {Element} The Element object
9716      * @static
9717      */
9718     El.get = function(el){
9719         var ex, elm, id;
9720         if(!el){ return null; }
9721         if(typeof el == "string"){ // element id
9722             if(!(elm = document.getElementById(el))){
9723                 return null;
9724             }
9725             if(ex = El.cache[el]){
9726                 ex.dom = elm;
9727             }else{
9728                 ex = El.cache[el] = new El(elm);
9729             }
9730             return ex;
9731         }else if(el.tagName){ // dom element
9732             if(!(id = el.id)){
9733                 id = Roo.id(el);
9734             }
9735             if(ex = El.cache[id]){
9736                 ex.dom = el;
9737             }else{
9738                 ex = El.cache[id] = new El(el);
9739             }
9740             return ex;
9741         }else if(el instanceof El){
9742             if(el != docEl){
9743                 el.dom = document.getElementById(el.id) || el.dom; // refresh dom element in case no longer valid,
9744                                                               // catch case where it hasn't been appended
9745                 El.cache[el.id] = el; // in case it was created directly with Element(), let's cache it
9746             }
9747             return el;
9748         }else if(el.isComposite){
9749             return el;
9750         }else if(el instanceof Array){
9751             return El.select(el);
9752         }else if(el == document){
9753             // create a bogus element object representing the document object
9754             if(!docEl){
9755                 var f = function(){};
9756                 f.prototype = El.prototype;
9757                 docEl = new f();
9758                 docEl.dom = document;
9759             }
9760             return docEl;
9761         }
9762         return null;
9763     };
9764
9765     // private
9766     El.uncache = function(el){
9767         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
9768             if(a[i]){
9769                 delete El.cache[a[i].id || a[i]];
9770             }
9771         }
9772     };
9773
9774     // private
9775     // Garbage collection - uncache elements/purge listeners on orphaned elements
9776     // so we don't hold a reference and cause the browser to retain them
9777     El.garbageCollect = function(){
9778         if(!Roo.enableGarbageCollector){
9779             clearInterval(El.collectorThread);
9780             return;
9781         }
9782         for(var eid in El.cache){
9783             var el = El.cache[eid], d = el.dom;
9784             // -------------------------------------------------------
9785             // Determining what is garbage:
9786             // -------------------------------------------------------
9787             // !d
9788             // dom node is null, definitely garbage
9789             // -------------------------------------------------------
9790             // !d.parentNode
9791             // no parentNode == direct orphan, definitely garbage
9792             // -------------------------------------------------------
9793             // !d.offsetParent && !document.getElementById(eid)
9794             // display none elements have no offsetParent so we will
9795             // also try to look it up by it's id. However, check
9796             // offsetParent first so we don't do unneeded lookups.
9797             // This enables collection of elements that are not orphans
9798             // directly, but somewhere up the line they have an orphan
9799             // parent.
9800             // -------------------------------------------------------
9801             if(!d || !d.parentNode || (!d.offsetParent && !document.getElementById(eid))){
9802                 delete El.cache[eid];
9803                 if(d && Roo.enableListenerCollection){
9804                     E.purgeElement(d);
9805                 }
9806             }
9807         }
9808     }
9809     El.collectorThreadId = setInterval(El.garbageCollect, 30000);
9810
9811
9812     // dom is optional
9813     El.Flyweight = function(dom){
9814         this.dom = dom;
9815     };
9816     El.Flyweight.prototype = El.prototype;
9817
9818     El._flyweights = {};
9819     /**
9820      * Gets the globally shared flyweight Element, with the passed node as the active element. Do not store a reference to this element -
9821      * the dom node can be overwritten by other code.
9822      * @param {String/HTMLElement} el The dom node or id
9823      * @param {String} named (optional) Allows for creation of named reusable flyweights to
9824      *                                  prevent conflicts (e.g. internally Roo uses "_internal")
9825      * @static
9826      * @return {Element} The shared Element object
9827      */
9828     El.fly = function(el, named){
9829         named = named || '_global';
9830         el = Roo.getDom(el);
9831         if(!el){
9832             return null;
9833         }
9834         if(!El._flyweights[named]){
9835             El._flyweights[named] = new El.Flyweight();
9836         }
9837         El._flyweights[named].dom = el;
9838         return El._flyweights[named];
9839     };
9840
9841     /**
9842      * Static method to retrieve Element objects. Uses simple caching to consistently return the same object.
9843      * Automatically fixes if an object was recreated with the same id via AJAX or DOM.
9844      * Shorthand of {@link Roo.Element#get}
9845      * @param {String/HTMLElement/Element} el The id of the node, a DOM Node or an existing Element.
9846      * @return {Element} The Element object
9847      * @member Roo
9848      * @method get
9849      */
9850     Roo.get = El.get;
9851     /**
9852      * Gets the globally shared flyweight Element, with the passed node as the active element. Do not store a reference to this element -
9853      * the dom node can be overwritten by other code.
9854      * Shorthand of {@link Roo.Element#fly}
9855      * @param {String/HTMLElement} el The dom node or id
9856      * @param {String} named (optional) Allows for creation of named reusable flyweights to
9857      *                                  prevent conflicts (e.g. internally Roo uses "_internal")
9858      * @static
9859      * @return {Element} The shared Element object
9860      * @member Roo
9861      * @method fly
9862      */
9863     Roo.fly = El.fly;
9864
9865     // speedy lookup for elements never to box adjust
9866     var noBoxAdjust = Roo.isStrict ? {
9867         select:1
9868     } : {
9869         input:1, select:1, textarea:1
9870     };
9871     if(Roo.isIE || Roo.isGecko){
9872         noBoxAdjust['button'] = 1;
9873     }
9874
9875
9876     Roo.EventManager.on(window, 'unload', function(){
9877         delete El.cache;
9878         delete El._flyweights;
9879     });
9880 })();
9881
9882
9883
9884
9885 if(Roo.DomQuery){
9886     Roo.Element.selectorFunction = Roo.DomQuery.select;
9887 }
9888
9889 Roo.Element.select = function(selector, unique, root){
9890     var els;
9891     if(typeof selector == "string"){
9892         els = Roo.Element.selectorFunction(selector, root);
9893     }else if(selector.length !== undefined){
9894         els = selector;
9895     }else{
9896         throw "Invalid selector";
9897     }
9898     if(unique === true){
9899         return new Roo.CompositeElement(els);
9900     }else{
9901         return new Roo.CompositeElementLite(els);
9902     }
9903 };
9904 /**
9905  * Selects elements based on the passed CSS selector to enable working on them as 1.
9906  * @param {String/Array} selector The CSS selector or an array of elements
9907  * @param {Boolean} unique (optional) true to create a unique Roo.Element for each element (defaults to a shared flyweight object)
9908  * @param {HTMLElement/String} root (optional) The root element of the query or id of the root
9909  * @return {CompositeElementLite/CompositeElement}
9910  * @member Roo
9911  * @method select
9912  */
9913 Roo.select = Roo.Element.select;
9914
9915
9916
9917
9918
9919
9920
9921
9922
9923
9924
9925
9926
9927
9928 /*
9929  * Based on:
9930  * Ext JS Library 1.1.1
9931  * Copyright(c) 2006-2007, Ext JS, LLC.
9932  *
9933  * Originally Released Under LGPL - original licence link has changed is not relivant.
9934  *
9935  * Fork - LGPL
9936  * <script type="text/javascript">
9937  */
9938
9939
9940
9941 //Notifies Element that fx methods are available
9942 Roo.enableFx = true;
9943
9944 /**
9945  * @class Roo.Fx
9946  * <p>A class to provide basic animation and visual effects support.  <b>Note:</b> This class is automatically applied
9947  * to the {@link Roo.Element} interface when included, so all effects calls should be performed via Element.
9948  * Conversely, since the effects are not actually defined in Element, Roo.Fx <b>must</b> be included in order for the 
9949  * Element effects to work.</p><br/>
9950  *
9951  * <p>It is important to note that although the Fx methods and many non-Fx Element methods support "method chaining" in that
9952  * they return the Element object itself as the method return value, it is not always possible to mix the two in a single
9953  * method chain.  The Fx methods use an internal effects queue so that each effect can be properly timed and sequenced.
9954  * Non-Fx methods, on the other hand, have no such internal queueing and will always execute immediately.  For this reason,
9955  * while it may be possible to mix certain Fx and non-Fx method calls in a single chain, it may not always provide the
9956  * expected results and should be done with care.</p><br/>
9957  *
9958  * <p>Motion effects support 8-way anchoring, meaning that you can choose one of 8 different anchor points on the Element
9959  * that will serve as either the start or end point of the animation.  Following are all of the supported anchor positions:</p>
9960 <pre>
9961 Value  Description
9962 -----  -----------------------------
9963 tl     The top left corner
9964 t      The center of the top edge
9965 tr     The top right corner
9966 l      The center of the left edge
9967 r      The center of the right edge
9968 bl     The bottom left corner
9969 b      The center of the bottom edge
9970 br     The bottom right corner
9971 </pre>
9972  * <b>Although some Fx methods accept specific custom config parameters, the ones shown in the Config Options section
9973  * below are common options that can be passed to any Fx method.</b>
9974  * @cfg {Function} callback A function called when the effect is finished
9975  * @cfg {Object} scope The scope of the effect function
9976  * @cfg {String} easing A valid Easing value for the effect
9977  * @cfg {String} afterCls A css class to apply after the effect
9978  * @cfg {Number} duration The length of time (in seconds) that the effect should last
9979  * @cfg {Boolean} remove Whether the Element should be removed from the DOM and destroyed after the effect finishes
9980  * @cfg {Boolean} useDisplay Whether to use the <i>display</i> CSS property instead of <i>visibility</i> when hiding Elements (only applies to 
9981  * effects that end with the element being visually hidden, ignored otherwise)
9982  * @cfg {String/Object/Function} afterStyle A style specification string, e.g. "width:100px", or an object in the form {width:"100px"}, or
9983  * a function which returns such a specification that will be applied to the Element after the effect finishes
9984  * @cfg {Boolean} block Whether the effect should block other effects from queueing while it runs
9985  * @cfg {Boolean} concurrent Whether to allow subsequently-queued effects to run at the same time as the current effect, or to ensure that they run in sequence
9986  * @cfg {Boolean} stopFx Whether subsequent effects should be stopped and removed after the current effect finishes
9987  */
9988 Roo.Fx = {
9989         /**
9990          * Slides the element into view.  An anchor point can be optionally passed to set the point of
9991          * origin for the slide effect.  This function automatically handles wrapping the element with
9992          * a fixed-size container if needed.  See the Fx class overview for valid anchor point options.
9993          * Usage:
9994          *<pre><code>
9995 // default: slide the element in from the top
9996 el.slideIn();
9997
9998 // custom: slide the element in from the right with a 2-second duration
9999 el.slideIn('r', { duration: 2 });
10000
10001 // common config options shown with default values
10002 el.slideIn('t', {
10003     easing: 'easeOut',
10004     duration: .5
10005 });
10006 </code></pre>
10007          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to top: 't')
10008          * @param {Object} options (optional) Object literal with any of the Fx config options
10009          * @return {Roo.Element} The Element
10010          */
10011     slideIn : function(anchor, o){
10012         var el = this.getFxEl();
10013         o = o || {};
10014
10015         el.queueFx(o, function(){
10016
10017             anchor = anchor || "t";
10018
10019             // fix display to visibility
10020             this.fixDisplay();
10021
10022             // restore values after effect
10023             var r = this.getFxRestore();
10024             var b = this.getBox();
10025             // fixed size for slide
10026             this.setSize(b);
10027
10028             // wrap if needed
10029             var wrap = this.fxWrap(r.pos, o, "hidden");
10030
10031             var st = this.dom.style;
10032             st.visibility = "visible";
10033             st.position = "absolute";
10034
10035             // clear out temp styles after slide and unwrap
10036             var after = function(){
10037                 el.fxUnwrap(wrap, r.pos, o);
10038                 st.width = r.width;
10039                 st.height = r.height;
10040                 el.afterFx(o);
10041             };
10042             // time to calc the positions
10043             var a, pt = {to: [b.x, b.y]}, bw = {to: b.width}, bh = {to: b.height};
10044
10045             switch(anchor.toLowerCase()){
10046                 case "t":
10047                     wrap.setSize(b.width, 0);
10048                     st.left = st.bottom = "0";
10049                     a = {height: bh};
10050                 break;
10051                 case "l":
10052                     wrap.setSize(0, b.height);
10053                     st.right = st.top = "0";
10054                     a = {width: bw};
10055                 break;
10056                 case "r":
10057                     wrap.setSize(0, b.height);
10058                     wrap.setX(b.right);
10059                     st.left = st.top = "0";
10060                     a = {width: bw, points: pt};
10061                 break;
10062                 case "b":
10063                     wrap.setSize(b.width, 0);
10064                     wrap.setY(b.bottom);
10065                     st.left = st.top = "0";
10066                     a = {height: bh, points: pt};
10067                 break;
10068                 case "tl":
10069                     wrap.setSize(0, 0);
10070                     st.right = st.bottom = "0";
10071                     a = {width: bw, height: bh};
10072                 break;
10073                 case "bl":
10074                     wrap.setSize(0, 0);
10075                     wrap.setY(b.y+b.height);
10076                     st.right = st.top = "0";
10077                     a = {width: bw, height: bh, points: pt};
10078                 break;
10079                 case "br":
10080                     wrap.setSize(0, 0);
10081                     wrap.setXY([b.right, b.bottom]);
10082                     st.left = st.top = "0";
10083                     a = {width: bw, height: bh, points: pt};
10084                 break;
10085                 case "tr":
10086                     wrap.setSize(0, 0);
10087                     wrap.setX(b.x+b.width);
10088                     st.left = st.bottom = "0";
10089                     a = {width: bw, height: bh, points: pt};
10090                 break;
10091             }
10092             this.dom.style.visibility = "visible";
10093             wrap.show();
10094
10095             arguments.callee.anim = wrap.fxanim(a,
10096                 o,
10097                 'motion',
10098                 .5,
10099                 'easeOut', after);
10100         });
10101         return this;
10102     },
10103     
10104         /**
10105          * Slides the element out of view.  An anchor point can be optionally passed to set the end point
10106          * for the slide effect.  When the effect is completed, the element will be hidden (visibility = 
10107          * 'hidden') but block elements will still take up space in the document.  The element must be removed
10108          * from the DOM using the 'remove' config option if desired.  This function automatically handles 
10109          * wrapping the element with a fixed-size container if needed.  See the Fx class overview for valid anchor point options.
10110          * Usage:
10111          *<pre><code>
10112 // default: slide the element out to the top
10113 el.slideOut();
10114
10115 // custom: slide the element out to the right with a 2-second duration
10116 el.slideOut('r', { duration: 2 });
10117
10118 // common config options shown with default values
10119 el.slideOut('t', {
10120     easing: 'easeOut',
10121     duration: .5,
10122     remove: false,
10123     useDisplay: false
10124 });
10125 </code></pre>
10126          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to top: 't')
10127          * @param {Object} options (optional) Object literal with any of the Fx config options
10128          * @return {Roo.Element} The Element
10129          */
10130     slideOut : function(anchor, o){
10131         var el = this.getFxEl();
10132         o = o || {};
10133
10134         el.queueFx(o, function(){
10135
10136             anchor = anchor || "t";
10137
10138             // restore values after effect
10139             var r = this.getFxRestore();
10140             
10141             var b = this.getBox();
10142             // fixed size for slide
10143             this.setSize(b);
10144
10145             // wrap if needed
10146             var wrap = this.fxWrap(r.pos, o, "visible");
10147
10148             var st = this.dom.style;
10149             st.visibility = "visible";
10150             st.position = "absolute";
10151
10152             wrap.setSize(b);
10153
10154             var after = function(){
10155                 if(o.useDisplay){
10156                     el.setDisplayed(false);
10157                 }else{
10158                     el.hide();
10159                 }
10160
10161                 el.fxUnwrap(wrap, r.pos, o);
10162
10163                 st.width = r.width;
10164                 st.height = r.height;
10165
10166                 el.afterFx(o);
10167             };
10168
10169             var a, zero = {to: 0};
10170             switch(anchor.toLowerCase()){
10171                 case "t":
10172                     st.left = st.bottom = "0";
10173                     a = {height: zero};
10174                 break;
10175                 case "l":
10176                     st.right = st.top = "0";
10177                     a = {width: zero};
10178                 break;
10179                 case "r":
10180                     st.left = st.top = "0";
10181                     a = {width: zero, points: {to:[b.right, b.y]}};
10182                 break;
10183                 case "b":
10184                     st.left = st.top = "0";
10185                     a = {height: zero, points: {to:[b.x, b.bottom]}};
10186                 break;
10187                 case "tl":
10188                     st.right = st.bottom = "0";
10189                     a = {width: zero, height: zero};
10190                 break;
10191                 case "bl":
10192                     st.right = st.top = "0";
10193                     a = {width: zero, height: zero, points: {to:[b.x, b.bottom]}};
10194                 break;
10195                 case "br":
10196                     st.left = st.top = "0";
10197                     a = {width: zero, height: zero, points: {to:[b.x+b.width, b.bottom]}};
10198                 break;
10199                 case "tr":
10200                     st.left = st.bottom = "0";
10201                     a = {width: zero, height: zero, points: {to:[b.right, b.y]}};
10202                 break;
10203             }
10204
10205             arguments.callee.anim = wrap.fxanim(a,
10206                 o,
10207                 'motion',
10208                 .5,
10209                 "easeOut", after);
10210         });
10211         return this;
10212     },
10213
10214         /**
10215          * Fades the element out while slowly expanding it in all directions.  When the effect is completed, the 
10216          * element will be hidden (visibility = 'hidden') but block elements will still take up space in the document. 
10217          * The element must be removed from the DOM using the 'remove' config option if desired.
10218          * Usage:
10219          *<pre><code>
10220 // default
10221 el.puff();
10222
10223 // common config options shown with default values
10224 el.puff({
10225     easing: 'easeOut',
10226     duration: .5,
10227     remove: false,
10228     useDisplay: false
10229 });
10230 </code></pre>
10231          * @param {Object} options (optional) Object literal with any of the Fx config options
10232          * @return {Roo.Element} The Element
10233          */
10234     puff : function(o){
10235         var el = this.getFxEl();
10236         o = o || {};
10237
10238         el.queueFx(o, function(){
10239             this.clearOpacity();
10240             this.show();
10241
10242             // restore values after effect
10243             var r = this.getFxRestore();
10244             var st = this.dom.style;
10245
10246             var after = function(){
10247                 if(o.useDisplay){
10248                     el.setDisplayed(false);
10249                 }else{
10250                     el.hide();
10251                 }
10252
10253                 el.clearOpacity();
10254
10255                 el.setPositioning(r.pos);
10256                 st.width = r.width;
10257                 st.height = r.height;
10258                 st.fontSize = '';
10259                 el.afterFx(o);
10260             };
10261
10262             var width = this.getWidth();
10263             var height = this.getHeight();
10264
10265             arguments.callee.anim = this.fxanim({
10266                     width : {to: this.adjustWidth(width * 2)},
10267                     height : {to: this.adjustHeight(height * 2)},
10268                     points : {by: [-(width * .5), -(height * .5)]},
10269                     opacity : {to: 0},
10270                     fontSize: {to:200, unit: "%"}
10271                 },
10272                 o,
10273                 'motion',
10274                 .5,
10275                 "easeOut", after);
10276         });
10277         return this;
10278     },
10279
10280         /**
10281          * Blinks the element as if it was clicked and then collapses on its center (similar to switching off a television).
10282          * When the effect is completed, the element will be hidden (visibility = 'hidden') but block elements will still 
10283          * take up space in the document. The element must be removed from the DOM using the 'remove' config option if desired.
10284          * Usage:
10285          *<pre><code>
10286 // default
10287 el.switchOff();
10288
10289 // all config options shown with default values
10290 el.switchOff({
10291     easing: 'easeIn',
10292     duration: .3,
10293     remove: false,
10294     useDisplay: false
10295 });
10296 </code></pre>
10297          * @param {Object} options (optional) Object literal with any of the Fx config options
10298          * @return {Roo.Element} The Element
10299          */
10300     switchOff : function(o){
10301         var el = this.getFxEl();
10302         o = o || {};
10303
10304         el.queueFx(o, function(){
10305             this.clearOpacity();
10306             this.clip();
10307
10308             // restore values after effect
10309             var r = this.getFxRestore();
10310             var st = this.dom.style;
10311
10312             var after = function(){
10313                 if(o.useDisplay){
10314                     el.setDisplayed(false);
10315                 }else{
10316                     el.hide();
10317                 }
10318
10319                 el.clearOpacity();
10320                 el.setPositioning(r.pos);
10321                 st.width = r.width;
10322                 st.height = r.height;
10323
10324                 el.afterFx(o);
10325             };
10326
10327             this.fxanim({opacity:{to:0.3}}, null, null, .1, null, function(){
10328                 this.clearOpacity();
10329                 (function(){
10330                     this.fxanim({
10331                         height:{to:1},
10332                         points:{by:[0, this.getHeight() * .5]}
10333                     }, o, 'motion', 0.3, 'easeIn', after);
10334                 }).defer(100, this);
10335             });
10336         });
10337         return this;
10338     },
10339
10340     /**
10341      * Highlights the Element by setting a color (applies to the background-color by default, but can be
10342      * changed using the "attr" config option) and then fading back to the original color. If no original
10343      * color is available, you should provide the "endColor" config option which will be cleared after the animation.
10344      * Usage:
10345 <pre><code>
10346 // default: highlight background to yellow
10347 el.highlight();
10348
10349 // custom: highlight foreground text to blue for 2 seconds
10350 el.highlight("0000ff", { attr: 'color', duration: 2 });
10351
10352 // common config options shown with default values
10353 el.highlight("ffff9c", {
10354     attr: "background-color", //can be any valid CSS property (attribute) that supports a color value
10355     endColor: (current color) or "ffffff",
10356     easing: 'easeIn',
10357     duration: 1
10358 });
10359 </code></pre>
10360      * @param {String} color (optional) The highlight color. Should be a 6 char hex color without the leading # (defaults to yellow: 'ffff9c')
10361      * @param {Object} options (optional) Object literal with any of the Fx config options
10362      * @return {Roo.Element} The Element
10363      */ 
10364     highlight : function(color, o){
10365         var el = this.getFxEl();
10366         o = o || {};
10367
10368         el.queueFx(o, function(){
10369             color = color || "ffff9c";
10370             attr = o.attr || "backgroundColor";
10371
10372             this.clearOpacity();
10373             this.show();
10374
10375             var origColor = this.getColor(attr);
10376             var restoreColor = this.dom.style[attr];
10377             endColor = (o.endColor || origColor) || "ffffff";
10378
10379             var after = function(){
10380                 el.dom.style[attr] = restoreColor;
10381                 el.afterFx(o);
10382             };
10383
10384             var a = {};
10385             a[attr] = {from: color, to: endColor};
10386             arguments.callee.anim = this.fxanim(a,
10387                 o,
10388                 'color',
10389                 1,
10390                 'easeIn', after);
10391         });
10392         return this;
10393     },
10394
10395    /**
10396     * Shows a ripple of exploding, attenuating borders to draw attention to an Element.
10397     * Usage:
10398 <pre><code>
10399 // default: a single light blue ripple
10400 el.frame();
10401
10402 // custom: 3 red ripples lasting 3 seconds total
10403 el.frame("ff0000", 3, { duration: 3 });
10404
10405 // common config options shown with default values
10406 el.frame("C3DAF9", 1, {
10407     duration: 1 //duration of entire animation (not each individual ripple)
10408     // Note: Easing is not configurable and will be ignored if included
10409 });
10410 </code></pre>
10411     * @param {String} color (optional) The color of the border.  Should be a 6 char hex color without the leading # (defaults to light blue: 'C3DAF9').
10412     * @param {Number} count (optional) The number of ripples to display (defaults to 1)
10413     * @param {Object} options (optional) Object literal with any of the Fx config options
10414     * @return {Roo.Element} The Element
10415     */
10416     frame : function(color, count, o){
10417         var el = this.getFxEl();
10418         o = o || {};
10419
10420         el.queueFx(o, function(){
10421             color = color || "#C3DAF9";
10422             if(color.length == 6){
10423                 color = "#" + color;
10424             }
10425             count = count || 1;
10426             duration = o.duration || 1;
10427             this.show();
10428
10429             var b = this.getBox();
10430             var animFn = function(){
10431                 var proxy = this.createProxy({
10432
10433                      style:{
10434                         visbility:"hidden",
10435                         position:"absolute",
10436                         "z-index":"35000", // yee haw
10437                         border:"0px solid " + color
10438                      }
10439                   });
10440                 var scale = Roo.isBorderBox ? 2 : 1;
10441                 proxy.animate({
10442                     top:{from:b.y, to:b.y - 20},
10443                     left:{from:b.x, to:b.x - 20},
10444                     borderWidth:{from:0, to:10},
10445                     opacity:{from:1, to:0},
10446                     height:{from:b.height, to:(b.height + (20*scale))},
10447                     width:{from:b.width, to:(b.width + (20*scale))}
10448                 }, duration, function(){
10449                     proxy.remove();
10450                 });
10451                 if(--count > 0){
10452                      animFn.defer((duration/2)*1000, this);
10453                 }else{
10454                     el.afterFx(o);
10455                 }
10456             };
10457             animFn.call(this);
10458         });
10459         return this;
10460     },
10461
10462    /**
10463     * Creates a pause before any subsequent queued effects begin.  If there are
10464     * no effects queued after the pause it will have no effect.
10465     * Usage:
10466 <pre><code>
10467 el.pause(1);
10468 </code></pre>
10469     * @param {Number} seconds The length of time to pause (in seconds)
10470     * @return {Roo.Element} The Element
10471     */
10472     pause : function(seconds){
10473         var el = this.getFxEl();
10474         var o = {};
10475
10476         el.queueFx(o, function(){
10477             setTimeout(function(){
10478                 el.afterFx(o);
10479             }, seconds * 1000);
10480         });
10481         return this;
10482     },
10483
10484    /**
10485     * Fade an element in (from transparent to opaque).  The ending opacity can be specified
10486     * using the "endOpacity" config option.
10487     * Usage:
10488 <pre><code>
10489 // default: fade in from opacity 0 to 100%
10490 el.fadeIn();
10491
10492 // custom: fade in from opacity 0 to 75% over 2 seconds
10493 el.fadeIn({ endOpacity: .75, duration: 2});
10494
10495 // common config options shown with default values
10496 el.fadeIn({
10497     endOpacity: 1, //can be any value between 0 and 1 (e.g. .5)
10498     easing: 'easeOut',
10499     duration: .5
10500 });
10501 </code></pre>
10502     * @param {Object} options (optional) Object literal with any of the Fx config options
10503     * @return {Roo.Element} The Element
10504     */
10505     fadeIn : function(o){
10506         var el = this.getFxEl();
10507         o = o || {};
10508         el.queueFx(o, function(){
10509             this.setOpacity(0);
10510             this.fixDisplay();
10511             this.dom.style.visibility = 'visible';
10512             var to = o.endOpacity || 1;
10513             arguments.callee.anim = this.fxanim({opacity:{to:to}},
10514                 o, null, .5, "easeOut", function(){
10515                 if(to == 1){
10516                     this.clearOpacity();
10517                 }
10518                 el.afterFx(o);
10519             });
10520         });
10521         return this;
10522     },
10523
10524    /**
10525     * Fade an element out (from opaque to transparent).  The ending opacity can be specified
10526     * using the "endOpacity" config option.
10527     * Usage:
10528 <pre><code>
10529 // default: fade out from the element's current opacity to 0
10530 el.fadeOut();
10531
10532 // custom: fade out from the element's current opacity to 25% over 2 seconds
10533 el.fadeOut({ endOpacity: .25, duration: 2});
10534
10535 // common config options shown with default values
10536 el.fadeOut({
10537     endOpacity: 0, //can be any value between 0 and 1 (e.g. .5)
10538     easing: 'easeOut',
10539     duration: .5
10540     remove: false,
10541     useDisplay: false
10542 });
10543 </code></pre>
10544     * @param {Object} options (optional) Object literal with any of the Fx config options
10545     * @return {Roo.Element} The Element
10546     */
10547     fadeOut : function(o){
10548         var el = this.getFxEl();
10549         o = o || {};
10550         el.queueFx(o, function(){
10551             arguments.callee.anim = this.fxanim({opacity:{to:o.endOpacity || 0}},
10552                 o, null, .5, "easeOut", function(){
10553                 if(this.visibilityMode == Roo.Element.DISPLAY || o.useDisplay){
10554                      this.dom.style.display = "none";
10555                 }else{
10556                      this.dom.style.visibility = "hidden";
10557                 }
10558                 this.clearOpacity();
10559                 el.afterFx(o);
10560             });
10561         });
10562         return this;
10563     },
10564
10565    /**
10566     * Animates the transition of an element's dimensions from a starting height/width
10567     * to an ending height/width.
10568     * Usage:
10569 <pre><code>
10570 // change height and width to 100x100 pixels
10571 el.scale(100, 100);
10572
10573 // common config options shown with default values.  The height and width will default to
10574 // the element's existing values if passed as null.
10575 el.scale(
10576     [element's width],
10577     [element's height], {
10578     easing: 'easeOut',
10579     duration: .35
10580 });
10581 </code></pre>
10582     * @param {Number} width  The new width (pass undefined to keep the original width)
10583     * @param {Number} height  The new height (pass undefined to keep the original height)
10584     * @param {Object} options (optional) Object literal with any of the Fx config options
10585     * @return {Roo.Element} The Element
10586     */
10587     scale : function(w, h, o){
10588         this.shift(Roo.apply({}, o, {
10589             width: w,
10590             height: h
10591         }));
10592         return this;
10593     },
10594
10595    /**
10596     * Animates the transition of any combination of an element's dimensions, xy position and/or opacity.
10597     * Any of these properties not specified in the config object will not be changed.  This effect 
10598     * requires that at least one new dimension, position or opacity setting must be passed in on
10599     * the config object in order for the function to have any effect.
10600     * Usage:
10601 <pre><code>
10602 // slide the element horizontally to x position 200 while changing the height and opacity
10603 el.shift({ x: 200, height: 50, opacity: .8 });
10604
10605 // common config options shown with default values.
10606 el.shift({
10607     width: [element's width],
10608     height: [element's height],
10609     x: [element's x position],
10610     y: [element's y position],
10611     opacity: [element's opacity],
10612     easing: 'easeOut',
10613     duration: .35
10614 });
10615 </code></pre>
10616     * @param {Object} options  Object literal with any of the Fx config options
10617     * @return {Roo.Element} The Element
10618     */
10619     shift : function(o){
10620         var el = this.getFxEl();
10621         o = o || {};
10622         el.queueFx(o, function(){
10623             var a = {}, w = o.width, h = o.height, x = o.x, y = o.y,  op = o.opacity;
10624             if(w !== undefined){
10625                 a.width = {to: this.adjustWidth(w)};
10626             }
10627             if(h !== undefined){
10628                 a.height = {to: this.adjustHeight(h)};
10629             }
10630             if(x !== undefined || y !== undefined){
10631                 a.points = {to: [
10632                     x !== undefined ? x : this.getX(),
10633                     y !== undefined ? y : this.getY()
10634                 ]};
10635             }
10636             if(op !== undefined){
10637                 a.opacity = {to: op};
10638             }
10639             if(o.xy !== undefined){
10640                 a.points = {to: o.xy};
10641             }
10642             arguments.callee.anim = this.fxanim(a,
10643                 o, 'motion', .35, "easeOut", function(){
10644                 el.afterFx(o);
10645             });
10646         });
10647         return this;
10648     },
10649
10650         /**
10651          * Slides the element while fading it out of view.  An anchor point can be optionally passed to set the 
10652          * ending point of the effect.
10653          * Usage:
10654          *<pre><code>
10655 // default: slide the element downward while fading out
10656 el.ghost();
10657
10658 // custom: slide the element out to the right with a 2-second duration
10659 el.ghost('r', { duration: 2 });
10660
10661 // common config options shown with default values
10662 el.ghost('b', {
10663     easing: 'easeOut',
10664     duration: .5
10665     remove: false,
10666     useDisplay: false
10667 });
10668 </code></pre>
10669          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to bottom: 'b')
10670          * @param {Object} options (optional) Object literal with any of the Fx config options
10671          * @return {Roo.Element} The Element
10672          */
10673     ghost : function(anchor, o){
10674         var el = this.getFxEl();
10675         o = o || {};
10676
10677         el.queueFx(o, function(){
10678             anchor = anchor || "b";
10679
10680             // restore values after effect
10681             var r = this.getFxRestore();
10682             var w = this.getWidth(),
10683                 h = this.getHeight();
10684
10685             var st = this.dom.style;
10686
10687             var after = function(){
10688                 if(o.useDisplay){
10689                     el.setDisplayed(false);
10690                 }else{
10691                     el.hide();
10692                 }
10693
10694                 el.clearOpacity();
10695                 el.setPositioning(r.pos);
10696                 st.width = r.width;
10697                 st.height = r.height;
10698
10699                 el.afterFx(o);
10700             };
10701
10702             var a = {opacity: {to: 0}, points: {}}, pt = a.points;
10703             switch(anchor.toLowerCase()){
10704                 case "t":
10705                     pt.by = [0, -h];
10706                 break;
10707                 case "l":
10708                     pt.by = [-w, 0];
10709                 break;
10710                 case "r":
10711                     pt.by = [w, 0];
10712                 break;
10713                 case "b":
10714                     pt.by = [0, h];
10715                 break;
10716                 case "tl":
10717                     pt.by = [-w, -h];
10718                 break;
10719                 case "bl":
10720                     pt.by = [-w, h];
10721                 break;
10722                 case "br":
10723                     pt.by = [w, h];
10724                 break;
10725                 case "tr":
10726                     pt.by = [w, -h];
10727                 break;
10728             }
10729
10730             arguments.callee.anim = this.fxanim(a,
10731                 o,
10732                 'motion',
10733                 .5,
10734                 "easeOut", after);
10735         });
10736         return this;
10737     },
10738
10739         /**
10740          * Ensures that all effects queued after syncFx is called on the element are
10741          * run concurrently.  This is the opposite of {@link #sequenceFx}.
10742          * @return {Roo.Element} The Element
10743          */
10744     syncFx : function(){
10745         this.fxDefaults = Roo.apply(this.fxDefaults || {}, {
10746             block : false,
10747             concurrent : true,
10748             stopFx : false
10749         });
10750         return this;
10751     },
10752
10753         /**
10754          * Ensures that all effects queued after sequenceFx is called on the element are
10755          * run in sequence.  This is the opposite of {@link #syncFx}.
10756          * @return {Roo.Element} The Element
10757          */
10758     sequenceFx : function(){
10759         this.fxDefaults = Roo.apply(this.fxDefaults || {}, {
10760             block : false,
10761             concurrent : false,
10762             stopFx : false
10763         });
10764         return this;
10765     },
10766
10767         /* @private */
10768     nextFx : function(){
10769         var ef = this.fxQueue[0];
10770         if(ef){
10771             ef.call(this);
10772         }
10773     },
10774
10775         /**
10776          * Returns true if the element has any effects actively running or queued, else returns false.
10777          * @return {Boolean} True if element has active effects, else false
10778          */
10779     hasActiveFx : function(){
10780         return this.fxQueue && this.fxQueue[0];
10781     },
10782
10783         /**
10784          * Stops any running effects and clears the element's internal effects queue if it contains
10785          * any additional effects that haven't started yet.
10786          * @return {Roo.Element} The Element
10787          */
10788     stopFx : function(){
10789         if(this.hasActiveFx()){
10790             var cur = this.fxQueue[0];
10791             if(cur && cur.anim && cur.anim.isAnimated()){
10792                 this.fxQueue = [cur]; // clear out others
10793                 cur.anim.stop(true);
10794             }
10795         }
10796         return this;
10797     },
10798
10799         /* @private */
10800     beforeFx : function(o){
10801         if(this.hasActiveFx() && !o.concurrent){
10802            if(o.stopFx){
10803                this.stopFx();
10804                return true;
10805            }
10806            return false;
10807         }
10808         return true;
10809     },
10810
10811         /**
10812          * Returns true if the element is currently blocking so that no other effect can be queued
10813          * until this effect is finished, else returns false if blocking is not set.  This is commonly
10814          * used to ensure that an effect initiated by a user action runs to completion prior to the
10815          * same effect being restarted (e.g., firing only one effect even if the user clicks several times).
10816          * @return {Boolean} True if blocking, else false
10817          */
10818     hasFxBlock : function(){
10819         var q = this.fxQueue;
10820         return q && q[0] && q[0].block;
10821     },
10822
10823         /* @private */
10824     queueFx : function(o, fn){
10825         if(!this.fxQueue){
10826             this.fxQueue = [];
10827         }
10828         if(!this.hasFxBlock()){
10829             Roo.applyIf(o, this.fxDefaults);
10830             if(!o.concurrent){
10831                 var run = this.beforeFx(o);
10832                 fn.block = o.block;
10833                 this.fxQueue.push(fn);
10834                 if(run){
10835                     this.nextFx();
10836                 }
10837             }else{
10838                 fn.call(this);
10839             }
10840         }
10841         return this;
10842     },
10843
10844         /* @private */
10845     fxWrap : function(pos, o, vis){
10846         var wrap;
10847         if(!o.wrap || !(wrap = Roo.get(o.wrap))){
10848             var wrapXY;
10849             if(o.fixPosition){
10850                 wrapXY = this.getXY();
10851             }
10852             var div = document.createElement("div");
10853             div.style.visibility = vis;
10854             wrap = Roo.get(this.dom.parentNode.insertBefore(div, this.dom));
10855             wrap.setPositioning(pos);
10856             if(wrap.getStyle("position") == "static"){
10857                 wrap.position("relative");
10858             }
10859             this.clearPositioning('auto');
10860             wrap.clip();
10861             wrap.dom.appendChild(this.dom);
10862             if(wrapXY){
10863                 wrap.setXY(wrapXY);
10864             }
10865         }
10866         return wrap;
10867     },
10868
10869         /* @private */
10870     fxUnwrap : function(wrap, pos, o){
10871         this.clearPositioning();
10872         this.setPositioning(pos);
10873         if(!o.wrap){
10874             wrap.dom.parentNode.insertBefore(this.dom, wrap.dom);
10875             wrap.remove();
10876         }
10877     },
10878
10879         /* @private */
10880     getFxRestore : function(){
10881         var st = this.dom.style;
10882         return {pos: this.getPositioning(), width: st.width, height : st.height};
10883     },
10884
10885         /* @private */
10886     afterFx : function(o){
10887         if(o.afterStyle){
10888             this.applyStyles(o.afterStyle);
10889         }
10890         if(o.afterCls){
10891             this.addClass(o.afterCls);
10892         }
10893         if(o.remove === true){
10894             this.remove();
10895         }
10896         Roo.callback(o.callback, o.scope, [this]);
10897         if(!o.concurrent){
10898             this.fxQueue.shift();
10899             this.nextFx();
10900         }
10901     },
10902
10903         /* @private */
10904     getFxEl : function(){ // support for composite element fx
10905         return Roo.get(this.dom);
10906     },
10907
10908         /* @private */
10909     fxanim : function(args, opt, animType, defaultDur, defaultEase, cb){
10910         animType = animType || 'run';
10911         opt = opt || {};
10912         var anim = Roo.lib.Anim[animType](
10913             this.dom, args,
10914             (opt.duration || defaultDur) || .35,
10915             (opt.easing || defaultEase) || 'easeOut',
10916             function(){
10917                 Roo.callback(cb, this);
10918             },
10919             this
10920         );
10921         opt.anim = anim;
10922         return anim;
10923     }
10924 };
10925
10926 // backwords compat
10927 Roo.Fx.resize = Roo.Fx.scale;
10928
10929 //When included, Roo.Fx is automatically applied to Element so that all basic
10930 //effects are available directly via the Element API
10931 Roo.apply(Roo.Element.prototype, Roo.Fx);/*
10932  * Based on:
10933  * Ext JS Library 1.1.1
10934  * Copyright(c) 2006-2007, Ext JS, LLC.
10935  *
10936  * Originally Released Under LGPL - original licence link has changed is not relivant.
10937  *
10938  * Fork - LGPL
10939  * <script type="text/javascript">
10940  */
10941
10942
10943 /**
10944  * @class Roo.CompositeElement
10945  * Standard composite class. Creates a Roo.Element for every element in the collection.
10946  * <br><br>
10947  * <b>NOTE: Although they are not listed, this class supports all of the set/update methods of Roo.Element. All Roo.Element
10948  * actions will be performed on all the elements in this collection.</b>
10949  * <br><br>
10950  * All methods return <i>this</i> and can be chained.
10951  <pre><code>
10952  var els = Roo.select("#some-el div.some-class", true);
10953  // or select directly from an existing element
10954  var el = Roo.get('some-el');
10955  el.select('div.some-class', true);
10956
10957  els.setWidth(100); // all elements become 100 width
10958  els.hide(true); // all elements fade out and hide
10959  // or
10960  els.setWidth(100).hide(true);
10961  </code></pre>
10962  */
10963 Roo.CompositeElement = function(els){
10964     this.elements = [];
10965     this.addElements(els);
10966 };
10967 Roo.CompositeElement.prototype = {
10968     isComposite: true,
10969     addElements : function(els){
10970         if(!els) return this;
10971         if(typeof els == "string"){
10972             els = Roo.Element.selectorFunction(els);
10973         }
10974         var yels = this.elements;
10975         var index = yels.length-1;
10976         for(var i = 0, len = els.length; i < len; i++) {
10977                 yels[++index] = Roo.get(els[i]);
10978         }
10979         return this;
10980     },
10981
10982     /**
10983     * Clears this composite and adds the elements returned by the passed selector.
10984     * @param {String/Array} els A string CSS selector, an array of elements or an element
10985     * @return {CompositeElement} this
10986     */
10987     fill : function(els){
10988         this.elements = [];
10989         this.add(els);
10990         return this;
10991     },
10992
10993     /**
10994     * Filters this composite to only elements that match the passed selector.
10995     * @param {String} selector A string CSS selector
10996     * @param {Boolean} inverse return inverse filter (not matches)
10997     * @return {CompositeElement} this
10998     */
10999     filter : function(selector, inverse){
11000         var els = [];
11001         inverse = inverse || false;
11002         this.each(function(el){
11003             var match = inverse ? !el.is(selector) : el.is(selector);
11004             if(match){
11005                 els[els.length] = el.dom;
11006             }
11007         });
11008         this.fill(els);
11009         return this;
11010     },
11011
11012     invoke : function(fn, args){
11013         var els = this.elements;
11014         for(var i = 0, len = els.length; i < len; i++) {
11015                 Roo.Element.prototype[fn].apply(els[i], args);
11016         }
11017         return this;
11018     },
11019     /**
11020     * Adds elements to this composite.
11021     * @param {String/Array} els A string CSS selector, an array of elements or an element
11022     * @return {CompositeElement} this
11023     */
11024     add : function(els){
11025         if(typeof els == "string"){
11026             this.addElements(Roo.Element.selectorFunction(els));
11027         }else if(els.length !== undefined){
11028             this.addElements(els);
11029         }else{
11030             this.addElements([els]);
11031         }
11032         return this;
11033     },
11034     /**
11035     * Calls the passed function passing (el, this, index) for each element in this composite.
11036     * @param {Function} fn The function to call
11037     * @param {Object} scope (optional) The <i>this</i> object (defaults to the element)
11038     * @return {CompositeElement} this
11039     */
11040     each : function(fn, scope){
11041         var els = this.elements;
11042         for(var i = 0, len = els.length; i < len; i++){
11043             if(fn.call(scope || els[i], els[i], this, i) === false) {
11044                 break;
11045             }
11046         }
11047         return this;
11048     },
11049
11050     /**
11051      * Returns the Element object at the specified index
11052      * @param {Number} index
11053      * @return {Roo.Element}
11054      */
11055     item : function(index){
11056         return this.elements[index] || null;
11057     },
11058
11059     /**
11060      * Returns the first Element
11061      * @return {Roo.Element}
11062      */
11063     first : function(){
11064         return this.item(0);
11065     },
11066
11067     /**
11068      * Returns the last Element
11069      * @return {Roo.Element}
11070      */
11071     last : function(){
11072         return this.item(this.elements.length-1);
11073     },
11074
11075     /**
11076      * Returns the number of elements in this composite
11077      * @return Number
11078      */
11079     getCount : function(){
11080         return this.elements.length;
11081     },
11082
11083     /**
11084      * Returns true if this composite contains the passed element
11085      * @return Boolean
11086      */
11087     contains : function(el){
11088         return this.indexOf(el) !== -1;
11089     },
11090
11091     /**
11092      * Returns true if this composite contains the passed element
11093      * @return Boolean
11094      */
11095     indexOf : function(el){
11096         return this.elements.indexOf(Roo.get(el));
11097     },
11098
11099
11100     /**
11101     * Removes the specified element(s).
11102     * @param {Mixed} el The id of an element, the Element itself, the index of the element in this composite
11103     * or an array of any of those.
11104     * @param {Boolean} removeDom (optional) True to also remove the element from the document
11105     * @return {CompositeElement} this
11106     */
11107     removeElement : function(el, removeDom){
11108         if(el instanceof Array){
11109             for(var i = 0, len = el.length; i < len; i++){
11110                 this.removeElement(el[i]);
11111             }
11112             return this;
11113         }
11114         var index = typeof el == 'number' ? el : this.indexOf(el);
11115         if(index !== -1){
11116             if(removeDom){
11117                 var d = this.elements[index];
11118                 if(d.dom){
11119                     d.remove();
11120                 }else{
11121                     d.parentNode.removeChild(d);
11122                 }
11123             }
11124             this.elements.splice(index, 1);
11125         }
11126         return this;
11127     },
11128
11129     /**
11130     * Replaces the specified element with the passed element.
11131     * @param {String/HTMLElement/Element/Number} el The id of an element, the Element itself, the index of the element in this composite
11132     * to replace.
11133     * @param {String/HTMLElement/Element} replacement The id of an element or the Element itself.
11134     * @param {Boolean} domReplace (Optional) True to remove and replace the element in the document too.
11135     * @return {CompositeElement} this
11136     */
11137     replaceElement : function(el, replacement, domReplace){
11138         var index = typeof el == 'number' ? el : this.indexOf(el);
11139         if(index !== -1){
11140             if(domReplace){
11141                 this.elements[index].replaceWith(replacement);
11142             }else{
11143                 this.elements.splice(index, 1, Roo.get(replacement))
11144             }
11145         }
11146         return this;
11147     },
11148
11149     /**
11150      * Removes all elements.
11151      */
11152     clear : function(){
11153         this.elements = [];
11154     }
11155 };
11156 (function(){
11157     Roo.CompositeElement.createCall = function(proto, fnName){
11158         if(!proto[fnName]){
11159             proto[fnName] = function(){
11160                 return this.invoke(fnName, arguments);
11161             };
11162         }
11163     };
11164     for(var fnName in Roo.Element.prototype){
11165         if(typeof Roo.Element.prototype[fnName] == "function"){
11166             Roo.CompositeElement.createCall(Roo.CompositeElement.prototype, fnName);
11167         }
11168     };
11169 })();
11170 /*
11171  * Based on:
11172  * Ext JS Library 1.1.1
11173  * Copyright(c) 2006-2007, Ext JS, LLC.
11174  *
11175  * Originally Released Under LGPL - original licence link has changed is not relivant.
11176  *
11177  * Fork - LGPL
11178  * <script type="text/javascript">
11179  */
11180
11181 /**
11182  * @class Roo.CompositeElementLite
11183  * @extends Roo.CompositeElement
11184  * Flyweight composite class. Reuses the same Roo.Element for element operations.
11185  <pre><code>
11186  var els = Roo.select("#some-el div.some-class");
11187  // or select directly from an existing element
11188  var el = Roo.get('some-el');
11189  el.select('div.some-class');
11190
11191  els.setWidth(100); // all elements become 100 width
11192  els.hide(true); // all elements fade out and hide
11193  // or
11194  els.setWidth(100).hide(true);
11195  </code></pre><br><br>
11196  * <b>NOTE: Although they are not listed, this class supports all of the set/update methods of Roo.Element. All Roo.Element
11197  * actions will be performed on all the elements in this collection.</b>
11198  */
11199 Roo.CompositeElementLite = function(els){
11200     Roo.CompositeElementLite.superclass.constructor.call(this, els);
11201     this.el = new Roo.Element.Flyweight();
11202 };
11203 Roo.extend(Roo.CompositeElementLite, Roo.CompositeElement, {
11204     addElements : function(els){
11205         if(els){
11206             if(els instanceof Array){
11207                 this.elements = this.elements.concat(els);
11208             }else{
11209                 var yels = this.elements;
11210                 var index = yels.length-1;
11211                 for(var i = 0, len = els.length; i < len; i++) {
11212                     yels[++index] = els[i];
11213                 }
11214             }
11215         }
11216         return this;
11217     },
11218     invoke : function(fn, args){
11219         var els = this.elements;
11220         var el = this.el;
11221         for(var i = 0, len = els.length; i < len; i++) {
11222             el.dom = els[i];
11223                 Roo.Element.prototype[fn].apply(el, args);
11224         }
11225         return this;
11226     },
11227     /**
11228      * Returns a flyweight Element of the dom element object at the specified index
11229      * @param {Number} index
11230      * @return {Roo.Element}
11231      */
11232     item : function(index){
11233         if(!this.elements[index]){
11234             return null;
11235         }
11236         this.el.dom = this.elements[index];
11237         return this.el;
11238     },
11239
11240     // fixes scope with flyweight
11241     addListener : function(eventName, handler, scope, opt){
11242         var els = this.elements;
11243         for(var i = 0, len = els.length; i < len; i++) {
11244             Roo.EventManager.on(els[i], eventName, handler, scope || els[i], opt);
11245         }
11246         return this;
11247     },
11248
11249     /**
11250     * Calls the passed function passing (el, this, index) for each element in this composite. <b>The element
11251     * passed is the flyweight (shared) Roo.Element instance, so if you require a
11252     * a reference to the dom node, use el.dom.</b>
11253     * @param {Function} fn The function to call
11254     * @param {Object} scope (optional) The <i>this</i> object (defaults to the element)
11255     * @return {CompositeElement} this
11256     */
11257     each : function(fn, scope){
11258         var els = this.elements;
11259         var el = this.el;
11260         for(var i = 0, len = els.length; i < len; i++){
11261             el.dom = els[i];
11262                 if(fn.call(scope || el, el, this, i) === false){
11263                 break;
11264             }
11265         }
11266         return this;
11267     },
11268
11269     indexOf : function(el){
11270         return this.elements.indexOf(Roo.getDom(el));
11271     },
11272
11273     replaceElement : function(el, replacement, domReplace){
11274         var index = typeof el == 'number' ? el : this.indexOf(el);
11275         if(index !== -1){
11276             replacement = Roo.getDom(replacement);
11277             if(domReplace){
11278                 var d = this.elements[index];
11279                 d.parentNode.insertBefore(replacement, d);
11280                 d.parentNode.removeChild(d);
11281             }
11282             this.elements.splice(index, 1, replacement);
11283         }
11284         return this;
11285     }
11286 });
11287 Roo.CompositeElementLite.prototype.on = Roo.CompositeElementLite.prototype.addListener;
11288
11289 /*
11290  * Based on:
11291  * Ext JS Library 1.1.1
11292  * Copyright(c) 2006-2007, Ext JS, LLC.
11293  *
11294  * Originally Released Under LGPL - original licence link has changed is not relivant.
11295  *
11296  * Fork - LGPL
11297  * <script type="text/javascript">
11298  */
11299
11300  
11301
11302 /**
11303  * @class Roo.data.Connection
11304  * @extends Roo.util.Observable
11305  * The class encapsulates a connection to the page's originating domain, allowing requests to be made
11306  * either to a configured URL, or to a URL specified at request time.<br><br>
11307  * <p>
11308  * Requests made by this class are asynchronous, and will return immediately. No data from
11309  * the server will be available to the statement immediately following the {@link #request} call.
11310  * To process returned data, use a callback in the request options object, or an event listener.</p><br>
11311  * <p>
11312  * Note: If you are doing a file upload, you will not get a normal response object sent back to
11313  * your callback or event handler.  Since the upload is handled via in IFRAME, there is no XMLHttpRequest.
11314  * The response object is created using the innerHTML of the IFRAME's document as the responseText
11315  * property and, if present, the IFRAME's XML document as the responseXML property.</p><br>
11316  * This means that a valid XML or HTML document must be returned. If JSON data is required, it is suggested
11317  * that it be placed either inside a &lt;textarea> in an HTML document and retrieved from the responseText
11318  * using a regex, or inside a CDATA section in an XML document and retrieved from the responseXML using
11319  * standard DOM methods.
11320  * @constructor
11321  * @param {Object} config a configuration object.
11322  */
11323 Roo.data.Connection = function(config){
11324     Roo.apply(this, config);
11325     this.addEvents({
11326         /**
11327          * @event beforerequest
11328          * Fires before a network request is made to retrieve a data object.
11329          * @param {Connection} conn This Connection object.
11330          * @param {Object} options The options config object passed to the {@link #request} method.
11331          */
11332         "beforerequest" : true,
11333         /**
11334          * @event requestcomplete
11335          * Fires if the request was successfully completed.
11336          * @param {Connection} conn This Connection object.
11337          * @param {Object} response The XHR object containing the response data.
11338          * See {@link http://www.w3.org/TR/XMLHttpRequest/} for details.
11339          * @param {Object} options The options config object passed to the {@link #request} method.
11340          */
11341         "requestcomplete" : true,
11342         /**
11343          * @event requestexception
11344          * Fires if an error HTTP status was returned from the server.
11345          * See {@link http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html} for details of HTTP status codes.
11346          * @param {Connection} conn This Connection object.
11347          * @param {Object} response The XHR object containing the response data.
11348          * See {@link http://www.w3.org/TR/XMLHttpRequest/} for details.
11349          * @param {Object} options The options config object passed to the {@link #request} method.
11350          */
11351         "requestexception" : true
11352     });
11353     Roo.data.Connection.superclass.constructor.call(this);
11354 };
11355
11356 Roo.extend(Roo.data.Connection, Roo.util.Observable, {
11357     /**
11358      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
11359      */
11360     /**
11361      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
11362      * extra parameters to each request made by this object. (defaults to undefined)
11363      */
11364     /**
11365      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
11366      *  to each request made by this object. (defaults to undefined)
11367      */
11368     /**
11369      * @cfg {String} method (Optional) The default HTTP method to be used for requests. (defaults to undefined; if not set but parms are present will use POST, otherwise GET)
11370      */
11371     /**
11372      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
11373      */
11374     timeout : 30000,
11375     /**
11376      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
11377      * @type Boolean
11378      */
11379     autoAbort:false,
11380
11381     /**
11382      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
11383      * @type Boolean
11384      */
11385     disableCaching: true,
11386
11387     /**
11388      * Sends an HTTP request to a remote server.
11389      * @param {Object} options An object which may contain the following properties:<ul>
11390      * <li><b>url</b> {String} (Optional) The URL to which to send the request. Defaults to configured URL</li>
11391      * <li><b>params</b> {Object/String/Function} (Optional) An object containing properties which are used as parameters to the
11392      * request, a url encoded string or a function to call to get either.</li>
11393      * <li><b>method</b> {String} (Optional) The HTTP method to use for the request. Defaults to the configured method, or
11394      * if no method was configured, "GET" if no parameters are being sent, and "POST" if parameters are being sent.</li>
11395      * <li><b>callback</b> {Function} (Optional) The function to be called upon receipt of the HTTP response.
11396      * The callback is called regardless of success or failure and is passed the following parameters:<ul>
11397      * <li>options {Object} The parameter to the request call.</li>
11398      * <li>success {Boolean} True if the request succeeded.</li>
11399      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
11400      * </ul></li>
11401      * <li><b>success</b> {Function} (Optional) The function to be called upon success of the request.
11402      * The callback is passed the following parameters:<ul>
11403      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
11404      * <li>options {Object} The parameter to the request call.</li>
11405      * </ul></li>
11406      * <li><b>failure</b> {Function} (Optional) The function to be called upon failure of the request.
11407      * The callback is passed the following parameters:<ul>
11408      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
11409      * <li>options {Object} The parameter to the request call.</li>
11410      * </ul></li>
11411      * <li><b>scope</b> {Object} (Optional) The scope in which to execute the callbacks: The "this" object
11412      * for the callback function. Defaults to the browser window.</li>
11413      * <li><b>form</b> {Object/String} (Optional) A form object or id to pull parameters from.</li>
11414      * <li><b>isUpload</b> {Boolean} (Optional) True if the form object is a file upload (will usually be automatically detected).</li>
11415      * <li><b>headers</b> {Object} (Optional) Request headers to set for the request.</li>
11416      * <li><b>xmlData</b> {Object} (Optional) XML document to use for the post. Note: This will be used instead of
11417      * params for the post data. Any params will be appended to the URL.</li>
11418      * <li><b>disableCaching</b> {Boolean} (Optional) True to add a unique cache-buster param to GET requests.</li>
11419      * </ul>
11420      * @return {Number} transactionId
11421      */
11422     request : function(o){
11423         if(this.fireEvent("beforerequest", this, o) !== false){
11424             var p = o.params;
11425
11426             if(typeof p == "function"){
11427                 p = p.call(o.scope||window, o);
11428             }
11429             if(typeof p == "object"){
11430                 p = Roo.urlEncode(o.params);
11431             }
11432             if(this.extraParams){
11433                 var extras = Roo.urlEncode(this.extraParams);
11434                 p = p ? (p + '&' + extras) : extras;
11435             }
11436
11437             var url = o.url || this.url;
11438             if(typeof url == 'function'){
11439                 url = url.call(o.scope||window, o);
11440             }
11441
11442             if(o.form){
11443                 var form = Roo.getDom(o.form);
11444                 url = url || form.action;
11445
11446                 var enctype = form.getAttribute("enctype");
11447                 if(o.isUpload || (enctype && enctype.toLowerCase() == 'multipart/form-data')){
11448                     return this.doFormUpload(o, p, url);
11449                 }
11450                 var f = Roo.lib.Ajax.serializeForm(form);
11451                 p = p ? (p + '&' + f) : f;
11452             }
11453
11454             var hs = o.headers;
11455             if(this.defaultHeaders){
11456                 hs = Roo.apply(hs || {}, this.defaultHeaders);
11457                 if(!o.headers){
11458                     o.headers = hs;
11459                 }
11460             }
11461
11462             var cb = {
11463                 success: this.handleResponse,
11464                 failure: this.handleFailure,
11465                 scope: this,
11466                 argument: {options: o},
11467                 timeout : o.timeout || this.timeout
11468             };
11469
11470             var method = o.method||this.method||(p ? "POST" : "GET");
11471
11472             if(method == 'GET' && (this.disableCaching && o.disableCaching !== false) || o.disableCaching === true){
11473                 url += (url.indexOf('?') != -1 ? '&' : '?') + '_dc=' + (new Date().getTime());
11474             }
11475
11476             if(typeof o.autoAbort == 'boolean'){ // options gets top priority
11477                 if(o.autoAbort){
11478                     this.abort();
11479                 }
11480             }else if(this.autoAbort !== false){
11481                 this.abort();
11482             }
11483
11484             if((method == 'GET' && p) || o.xmlData){
11485                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
11486                 p = '';
11487             }
11488             this.transId = Roo.lib.Ajax.request(method, url, cb, p, o);
11489             return this.transId;
11490         }else{
11491             Roo.callback(o.callback, o.scope, [o, null, null]);
11492             return null;
11493         }
11494     },
11495
11496     /**
11497      * Determine whether this object has a request outstanding.
11498      * @param {Number} transactionId (Optional) defaults to the last transaction
11499      * @return {Boolean} True if there is an outstanding request.
11500      */
11501     isLoading : function(transId){
11502         if(transId){
11503             return Roo.lib.Ajax.isCallInProgress(transId);
11504         }else{
11505             return this.transId ? true : false;
11506         }
11507     },
11508
11509     /**
11510      * Aborts any outstanding request.
11511      * @param {Number} transactionId (Optional) defaults to the last transaction
11512      */
11513     abort : function(transId){
11514         if(transId || this.isLoading()){
11515             Roo.lib.Ajax.abort(transId || this.transId);
11516         }
11517     },
11518
11519     // private
11520     handleResponse : function(response){
11521         this.transId = false;
11522         var options = response.argument.options;
11523         response.argument = options ? options.argument : null;
11524         this.fireEvent("requestcomplete", this, response, options);
11525         Roo.callback(options.success, options.scope, [response, options]);
11526         Roo.callback(options.callback, options.scope, [options, true, response]);
11527     },
11528
11529     // private
11530     handleFailure : function(response, e){
11531         this.transId = false;
11532         var options = response.argument.options;
11533         response.argument = options ? options.argument : null;
11534         this.fireEvent("requestexception", this, response, options, e);
11535         Roo.callback(options.failure, options.scope, [response, options]);
11536         Roo.callback(options.callback, options.scope, [options, false, response]);
11537     },
11538
11539     // private
11540     doFormUpload : function(o, ps, url){
11541         var id = Roo.id();
11542         var frame = document.createElement('iframe');
11543         frame.id = id;
11544         frame.name = id;
11545         frame.className = 'x-hidden';
11546         if(Roo.isIE){
11547             frame.src = Roo.SSL_SECURE_URL;
11548         }
11549         document.body.appendChild(frame);
11550
11551         if(Roo.isIE){
11552            document.frames[id].name = id;
11553         }
11554
11555         var form = Roo.getDom(o.form);
11556         form.target = id;
11557         form.method = 'POST';
11558         form.enctype = form.encoding = 'multipart/form-data';
11559         if(url){
11560             form.action = url;
11561         }
11562
11563         var hiddens, hd;
11564         if(ps){ // add dynamic params
11565             hiddens = [];
11566             ps = Roo.urlDecode(ps, false);
11567             for(var k in ps){
11568                 if(ps.hasOwnProperty(k)){
11569                     hd = document.createElement('input');
11570                     hd.type = 'hidden';
11571                     hd.name = k;
11572                     hd.value = ps[k];
11573                     form.appendChild(hd);
11574                     hiddens.push(hd);
11575                 }
11576             }
11577         }
11578
11579         function cb(){
11580             var r = {  // bogus response object
11581                 responseText : '',
11582                 responseXML : null
11583             };
11584
11585             r.argument = o ? o.argument : null;
11586
11587             try { //
11588                 var doc;
11589                 if(Roo.isIE){
11590                     doc = frame.contentWindow.document;
11591                 }else {
11592                     doc = (frame.contentDocument || window.frames[id].document);
11593                 }
11594                 if(doc && doc.body){
11595                     r.responseText = doc.body.innerHTML;
11596                 }
11597                 if(doc && doc.XMLDocument){
11598                     r.responseXML = doc.XMLDocument;
11599                 }else {
11600                     r.responseXML = doc;
11601                 }
11602             }
11603             catch(e) {
11604                 // ignore
11605             }
11606
11607             Roo.EventManager.removeListener(frame, 'load', cb, this);
11608
11609             this.fireEvent("requestcomplete", this, r, o);
11610             Roo.callback(o.success, o.scope, [r, o]);
11611             Roo.callback(o.callback, o.scope, [o, true, r]);
11612
11613             setTimeout(function(){document.body.removeChild(frame);}, 100);
11614         }
11615
11616         Roo.EventManager.on(frame, 'load', cb, this);
11617         form.submit();
11618
11619         if(hiddens){ // remove dynamic params
11620             for(var i = 0, len = hiddens.length; i < len; i++){
11621                 form.removeChild(hiddens[i]);
11622             }
11623         }
11624     }
11625 });
11626 /*
11627  * Based on:
11628  * Ext JS Library 1.1.1
11629  * Copyright(c) 2006-2007, Ext JS, LLC.
11630  *
11631  * Originally Released Under LGPL - original licence link has changed is not relivant.
11632  *
11633  * Fork - LGPL
11634  * <script type="text/javascript">
11635  */
11636  
11637 /**
11638  * Global Ajax request class.
11639  * 
11640  * @class Roo.Ajax
11641  * @extends Roo.data.Connection
11642  * @static
11643  * 
11644  * @cfg {String} url  The default URL to be used for requests to the server. (defaults to undefined)
11645  * @cfg {Object} extraParams  An object containing properties which are used as extra parameters to each request made by this object. (defaults to undefined)
11646  * @cfg {Object} defaultHeaders  An object containing request headers which are added to each request made by this object. (defaults to undefined)
11647  * @cfg {String} method (Optional)  The default HTTP method to be used for requests. (defaults to undefined; if not set but parms are present will use POST, otherwise GET)
11648  * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
11649  * @cfg {Boolean} autoAbort (Optional) Whether a new request should abort any pending requests. (defaults to false)
11650  * @cfg {Boolean} disableCaching (Optional)   True to add a unique cache-buster param to GET requests. (defaults to true)
11651  */
11652 Roo.Ajax = new Roo.data.Connection({
11653     // fix up the docs
11654     /**
11655      * @scope Roo.Ajax
11656      * @type {Boolear} 
11657      */
11658     autoAbort : false,
11659
11660     /**
11661      * Serialize the passed form into a url encoded string
11662      * @scope Roo.Ajax
11663      * @param {String/HTMLElement} form
11664      * @return {String}
11665      */
11666     serializeForm : function(form){
11667         return Roo.lib.Ajax.serializeForm(form);
11668     }
11669 });/*
11670  * Based on:
11671  * Ext JS Library 1.1.1
11672  * Copyright(c) 2006-2007, Ext JS, LLC.
11673  *
11674  * Originally Released Under LGPL - original licence link has changed is not relivant.
11675  *
11676  * Fork - LGPL
11677  * <script type="text/javascript">
11678  */
11679
11680  
11681 /**
11682  * @class Roo.UpdateManager
11683  * @extends Roo.util.Observable
11684  * Provides AJAX-style update for Element object.<br><br>
11685  * Usage:<br>
11686  * <pre><code>
11687  * // Get it from a Roo.Element object
11688  * var el = Roo.get("foo");
11689  * var mgr = el.getUpdateManager();
11690  * mgr.update("http://myserver.com/index.php", "param1=1&amp;param2=2");
11691  * ...
11692  * mgr.formUpdate("myFormId", "http://myserver.com/index.php");
11693  * <br>
11694  * // or directly (returns the same UpdateManager instance)
11695  * var mgr = new Roo.UpdateManager("myElementId");
11696  * mgr.startAutoRefresh(60, "http://myserver.com/index.php");
11697  * mgr.on("update", myFcnNeedsToKnow);
11698  * <br>
11699    // short handed call directly from the element object
11700    Roo.get("foo").load({
11701         url: "bar.php",
11702         scripts:true,
11703         params: "for=bar",
11704         text: "Loading Foo..."
11705    });
11706  * </code></pre>
11707  * @constructor
11708  * Create new UpdateManager directly.
11709  * @param {String/HTMLElement/Roo.Element} el The element to update
11710  * @param {Boolean} forceNew (optional) By default the constructor checks to see if the passed element already has an UpdateManager and if it does it returns the same instance. This will skip that check (useful for extending this class).
11711  */
11712 Roo.UpdateManager = function(el, forceNew){
11713     el = Roo.get(el);
11714     if(!forceNew && el.updateManager){
11715         return el.updateManager;
11716     }
11717     /**
11718      * The Element object
11719      * @type Roo.Element
11720      */
11721     this.el = el;
11722     /**
11723      * Cached url to use for refreshes. Overwritten every time update() is called unless "discardUrl" param is set to true.
11724      * @type String
11725      */
11726     this.defaultUrl = null;
11727
11728     this.addEvents({
11729         /**
11730          * @event beforeupdate
11731          * Fired before an update is made, return false from your handler and the update is cancelled.
11732          * @param {Roo.Element} el
11733          * @param {String/Object/Function} url
11734          * @param {String/Object} params
11735          */
11736         "beforeupdate": true,
11737         /**
11738          * @event update
11739          * Fired after successful update is made.
11740          * @param {Roo.Element} el
11741          * @param {Object} oResponseObject The response Object
11742          */
11743         "update": true,
11744         /**
11745          * @event failure
11746          * Fired on update failure.
11747          * @param {Roo.Element} el
11748          * @param {Object} oResponseObject The response Object
11749          */
11750         "failure": true
11751     });
11752     var d = Roo.UpdateManager.defaults;
11753     /**
11754      * Blank page URL to use with SSL file uploads (Defaults to Roo.UpdateManager.defaults.sslBlankUrl or "about:blank").
11755      * @type String
11756      */
11757     this.sslBlankUrl = d.sslBlankUrl;
11758     /**
11759      * Whether to append unique parameter on get request to disable caching (Defaults to Roo.UpdateManager.defaults.disableCaching or false).
11760      * @type Boolean
11761      */
11762     this.disableCaching = d.disableCaching;
11763     /**
11764      * Text for loading indicator (Defaults to Roo.UpdateManager.defaults.indicatorText or '&lt;div class="loading-indicator"&gt;Loading...&lt;/div&gt;').
11765      * @type String
11766      */
11767     this.indicatorText = d.indicatorText;
11768     /**
11769      * Whether to show indicatorText when loading (Defaults to Roo.UpdateManager.defaults.showLoadIndicator or true).
11770      * @type String
11771      */
11772     this.showLoadIndicator = d.showLoadIndicator;
11773     /**
11774      * Timeout for requests or form posts in seconds (Defaults to Roo.UpdateManager.defaults.timeout or 30 seconds).
11775      * @type Number
11776      */
11777     this.timeout = d.timeout;
11778
11779     /**
11780      * True to process scripts in the output (Defaults to Roo.UpdateManager.defaults.loadScripts (false)).
11781      * @type Boolean
11782      */
11783     this.loadScripts = d.loadScripts;
11784
11785     /**
11786      * Transaction object of current executing transaction
11787      */
11788     this.transaction = null;
11789
11790     /**
11791      * @private
11792      */
11793     this.autoRefreshProcId = null;
11794     /**
11795      * Delegate for refresh() prebound to "this", use myUpdater.refreshDelegate.createCallback(arg1, arg2) to bind arguments
11796      * @type Function
11797      */
11798     this.refreshDelegate = this.refresh.createDelegate(this);
11799     /**
11800      * Delegate for update() prebound to "this", use myUpdater.updateDelegate.createCallback(arg1, arg2) to bind arguments
11801      * @type Function
11802      */
11803     this.updateDelegate = this.update.createDelegate(this);
11804     /**
11805      * Delegate for formUpdate() prebound to "this", use myUpdater.formUpdateDelegate.createCallback(arg1, arg2) to bind arguments
11806      * @type Function
11807      */
11808     this.formUpdateDelegate = this.formUpdate.createDelegate(this);
11809     /**
11810      * @private
11811      */
11812     this.successDelegate = this.processSuccess.createDelegate(this);
11813     /**
11814      * @private
11815      */
11816     this.failureDelegate = this.processFailure.createDelegate(this);
11817
11818     if(!this.renderer){
11819      /**
11820       * The renderer for this UpdateManager. Defaults to {@link Roo.UpdateManager.BasicRenderer}.
11821       */
11822     this.renderer = new Roo.UpdateManager.BasicRenderer();
11823     }
11824     
11825     Roo.UpdateManager.superclass.constructor.call(this);
11826 };
11827
11828 Roo.extend(Roo.UpdateManager, Roo.util.Observable, {
11829     /**
11830      * Get the Element this UpdateManager is bound to
11831      * @return {Roo.Element} The element
11832      */
11833     getEl : function(){
11834         return this.el;
11835     },
11836     /**
11837      * Performs an async request, updating this element with the response. If params are specified it uses POST, otherwise it uses GET.
11838      * @param {Object/String/Function} url The url for this request or a function to call to get the url or a config object containing any of the following options:
11839 <pre><code>
11840 um.update({<br/>
11841     url: "your-url.php",<br/>
11842     params: {param1: "foo", param2: "bar"}, // or a URL encoded string<br/>
11843     callback: yourFunction,<br/>
11844     scope: yourObject, //(optional scope)  <br/>
11845     discardUrl: false, <br/>
11846     nocache: false,<br/>
11847     text: "Loading...",<br/>
11848     timeout: 30,<br/>
11849     scripts: false<br/>
11850 });
11851 </code></pre>
11852      * The only required property is url. The optional properties nocache, text and scripts
11853      * are shorthand for disableCaching, indicatorText and loadScripts and are used to set their associated property on this UpdateManager instance.
11854      * @param {String/Object} params (optional) The parameters to pass as either a url encoded string "param1=1&amp;param2=2" or an object {param1: 1, param2: 2}
11855      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess, oResponse)
11856      * @param {Boolean} discardUrl (optional) By default when you execute an update the defaultUrl is changed to the last used url. If true, it will not store the url.
11857      */
11858     update : function(url, params, callback, discardUrl){
11859         if(this.fireEvent("beforeupdate", this.el, url, params) !== false){
11860             var method = this.method,
11861                 cfg;
11862             if(typeof url == "object"){ // must be config object
11863                 cfg = url;
11864                 url = cfg.url;
11865                 params = params || cfg.params;
11866                 callback = callback || cfg.callback;
11867                 discardUrl = discardUrl || cfg.discardUrl;
11868                 if(callback && cfg.scope){
11869                     callback = callback.createDelegate(cfg.scope);
11870                 }
11871                 if(typeof cfg.method != "undefined"){method = cfg.method;};
11872                 if(typeof cfg.nocache != "undefined"){this.disableCaching = cfg.nocache;};
11873                 if(typeof cfg.text != "undefined"){this.indicatorText = '<div class="loading-indicator">'+cfg.text+"</div>";};
11874                 if(typeof cfg.scripts != "undefined"){this.loadScripts = cfg.scripts;};
11875                 if(typeof cfg.timeout != "undefined"){this.timeout = cfg.timeout;};
11876             }
11877             this.showLoading();
11878             if(!discardUrl){
11879                 this.defaultUrl = url;
11880             }
11881             if(typeof url == "function"){
11882                 url = url.call(this);
11883             }
11884
11885             method = method || (params ? "POST" : "GET");
11886             if(method == "GET"){
11887                 url = this.prepareUrl(url);
11888             }
11889
11890             var o = Roo.apply(cfg ||{}, {
11891                 url : url,
11892                 params: params,
11893                 success: this.successDelegate,
11894                 failure: this.failureDelegate,
11895                 callback: undefined,
11896                 timeout: (this.timeout*1000),
11897                 argument: {"url": url, "form": null, "callback": callback, "params": params}
11898             });
11899             Roo.log("updated manager called with timeout of " + o.timeout);
11900             this.transaction = Roo.Ajax.request(o);
11901         }
11902     },
11903
11904     /**
11905      * Performs an async form post, updating this element with the response. If the form has the attribute enctype="multipart/form-data", it assumes it's a file upload.
11906      * Uses this.sslBlankUrl for SSL file uploads to prevent IE security warning.
11907      * @param {String/HTMLElement} form The form Id or form element
11908      * @param {String} url (optional) The url to pass the form to. If omitted the action attribute on the form will be used.
11909      * @param {Boolean} reset (optional) Whether to try to reset the form after the update
11910      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess, oResponse)
11911      */
11912     formUpdate : function(form, url, reset, callback){
11913         if(this.fireEvent("beforeupdate", this.el, form, url) !== false){
11914             if(typeof url == "function"){
11915                 url = url.call(this);
11916             }
11917             form = Roo.getDom(form);
11918             this.transaction = Roo.Ajax.request({
11919                 form: form,
11920                 url:url,
11921                 success: this.successDelegate,
11922                 failure: this.failureDelegate,
11923                 timeout: (this.timeout*1000),
11924                 argument: {"url": url, "form": form, "callback": callback, "reset": reset}
11925             });
11926             this.showLoading.defer(1, this);
11927         }
11928     },
11929
11930     /**
11931      * Refresh the element with the last used url or defaultUrl. If there is no url, it returns immediately
11932      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
11933      */
11934     refresh : function(callback){
11935         if(this.defaultUrl == null){
11936             return;
11937         }
11938         this.update(this.defaultUrl, null, callback, true);
11939     },
11940
11941     /**
11942      * Set this element to auto refresh.
11943      * @param {Number} interval How often to update (in seconds).
11944      * @param {String/Function} url (optional) The url for this request or a function to call to get the url (Defaults to the last used url)
11945      * @param {String/Object} params (optional) The parameters to pass as either a url encoded string "&param1=1&param2=2" or as an object {param1: 1, param2: 2}
11946      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
11947      * @param {Boolean} refreshNow (optional) Whether to execute the refresh now, or wait the interval
11948      */
11949     startAutoRefresh : function(interval, url, params, callback, refreshNow){
11950         if(refreshNow){
11951             this.update(url || this.defaultUrl, params, callback, true);
11952         }
11953         if(this.autoRefreshProcId){
11954             clearInterval(this.autoRefreshProcId);
11955         }
11956         this.autoRefreshProcId = setInterval(this.update.createDelegate(this, [url || this.defaultUrl, params, callback, true]), interval*1000);
11957     },
11958
11959     /**
11960      * Stop auto refresh on this element.
11961      */
11962      stopAutoRefresh : function(){
11963         if(this.autoRefreshProcId){
11964             clearInterval(this.autoRefreshProcId);
11965             delete this.autoRefreshProcId;
11966         }
11967     },
11968
11969     isAutoRefreshing : function(){
11970        return this.autoRefreshProcId ? true : false;
11971     },
11972     /**
11973      * Called to update the element to "Loading" state. Override to perform custom action.
11974      */
11975     showLoading : function(){
11976         if(this.showLoadIndicator){
11977             this.el.update(this.indicatorText);
11978         }
11979     },
11980
11981     /**
11982      * Adds unique parameter to query string if disableCaching = true
11983      * @private
11984      */
11985     prepareUrl : function(url){
11986         if(this.disableCaching){
11987             var append = "_dc=" + (new Date().getTime());
11988             if(url.indexOf("?") !== -1){
11989                 url += "&" + append;
11990             }else{
11991                 url += "?" + append;
11992             }
11993         }
11994         return url;
11995     },
11996
11997     /**
11998      * @private
11999      */
12000     processSuccess : function(response){
12001         this.transaction = null;
12002         if(response.argument.form && response.argument.reset){
12003             try{ // put in try/catch since some older FF releases had problems with this
12004                 response.argument.form.reset();
12005             }catch(e){}
12006         }
12007         if(this.loadScripts){
12008             this.renderer.render(this.el, response, this,
12009                 this.updateComplete.createDelegate(this, [response]));
12010         }else{
12011             this.renderer.render(this.el, response, this);
12012             this.updateComplete(response);
12013         }
12014     },
12015
12016     updateComplete : function(response){
12017         this.fireEvent("update", this.el, response);
12018         if(typeof response.argument.callback == "function"){
12019             response.argument.callback(this.el, true, response);
12020         }
12021     },
12022
12023     /**
12024      * @private
12025      */
12026     processFailure : function(response){
12027         this.transaction = null;
12028         this.fireEvent("failure", this.el, response);
12029         if(typeof response.argument.callback == "function"){
12030             response.argument.callback(this.el, false, response);
12031         }
12032     },
12033
12034     /**
12035      * Set the content renderer for this UpdateManager. See {@link Roo.UpdateManager.BasicRenderer#render} for more details.
12036      * @param {Object} renderer The object implementing the render() method
12037      */
12038     setRenderer : function(renderer){
12039         this.renderer = renderer;
12040     },
12041
12042     getRenderer : function(){
12043        return this.renderer;
12044     },
12045
12046     /**
12047      * Set the defaultUrl used for updates
12048      * @param {String/Function} defaultUrl The url or a function to call to get the url
12049      */
12050     setDefaultUrl : function(defaultUrl){
12051         this.defaultUrl = defaultUrl;
12052     },
12053
12054     /**
12055      * Aborts the executing transaction
12056      */
12057     abort : function(){
12058         if(this.transaction){
12059             Roo.Ajax.abort(this.transaction);
12060         }
12061     },
12062
12063     /**
12064      * Returns true if an update is in progress
12065      * @return {Boolean}
12066      */
12067     isUpdating : function(){
12068         if(this.transaction){
12069             return Roo.Ajax.isLoading(this.transaction);
12070         }
12071         return false;
12072     }
12073 });
12074
12075 /**
12076  * @class Roo.UpdateManager.defaults
12077  * @static (not really - but it helps the doc tool)
12078  * The defaults collection enables customizing the default properties of UpdateManager
12079  */
12080    Roo.UpdateManager.defaults = {
12081        /**
12082          * Timeout for requests or form posts in seconds (Defaults 30 seconds).
12083          * @type Number
12084          */
12085          timeout : 30,
12086
12087          /**
12088          * True to process scripts by default (Defaults to false).
12089          * @type Boolean
12090          */
12091         loadScripts : false,
12092
12093         /**
12094         * Blank page URL to use with SSL file uploads (Defaults to "javascript:false").
12095         * @type String
12096         */
12097         sslBlankUrl : (Roo.SSL_SECURE_URL || "javascript:false"),
12098         /**
12099          * Whether to append unique parameter on get request to disable caching (Defaults to false).
12100          * @type Boolean
12101          */
12102         disableCaching : false,
12103         /**
12104          * Whether to show indicatorText when loading (Defaults to true).
12105          * @type Boolean
12106          */
12107         showLoadIndicator : true,
12108         /**
12109          * Text for loading indicator (Defaults to '&lt;div class="loading-indicator"&gt;Loading...&lt;/div&gt;').
12110          * @type String
12111          */
12112         indicatorText : '<div class="loading-indicator">Loading...</div>'
12113    };
12114
12115 /**
12116  * Static convenience method. This method is deprecated in favor of el.load({url:'foo.php', ...}).
12117  *Usage:
12118  * <pre><code>Roo.UpdateManager.updateElement("my-div", "stuff.php");</code></pre>
12119  * @param {String/HTMLElement/Roo.Element} el The element to update
12120  * @param {String} url The url
12121  * @param {String/Object} params (optional) Url encoded param string or an object of name/value pairs
12122  * @param {Object} options (optional) A config object with any of the UpdateManager properties you want to set - for example: {disableCaching:true, indicatorText: "Loading data..."}
12123  * @static
12124  * @deprecated
12125  * @member Roo.UpdateManager
12126  */
12127 Roo.UpdateManager.updateElement = function(el, url, params, options){
12128     var um = Roo.get(el, true).getUpdateManager();
12129     Roo.apply(um, options);
12130     um.update(url, params, options ? options.callback : null);
12131 };
12132 // alias for backwards compat
12133 Roo.UpdateManager.update = Roo.UpdateManager.updateElement;
12134 /**
12135  * @class Roo.UpdateManager.BasicRenderer
12136  * Default Content renderer. Updates the elements innerHTML with the responseText.
12137  */
12138 Roo.UpdateManager.BasicRenderer = function(){};
12139
12140 Roo.UpdateManager.BasicRenderer.prototype = {
12141     /**
12142      * This is called when the transaction is completed and it's time to update the element - The BasicRenderer
12143      * updates the elements innerHTML with the responseText - To perform a custom render (i.e. XML or JSON processing),
12144      * create an object with a "render(el, response)" method and pass it to setRenderer on the UpdateManager.
12145      * @param {Roo.Element} el The element being rendered
12146      * @param {Object} response The YUI Connect response object
12147      * @param {UpdateManager} updateManager The calling update manager
12148      * @param {Function} callback A callback that will need to be called if loadScripts is true on the UpdateManager
12149      */
12150      render : function(el, response, updateManager, callback){
12151         el.update(response.responseText, updateManager.loadScripts, callback);
12152     }
12153 };
12154 /*
12155  * Based on:
12156  * Roo JS
12157  * (c)) Alan Knowles
12158  * Licence : LGPL
12159  */
12160
12161
12162 /**
12163  * @class Roo.DomTemplate
12164  * @extends Roo.Template
12165  * An effort at a dom based template engine..
12166  *
12167  * Similar to XTemplate, except it uses dom parsing to create the template..
12168  *
12169  * Supported features:
12170  *
12171  *  Tags:
12172
12173 <pre><code>
12174       {a_variable} - output encoded.
12175       {a_variable.format:("Y-m-d")} - call a method on the variable
12176       {a_variable:raw} - unencoded output
12177       {a_variable:toFixed(1,2)} - Roo.util.Format."toFixed"
12178       {a_variable:this.method_on_template(...)} - call a method on the template object.
12179  
12180 </code></pre>
12181  *  The tpl tag:
12182 <pre><code>
12183         &lt;div roo-for="a_variable or condition.."&gt;&lt;/div&gt;
12184         &lt;div roo-if="a_variable or condition"&gt;&lt;/div&gt;
12185         &lt;div roo-exec="some javascript"&gt;&lt;/div&gt;
12186         &lt;div roo-name="named_template"&gt;&lt;/div&gt; 
12187   
12188 </code></pre>
12189  *      
12190  */
12191 Roo.DomTemplate = function()
12192 {
12193      Roo.DomTemplate.superclass.constructor.apply(this, arguments);
12194      if (this.html) {
12195         this.compile();
12196      }
12197 };
12198
12199
12200 Roo.extend(Roo.DomTemplate, Roo.Template, {
12201     /**
12202      * id counter for sub templates.
12203      */
12204     id : 0,
12205     /**
12206      * flag to indicate if dom parser is inside a pre,
12207      * it will strip whitespace if not.
12208      */
12209     inPre : false,
12210     
12211     /**
12212      * The various sub templates
12213      */
12214     tpls : false,
12215     
12216     
12217     
12218     /**
12219      *
12220      * basic tag replacing syntax
12221      * WORD:WORD()
12222      *
12223      * // you can fake an object call by doing this
12224      *  x.t:(test,tesT) 
12225      * 
12226      */
12227     re : /(\{|\%7B)([\w-\.]+)(?:\:([\w\.]*)(?:\(([^)]*?)?\))?)?(\}|\%7D)/g,
12228     //re : /\{([\w-\.]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
12229     
12230     iterChild : function (node, method) {
12231         
12232         var oldPre = this.inPre;
12233         if (node.tagName == 'PRE') {
12234             this.inPre = true;
12235         }
12236         for( var i = 0; i < node.childNodes.length; i++) {
12237             method.call(this, node.childNodes[i]);
12238         }
12239         this.inPre = oldPre;
12240     },
12241     
12242     
12243     
12244     /**
12245      * compile the template
12246      *
12247      * This is not recursive, so I'm not sure how nested templates are really going to be handled..
12248      *
12249      */
12250     compile: function()
12251     {
12252         var s = this.html;
12253         
12254         // covert the html into DOM...
12255         var doc = false;
12256         var div =false;
12257         try {
12258             doc = document.implementation.createHTMLDocument("");
12259             doc.documentElement.innerHTML =   this.html  ;
12260             div = doc.documentElement;
12261         } catch (e) {
12262             // old IE... - nasty -- it causes all sorts of issues.. with
12263             // images getting pulled from server..
12264             div = document.createElement('div');
12265             div.innerHTML = this.html;
12266         }
12267         //doc.documentElement.innerHTML = htmlBody
12268          
12269         
12270         
12271         this.tpls = [];
12272         var _t = this;
12273         this.iterChild(div, function(n) {_t.compileNode(n, true); });
12274         
12275         var tpls = this.tpls;
12276         
12277         // create a top level template from the snippet..
12278         
12279         //Roo.log(div.innerHTML);
12280         
12281         var tpl = {
12282             uid : 'master',
12283             id : this.id++,
12284             attr : false,
12285             value : false,
12286             body : div.innerHTML,
12287             
12288             forCall : false,
12289             execCall : false,
12290             dom : div,
12291             isTop : true
12292             
12293         };
12294         tpls.unshift(tpl);
12295         
12296         
12297         // compile them...
12298         this.tpls = [];
12299         Roo.each(tpls, function(tp){
12300             this.compileTpl(tp);
12301             this.tpls[tp.id] = tp;
12302         }, this);
12303         
12304         this.master = tpls[0];
12305         return this;
12306         
12307         
12308     },
12309     
12310     compileNode : function(node, istop) {
12311         // test for
12312         //Roo.log(node);
12313         
12314         
12315         // skip anything not a tag..
12316         if (node.nodeType != 1) {
12317             if (node.nodeType == 3 && !this.inPre) {
12318                 // reduce white space..
12319                 node.nodeValue = node.nodeValue.replace(/\s+/g, ' '); 
12320                 
12321             }
12322             return;
12323         }
12324         
12325         var tpl = {
12326             uid : false,
12327             id : false,
12328             attr : false,
12329             value : false,
12330             body : '',
12331             
12332             forCall : false,
12333             execCall : false,
12334             dom : false,
12335             isTop : istop
12336             
12337             
12338         };
12339         
12340         
12341         switch(true) {
12342             case (node.hasAttribute('roo-for')): tpl.attr = 'for'; break;
12343             case (node.hasAttribute('roo-if')): tpl.attr = 'if'; break;
12344             case (node.hasAttribute('roo-name')): tpl.attr = 'name'; break;
12345             case (node.hasAttribute('roo-exec')): tpl.attr = 'exec'; break;
12346             // no default..
12347         }
12348         
12349         
12350         if (!tpl.attr) {
12351             // just itterate children..
12352             this.iterChild(node,this.compileNode);
12353             return;
12354         }
12355         tpl.uid = this.id++;
12356         tpl.value = node.getAttribute('roo-' +  tpl.attr);
12357         node.removeAttribute('roo-'+ tpl.attr);
12358         if (tpl.attr != 'name') {
12359             var placeholder = document.createTextNode('{domtpl' + tpl.uid + '}');
12360             node.parentNode.replaceChild(placeholder,  node);
12361         } else {
12362             
12363             var placeholder =  document.createElement('span');
12364             placeholder.className = 'roo-tpl-' + tpl.value;
12365             node.parentNode.replaceChild(placeholder,  node);
12366         }
12367         
12368         // parent now sees '{domtplXXXX}
12369         this.iterChild(node,this.compileNode);
12370         
12371         // we should now have node body...
12372         var div = document.createElement('div');
12373         div.appendChild(node);
12374         tpl.dom = node;
12375         // this has the unfortunate side effect of converting tagged attributes
12376         // eg. href="{...}" into %7C...%7D
12377         // this has been fixed by searching for those combo's although it's a bit hacky..
12378         
12379         
12380         tpl.body = div.innerHTML;
12381         
12382         
12383          
12384         tpl.id = tpl.uid;
12385         switch(tpl.attr) {
12386             case 'for' :
12387                 switch (tpl.value) {
12388                     case '.':  tpl.forCall = new Function('values', 'parent', 'with(values){ return values; }'); break;
12389                     case '..': tpl.forCall= new Function('values', 'parent', 'with(values){ return parent; }'); break;
12390                     default:   tpl.forCall= new Function('values', 'parent', 'with(values){ return '+tpl.value+'; }');
12391                 }
12392                 break;
12393             
12394             case 'exec':
12395                 tpl.execCall = new Function('values', 'parent', 'with(values){ '+(Roo.util.Format.htmlDecode(tpl.value))+'; }');
12396                 break;
12397             
12398             case 'if':     
12399                 tpl.ifCall = new Function('values', 'parent', 'with(values){ return '+(Roo.util.Format.htmlDecode(tpl.value))+'; }');
12400                 break;
12401             
12402             case 'name':
12403                 tpl.id  = tpl.value; // replace non characters???
12404                 break;
12405             
12406         }
12407         
12408         
12409         this.tpls.push(tpl);
12410         
12411         
12412         
12413     },
12414     
12415     
12416     
12417     
12418     /**
12419      * Compile a segment of the template into a 'sub-template'
12420      *
12421      * 
12422      * 
12423      *
12424      */
12425     compileTpl : function(tpl)
12426     {
12427         var fm = Roo.util.Format;
12428         var useF = this.disableFormats !== true;
12429         
12430         var sep = Roo.isGecko ? "+\n" : ",\n";
12431         
12432         var undef = function(str) {
12433             Roo.debug && Roo.log("Property not found :"  + str);
12434             return '';
12435         };
12436           
12437         //Roo.log(tpl.body);
12438         
12439         
12440         
12441         var fn = function(m, lbrace, name, format, args)
12442         {
12443             //Roo.log("ARGS");
12444             //Roo.log(arguments);
12445             args = args ? args.replace(/\\'/g,"'") : args;
12446             //["{TEST:(a,b,c)}", "TEST", "", "a,b,c", 0, "{TEST:(a,b,c)}"]
12447             if (typeof(format) == 'undefined') {
12448                 format =  'htmlEncode'; 
12449             }
12450             if (format == 'raw' ) {
12451                 format = false;
12452             }
12453             
12454             if(name.substr(0, 6) == 'domtpl'){
12455                 return "'"+ sep +'this.applySubTemplate('+name.substr(6)+', values, parent)'+sep+"'";
12456             }
12457             
12458             // build an array of options to determine if value is undefined..
12459             
12460             // basically get 'xxxx.yyyy' then do
12461             // (typeof(xxxx) == 'undefined' || typeof(xxx.yyyy) == 'undefined') ?
12462             //    (function () { Roo.log("Property not found"); return ''; })() :
12463             //    ......
12464             
12465             var udef_ar = [];
12466             var lookfor = '';
12467             Roo.each(name.split('.'), function(st) {
12468                 lookfor += (lookfor.length ? '.': '') + st;
12469                 udef_ar.push(  "(typeof(" + lookfor + ") == 'undefined')"  );
12470             });
12471             
12472             var udef_st = '((' + udef_ar.join(" || ") +") ? undef('" + name + "') : "; // .. needs )
12473             
12474             
12475             if(format && useF){
12476                 
12477                 args = args ? ',' + args : "";
12478                  
12479                 if(format.substr(0, 5) != "this."){
12480                     format = "fm." + format + '(';
12481                 }else{
12482                     format = 'this.call("'+ format.substr(5) + '", ';
12483                     args = ", values";
12484                 }
12485                 
12486                 return "'"+ sep +   udef_st   +    format + name + args + "))"+sep+"'";
12487             }
12488              
12489             if (args && args.length) {
12490                 // called with xxyx.yuu:(test,test)
12491                 // change to ()
12492                 return "'"+ sep + udef_st  + name + '(' +  args + "))"+sep+"'";
12493             }
12494             // raw.. - :raw modifier..
12495             return "'"+ sep + udef_st  + name + ")"+sep+"'";
12496             
12497         };
12498         var body;
12499         // branched to use + in gecko and [].join() in others
12500         if(Roo.isGecko){
12501             body = "tpl.compiled = function(values, parent){  with(values) { return '" +
12502                    tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
12503                     "';};};";
12504         }else{
12505             body = ["tpl.compiled = function(values, parent){  with (values) { return ['"];
12506             body.push(tpl.body.replace(/(\r\n|\n)/g,
12507                             '\\n').replace(/'/g, "\\'").replace(this.re, fn));
12508             body.push("'].join('');};};");
12509             body = body.join('');
12510         }
12511         
12512         Roo.debug && Roo.log(body.replace(/\\n/,'\n'));
12513        
12514         /** eval:var:tpl eval:var:fm eval:var:useF eval:var:undef  */
12515         eval(body);
12516         
12517         return this;
12518     },
12519      
12520     /**
12521      * same as applyTemplate, except it's done to one of the subTemplates
12522      * when using named templates, you can do:
12523      *
12524      * var str = pl.applySubTemplate('your-name', values);
12525      *
12526      * 
12527      * @param {Number} id of the template
12528      * @param {Object} values to apply to template
12529      * @param {Object} parent (normaly the instance of this object)
12530      */
12531     applySubTemplate : function(id, values, parent)
12532     {
12533         
12534         
12535         var t = this.tpls[id];
12536         
12537         
12538         try { 
12539             if(t.ifCall && !t.ifCall.call(this, values, parent)){
12540                 Roo.debug && Roo.log('if call on ' + t.value + ' return false');
12541                 return '';
12542             }
12543         } catch(e) {
12544             Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on roo-if="' + t.value + '" - ' + e.toString());
12545             Roo.log(values);
12546           
12547             return '';
12548         }
12549         try { 
12550             
12551             if(t.execCall && t.execCall.call(this, values, parent)){
12552                 return '';
12553             }
12554         } catch(e) {
12555             Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on roo-for="' + t.value + '" - ' + e.toString());
12556             Roo.log(values);
12557             return '';
12558         }
12559         
12560         try {
12561             var vs = t.forCall ? t.forCall.call(this, values, parent) : values;
12562             parent = t.target ? values : parent;
12563             if(t.forCall && vs instanceof Array){
12564                 var buf = [];
12565                 for(var i = 0, len = vs.length; i < len; i++){
12566                     try {
12567                         buf[buf.length] = t.compiled.call(this, vs[i], parent);
12568                     } catch (e) {
12569                         Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on body="' + t.value + '" - ' + e.toString());
12570                         Roo.log(e.body);
12571                         //Roo.log(t.compiled);
12572                         Roo.log(vs[i]);
12573                     }   
12574                 }
12575                 return buf.join('');
12576             }
12577         } catch (e) {
12578             Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on roo-for="' + t.value + '" - ' + e.toString());
12579             Roo.log(values);
12580             return '';
12581         }
12582         try {
12583             return t.compiled.call(this, vs, parent);
12584         } catch (e) {
12585             Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on body="' + t.value + '" - ' + e.toString());
12586             Roo.log(e.body);
12587             //Roo.log(t.compiled);
12588             Roo.log(values);
12589             return '';
12590         }
12591     },
12592
12593    
12594
12595     applyTemplate : function(values){
12596         return this.master.compiled.call(this, values, {});
12597         //var s = this.subs;
12598     },
12599
12600     apply : function(){
12601         return this.applyTemplate.apply(this, arguments);
12602     }
12603
12604  });
12605
12606 Roo.DomTemplate.from = function(el){
12607     el = Roo.getDom(el);
12608     return new Roo.Domtemplate(el.value || el.innerHTML);
12609 };/*
12610  * Based on:
12611  * Ext JS Library 1.1.1
12612  * Copyright(c) 2006-2007, Ext JS, LLC.
12613  *
12614  * Originally Released Under LGPL - original licence link has changed is not relivant.
12615  *
12616  * Fork - LGPL
12617  * <script type="text/javascript">
12618  */
12619
12620 /**
12621  * @class Roo.util.DelayedTask
12622  * Provides a convenient method of performing setTimeout where a new
12623  * timeout cancels the old timeout. An example would be performing validation on a keypress.
12624  * You can use this class to buffer
12625  * the keypress events for a certain number of milliseconds, and perform only if they stop
12626  * for that amount of time.
12627  * @constructor The parameters to this constructor serve as defaults and are not required.
12628  * @param {Function} fn (optional) The default function to timeout
12629  * @param {Object} scope (optional) The default scope of that timeout
12630  * @param {Array} args (optional) The default Array of arguments
12631  */
12632 Roo.util.DelayedTask = function(fn, scope, args){
12633     var id = null, d, t;
12634
12635     var call = function(){
12636         var now = new Date().getTime();
12637         if(now - t >= d){
12638             clearInterval(id);
12639             id = null;
12640             fn.apply(scope, args || []);
12641         }
12642     };
12643     /**
12644      * Cancels any pending timeout and queues a new one
12645      * @param {Number} delay The milliseconds to delay
12646      * @param {Function} newFn (optional) Overrides function passed to constructor
12647      * @param {Object} newScope (optional) Overrides scope passed to constructor
12648      * @param {Array} newArgs (optional) Overrides args passed to constructor
12649      */
12650     this.delay = function(delay, newFn, newScope, newArgs){
12651         if(id && delay != d){
12652             this.cancel();
12653         }
12654         d = delay;
12655         t = new Date().getTime();
12656         fn = newFn || fn;
12657         scope = newScope || scope;
12658         args = newArgs || args;
12659         if(!id){
12660             id = setInterval(call, d);
12661         }
12662     };
12663
12664     /**
12665      * Cancel the last queued timeout
12666      */
12667     this.cancel = function(){
12668         if(id){
12669             clearInterval(id);
12670             id = null;
12671         }
12672     };
12673 };/*
12674  * Based on:
12675  * Ext JS Library 1.1.1
12676  * Copyright(c) 2006-2007, Ext JS, LLC.
12677  *
12678  * Originally Released Under LGPL - original licence link has changed is not relivant.
12679  *
12680  * Fork - LGPL
12681  * <script type="text/javascript">
12682  */
12683  
12684  
12685 Roo.util.TaskRunner = function(interval){
12686     interval = interval || 10;
12687     var tasks = [], removeQueue = [];
12688     var id = 0;
12689     var running = false;
12690
12691     var stopThread = function(){
12692         running = false;
12693         clearInterval(id);
12694         id = 0;
12695     };
12696
12697     var startThread = function(){
12698         if(!running){
12699             running = true;
12700             id = setInterval(runTasks, interval);
12701         }
12702     };
12703
12704     var removeTask = function(task){
12705         removeQueue.push(task);
12706         if(task.onStop){
12707             task.onStop();
12708         }
12709     };
12710
12711     var runTasks = function(){
12712         if(removeQueue.length > 0){
12713             for(var i = 0, len = removeQueue.length; i < len; i++){
12714                 tasks.remove(removeQueue[i]);
12715             }
12716             removeQueue = [];
12717             if(tasks.length < 1){
12718                 stopThread();
12719                 return;
12720             }
12721         }
12722         var now = new Date().getTime();
12723         for(var i = 0, len = tasks.length; i < len; ++i){
12724             var t = tasks[i];
12725             var itime = now - t.taskRunTime;
12726             if(t.interval <= itime){
12727                 var rt = t.run.apply(t.scope || t, t.args || [++t.taskRunCount]);
12728                 t.taskRunTime = now;
12729                 if(rt === false || t.taskRunCount === t.repeat){
12730                     removeTask(t);
12731                     return;
12732                 }
12733             }
12734             if(t.duration && t.duration <= (now - t.taskStartTime)){
12735                 removeTask(t);
12736             }
12737         }
12738     };
12739
12740     /**
12741      * Queues a new task.
12742      * @param {Object} task
12743      */
12744     this.start = function(task){
12745         tasks.push(task);
12746         task.taskStartTime = new Date().getTime();
12747         task.taskRunTime = 0;
12748         task.taskRunCount = 0;
12749         startThread();
12750         return task;
12751     };
12752
12753     this.stop = function(task){
12754         removeTask(task);
12755         return task;
12756     };
12757
12758     this.stopAll = function(){
12759         stopThread();
12760         for(var i = 0, len = tasks.length; i < len; i++){
12761             if(tasks[i].onStop){
12762                 tasks[i].onStop();
12763             }
12764         }
12765         tasks = [];
12766         removeQueue = [];
12767     };
12768 };
12769
12770 Roo.TaskMgr = new Roo.util.TaskRunner();/*
12771  * Based on:
12772  * Ext JS Library 1.1.1
12773  * Copyright(c) 2006-2007, Ext JS, LLC.
12774  *
12775  * Originally Released Under LGPL - original licence link has changed is not relivant.
12776  *
12777  * Fork - LGPL
12778  * <script type="text/javascript">
12779  */
12780
12781  
12782 /**
12783  * @class Roo.util.MixedCollection
12784  * @extends Roo.util.Observable
12785  * A Collection class that maintains both numeric indexes and keys and exposes events.
12786  * @constructor
12787  * @param {Boolean} allowFunctions True if the addAll function should add function references to the
12788  * collection (defaults to false)
12789  * @param {Function} keyFn A function that can accept an item of the type(s) stored in this MixedCollection
12790  * and return the key value for that item.  This is used when available to look up the key on items that
12791  * were passed without an explicit key parameter to a MixedCollection method.  Passing this parameter is
12792  * equivalent to providing an implementation for the {@link #getKey} method.
12793  */
12794 Roo.util.MixedCollection = function(allowFunctions, keyFn){
12795     this.items = [];
12796     this.map = {};
12797     this.keys = [];
12798     this.length = 0;
12799     this.addEvents({
12800         /**
12801          * @event clear
12802          * Fires when the collection is cleared.
12803          */
12804         "clear" : true,
12805         /**
12806          * @event add
12807          * Fires when an item is added to the collection.
12808          * @param {Number} index The index at which the item was added.
12809          * @param {Object} o The item added.
12810          * @param {String} key The key associated with the added item.
12811          */
12812         "add" : true,
12813         /**
12814          * @event replace
12815          * Fires when an item is replaced in the collection.
12816          * @param {String} key he key associated with the new added.
12817          * @param {Object} old The item being replaced.
12818          * @param {Object} new The new item.
12819          */
12820         "replace" : true,
12821         /**
12822          * @event remove
12823          * Fires when an item is removed from the collection.
12824          * @param {Object} o The item being removed.
12825          * @param {String} key (optional) The key associated with the removed item.
12826          */
12827         "remove" : true,
12828         "sort" : true
12829     });
12830     this.allowFunctions = allowFunctions === true;
12831     if(keyFn){
12832         this.getKey = keyFn;
12833     }
12834     Roo.util.MixedCollection.superclass.constructor.call(this);
12835 };
12836
12837 Roo.extend(Roo.util.MixedCollection, Roo.util.Observable, {
12838     allowFunctions : false,
12839     
12840 /**
12841  * Adds an item to the collection.
12842  * @param {String} key The key to associate with the item
12843  * @param {Object} o The item to add.
12844  * @return {Object} The item added.
12845  */
12846     add : function(key, o){
12847         if(arguments.length == 1){
12848             o = arguments[0];
12849             key = this.getKey(o);
12850         }
12851         if(typeof key == "undefined" || key === null){
12852             this.length++;
12853             this.items.push(o);
12854             this.keys.push(null);
12855         }else{
12856             var old = this.map[key];
12857             if(old){
12858                 return this.replace(key, o);
12859             }
12860             this.length++;
12861             this.items.push(o);
12862             this.map[key] = o;
12863             this.keys.push(key);
12864         }
12865         this.fireEvent("add", this.length-1, o, key);
12866         return o;
12867     },
12868        
12869 /**
12870   * MixedCollection has a generic way to fetch keys if you implement getKey.
12871 <pre><code>
12872 // normal way
12873 var mc = new Roo.util.MixedCollection();
12874 mc.add(someEl.dom.id, someEl);
12875 mc.add(otherEl.dom.id, otherEl);
12876 //and so on
12877
12878 // using getKey
12879 var mc = new Roo.util.MixedCollection();
12880 mc.getKey = function(el){
12881    return el.dom.id;
12882 };
12883 mc.add(someEl);
12884 mc.add(otherEl);
12885
12886 // or via the constructor
12887 var mc = new Roo.util.MixedCollection(false, function(el){
12888    return el.dom.id;
12889 });
12890 mc.add(someEl);
12891 mc.add(otherEl);
12892 </code></pre>
12893  * @param o {Object} The item for which to find the key.
12894  * @return {Object} The key for the passed item.
12895  */
12896     getKey : function(o){
12897          return o.id; 
12898     },
12899    
12900 /**
12901  * Replaces an item in the collection.
12902  * @param {String} key The key associated with the item to replace, or the item to replace.
12903  * @param o {Object} o (optional) If the first parameter passed was a key, the item to associate with that key.
12904  * @return {Object}  The new item.
12905  */
12906     replace : function(key, o){
12907         if(arguments.length == 1){
12908             o = arguments[0];
12909             key = this.getKey(o);
12910         }
12911         var old = this.item(key);
12912         if(typeof key == "undefined" || key === null || typeof old == "undefined"){
12913              return this.add(key, o);
12914         }
12915         var index = this.indexOfKey(key);
12916         this.items[index] = o;
12917         this.map[key] = o;
12918         this.fireEvent("replace", key, old, o);
12919         return o;
12920     },
12921    
12922 /**
12923  * Adds all elements of an Array or an Object to the collection.
12924  * @param {Object/Array} objs An Object containing properties which will be added to the collection, or
12925  * an Array of values, each of which are added to the collection.
12926  */
12927     addAll : function(objs){
12928         if(arguments.length > 1 || objs instanceof Array){
12929             var args = arguments.length > 1 ? arguments : objs;
12930             for(var i = 0, len = args.length; i < len; i++){
12931                 this.add(args[i]);
12932             }
12933         }else{
12934             for(var key in objs){
12935                 if(this.allowFunctions || typeof objs[key] != "function"){
12936                     this.add(key, objs[key]);
12937                 }
12938             }
12939         }
12940     },
12941    
12942 /**
12943  * Executes the specified function once for every item in the collection, passing each
12944  * item as the first and only parameter. returning false from the function will stop the iteration.
12945  * @param {Function} fn The function to execute for each item.
12946  * @param {Object} scope (optional) The scope in which to execute the function.
12947  */
12948     each : function(fn, scope){
12949         var items = [].concat(this.items); // each safe for removal
12950         for(var i = 0, len = items.length; i < len; i++){
12951             if(fn.call(scope || items[i], items[i], i, len) === false){
12952                 break;
12953             }
12954         }
12955     },
12956    
12957 /**
12958  * Executes the specified function once for every key in the collection, passing each
12959  * key, and its associated item as the first two parameters.
12960  * @param {Function} fn The function to execute for each item.
12961  * @param {Object} scope (optional) The scope in which to execute the function.
12962  */
12963     eachKey : function(fn, scope){
12964         for(var i = 0, len = this.keys.length; i < len; i++){
12965             fn.call(scope || window, this.keys[i], this.items[i], i, len);
12966         }
12967     },
12968    
12969 /**
12970  * Returns the first item in the collection which elicits a true return value from the
12971  * passed selection function.
12972  * @param {Function} fn The selection function to execute for each item.
12973  * @param {Object} scope (optional) The scope in which to execute the function.
12974  * @return {Object} The first item in the collection which returned true from the selection function.
12975  */
12976     find : function(fn, scope){
12977         for(var i = 0, len = this.items.length; i < len; i++){
12978             if(fn.call(scope || window, this.items[i], this.keys[i])){
12979                 return this.items[i];
12980             }
12981         }
12982         return null;
12983     },
12984    
12985 /**
12986  * Inserts an item at the specified index in the collection.
12987  * @param {Number} index The index to insert the item at.
12988  * @param {String} key The key to associate with the new item, or the item itself.
12989  * @param {Object} o  (optional) If the second parameter was a key, the new item.
12990  * @return {Object} The item inserted.
12991  */
12992     insert : function(index, key, o){
12993         if(arguments.length == 2){
12994             o = arguments[1];
12995             key = this.getKey(o);
12996         }
12997         if(index >= this.length){
12998             return this.add(key, o);
12999         }
13000         this.length++;
13001         this.items.splice(index, 0, o);
13002         if(typeof key != "undefined" && key != null){
13003             this.map[key] = o;
13004         }
13005         this.keys.splice(index, 0, key);
13006         this.fireEvent("add", index, o, key);
13007         return o;
13008     },
13009    
13010 /**
13011  * Removed an item from the collection.
13012  * @param {Object} o The item to remove.
13013  * @return {Object} The item removed.
13014  */
13015     remove : function(o){
13016         return this.removeAt(this.indexOf(o));
13017     },
13018    
13019 /**
13020  * Remove an item from a specified index in the collection.
13021  * @param {Number} index The index within the collection of the item to remove.
13022  */
13023     removeAt : function(index){
13024         if(index < this.length && index >= 0){
13025             this.length--;
13026             var o = this.items[index];
13027             this.items.splice(index, 1);
13028             var key = this.keys[index];
13029             if(typeof key != "undefined"){
13030                 delete this.map[key];
13031             }
13032             this.keys.splice(index, 1);
13033             this.fireEvent("remove", o, key);
13034         }
13035     },
13036    
13037 /**
13038  * Removed an item associated with the passed key fom the collection.
13039  * @param {String} key The key of the item to remove.
13040  */
13041     removeKey : function(key){
13042         return this.removeAt(this.indexOfKey(key));
13043     },
13044    
13045 /**
13046  * Returns the number of items in the collection.
13047  * @return {Number} the number of items in the collection.
13048  */
13049     getCount : function(){
13050         return this.length; 
13051     },
13052    
13053 /**
13054  * Returns index within the collection of the passed Object.
13055  * @param {Object} o The item to find the index of.
13056  * @return {Number} index of the item.
13057  */
13058     indexOf : function(o){
13059         if(!this.items.indexOf){
13060             for(var i = 0, len = this.items.length; i < len; i++){
13061                 if(this.items[i] == o) return i;
13062             }
13063             return -1;
13064         }else{
13065             return this.items.indexOf(o);
13066         }
13067     },
13068    
13069 /**
13070  * Returns index within the collection of the passed key.
13071  * @param {String} key The key to find the index of.
13072  * @return {Number} index of the key.
13073  */
13074     indexOfKey : function(key){
13075         if(!this.keys.indexOf){
13076             for(var i = 0, len = this.keys.length; i < len; i++){
13077                 if(this.keys[i] == key) return i;
13078             }
13079             return -1;
13080         }else{
13081             return this.keys.indexOf(key);
13082         }
13083     },
13084    
13085 /**
13086  * Returns the item associated with the passed key OR index. Key has priority over index.
13087  * @param {String/Number} key The key or index of the item.
13088  * @return {Object} The item associated with the passed key.
13089  */
13090     item : function(key){
13091         var item = typeof this.map[key] != "undefined" ? this.map[key] : this.items[key];
13092         return typeof item != 'function' || this.allowFunctions ? item : null; // for prototype!
13093     },
13094     
13095 /**
13096  * Returns the item at the specified index.
13097  * @param {Number} index The index of the item.
13098  * @return {Object}
13099  */
13100     itemAt : function(index){
13101         return this.items[index];
13102     },
13103     
13104 /**
13105  * Returns the item associated with the passed key.
13106  * @param {String/Number} key The key of the item.
13107  * @return {Object} The item associated with the passed key.
13108  */
13109     key : function(key){
13110         return this.map[key];
13111     },
13112    
13113 /**
13114  * Returns true if the collection contains the passed Object as an item.
13115  * @param {Object} o  The Object to look for in the collection.
13116  * @return {Boolean} True if the collection contains the Object as an item.
13117  */
13118     contains : function(o){
13119         return this.indexOf(o) != -1;
13120     },
13121    
13122 /**
13123  * Returns true if the collection contains the passed Object as a key.
13124  * @param {String} key The key to look for in the collection.
13125  * @return {Boolean} True if the collection contains the Object as a key.
13126  */
13127     containsKey : function(key){
13128         return typeof this.map[key] != "undefined";
13129     },
13130    
13131 /**
13132  * Removes all items from the collection.
13133  */
13134     clear : function(){
13135         this.length = 0;
13136         this.items = [];
13137         this.keys = [];
13138         this.map = {};
13139         this.fireEvent("clear");
13140     },
13141    
13142 /**
13143  * Returns the first item in the collection.
13144  * @return {Object} the first item in the collection..
13145  */
13146     first : function(){
13147         return this.items[0]; 
13148     },
13149    
13150 /**
13151  * Returns the last item in the collection.
13152  * @return {Object} the last item in the collection..
13153  */
13154     last : function(){
13155         return this.items[this.length-1];   
13156     },
13157     
13158     _sort : function(property, dir, fn){
13159         var dsc = String(dir).toUpperCase() == "DESC" ? -1 : 1;
13160         fn = fn || function(a, b){
13161             return a-b;
13162         };
13163         var c = [], k = this.keys, items = this.items;
13164         for(var i = 0, len = items.length; i < len; i++){
13165             c[c.length] = {key: k[i], value: items[i], index: i};
13166         }
13167         c.sort(function(a, b){
13168             var v = fn(a[property], b[property]) * dsc;
13169             if(v == 0){
13170                 v = (a.index < b.index ? -1 : 1);
13171             }
13172             return v;
13173         });
13174         for(var i = 0, len = c.length; i < len; i++){
13175             items[i] = c[i].value;
13176             k[i] = c[i].key;
13177         }
13178         this.fireEvent("sort", this);
13179     },
13180     
13181     /**
13182      * Sorts this collection with the passed comparison function
13183      * @param {String} direction (optional) "ASC" or "DESC"
13184      * @param {Function} fn (optional) comparison function
13185      */
13186     sort : function(dir, fn){
13187         this._sort("value", dir, fn);
13188     },
13189     
13190     /**
13191      * Sorts this collection by keys
13192      * @param {String} direction (optional) "ASC" or "DESC"
13193      * @param {Function} fn (optional) a comparison function (defaults to case insensitive string)
13194      */
13195     keySort : function(dir, fn){
13196         this._sort("key", dir, fn || function(a, b){
13197             return String(a).toUpperCase()-String(b).toUpperCase();
13198         });
13199     },
13200     
13201     /**
13202      * Returns a range of items in this collection
13203      * @param {Number} startIndex (optional) defaults to 0
13204      * @param {Number} endIndex (optional) default to the last item
13205      * @return {Array} An array of items
13206      */
13207     getRange : function(start, end){
13208         var items = this.items;
13209         if(items.length < 1){
13210             return [];
13211         }
13212         start = start || 0;
13213         end = Math.min(typeof end == "undefined" ? this.length-1 : end, this.length-1);
13214         var r = [];
13215         if(start <= end){
13216             for(var i = start; i <= end; i++) {
13217                     r[r.length] = items[i];
13218             }
13219         }else{
13220             for(var i = start; i >= end; i--) {
13221                     r[r.length] = items[i];
13222             }
13223         }
13224         return r;
13225     },
13226         
13227     /**
13228      * Filter the <i>objects</i> in this collection by a specific property. 
13229      * Returns a new collection that has been filtered.
13230      * @param {String} property A property on your objects
13231      * @param {String/RegExp} value Either string that the property values 
13232      * should start with or a RegExp to test against the property
13233      * @return {MixedCollection} The new filtered collection
13234      */
13235     filter : function(property, value){
13236         if(!value.exec){ // not a regex
13237             value = String(value);
13238             if(value.length == 0){
13239                 return this.clone();
13240             }
13241             value = new RegExp("^" + Roo.escapeRe(value), "i");
13242         }
13243         return this.filterBy(function(o){
13244             return o && value.test(o[property]);
13245         });
13246         },
13247     
13248     /**
13249      * Filter by a function. * Returns a new collection that has been filtered.
13250      * The passed function will be called with each 
13251      * object in the collection. If the function returns true, the value is included 
13252      * otherwise it is filtered.
13253      * @param {Function} fn The function to be called, it will receive the args o (the object), k (the key)
13254      * @param {Object} scope (optional) The scope of the function (defaults to this) 
13255      * @return {MixedCollection} The new filtered collection
13256      */
13257     filterBy : function(fn, scope){
13258         var r = new Roo.util.MixedCollection();
13259         r.getKey = this.getKey;
13260         var k = this.keys, it = this.items;
13261         for(var i = 0, len = it.length; i < len; i++){
13262             if(fn.call(scope||this, it[i], k[i])){
13263                                 r.add(k[i], it[i]);
13264                         }
13265         }
13266         return r;
13267     },
13268     
13269     /**
13270      * Creates a duplicate of this collection
13271      * @return {MixedCollection}
13272      */
13273     clone : function(){
13274         var r = new Roo.util.MixedCollection();
13275         var k = this.keys, it = this.items;
13276         for(var i = 0, len = it.length; i < len; i++){
13277             r.add(k[i], it[i]);
13278         }
13279         r.getKey = this.getKey;
13280         return r;
13281     }
13282 });
13283 /**
13284  * Returns the item associated with the passed key or index.
13285  * @method
13286  * @param {String/Number} key The key or index of the item.
13287  * @return {Object} The item associated with the passed key.
13288  */
13289 Roo.util.MixedCollection.prototype.get = Roo.util.MixedCollection.prototype.item;/*
13290  * Based on:
13291  * Ext JS Library 1.1.1
13292  * Copyright(c) 2006-2007, Ext JS, LLC.
13293  *
13294  * Originally Released Under LGPL - original licence link has changed is not relivant.
13295  *
13296  * Fork - LGPL
13297  * <script type="text/javascript">
13298  */
13299 /**
13300  * @class Roo.util.JSON
13301  * Modified version of Douglas Crockford"s json.js that doesn"t
13302  * mess with the Object prototype 
13303  * http://www.json.org/js.html
13304  * @singleton
13305  */
13306 Roo.util.JSON = new (function(){
13307     var useHasOwn = {}.hasOwnProperty ? true : false;
13308     
13309     // crashes Safari in some instances
13310     //var validRE = /^("(\\.|[^"\\\n\r])*?"|[,:{}\[\]0-9.\-+Eaeflnr-u \n\r\t])+?$/;
13311     
13312     var pad = function(n) {
13313         return n < 10 ? "0" + n : n;
13314     };
13315     
13316     var m = {
13317         "\b": '\\b',
13318         "\t": '\\t',
13319         "\n": '\\n',
13320         "\f": '\\f',
13321         "\r": '\\r',
13322         '"' : '\\"',
13323         "\\": '\\\\'
13324     };
13325
13326     var encodeString = function(s){
13327         if (/["\\\x00-\x1f]/.test(s)) {
13328             return '"' + s.replace(/([\x00-\x1f\\"])/g, function(a, b) {
13329                 var c = m[b];
13330                 if(c){
13331                     return c;
13332                 }
13333                 c = b.charCodeAt();
13334                 return "\\u00" +
13335                     Math.floor(c / 16).toString(16) +
13336                     (c % 16).toString(16);
13337             }) + '"';
13338         }
13339         return '"' + s + '"';
13340     };
13341     
13342     var encodeArray = function(o){
13343         var a = ["["], b, i, l = o.length, v;
13344             for (i = 0; i < l; i += 1) {
13345                 v = o[i];
13346                 switch (typeof v) {
13347                     case "undefined":
13348                     case "function":
13349                     case "unknown":
13350                         break;
13351                     default:
13352                         if (b) {
13353                             a.push(',');
13354                         }
13355                         a.push(v === null ? "null" : Roo.util.JSON.encode(v));
13356                         b = true;
13357                 }
13358             }
13359             a.push("]");
13360             return a.join("");
13361     };
13362     
13363     var encodeDate = function(o){
13364         return '"' + o.getFullYear() + "-" +
13365                 pad(o.getMonth() + 1) + "-" +
13366                 pad(o.getDate()) + "T" +
13367                 pad(o.getHours()) + ":" +
13368                 pad(o.getMinutes()) + ":" +
13369                 pad(o.getSeconds()) + '"';
13370     };
13371     
13372     /**
13373      * Encodes an Object, Array or other value
13374      * @param {Mixed} o The variable to encode
13375      * @return {String} The JSON string
13376      */
13377     this.encode = function(o)
13378     {
13379         // should this be extended to fully wrap stringify..
13380         
13381         if(typeof o == "undefined" || o === null){
13382             return "null";
13383         }else if(o instanceof Array){
13384             return encodeArray(o);
13385         }else if(o instanceof Date){
13386             return encodeDate(o);
13387         }else if(typeof o == "string"){
13388             return encodeString(o);
13389         }else if(typeof o == "number"){
13390             return isFinite(o) ? String(o) : "null";
13391         }else if(typeof o == "boolean"){
13392             return String(o);
13393         }else {
13394             var a = ["{"], b, i, v;
13395             for (i in o) {
13396                 if(!useHasOwn || o.hasOwnProperty(i)) {
13397                     v = o[i];
13398                     switch (typeof v) {
13399                     case "undefined":
13400                     case "function":
13401                     case "unknown":
13402                         break;
13403                     default:
13404                         if(b){
13405                             a.push(',');
13406                         }
13407                         a.push(this.encode(i), ":",
13408                                 v === null ? "null" : this.encode(v));
13409                         b = true;
13410                     }
13411                 }
13412             }
13413             a.push("}");
13414             return a.join("");
13415         }
13416     };
13417     
13418     /**
13419      * Decodes (parses) a JSON string to an object. If the JSON is invalid, this function throws a SyntaxError.
13420      * @param {String} json The JSON string
13421      * @return {Object} The resulting object
13422      */
13423     this.decode = function(json){
13424         
13425         return  /** eval:var:json */ eval("(" + json + ')');
13426     };
13427 })();
13428 /** 
13429  * Shorthand for {@link Roo.util.JSON#encode}
13430  * @member Roo encode 
13431  * @method */
13432 Roo.encode = typeof(JSON) != 'undefined' && JSON.stringify ? JSON.stringify : Roo.util.JSON.encode;
13433 /** 
13434  * Shorthand for {@link Roo.util.JSON#decode}
13435  * @member Roo decode 
13436  * @method */
13437 Roo.decode = typeof(JSON) != 'undefined' && JSON.parse ? JSON.parse : Roo.util.JSON.decode;
13438 /*
13439  * Based on:
13440  * Ext JS Library 1.1.1
13441  * Copyright(c) 2006-2007, Ext JS, LLC.
13442  *
13443  * Originally Released Under LGPL - original licence link has changed is not relivant.
13444  *
13445  * Fork - LGPL
13446  * <script type="text/javascript">
13447  */
13448  
13449 /**
13450  * @class Roo.util.Format
13451  * Reusable data formatting functions
13452  * @singleton
13453  */
13454 Roo.util.Format = function(){
13455     var trimRe = /^\s+|\s+$/g;
13456     return {
13457         /**
13458          * Truncate a string and add an ellipsis ('...') to the end if it exceeds the specified length
13459          * @param {String} value The string to truncate
13460          * @param {Number} length The maximum length to allow before truncating
13461          * @return {String} The converted text
13462          */
13463         ellipsis : function(value, len){
13464             if(value && value.length > len){
13465                 return value.substr(0, len-3)+"...";
13466             }
13467             return value;
13468         },
13469
13470         /**
13471          * Checks a reference and converts it to empty string if it is undefined
13472          * @param {Mixed} value Reference to check
13473          * @return {Mixed} Empty string if converted, otherwise the original value
13474          */
13475         undef : function(value){
13476             return typeof value != "undefined" ? value : "";
13477         },
13478
13479         /**
13480          * Convert certain characters (&, <, >, and ') to their HTML character equivalents for literal display in web pages.
13481          * @param {String} value The string to encode
13482          * @return {String} The encoded text
13483          */
13484         htmlEncode : function(value){
13485             return !value ? value : String(value).replace(/&/g, "&amp;").replace(/>/g, "&gt;").replace(/</g, "&lt;").replace(/"/g, "&quot;");
13486         },
13487
13488         /**
13489          * Convert certain characters (&, <, >, and ') from their HTML character equivalents.
13490          * @param {String} value The string to decode
13491          * @return {String} The decoded text
13492          */
13493         htmlDecode : function(value){
13494             return !value ? value : String(value).replace(/&amp;/g, "&").replace(/&gt;/g, ">").replace(/&lt;/g, "<").replace(/&quot;/g, '"');
13495         },
13496
13497         /**
13498          * Trims any whitespace from either side of a string
13499          * @param {String} value The text to trim
13500          * @return {String} The trimmed text
13501          */
13502         trim : function(value){
13503             return String(value).replace(trimRe, "");
13504         },
13505
13506         /**
13507          * Returns a substring from within an original string
13508          * @param {String} value The original text
13509          * @param {Number} start The start index of the substring
13510          * @param {Number} length The length of the substring
13511          * @return {String} The substring
13512          */
13513         substr : function(value, start, length){
13514             return String(value).substr(start, length);
13515         },
13516
13517         /**
13518          * Converts a string to all lower case letters
13519          * @param {String} value The text to convert
13520          * @return {String} The converted text
13521          */
13522         lowercase : function(value){
13523             return String(value).toLowerCase();
13524         },
13525
13526         /**
13527          * Converts a string to all upper case letters
13528          * @param {String} value The text to convert
13529          * @return {String} The converted text
13530          */
13531         uppercase : function(value){
13532             return String(value).toUpperCase();
13533         },
13534
13535         /**
13536          * Converts the first character only of a string to upper case
13537          * @param {String} value The text to convert
13538          * @return {String} The converted text
13539          */
13540         capitalize : function(value){
13541             return !value ? value : value.charAt(0).toUpperCase() + value.substr(1).toLowerCase();
13542         },
13543
13544         // private
13545         call : function(value, fn){
13546             if(arguments.length > 2){
13547                 var args = Array.prototype.slice.call(arguments, 2);
13548                 args.unshift(value);
13549                  
13550                 return /** eval:var:value */  eval(fn).apply(window, args);
13551             }else{
13552                 /** eval:var:value */
13553                 return /** eval:var:value */ eval(fn).call(window, value);
13554             }
13555         },
13556
13557        
13558         /**
13559          * safer version of Math.toFixed..??/
13560          * @param {Number/String} value The numeric value to format
13561          * @param {Number/String} value Decimal places 
13562          * @return {String} The formatted currency string
13563          */
13564         toFixed : function(v, n)
13565         {
13566             // why not use to fixed - precision is buggered???
13567             if (!n) {
13568                 return Math.round(v-0);
13569             }
13570             var fact = Math.pow(10,n+1);
13571             v = (Math.round((v-0)*fact))/fact;
13572             var z = (''+fact).substring(2);
13573             if (v == Math.floor(v)) {
13574                 return Math.floor(v) + '.' + z;
13575             }
13576             
13577             // now just padd decimals..
13578             var ps = String(v).split('.');
13579             var fd = (ps[1] + z);
13580             var r = fd.substring(0,n); 
13581             var rm = fd.substring(n); 
13582             if (rm < 5) {
13583                 return ps[0] + '.' + r;
13584             }
13585             r*=1; // turn it into a number;
13586             r++;
13587             if (String(r).length != n) {
13588                 ps[0]*=1;
13589                 ps[0]++;
13590                 r = String(r).substring(1); // chop the end off.
13591             }
13592             
13593             return ps[0] + '.' + r;
13594              
13595         },
13596         
13597         /**
13598          * Format a number as US currency
13599          * @param {Number/String} value The numeric value to format
13600          * @return {String} The formatted currency string
13601          */
13602         usMoney : function(v){
13603             return '$' + Roo.util.Format.number(v);
13604         },
13605         
13606         /**
13607          * Format a number
13608          * eventually this should probably emulate php's number_format
13609          * @param {Number/String} value The numeric value to format
13610          * @param {Number} decimals number of decimal places
13611          * @return {String} The formatted currency string
13612          */
13613         number : function(v,decimals)
13614         {
13615             // multiply and round.
13616             decimals = typeof(decimals) == 'undefined' ? 2 : decimals;
13617             var mul = Math.pow(10, decimals);
13618             var zero = String(mul).substring(1);
13619             v = (Math.round((v-0)*mul))/mul;
13620             
13621             // if it's '0' number.. then
13622             
13623             //v = (v == Math.floor(v)) ? v + "." + zero : ((v*10 == Math.floor(v*10)) ? v + "0" : v);
13624             v = String(v);
13625             var ps = v.split('.');
13626             var whole = ps[0];
13627             
13628             
13629             var r = /(\d+)(\d{3})/;
13630             // add comma's
13631             while (r.test(whole)) {
13632                 whole = whole.replace(r, '$1' + ',' + '$2');
13633             }
13634             
13635             
13636             var sub = ps[1] ?
13637                     // has decimals..
13638                     (decimals ?  ('.'+ ps[1] + zero.substring(ps[1].length)) : '') :
13639                     // does not have decimals
13640                     (decimals ? ('.' + zero) : '');
13641             
13642             
13643             return whole + sub ;
13644         },
13645         
13646         /**
13647          * Parse a value into a formatted date using the specified format pattern.
13648          * @param {Mixed} value The value to format
13649          * @param {String} format (optional) Any valid date format string (defaults to 'm/d/Y')
13650          * @return {String} The formatted date string
13651          */
13652         date : function(v, format){
13653             if(!v){
13654                 return "";
13655             }
13656             if(!(v instanceof Date)){
13657                 v = new Date(Date.parse(v));
13658             }
13659             return v.dateFormat(format || Roo.util.Format.defaults.date);
13660         },
13661
13662         /**
13663          * Returns a date rendering function that can be reused to apply a date format multiple times efficiently
13664          * @param {String} format Any valid date format string
13665          * @return {Function} The date formatting function
13666          */
13667         dateRenderer : function(format){
13668             return function(v){
13669                 return Roo.util.Format.date(v, format);  
13670             };
13671         },
13672
13673         // private
13674         stripTagsRE : /<\/?[^>]+>/gi,
13675         
13676         /**
13677          * Strips all HTML tags
13678          * @param {Mixed} value The text from which to strip tags
13679          * @return {String} The stripped text
13680          */
13681         stripTags : function(v){
13682             return !v ? v : String(v).replace(this.stripTagsRE, "");
13683         }
13684     };
13685 }();
13686 Roo.util.Format.defaults = {
13687     date : 'd/M/Y'
13688 };/*
13689  * Based on:
13690  * Ext JS Library 1.1.1
13691  * Copyright(c) 2006-2007, Ext JS, LLC.
13692  *
13693  * Originally Released Under LGPL - original licence link has changed is not relivant.
13694  *
13695  * Fork - LGPL
13696  * <script type="text/javascript">
13697  */
13698
13699
13700  
13701
13702 /**
13703  * @class Roo.MasterTemplate
13704  * @extends Roo.Template
13705  * Provides a template that can have child templates. The syntax is:
13706 <pre><code>
13707 var t = new Roo.MasterTemplate(
13708         '&lt;select name="{name}"&gt;',
13709                 '&lt;tpl name="options"&gt;&lt;option value="{value:trim}"&gt;{text:ellipsis(10)}&lt;/option&gt;&lt;/tpl&gt;',
13710         '&lt;/select&gt;'
13711 );
13712 t.add('options', {value: 'foo', text: 'bar'});
13713 // or you can add multiple child elements in one shot
13714 t.addAll('options', [
13715     {value: 'foo', text: 'bar'},
13716     {value: 'foo2', text: 'bar2'},
13717     {value: 'foo3', text: 'bar3'}
13718 ]);
13719 // then append, applying the master template values
13720 t.append('my-form', {name: 'my-select'});
13721 </code></pre>
13722 * A name attribute for the child template is not required if you have only one child
13723 * template or you want to refer to them by index.
13724  */
13725 Roo.MasterTemplate = function(){
13726     Roo.MasterTemplate.superclass.constructor.apply(this, arguments);
13727     this.originalHtml = this.html;
13728     var st = {};
13729     var m, re = this.subTemplateRe;
13730     re.lastIndex = 0;
13731     var subIndex = 0;
13732     while(m = re.exec(this.html)){
13733         var name = m[1], content = m[2];
13734         st[subIndex] = {
13735             name: name,
13736             index: subIndex,
13737             buffer: [],
13738             tpl : new Roo.Template(content)
13739         };
13740         if(name){
13741             st[name] = st[subIndex];
13742         }
13743         st[subIndex].tpl.compile();
13744         st[subIndex].tpl.call = this.call.createDelegate(this);
13745         subIndex++;
13746     }
13747     this.subCount = subIndex;
13748     this.subs = st;
13749 };
13750 Roo.extend(Roo.MasterTemplate, Roo.Template, {
13751     /**
13752     * The regular expression used to match sub templates
13753     * @type RegExp
13754     * @property
13755     */
13756     subTemplateRe : /<tpl(?:\sname="([\w-]+)")?>((?:.|\n)*?)<\/tpl>/gi,
13757
13758     /**
13759      * Applies the passed values to a child template.
13760      * @param {String/Number} name (optional) The name or index of the child template
13761      * @param {Array/Object} values The values to be applied to the template
13762      * @return {MasterTemplate} this
13763      */
13764      add : function(name, values){
13765         if(arguments.length == 1){
13766             values = arguments[0];
13767             name = 0;
13768         }
13769         var s = this.subs[name];
13770         s.buffer[s.buffer.length] = s.tpl.apply(values);
13771         return this;
13772     },
13773
13774     /**
13775      * Applies all the passed values to a child template.
13776      * @param {String/Number} name (optional) The name or index of the child template
13777      * @param {Array} values The values to be applied to the template, this should be an array of objects.
13778      * @param {Boolean} reset (optional) True to reset the template first
13779      * @return {MasterTemplate} this
13780      */
13781     fill : function(name, values, reset){
13782         var a = arguments;
13783         if(a.length == 1 || (a.length == 2 && typeof a[1] == "boolean")){
13784             values = a[0];
13785             name = 0;
13786             reset = a[1];
13787         }
13788         if(reset){
13789             this.reset();
13790         }
13791         for(var i = 0, len = values.length; i < len; i++){
13792             this.add(name, values[i]);
13793         }
13794         return this;
13795     },
13796
13797     /**
13798      * Resets the template for reuse
13799      * @return {MasterTemplate} this
13800      */
13801      reset : function(){
13802         var s = this.subs;
13803         for(var i = 0; i < this.subCount; i++){
13804             s[i].buffer = [];
13805         }
13806         return this;
13807     },
13808
13809     applyTemplate : function(values){
13810         var s = this.subs;
13811         var replaceIndex = -1;
13812         this.html = this.originalHtml.replace(this.subTemplateRe, function(m, name){
13813             return s[++replaceIndex].buffer.join("");
13814         });
13815         return Roo.MasterTemplate.superclass.applyTemplate.call(this, values);
13816     },
13817
13818     apply : function(){
13819         return this.applyTemplate.apply(this, arguments);
13820     },
13821
13822     compile : function(){return this;}
13823 });
13824
13825 /**
13826  * Alias for fill().
13827  * @method
13828  */
13829 Roo.MasterTemplate.prototype.addAll = Roo.MasterTemplate.prototype.fill;
13830  /**
13831  * Creates a template from the passed element's value (display:none textarea, preferred) or innerHTML. e.g.
13832  * var tpl = Roo.MasterTemplate.from('element-id');
13833  * @param {String/HTMLElement} el
13834  * @param {Object} config
13835  * @static
13836  */
13837 Roo.MasterTemplate.from = function(el, config){
13838     el = Roo.getDom(el);
13839     return new Roo.MasterTemplate(el.value || el.innerHTML, config || '');
13840 };/*
13841  * Based on:
13842  * Ext JS Library 1.1.1
13843  * Copyright(c) 2006-2007, Ext JS, LLC.
13844  *
13845  * Originally Released Under LGPL - original licence link has changed is not relivant.
13846  *
13847  * Fork - LGPL
13848  * <script type="text/javascript">
13849  */
13850
13851  
13852 /**
13853  * @class Roo.util.CSS
13854  * Utility class for manipulating CSS rules
13855  * @singleton
13856  */
13857 Roo.util.CSS = function(){
13858         var rules = null;
13859         var doc = document;
13860
13861     var camelRe = /(-[a-z])/gi;
13862     var camelFn = function(m, a){ return a.charAt(1).toUpperCase(); };
13863
13864    return {
13865    /**
13866     * Very simple dynamic creation of stylesheets from a text blob of rules.  The text will wrapped in a style
13867     * tag and appended to the HEAD of the document.
13868     * @param {String|Object} cssText The text containing the css rules
13869     * @param {String} id An id to add to the stylesheet for later removal
13870     * @return {StyleSheet}
13871     */
13872     createStyleSheet : function(cssText, id){
13873         var ss;
13874         var head = doc.getElementsByTagName("head")[0];
13875         var nrules = doc.createElement("style");
13876         nrules.setAttribute("type", "text/css");
13877         if(id){
13878             nrules.setAttribute("id", id);
13879         }
13880         if (typeof(cssText) != 'string') {
13881             // support object maps..
13882             // not sure if this a good idea.. 
13883             // perhaps it should be merged with the general css handling
13884             // and handle js style props.
13885             var cssTextNew = [];
13886             for(var n in cssText) {
13887                 var citems = [];
13888                 for(var k in cssText[n]) {
13889                     citems.push( k + ' : ' +cssText[n][k] + ';' );
13890                 }
13891                 cssTextNew.push( n + ' { ' + citems.join(' ') + '} ');
13892                 
13893             }
13894             cssText = cssTextNew.join("\n");
13895             
13896         }
13897        
13898        
13899        if(Roo.isIE){
13900            head.appendChild(nrules);
13901            ss = nrules.styleSheet;
13902            ss.cssText = cssText;
13903        }else{
13904            try{
13905                 nrules.appendChild(doc.createTextNode(cssText));
13906            }catch(e){
13907                nrules.cssText = cssText; 
13908            }
13909            head.appendChild(nrules);
13910            ss = nrules.styleSheet ? nrules.styleSheet : (nrules.sheet || doc.styleSheets[doc.styleSheets.length-1]);
13911        }
13912        this.cacheStyleSheet(ss);
13913        return ss;
13914    },
13915
13916    /**
13917     * Removes a style or link tag by id
13918     * @param {String} id The id of the tag
13919     */
13920    removeStyleSheet : function(id){
13921        var existing = doc.getElementById(id);
13922        if(existing){
13923            existing.parentNode.removeChild(existing);
13924        }
13925    },
13926
13927    /**
13928     * Dynamically swaps an existing stylesheet reference for a new one
13929     * @param {String} id The id of an existing link tag to remove
13930     * @param {String} url The href of the new stylesheet to include
13931     */
13932    swapStyleSheet : function(id, url){
13933        this.removeStyleSheet(id);
13934        var ss = doc.createElement("link");
13935        ss.setAttribute("rel", "stylesheet");
13936        ss.setAttribute("type", "text/css");
13937        ss.setAttribute("id", id);
13938        ss.setAttribute("href", url);
13939        doc.getElementsByTagName("head")[0].appendChild(ss);
13940    },
13941    
13942    /**
13943     * Refresh the rule cache if you have dynamically added stylesheets
13944     * @return {Object} An object (hash) of rules indexed by selector
13945     */
13946    refreshCache : function(){
13947        return this.getRules(true);
13948    },
13949
13950    // private
13951    cacheStyleSheet : function(stylesheet){
13952        if(!rules){
13953            rules = {};
13954        }
13955        try{// try catch for cross domain access issue
13956            var ssRules = stylesheet.cssRules || stylesheet.rules;
13957            for(var j = ssRules.length-1; j >= 0; --j){
13958                rules[ssRules[j].selectorText] = ssRules[j];
13959            }
13960        }catch(e){}
13961    },
13962    
13963    /**
13964     * Gets all css rules for the document
13965     * @param {Boolean} refreshCache true to refresh the internal cache
13966     * @return {Object} An object (hash) of rules indexed by selector
13967     */
13968    getRules : function(refreshCache){
13969                 if(rules == null || refreshCache){
13970                         rules = {};
13971                         var ds = doc.styleSheets;
13972                         for(var i =0, len = ds.length; i < len; i++){
13973                             try{
13974                         this.cacheStyleSheet(ds[i]);
13975                     }catch(e){} 
13976                 }
13977                 }
13978                 return rules;
13979         },
13980         
13981         /**
13982     * Gets an an individual CSS rule by selector(s)
13983     * @param {String/Array} selector The CSS selector or an array of selectors to try. The first selector that is found is returned.
13984     * @param {Boolean} refreshCache true to refresh the internal cache if you have recently updated any rules or added styles dynamically
13985     * @return {CSSRule} The CSS rule or null if one is not found
13986     */
13987    getRule : function(selector, refreshCache){
13988                 var rs = this.getRules(refreshCache);
13989                 if(!(selector instanceof Array)){
13990                     return rs[selector];
13991                 }
13992                 for(var i = 0; i < selector.length; i++){
13993                         if(rs[selector[i]]){
13994                                 return rs[selector[i]];
13995                         }
13996                 }
13997                 return null;
13998         },
13999         
14000         
14001         /**
14002     * Updates a rule property
14003     * @param {String/Array} selector If it's an array it tries each selector until it finds one. Stops immediately once one is found.
14004     * @param {String} property The css property
14005     * @param {String} value The new value for the property
14006     * @return {Boolean} true If a rule was found and updated
14007     */
14008    updateRule : function(selector, property, value){
14009                 if(!(selector instanceof Array)){
14010                         var rule = this.getRule(selector);
14011                         if(rule){
14012                                 rule.style[property.replace(camelRe, camelFn)] = value;
14013                                 return true;
14014                         }
14015                 }else{
14016                         for(var i = 0; i < selector.length; i++){
14017                                 if(this.updateRule(selector[i], property, value)){
14018                                         return true;
14019                                 }
14020                         }
14021                 }
14022                 return false;
14023         }
14024    };   
14025 }();/*
14026  * Based on:
14027  * Ext JS Library 1.1.1
14028  * Copyright(c) 2006-2007, Ext JS, LLC.
14029  *
14030  * Originally Released Under LGPL - original licence link has changed is not relivant.
14031  *
14032  * Fork - LGPL
14033  * <script type="text/javascript">
14034  */
14035
14036  
14037
14038 /**
14039  * @class Roo.util.ClickRepeater
14040  * @extends Roo.util.Observable
14041  * 
14042  * A wrapper class which can be applied to any element. Fires a "click" event while the
14043  * mouse is pressed. The interval between firings may be specified in the config but
14044  * defaults to 10 milliseconds.
14045  * 
14046  * Optionally, a CSS class may be applied to the element during the time it is pressed.
14047  * 
14048  * @cfg {String/HTMLElement/Element} el The element to act as a button.
14049  * @cfg {Number} delay The initial delay before the repeating event begins firing.
14050  * Similar to an autorepeat key delay.
14051  * @cfg {Number} interval The interval between firings of the "click" event. Default 10 ms.
14052  * @cfg {String} pressClass A CSS class name to be applied to the element while pressed.
14053  * @cfg {Boolean} accelerate True if autorepeating should start slowly and accelerate.
14054  *           "interval" and "delay" are ignored. "immediate" is honored.
14055  * @cfg {Boolean} preventDefault True to prevent the default click event
14056  * @cfg {Boolean} stopDefault True to stop the default click event
14057  * 
14058  * @history
14059  *     2007-02-02 jvs Original code contributed by Nige "Animal" White
14060  *     2007-02-02 jvs Renamed to ClickRepeater
14061  *   2007-02-03 jvs Modifications for FF Mac and Safari 
14062  *
14063  *  @constructor
14064  * @param {String/HTMLElement/Element} el The element to listen on
14065  * @param {Object} config
14066  **/
14067 Roo.util.ClickRepeater = function(el, config)
14068 {
14069     this.el = Roo.get(el);
14070     this.el.unselectable();
14071
14072     Roo.apply(this, config);
14073
14074     this.addEvents({
14075     /**
14076      * @event mousedown
14077      * Fires when the mouse button is depressed.
14078      * @param {Roo.util.ClickRepeater} this
14079      */
14080         "mousedown" : true,
14081     /**
14082      * @event click
14083      * Fires on a specified interval during the time the element is pressed.
14084      * @param {Roo.util.ClickRepeater} this
14085      */
14086         "click" : true,
14087     /**
14088      * @event mouseup
14089      * Fires when the mouse key is released.
14090      * @param {Roo.util.ClickRepeater} this
14091      */
14092         "mouseup" : true
14093     });
14094
14095     this.el.on("mousedown", this.handleMouseDown, this);
14096     if(this.preventDefault || this.stopDefault){
14097         this.el.on("click", function(e){
14098             if(this.preventDefault){
14099                 e.preventDefault();
14100             }
14101             if(this.stopDefault){
14102                 e.stopEvent();
14103             }
14104         }, this);
14105     }
14106
14107     // allow inline handler
14108     if(this.handler){
14109         this.on("click", this.handler,  this.scope || this);
14110     }
14111
14112     Roo.util.ClickRepeater.superclass.constructor.call(this);
14113 };
14114
14115 Roo.extend(Roo.util.ClickRepeater, Roo.util.Observable, {
14116     interval : 20,
14117     delay: 250,
14118     preventDefault : true,
14119     stopDefault : false,
14120     timer : 0,
14121
14122     // private
14123     handleMouseDown : function(){
14124         clearTimeout(this.timer);
14125         this.el.blur();
14126         if(this.pressClass){
14127             this.el.addClass(this.pressClass);
14128         }
14129         this.mousedownTime = new Date();
14130
14131         Roo.get(document).on("mouseup", this.handleMouseUp, this);
14132         this.el.on("mouseout", this.handleMouseOut, this);
14133
14134         this.fireEvent("mousedown", this);
14135         this.fireEvent("click", this);
14136         
14137         this.timer = this.click.defer(this.delay || this.interval, this);
14138     },
14139
14140     // private
14141     click : function(){
14142         this.fireEvent("click", this);
14143         this.timer = this.click.defer(this.getInterval(), this);
14144     },
14145
14146     // private
14147     getInterval: function(){
14148         if(!this.accelerate){
14149             return this.interval;
14150         }
14151         var pressTime = this.mousedownTime.getElapsed();
14152         if(pressTime < 500){
14153             return 400;
14154         }else if(pressTime < 1700){
14155             return 320;
14156         }else if(pressTime < 2600){
14157             return 250;
14158         }else if(pressTime < 3500){
14159             return 180;
14160         }else if(pressTime < 4400){
14161             return 140;
14162         }else if(pressTime < 5300){
14163             return 80;
14164         }else if(pressTime < 6200){
14165             return 50;
14166         }else{
14167             return 10;
14168         }
14169     },
14170
14171     // private
14172     handleMouseOut : function(){
14173         clearTimeout(this.timer);
14174         if(this.pressClass){
14175             this.el.removeClass(this.pressClass);
14176         }
14177         this.el.on("mouseover", this.handleMouseReturn, this);
14178     },
14179
14180     // private
14181     handleMouseReturn : function(){
14182         this.el.un("mouseover", this.handleMouseReturn);
14183         if(this.pressClass){
14184             this.el.addClass(this.pressClass);
14185         }
14186         this.click();
14187     },
14188
14189     // private
14190     handleMouseUp : function(){
14191         clearTimeout(this.timer);
14192         this.el.un("mouseover", this.handleMouseReturn);
14193         this.el.un("mouseout", this.handleMouseOut);
14194         Roo.get(document).un("mouseup", this.handleMouseUp);
14195         this.el.removeClass(this.pressClass);
14196         this.fireEvent("mouseup", this);
14197     }
14198 });/*
14199  * Based on:
14200  * Ext JS Library 1.1.1
14201  * Copyright(c) 2006-2007, Ext JS, LLC.
14202  *
14203  * Originally Released Under LGPL - original licence link has changed is not relivant.
14204  *
14205  * Fork - LGPL
14206  * <script type="text/javascript">
14207  */
14208
14209  
14210 /**
14211  * @class Roo.KeyNav
14212  * <p>Provides a convenient wrapper for normalized keyboard navigation.  KeyNav allows you to bind
14213  * navigation keys to function calls that will get called when the keys are pressed, providing an easy
14214  * way to implement custom navigation schemes for any UI component.</p>
14215  * <p>The following are all of the possible keys that can be implemented: enter, left, right, up, down, tab, esc,
14216  * pageUp, pageDown, del, home, end.  Usage:</p>
14217  <pre><code>
14218 var nav = new Roo.KeyNav("my-element", {
14219     "left" : function(e){
14220         this.moveLeft(e.ctrlKey);
14221     },
14222     "right" : function(e){
14223         this.moveRight(e.ctrlKey);
14224     },
14225     "enter" : function(e){
14226         this.save();
14227     },
14228     scope : this
14229 });
14230 </code></pre>
14231  * @constructor
14232  * @param {String/HTMLElement/Roo.Element} el The element to bind to
14233  * @param {Object} config The config
14234  */
14235 Roo.KeyNav = function(el, config){
14236     this.el = Roo.get(el);
14237     Roo.apply(this, config);
14238     if(!this.disabled){
14239         this.disabled = true;
14240         this.enable();
14241     }
14242 };
14243
14244 Roo.KeyNav.prototype = {
14245     /**
14246      * @cfg {Boolean} disabled
14247      * True to disable this KeyNav instance (defaults to false)
14248      */
14249     disabled : false,
14250     /**
14251      * @cfg {String} defaultEventAction
14252      * The method to call on the {@link Roo.EventObject} after this KeyNav intercepts a key.  Valid values are
14253      * {@link Roo.EventObject#stopEvent}, {@link Roo.EventObject#preventDefault} and
14254      * {@link Roo.EventObject#stopPropagation} (defaults to 'stopEvent')
14255      */
14256     defaultEventAction: "stopEvent",
14257     /**
14258      * @cfg {Boolean} forceKeyDown
14259      * Handle the keydown event instead of keypress (defaults to false).  KeyNav automatically does this for IE since
14260      * IE does not propagate special keys on keypress, but setting this to true will force other browsers to also
14261      * handle keydown instead of keypress.
14262      */
14263     forceKeyDown : false,
14264
14265     // private
14266     prepareEvent : function(e){
14267         var k = e.getKey();
14268         var h = this.keyToHandler[k];
14269         //if(h && this[h]){
14270         //    e.stopPropagation();
14271         //}
14272         if(Roo.isSafari && h && k >= 37 && k <= 40){
14273             e.stopEvent();
14274         }
14275     },
14276
14277     // private
14278     relay : function(e){
14279         var k = e.getKey();
14280         var h = this.keyToHandler[k];
14281         if(h && this[h]){
14282             if(this.doRelay(e, this[h], h) !== true){
14283                 e[this.defaultEventAction]();
14284             }
14285         }
14286     },
14287
14288     // private
14289     doRelay : function(e, h, hname){
14290         return h.call(this.scope || this, e);
14291     },
14292
14293     // possible handlers
14294     enter : false,
14295     left : false,
14296     right : false,
14297     up : false,
14298     down : false,
14299     tab : false,
14300     esc : false,
14301     pageUp : false,
14302     pageDown : false,
14303     del : false,
14304     home : false,
14305     end : false,
14306
14307     // quick lookup hash
14308     keyToHandler : {
14309         37 : "left",
14310         39 : "right",
14311         38 : "up",
14312         40 : "down",
14313         33 : "pageUp",
14314         34 : "pageDown",
14315         46 : "del",
14316         36 : "home",
14317         35 : "end",
14318         13 : "enter",
14319         27 : "esc",
14320         9  : "tab"
14321     },
14322
14323         /**
14324          * Enable this KeyNav
14325          */
14326         enable: function(){
14327                 if(this.disabled){
14328             // ie won't do special keys on keypress, no one else will repeat keys with keydown
14329             // the EventObject will normalize Safari automatically
14330             if(this.forceKeyDown || Roo.isIE || Roo.isAir){
14331                 this.el.on("keydown", this.relay,  this);
14332             }else{
14333                 this.el.on("keydown", this.prepareEvent,  this);
14334                 this.el.on("keypress", this.relay,  this);
14335             }
14336                     this.disabled = false;
14337                 }
14338         },
14339
14340         /**
14341          * Disable this KeyNav
14342          */
14343         disable: function(){
14344                 if(!this.disabled){
14345                     if(this.forceKeyDown || Roo.isIE || Roo.isAir){
14346                 this.el.un("keydown", this.relay);
14347             }else{
14348                 this.el.un("keydown", this.prepareEvent);
14349                 this.el.un("keypress", this.relay);
14350             }
14351                     this.disabled = true;
14352                 }
14353         }
14354 };/*
14355  * Based on:
14356  * Ext JS Library 1.1.1
14357  * Copyright(c) 2006-2007, Ext JS, LLC.
14358  *
14359  * Originally Released Under LGPL - original licence link has changed is not relivant.
14360  *
14361  * Fork - LGPL
14362  * <script type="text/javascript">
14363  */
14364
14365  
14366 /**
14367  * @class Roo.KeyMap
14368  * Handles mapping keys to actions for an element. One key map can be used for multiple actions.
14369  * The constructor accepts the same config object as defined by {@link #addBinding}.
14370  * If you bind a callback function to a KeyMap, anytime the KeyMap handles an expected key
14371  * combination it will call the function with this signature (if the match is a multi-key
14372  * combination the callback will still be called only once): (String key, Roo.EventObject e)
14373  * A KeyMap can also handle a string representation of keys.<br />
14374  * Usage:
14375  <pre><code>
14376 // map one key by key code
14377 var map = new Roo.KeyMap("my-element", {
14378     key: 13, // or Roo.EventObject.ENTER
14379     fn: myHandler,
14380     scope: myObject
14381 });
14382
14383 // map multiple keys to one action by string
14384 var map = new Roo.KeyMap("my-element", {
14385     key: "a\r\n\t",
14386     fn: myHandler,
14387     scope: myObject
14388 });
14389
14390 // map multiple keys to multiple actions by strings and array of codes
14391 var map = new Roo.KeyMap("my-element", [
14392     {
14393         key: [10,13],
14394         fn: function(){ alert("Return was pressed"); }
14395     }, {
14396         key: "abc",
14397         fn: function(){ alert('a, b or c was pressed'); }
14398     }, {
14399         key: "\t",
14400         ctrl:true,
14401         shift:true,
14402         fn: function(){ alert('Control + shift + tab was pressed.'); }
14403     }
14404 ]);
14405 </code></pre>
14406  * <b>Note: A KeyMap starts enabled</b>
14407  * @constructor
14408  * @param {String/HTMLElement/Roo.Element} el The element to bind to
14409  * @param {Object} config The config (see {@link #addBinding})
14410  * @param {String} eventName (optional) The event to bind to (defaults to "keydown")
14411  */
14412 Roo.KeyMap = function(el, config, eventName){
14413     this.el  = Roo.get(el);
14414     this.eventName = eventName || "keydown";
14415     this.bindings = [];
14416     if(config){
14417         this.addBinding(config);
14418     }
14419     this.enable();
14420 };
14421
14422 Roo.KeyMap.prototype = {
14423     /**
14424      * True to stop the event from bubbling and prevent the default browser action if the
14425      * key was handled by the KeyMap (defaults to false)
14426      * @type Boolean
14427      */
14428     stopEvent : false,
14429
14430     /**
14431      * Add a new binding to this KeyMap. The following config object properties are supported:
14432      * <pre>
14433 Property    Type             Description
14434 ----------  ---------------  ----------------------------------------------------------------------
14435 key         String/Array     A single keycode or an array of keycodes to handle
14436 shift       Boolean          True to handle key only when shift is pressed (defaults to false)
14437 ctrl        Boolean          True to handle key only when ctrl is pressed (defaults to false)
14438 alt         Boolean          True to handle key only when alt is pressed (defaults to false)
14439 fn          Function         The function to call when KeyMap finds the expected key combination
14440 scope       Object           The scope of the callback function
14441 </pre>
14442      *
14443      * Usage:
14444      * <pre><code>
14445 // Create a KeyMap
14446 var map = new Roo.KeyMap(document, {
14447     key: Roo.EventObject.ENTER,
14448     fn: handleKey,
14449     scope: this
14450 });
14451
14452 //Add a new binding to the existing KeyMap later
14453 map.addBinding({
14454     key: 'abc',
14455     shift: true,
14456     fn: handleKey,
14457     scope: this
14458 });
14459 </code></pre>
14460      * @param {Object/Array} config A single KeyMap config or an array of configs
14461      */
14462         addBinding : function(config){
14463         if(config instanceof Array){
14464             for(var i = 0, len = config.length; i < len; i++){
14465                 this.addBinding(config[i]);
14466             }
14467             return;
14468         }
14469         var keyCode = config.key,
14470             shift = config.shift, 
14471             ctrl = config.ctrl, 
14472             alt = config.alt,
14473             fn = config.fn,
14474             scope = config.scope;
14475         if(typeof keyCode == "string"){
14476             var ks = [];
14477             var keyString = keyCode.toUpperCase();
14478             for(var j = 0, len = keyString.length; j < len; j++){
14479                 ks.push(keyString.charCodeAt(j));
14480             }
14481             keyCode = ks;
14482         }
14483         var keyArray = keyCode instanceof Array;
14484         var handler = function(e){
14485             if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) &&  (!alt || e.altKey)){
14486                 var k = e.getKey();
14487                 if(keyArray){
14488                     for(var i = 0, len = keyCode.length; i < len; i++){
14489                         if(keyCode[i] == k){
14490                           if(this.stopEvent){
14491                               e.stopEvent();
14492                           }
14493                           fn.call(scope || window, k, e);
14494                           return;
14495                         }
14496                     }
14497                 }else{
14498                     if(k == keyCode){
14499                         if(this.stopEvent){
14500                            e.stopEvent();
14501                         }
14502                         fn.call(scope || window, k, e);
14503                     }
14504                 }
14505             }
14506         };
14507         this.bindings.push(handler);  
14508         },
14509
14510     /**
14511      * Shorthand for adding a single key listener
14512      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the
14513      * following options:
14514      * {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
14515      * @param {Function} fn The function to call
14516      * @param {Object} scope (optional) The scope of the function
14517      */
14518     on : function(key, fn, scope){
14519         var keyCode, shift, ctrl, alt;
14520         if(typeof key == "object" && !(key instanceof Array)){
14521             keyCode = key.key;
14522             shift = key.shift;
14523             ctrl = key.ctrl;
14524             alt = key.alt;
14525         }else{
14526             keyCode = key;
14527         }
14528         this.addBinding({
14529             key: keyCode,
14530             shift: shift,
14531             ctrl: ctrl,
14532             alt: alt,
14533             fn: fn,
14534             scope: scope
14535         })
14536     },
14537
14538     // private
14539     handleKeyDown : function(e){
14540             if(this.enabled){ //just in case
14541             var b = this.bindings;
14542             for(var i = 0, len = b.length; i < len; i++){
14543                 b[i].call(this, e);
14544             }
14545             }
14546         },
14547         
14548         /**
14549          * Returns true if this KeyMap is enabled
14550          * @return {Boolean} 
14551          */
14552         isEnabled : function(){
14553             return this.enabled;  
14554         },
14555         
14556         /**
14557          * Enables this KeyMap
14558          */
14559         enable: function(){
14560                 if(!this.enabled){
14561                     this.el.on(this.eventName, this.handleKeyDown, this);
14562                     this.enabled = true;
14563                 }
14564         },
14565
14566         /**
14567          * Disable this KeyMap
14568          */
14569         disable: function(){
14570                 if(this.enabled){
14571                     this.el.removeListener(this.eventName, this.handleKeyDown, this);
14572                     this.enabled = false;
14573                 }
14574         }
14575 };/*
14576  * Based on:
14577  * Ext JS Library 1.1.1
14578  * Copyright(c) 2006-2007, Ext JS, LLC.
14579  *
14580  * Originally Released Under LGPL - original licence link has changed is not relivant.
14581  *
14582  * Fork - LGPL
14583  * <script type="text/javascript">
14584  */
14585
14586  
14587 /**
14588  * @class Roo.util.TextMetrics
14589  * Provides precise pixel measurements for blocks of text so that you can determine exactly how high and
14590  * wide, in pixels, a given block of text will be.
14591  * @singleton
14592  */
14593 Roo.util.TextMetrics = function(){
14594     var shared;
14595     return {
14596         /**
14597          * Measures the size of the specified text
14598          * @param {String/HTMLElement} el The element, dom node or id from which to copy existing CSS styles
14599          * that can affect the size of the rendered text
14600          * @param {String} text The text to measure
14601          * @param {Number} fixedWidth (optional) If the text will be multiline, you have to set a fixed width
14602          * in order to accurately measure the text height
14603          * @return {Object} An object containing the text's size {width: (width), height: (height)}
14604          */
14605         measure : function(el, text, fixedWidth){
14606             if(!shared){
14607                 shared = Roo.util.TextMetrics.Instance(el, fixedWidth);
14608             }
14609             shared.bind(el);
14610             shared.setFixedWidth(fixedWidth || 'auto');
14611             return shared.getSize(text);
14612         },
14613
14614         /**
14615          * Return a unique TextMetrics instance that can be bound directly to an element and reused.  This reduces
14616          * the overhead of multiple calls to initialize the style properties on each measurement.
14617          * @param {String/HTMLElement} el The element, dom node or id that the instance will be bound to
14618          * @param {Number} fixedWidth (optional) If the text will be multiline, you have to set a fixed width
14619          * in order to accurately measure the text height
14620          * @return {Roo.util.TextMetrics.Instance} instance The new instance
14621          */
14622         createInstance : function(el, fixedWidth){
14623             return Roo.util.TextMetrics.Instance(el, fixedWidth);
14624         }
14625     };
14626 }();
14627
14628  
14629
14630 Roo.util.TextMetrics.Instance = function(bindTo, fixedWidth){
14631     var ml = new Roo.Element(document.createElement('div'));
14632     document.body.appendChild(ml.dom);
14633     ml.position('absolute');
14634     ml.setLeftTop(-1000, -1000);
14635     ml.hide();
14636
14637     if(fixedWidth){
14638         ml.setWidth(fixedWidth);
14639     }
14640      
14641     var instance = {
14642         /**
14643          * Returns the size of the specified text based on the internal element's style and width properties
14644          * @memberOf Roo.util.TextMetrics.Instance#
14645          * @param {String} text The text to measure
14646          * @return {Object} An object containing the text's size {width: (width), height: (height)}
14647          */
14648         getSize : function(text){
14649             ml.update(text);
14650             var s = ml.getSize();
14651             ml.update('');
14652             return s;
14653         },
14654
14655         /**
14656          * Binds this TextMetrics instance to an element from which to copy existing CSS styles
14657          * that can affect the size of the rendered text
14658          * @memberOf Roo.util.TextMetrics.Instance#
14659          * @param {String/HTMLElement} el The element, dom node or id
14660          */
14661         bind : function(el){
14662             ml.setStyle(
14663                 Roo.fly(el).getStyles('font-size','font-style', 'font-weight', 'font-family','line-height')
14664             );
14665         },
14666
14667         /**
14668          * Sets a fixed width on the internal measurement element.  If the text will be multiline, you have
14669          * to set a fixed width in order to accurately measure the text height.
14670          * @memberOf Roo.util.TextMetrics.Instance#
14671          * @param {Number} width The width to set on the element
14672          */
14673         setFixedWidth : function(width){
14674             ml.setWidth(width);
14675         },
14676
14677         /**
14678          * Returns the measured width of the specified text
14679          * @memberOf Roo.util.TextMetrics.Instance#
14680          * @param {String} text The text to measure
14681          * @return {Number} width The width in pixels
14682          */
14683         getWidth : function(text){
14684             ml.dom.style.width = 'auto';
14685             return this.getSize(text).width;
14686         },
14687
14688         /**
14689          * Returns the measured height of the specified text.  For multiline text, be sure to call
14690          * {@link #setFixedWidth} if necessary.
14691          * @memberOf Roo.util.TextMetrics.Instance#
14692          * @param {String} text The text to measure
14693          * @return {Number} height The height in pixels
14694          */
14695         getHeight : function(text){
14696             return this.getSize(text).height;
14697         }
14698     };
14699
14700     instance.bind(bindTo);
14701
14702     return instance;
14703 };
14704
14705 // backwards compat
14706 Roo.Element.measureText = Roo.util.TextMetrics.measure;/*
14707  * Based on:
14708  * Ext JS Library 1.1.1
14709  * Copyright(c) 2006-2007, Ext JS, LLC.
14710  *
14711  * Originally Released Under LGPL - original licence link has changed is not relivant.
14712  *
14713  * Fork - LGPL
14714  * <script type="text/javascript">
14715  */
14716
14717 /**
14718  * @class Roo.state.Provider
14719  * Abstract base class for state provider implementations. This class provides methods
14720  * for encoding and decoding <b>typed</b> variables including dates and defines the 
14721  * Provider interface.
14722  */
14723 Roo.state.Provider = function(){
14724     /**
14725      * @event statechange
14726      * Fires when a state change occurs.
14727      * @param {Provider} this This state provider
14728      * @param {String} key The state key which was changed
14729      * @param {String} value The encoded value for the state
14730      */
14731     this.addEvents({
14732         "statechange": true
14733     });
14734     this.state = {};
14735     Roo.state.Provider.superclass.constructor.call(this);
14736 };
14737 Roo.extend(Roo.state.Provider, Roo.util.Observable, {
14738     /**
14739      * Returns the current value for a key
14740      * @param {String} name The key name
14741      * @param {Mixed} defaultValue A default value to return if the key's value is not found
14742      * @return {Mixed} The state data
14743      */
14744     get : function(name, defaultValue){
14745         return typeof this.state[name] == "undefined" ?
14746             defaultValue : this.state[name];
14747     },
14748     
14749     /**
14750      * Clears a value from the state
14751      * @param {String} name The key name
14752      */
14753     clear : function(name){
14754         delete this.state[name];
14755         this.fireEvent("statechange", this, name, null);
14756     },
14757     
14758     /**
14759      * Sets the value for a key
14760      * @param {String} name The key name
14761      * @param {Mixed} value The value to set
14762      */
14763     set : function(name, value){
14764         this.state[name] = value;
14765         this.fireEvent("statechange", this, name, value);
14766     },
14767     
14768     /**
14769      * Decodes a string previously encoded with {@link #encodeValue}.
14770      * @param {String} value The value to decode
14771      * @return {Mixed} The decoded value
14772      */
14773     decodeValue : function(cookie){
14774         var re = /^(a|n|d|b|s|o)\:(.*)$/;
14775         var matches = re.exec(unescape(cookie));
14776         if(!matches || !matches[1]) return; // non state cookie
14777         var type = matches[1];
14778         var v = matches[2];
14779         switch(type){
14780             case "n":
14781                 return parseFloat(v);
14782             case "d":
14783                 return new Date(Date.parse(v));
14784             case "b":
14785                 return (v == "1");
14786             case "a":
14787                 var all = [];
14788                 var values = v.split("^");
14789                 for(var i = 0, len = values.length; i < len; i++){
14790                     all.push(this.decodeValue(values[i]));
14791                 }
14792                 return all;
14793            case "o":
14794                 var all = {};
14795                 var values = v.split("^");
14796                 for(var i = 0, len = values.length; i < len; i++){
14797                     var kv = values[i].split("=");
14798                     all[kv[0]] = this.decodeValue(kv[1]);
14799                 }
14800                 return all;
14801            default:
14802                 return v;
14803         }
14804     },
14805     
14806     /**
14807      * Encodes a value including type information.  Decode with {@link #decodeValue}.
14808      * @param {Mixed} value The value to encode
14809      * @return {String} The encoded value
14810      */
14811     encodeValue : function(v){
14812         var enc;
14813         if(typeof v == "number"){
14814             enc = "n:" + v;
14815         }else if(typeof v == "boolean"){
14816             enc = "b:" + (v ? "1" : "0");
14817         }else if(v instanceof Date){
14818             enc = "d:" + v.toGMTString();
14819         }else if(v instanceof Array){
14820             var flat = "";
14821             for(var i = 0, len = v.length; i < len; i++){
14822                 flat += this.encodeValue(v[i]);
14823                 if(i != len-1) flat += "^";
14824             }
14825             enc = "a:" + flat;
14826         }else if(typeof v == "object"){
14827             var flat = "";
14828             for(var key in v){
14829                 if(typeof v[key] != "function"){
14830                     flat += key + "=" + this.encodeValue(v[key]) + "^";
14831                 }
14832             }
14833             enc = "o:" + flat.substring(0, flat.length-1);
14834         }else{
14835             enc = "s:" + v;
14836         }
14837         return escape(enc);        
14838     }
14839 });
14840
14841 /*
14842  * Based on:
14843  * Ext JS Library 1.1.1
14844  * Copyright(c) 2006-2007, Ext JS, LLC.
14845  *
14846  * Originally Released Under LGPL - original licence link has changed is not relivant.
14847  *
14848  * Fork - LGPL
14849  * <script type="text/javascript">
14850  */
14851 /**
14852  * @class Roo.state.Manager
14853  * This is the global state manager. By default all components that are "state aware" check this class
14854  * for state information if you don't pass them a custom state provider. In order for this class
14855  * to be useful, it must be initialized with a provider when your application initializes.
14856  <pre><code>
14857 // in your initialization function
14858 init : function(){
14859    Roo.state.Manager.setProvider(new Roo.state.CookieProvider());
14860    ...
14861    // supposed you have a {@link Roo.BorderLayout}
14862    var layout = new Roo.BorderLayout(...);
14863    layout.restoreState();
14864    // or a {Roo.BasicDialog}
14865    var dialog = new Roo.BasicDialog(...);
14866    dialog.restoreState();
14867  </code></pre>
14868  * @singleton
14869  */
14870 Roo.state.Manager = function(){
14871     var provider = new Roo.state.Provider();
14872     
14873     return {
14874         /**
14875          * Configures the default state provider for your application
14876          * @param {Provider} stateProvider The state provider to set
14877          */
14878         setProvider : function(stateProvider){
14879             provider = stateProvider;
14880         },
14881         
14882         /**
14883          * Returns the current value for a key
14884          * @param {String} name The key name
14885          * @param {Mixed} defaultValue The default value to return if the key lookup does not match
14886          * @return {Mixed} The state data
14887          */
14888         get : function(key, defaultValue){
14889             return provider.get(key, defaultValue);
14890         },
14891         
14892         /**
14893          * Sets the value for a key
14894          * @param {String} name The key name
14895          * @param {Mixed} value The state data
14896          */
14897          set : function(key, value){
14898             provider.set(key, value);
14899         },
14900         
14901         /**
14902          * Clears a value from the state
14903          * @param {String} name The key name
14904          */
14905         clear : function(key){
14906             provider.clear(key);
14907         },
14908         
14909         /**
14910          * Gets the currently configured state provider
14911          * @return {Provider} The state provider
14912          */
14913         getProvider : function(){
14914             return provider;
14915         }
14916     };
14917 }();
14918 /*
14919  * Based on:
14920  * Ext JS Library 1.1.1
14921  * Copyright(c) 2006-2007, Ext JS, LLC.
14922  *
14923  * Originally Released Under LGPL - original licence link has changed is not relivant.
14924  *
14925  * Fork - LGPL
14926  * <script type="text/javascript">
14927  */
14928 /**
14929  * @class Roo.state.CookieProvider
14930  * @extends Roo.state.Provider
14931  * The default Provider implementation which saves state via cookies.
14932  * <br />Usage:
14933  <pre><code>
14934    var cp = new Roo.state.CookieProvider({
14935        path: "/cgi-bin/",
14936        expires: new Date(new Date().getTime()+(1000*60*60*24*30)); //30 days
14937        domain: "roojs.com"
14938    })
14939    Roo.state.Manager.setProvider(cp);
14940  </code></pre>
14941  * @cfg {String} path The path for which the cookie is active (defaults to root '/' which makes it active for all pages in the site)
14942  * @cfg {Date} expires The cookie expiration date (defaults to 7 days from now)
14943  * @cfg {String} domain The domain to save the cookie for.  Note that you cannot specify a different domain than
14944  * your page is on, but you can specify a sub-domain, or simply the domain itself like 'roojs.com' to include
14945  * all sub-domains if you need to access cookies across different sub-domains (defaults to null which uses the same
14946  * domain the page is running on including the 'www' like 'www.roojs.com')
14947  * @cfg {Boolean} secure True if the site is using SSL (defaults to false)
14948  * @constructor
14949  * Create a new CookieProvider
14950  * @param {Object} config The configuration object
14951  */
14952 Roo.state.CookieProvider = function(config){
14953     Roo.state.CookieProvider.superclass.constructor.call(this);
14954     this.path = "/";
14955     this.expires = new Date(new Date().getTime()+(1000*60*60*24*7)); //7 days
14956     this.domain = null;
14957     this.secure = false;
14958     Roo.apply(this, config);
14959     this.state = this.readCookies();
14960 };
14961
14962 Roo.extend(Roo.state.CookieProvider, Roo.state.Provider, {
14963     // private
14964     set : function(name, value){
14965         if(typeof value == "undefined" || value === null){
14966             this.clear(name);
14967             return;
14968         }
14969         this.setCookie(name, value);
14970         Roo.state.CookieProvider.superclass.set.call(this, name, value);
14971     },
14972
14973     // private
14974     clear : function(name){
14975         this.clearCookie(name);
14976         Roo.state.CookieProvider.superclass.clear.call(this, name);
14977     },
14978
14979     // private
14980     readCookies : function(){
14981         var cookies = {};
14982         var c = document.cookie + ";";
14983         var re = /\s?(.*?)=(.*?);/g;
14984         var matches;
14985         while((matches = re.exec(c)) != null){
14986             var name = matches[1];
14987             var value = matches[2];
14988             if(name && name.substring(0,3) == "ys-"){
14989                 cookies[name.substr(3)] = this.decodeValue(value);
14990             }
14991         }
14992         return cookies;
14993     },
14994
14995     // private
14996     setCookie : function(name, value){
14997         document.cookie = "ys-"+ name + "=" + this.encodeValue(value) +
14998            ((this.expires == null) ? "" : ("; expires=" + this.expires.toGMTString())) +
14999            ((this.path == null) ? "" : ("; path=" + this.path)) +
15000            ((this.domain == null) ? "" : ("; domain=" + this.domain)) +
15001            ((this.secure == true) ? "; secure" : "");
15002     },
15003
15004     // private
15005     clearCookie : function(name){
15006         document.cookie = "ys-" + name + "=null; expires=Thu, 01-Jan-70 00:00:01 GMT" +
15007            ((this.path == null) ? "" : ("; path=" + this.path)) +
15008            ((this.domain == null) ? "" : ("; domain=" + this.domain)) +
15009            ((this.secure == true) ? "; secure" : "");
15010     }
15011 });/*
15012  * Based on:
15013  * Ext JS Library 1.1.1
15014  * Copyright(c) 2006-2007, Ext JS, LLC.
15015  *
15016  * Originally Released Under LGPL - original licence link has changed is not relivant.
15017  *
15018  * Fork - LGPL
15019  * <script type="text/javascript">
15020  */
15021  
15022
15023 /**
15024  * @class Roo.ComponentMgr
15025  * Provides a common registry of all components on a page so that they can be easily accessed by component id (see {@link Roo.getCmp}).
15026  * @singleton
15027  */
15028 Roo.ComponentMgr = function(){
15029     var all = new Roo.util.MixedCollection();
15030
15031     return {
15032         /**
15033          * Registers a component.
15034          * @param {Roo.Component} c The component
15035          */
15036         register : function(c){
15037             all.add(c);
15038         },
15039
15040         /**
15041          * Unregisters a component.
15042          * @param {Roo.Component} c The component
15043          */
15044         unregister : function(c){
15045             all.remove(c);
15046         },
15047
15048         /**
15049          * Returns a component by id
15050          * @param {String} id The component id
15051          */
15052         get : function(id){
15053             return all.get(id);
15054         },
15055
15056         /**
15057          * Registers a function that will be called when a specified component is added to ComponentMgr
15058          * @param {String} id The component id
15059          * @param {Funtction} fn The callback function
15060          * @param {Object} scope The scope of the callback
15061          */
15062         onAvailable : function(id, fn, scope){
15063             all.on("add", function(index, o){
15064                 if(o.id == id){
15065                     fn.call(scope || o, o);
15066                     all.un("add", fn, scope);
15067                 }
15068             });
15069         }
15070     };
15071 }();/*
15072  * Based on:
15073  * Ext JS Library 1.1.1
15074  * Copyright(c) 2006-2007, Ext JS, LLC.
15075  *
15076  * Originally Released Under LGPL - original licence link has changed is not relivant.
15077  *
15078  * Fork - LGPL
15079  * <script type="text/javascript">
15080  */
15081  
15082 /**
15083  * @class Roo.Component
15084  * @extends Roo.util.Observable
15085  * Base class for all major Roo components.  All subclasses of Component can automatically participate in the standard
15086  * Roo component lifecycle of creation, rendering and destruction.  They also have automatic support for basic hide/show
15087  * and enable/disable behavior.  Component allows any subclass to be lazy-rendered into any {@link Roo.Container} and
15088  * to be automatically registered with the {@link Roo.ComponentMgr} so that it can be referenced at any time via {@link Roo.getCmp}.
15089  * All visual components (widgets) that require rendering into a layout should subclass Component.
15090  * @constructor
15091  * @param {Roo.Element/String/Object} config The configuration options.  If an element is passed, it is set as the internal
15092  * element and its id used as the component id.  If a string is passed, it is assumed to be the id of an existing element
15093  * and is used as the component id.  Otherwise, it is assumed to be a standard config object and is applied to the component.
15094  */
15095 Roo.Component = function(config){
15096     config = config || {};
15097     if(config.tagName || config.dom || typeof config == "string"){ // element object
15098         config = {el: config, id: config.id || config};
15099     }
15100     this.initialConfig = config;
15101
15102     Roo.apply(this, config);
15103     this.addEvents({
15104         /**
15105          * @event disable
15106          * Fires after the component is disabled.
15107              * @param {Roo.Component} this
15108              */
15109         disable : true,
15110         /**
15111          * @event enable
15112          * Fires after the component is enabled.
15113              * @param {Roo.Component} this
15114              */
15115         enable : true,
15116         /**
15117          * @event beforeshow
15118          * Fires before the component is shown.  Return false to stop the show.
15119              * @param {Roo.Component} this
15120              */
15121         beforeshow : true,
15122         /**
15123          * @event show
15124          * Fires after the component is shown.
15125              * @param {Roo.Component} this
15126              */
15127         show : true,
15128         /**
15129          * @event beforehide
15130          * Fires before the component is hidden. Return false to stop the hide.
15131              * @param {Roo.Component} this
15132              */
15133         beforehide : true,
15134         /**
15135          * @event hide
15136          * Fires after the component is hidden.
15137              * @param {Roo.Component} this
15138              */
15139         hide : true,
15140         /**
15141          * @event beforerender
15142          * Fires before the component is rendered. Return false to stop the render.
15143              * @param {Roo.Component} this
15144              */
15145         beforerender : true,
15146         /**
15147          * @event render
15148          * Fires after the component is rendered.
15149              * @param {Roo.Component} this
15150              */
15151         render : true,
15152         /**
15153          * @event beforedestroy
15154          * Fires before the component is destroyed. Return false to stop the destroy.
15155              * @param {Roo.Component} this
15156              */
15157         beforedestroy : true,
15158         /**
15159          * @event destroy
15160          * Fires after the component is destroyed.
15161              * @param {Roo.Component} this
15162              */
15163         destroy : true
15164     });
15165     if(!this.id){
15166         this.id = "ext-comp-" + (++Roo.Component.AUTO_ID);
15167     }
15168     Roo.ComponentMgr.register(this);
15169     Roo.Component.superclass.constructor.call(this);
15170     this.initComponent();
15171     if(this.renderTo){ // not supported by all components yet. use at your own risk!
15172         this.render(this.renderTo);
15173         delete this.renderTo;
15174     }
15175 };
15176
15177 /** @private */
15178 Roo.Component.AUTO_ID = 1000;
15179
15180 Roo.extend(Roo.Component, Roo.util.Observable, {
15181     /**
15182      * @scope Roo.Component.prototype
15183      * @type {Boolean}
15184      * true if this component is hidden. Read-only.
15185      */
15186     hidden : false,
15187     /**
15188      * @type {Boolean}
15189      * true if this component is disabled. Read-only.
15190      */
15191     disabled : false,
15192     /**
15193      * @type {Boolean}
15194      * true if this component has been rendered. Read-only.
15195      */
15196     rendered : false,
15197     
15198     /** @cfg {String} disableClass
15199      * CSS class added to the component when it is disabled (defaults to "x-item-disabled").
15200      */
15201     disabledClass : "x-item-disabled",
15202         /** @cfg {Boolean} allowDomMove
15203          * Whether the component can move the Dom node when rendering (defaults to true).
15204          */
15205     allowDomMove : true,
15206     /** @cfg {String} hideMode
15207      * How this component should hidden. Supported values are
15208      * "visibility" (css visibility), "offsets" (negative offset position) and
15209      * "display" (css display) - defaults to "display".
15210      */
15211     hideMode: 'display',
15212
15213     /** @private */
15214     ctype : "Roo.Component",
15215
15216     /**
15217      * @cfg {String} actionMode 
15218      * which property holds the element that used for  hide() / show() / disable() / enable()
15219      * default is 'el' 
15220      */
15221     actionMode : "el",
15222
15223     /** @private */
15224     getActionEl : function(){
15225         return this[this.actionMode];
15226     },
15227
15228     initComponent : Roo.emptyFn,
15229     /**
15230      * If this is a lazy rendering component, render it to its container element.
15231      * @param {String/HTMLElement/Element} container (optional) The element this component should be rendered into. If it is being applied to existing markup, this should be left off.
15232      */
15233     render : function(container, position){
15234         if(!this.rendered && this.fireEvent("beforerender", this) !== false){
15235             if(!container && this.el){
15236                 this.el = Roo.get(this.el);
15237                 container = this.el.dom.parentNode;
15238                 this.allowDomMove = false;
15239             }
15240             this.container = Roo.get(container);
15241             this.rendered = true;
15242             if(position !== undefined){
15243                 if(typeof position == 'number'){
15244                     position = this.container.dom.childNodes[position];
15245                 }else{
15246                     position = Roo.getDom(position);
15247                 }
15248             }
15249             this.onRender(this.container, position || null);
15250             if(this.cls){
15251                 this.el.addClass(this.cls);
15252                 delete this.cls;
15253             }
15254             if(this.style){
15255                 this.el.applyStyles(this.style);
15256                 delete this.style;
15257             }
15258             this.fireEvent("render", this);
15259             this.afterRender(this.container);
15260             if(this.hidden){
15261                 this.hide();
15262             }
15263             if(this.disabled){
15264                 this.disable();
15265             }
15266         }
15267         return this;
15268     },
15269
15270     /** @private */
15271     // default function is not really useful
15272     onRender : function(ct, position){
15273         if(this.el){
15274             this.el = Roo.get(this.el);
15275             if(this.allowDomMove !== false){
15276                 ct.dom.insertBefore(this.el.dom, position);
15277             }
15278         }
15279     },
15280
15281     /** @private */
15282     getAutoCreate : function(){
15283         var cfg = typeof this.autoCreate == "object" ?
15284                       this.autoCreate : Roo.apply({}, this.defaultAutoCreate);
15285         if(this.id && !cfg.id){
15286             cfg.id = this.id;
15287         }
15288         return cfg;
15289     },
15290
15291     /** @private */
15292     afterRender : Roo.emptyFn,
15293
15294     /**
15295      * Destroys this component by purging any event listeners, removing the component's element from the DOM,
15296      * removing the component from its {@link Roo.Container} (if applicable) and unregistering it from {@link Roo.ComponentMgr}.
15297      */
15298     destroy : function(){
15299         if(this.fireEvent("beforedestroy", this) !== false){
15300             this.purgeListeners();
15301             this.beforeDestroy();
15302             if(this.rendered){
15303                 this.el.removeAllListeners();
15304                 this.el.remove();
15305                 if(this.actionMode == "container"){
15306                     this.container.remove();
15307                 }
15308             }
15309             this.onDestroy();
15310             Roo.ComponentMgr.unregister(this);
15311             this.fireEvent("destroy", this);
15312         }
15313     },
15314
15315         /** @private */
15316     beforeDestroy : function(){
15317
15318     },
15319
15320         /** @private */
15321         onDestroy : function(){
15322
15323     },
15324
15325     /**
15326      * Returns the underlying {@link Roo.Element}.
15327      * @return {Roo.Element} The element
15328      */
15329     getEl : function(){
15330         return this.el;
15331     },
15332
15333     /**
15334      * Returns the id of this component.
15335      * @return {String}
15336      */
15337     getId : function(){
15338         return this.id;
15339     },
15340
15341     /**
15342      * Try to focus this component.
15343      * @param {Boolean} selectText True to also select the text in this component (if applicable)
15344      * @return {Roo.Component} this
15345      */
15346     focus : function(selectText){
15347         if(this.rendered){
15348             this.el.focus();
15349             if(selectText === true){
15350                 this.el.dom.select();
15351             }
15352         }
15353         return this;
15354     },
15355
15356     /** @private */
15357     blur : function(){
15358         if(this.rendered){
15359             this.el.blur();
15360         }
15361         return this;
15362     },
15363
15364     /**
15365      * Disable this component.
15366      * @return {Roo.Component} this
15367      */
15368     disable : function(){
15369         if(this.rendered){
15370             this.onDisable();
15371         }
15372         this.disabled = true;
15373         this.fireEvent("disable", this);
15374         return this;
15375     },
15376
15377         // private
15378     onDisable : function(){
15379         this.getActionEl().addClass(this.disabledClass);
15380         this.el.dom.disabled = true;
15381     },
15382
15383     /**
15384      * Enable this component.
15385      * @return {Roo.Component} this
15386      */
15387     enable : function(){
15388         if(this.rendered){
15389             this.onEnable();
15390         }
15391         this.disabled = false;
15392         this.fireEvent("enable", this);
15393         return this;
15394     },
15395
15396         // private
15397     onEnable : function(){
15398         this.getActionEl().removeClass(this.disabledClass);
15399         this.el.dom.disabled = false;
15400     },
15401
15402     /**
15403      * Convenience function for setting disabled/enabled by boolean.
15404      * @param {Boolean} disabled
15405      */
15406     setDisabled : function(disabled){
15407         this[disabled ? "disable" : "enable"]();
15408     },
15409
15410     /**
15411      * Show this component.
15412      * @return {Roo.Component} this
15413      */
15414     show: function(){
15415         if(this.fireEvent("beforeshow", this) !== false){
15416             this.hidden = false;
15417             if(this.rendered){
15418                 this.onShow();
15419             }
15420             this.fireEvent("show", this);
15421         }
15422         return this;
15423     },
15424
15425     // private
15426     onShow : function(){
15427         var ae = this.getActionEl();
15428         if(this.hideMode == 'visibility'){
15429             ae.dom.style.visibility = "visible";
15430         }else if(this.hideMode == 'offsets'){
15431             ae.removeClass('x-hidden');
15432         }else{
15433             ae.dom.style.display = "";
15434         }
15435     },
15436
15437     /**
15438      * Hide this component.
15439      * @return {Roo.Component} this
15440      */
15441     hide: function(){
15442         if(this.fireEvent("beforehide", this) !== false){
15443             this.hidden = true;
15444             if(this.rendered){
15445                 this.onHide();
15446             }
15447             this.fireEvent("hide", this);
15448         }
15449         return this;
15450     },
15451
15452     // private
15453     onHide : function(){
15454         var ae = this.getActionEl();
15455         if(this.hideMode == 'visibility'){
15456             ae.dom.style.visibility = "hidden";
15457         }else if(this.hideMode == 'offsets'){
15458             ae.addClass('x-hidden');
15459         }else{
15460             ae.dom.style.display = "none";
15461         }
15462     },
15463
15464     /**
15465      * Convenience function to hide or show this component by boolean.
15466      * @param {Boolean} visible True to show, false to hide
15467      * @return {Roo.Component} this
15468      */
15469     setVisible: function(visible){
15470         if(visible) {
15471             this.show();
15472         }else{
15473             this.hide();
15474         }
15475         return this;
15476     },
15477
15478     /**
15479      * Returns true if this component is visible.
15480      */
15481     isVisible : function(){
15482         return this.getActionEl().isVisible();
15483     },
15484
15485     cloneConfig : function(overrides){
15486         overrides = overrides || {};
15487         var id = overrides.id || Roo.id();
15488         var cfg = Roo.applyIf(overrides, this.initialConfig);
15489         cfg.id = id; // prevent dup id
15490         return new this.constructor(cfg);
15491     }
15492 });/*
15493  * Based on:
15494  * Ext JS Library 1.1.1
15495  * Copyright(c) 2006-2007, Ext JS, LLC.
15496  *
15497  * Originally Released Under LGPL - original licence link has changed is not relivant.
15498  *
15499  * Fork - LGPL
15500  * <script type="text/javascript">
15501  */
15502
15503 /**
15504  * @class Roo.BoxComponent
15505  * @extends Roo.Component
15506  * Base class for any visual {@link Roo.Component} that uses a box container.  BoxComponent provides automatic box
15507  * model adjustments for sizing and positioning and will work correctly withnin the Component rendering model.  All
15508  * container classes should subclass BoxComponent so that they will work consistently when nested within other Ext
15509  * layout containers.
15510  * @constructor
15511  * @param {Roo.Element/String/Object} config The configuration options.
15512  */
15513 Roo.BoxComponent = function(config){
15514     Roo.Component.call(this, config);
15515     this.addEvents({
15516         /**
15517          * @event resize
15518          * Fires after the component is resized.
15519              * @param {Roo.Component} this
15520              * @param {Number} adjWidth The box-adjusted width that was set
15521              * @param {Number} adjHeight The box-adjusted height that was set
15522              * @param {Number} rawWidth The width that was originally specified
15523              * @param {Number} rawHeight The height that was originally specified
15524              */
15525         resize : true,
15526         /**
15527          * @event move
15528          * Fires after the component is moved.
15529              * @param {Roo.Component} this
15530              * @param {Number} x The new x position
15531              * @param {Number} y The new y position
15532              */
15533         move : true
15534     });
15535 };
15536
15537 Roo.extend(Roo.BoxComponent, Roo.Component, {
15538     // private, set in afterRender to signify that the component has been rendered
15539     boxReady : false,
15540     // private, used to defer height settings to subclasses
15541     deferHeight: false,
15542     /** @cfg {Number} width
15543      * width (optional) size of component
15544      */
15545      /** @cfg {Number} height
15546      * height (optional) size of component
15547      */
15548      
15549     /**
15550      * Sets the width and height of the component.  This method fires the resize event.  This method can accept
15551      * either width and height as separate numeric arguments, or you can pass a size object like {width:10, height:20}.
15552      * @param {Number/Object} width The new width to set, or a size object in the format {width, height}
15553      * @param {Number} height The new height to set (not required if a size object is passed as the first arg)
15554      * @return {Roo.BoxComponent} this
15555      */
15556     setSize : function(w, h){
15557         // support for standard size objects
15558         if(typeof w == 'object'){
15559             h = w.height;
15560             w = w.width;
15561         }
15562         // not rendered
15563         if(!this.boxReady){
15564             this.width = w;
15565             this.height = h;
15566             return this;
15567         }
15568
15569         // prevent recalcs when not needed
15570         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
15571             return this;
15572         }
15573         this.lastSize = {width: w, height: h};
15574
15575         var adj = this.adjustSize(w, h);
15576         var aw = adj.width, ah = adj.height;
15577         if(aw !== undefined || ah !== undefined){ // this code is nasty but performs better with floaters
15578             var rz = this.getResizeEl();
15579             if(!this.deferHeight && aw !== undefined && ah !== undefined){
15580                 rz.setSize(aw, ah);
15581             }else if(!this.deferHeight && ah !== undefined){
15582                 rz.setHeight(ah);
15583             }else if(aw !== undefined){
15584                 rz.setWidth(aw);
15585             }
15586             this.onResize(aw, ah, w, h);
15587             this.fireEvent('resize', this, aw, ah, w, h);
15588         }
15589         return this;
15590     },
15591
15592     /**
15593      * Gets the current size of the component's underlying element.
15594      * @return {Object} An object containing the element's size {width: (element width), height: (element height)}
15595      */
15596     getSize : function(){
15597         return this.el.getSize();
15598     },
15599
15600     /**
15601      * Gets the current XY position of the component's underlying element.
15602      * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
15603      * @return {Array} The XY position of the element (e.g., [100, 200])
15604      */
15605     getPosition : function(local){
15606         if(local === true){
15607             return [this.el.getLeft(true), this.el.getTop(true)];
15608         }
15609         return this.xy || this.el.getXY();
15610     },
15611
15612     /**
15613      * Gets the current box measurements of the component's underlying element.
15614      * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
15615      * @returns {Object} box An object in the format {x, y, width, height}
15616      */
15617     getBox : function(local){
15618         var s = this.el.getSize();
15619         if(local){
15620             s.x = this.el.getLeft(true);
15621             s.y = this.el.getTop(true);
15622         }else{
15623             var xy = this.xy || this.el.getXY();
15624             s.x = xy[0];
15625             s.y = xy[1];
15626         }
15627         return s;
15628     },
15629
15630     /**
15631      * Sets the current box measurements of the component's underlying element.
15632      * @param {Object} box An object in the format {x, y, width, height}
15633      * @returns {Roo.BoxComponent} this
15634      */
15635     updateBox : function(box){
15636         this.setSize(box.width, box.height);
15637         this.setPagePosition(box.x, box.y);
15638         return this;
15639     },
15640
15641     // protected
15642     getResizeEl : function(){
15643         return this.resizeEl || this.el;
15644     },
15645
15646     // protected
15647     getPositionEl : function(){
15648         return this.positionEl || this.el;
15649     },
15650
15651     /**
15652      * Sets the left and top of the component.  To set the page XY position instead, use {@link #setPagePosition}.
15653      * This method fires the move event.
15654      * @param {Number} left The new left
15655      * @param {Number} top The new top
15656      * @returns {Roo.BoxComponent} this
15657      */
15658     setPosition : function(x, y){
15659         this.x = x;
15660         this.y = y;
15661         if(!this.boxReady){
15662             return this;
15663         }
15664         var adj = this.adjustPosition(x, y);
15665         var ax = adj.x, ay = adj.y;
15666
15667         var el = this.getPositionEl();
15668         if(ax !== undefined || ay !== undefined){
15669             if(ax !== undefined && ay !== undefined){
15670                 el.setLeftTop(ax, ay);
15671             }else if(ax !== undefined){
15672                 el.setLeft(ax);
15673             }else if(ay !== undefined){
15674                 el.setTop(ay);
15675             }
15676             this.onPosition(ax, ay);
15677             this.fireEvent('move', this, ax, ay);
15678         }
15679         return this;
15680     },
15681
15682     /**
15683      * Sets the page XY position of the component.  To set the left and top instead, use {@link #setPosition}.
15684      * This method fires the move event.
15685      * @param {Number} x The new x position
15686      * @param {Number} y The new y position
15687      * @returns {Roo.BoxComponent} this
15688      */
15689     setPagePosition : function(x, y){
15690         this.pageX = x;
15691         this.pageY = y;
15692         if(!this.boxReady){
15693             return;
15694         }
15695         if(x === undefined || y === undefined){ // cannot translate undefined points
15696             return;
15697         }
15698         var p = this.el.translatePoints(x, y);
15699         this.setPosition(p.left, p.top);
15700         return this;
15701     },
15702
15703     // private
15704     onRender : function(ct, position){
15705         Roo.BoxComponent.superclass.onRender.call(this, ct, position);
15706         if(this.resizeEl){
15707             this.resizeEl = Roo.get(this.resizeEl);
15708         }
15709         if(this.positionEl){
15710             this.positionEl = Roo.get(this.positionEl);
15711         }
15712     },
15713
15714     // private
15715     afterRender : function(){
15716         Roo.BoxComponent.superclass.afterRender.call(this);
15717         this.boxReady = true;
15718         this.setSize(this.width, this.height);
15719         if(this.x || this.y){
15720             this.setPosition(this.x, this.y);
15721         }
15722         if(this.pageX || this.pageY){
15723             this.setPagePosition(this.pageX, this.pageY);
15724         }
15725     },
15726
15727     /**
15728      * Force the component's size to recalculate based on the underlying element's current height and width.
15729      * @returns {Roo.BoxComponent} this
15730      */
15731     syncSize : function(){
15732         delete this.lastSize;
15733         this.setSize(this.el.getWidth(), this.el.getHeight());
15734         return this;
15735     },
15736
15737     /**
15738      * Called after the component is resized, this method is empty by default but can be implemented by any
15739      * subclass that needs to perform custom logic after a resize occurs.
15740      * @param {Number} adjWidth The box-adjusted width that was set
15741      * @param {Number} adjHeight The box-adjusted height that was set
15742      * @param {Number} rawWidth The width that was originally specified
15743      * @param {Number} rawHeight The height that was originally specified
15744      */
15745     onResize : function(adjWidth, adjHeight, rawWidth, rawHeight){
15746
15747     },
15748
15749     /**
15750      * Called after the component is moved, this method is empty by default but can be implemented by any
15751      * subclass that needs to perform custom logic after a move occurs.
15752      * @param {Number} x The new x position
15753      * @param {Number} y The new y position
15754      */
15755     onPosition : function(x, y){
15756
15757     },
15758
15759     // private
15760     adjustSize : function(w, h){
15761         if(this.autoWidth){
15762             w = 'auto';
15763         }
15764         if(this.autoHeight){
15765             h = 'auto';
15766         }
15767         return {width : w, height: h};
15768     },
15769
15770     // private
15771     adjustPosition : function(x, y){
15772         return {x : x, y: y};
15773     }
15774 });/*
15775  * Original code for Roojs - LGPL
15776  * <script type="text/javascript">
15777  */
15778  
15779 /**
15780  * @class Roo.XComponent
15781  * A delayed Element creator...
15782  * Or a way to group chunks of interface together.
15783  * technically this is a wrapper around a tree of Roo elements (which defines a 'module'),
15784  *  used in conjunction with XComponent.build() it will create an instance of each element,
15785  *  then call addxtype() to build the User interface.
15786  * 
15787  * Mypart.xyx = new Roo.XComponent({
15788
15789     parent : 'Mypart.xyz', // empty == document.element.!!
15790     order : '001',
15791     name : 'xxxx'
15792     region : 'xxxx'
15793     disabled : function() {} 
15794      
15795     tree : function() { // return an tree of xtype declared components
15796         var MODULE = this;
15797         return 
15798         {
15799             xtype : 'NestedLayoutPanel',
15800             // technicall
15801         }
15802      ]
15803  *})
15804  *
15805  *
15806  * It can be used to build a big heiracy, with parent etc.
15807  * or you can just use this to render a single compoent to a dom element
15808  * MYPART.render(Roo.Element | String(id) | dom_element )
15809  *
15810  *
15811  * Usage patterns.
15812  *
15813  * Classic Roo
15814  *
15815  * Roo is designed primarily as a single page application, so the UI build for a standard interface will
15816  * expect a single 'TOP' level module normally indicated by the 'parent' of the XComponent definition being defined as false.
15817  *
15818  * Each sub module is expected to have a parent pointing to the class name of it's parent module.
15819  *
15820  * When the top level is false, a 'Roo.BorderLayout' is created and the element is flagged as 'topModule'
15821  * - if mulitple topModules exist, the last one is defined as the top module.
15822  *
15823  * Embeded Roo
15824  * 
15825  * When the top level or multiple modules are to embedded into a existing HTML page,
15826  * the parent element can container '#id' of the element where the module will be drawn.
15827  *
15828  * Bootstrap Roo
15829  *
15830  * Unlike classic Roo, the bootstrap tends not to be used as a single page.
15831  * it relies more on a include mechanism, where sub modules are included into an outer page.
15832  * This is normally managed by the builder tools using Roo.apply( options, Included.Sub.Module )
15833  * 
15834  * Bootstrap Roo Included elements
15835  *
15836  * Our builder application needs the ability to preview these sub compoennts. They will normally have parent=false set,
15837  * hence confusing the component builder as it thinks there are multiple top level elements. 
15838  *
15839  * 
15840  * 
15841  * @extends Roo.util.Observable
15842  * @constructor
15843  * @param cfg {Object} configuration of component
15844  * 
15845  */
15846 Roo.XComponent = function(cfg) {
15847     Roo.apply(this, cfg);
15848     this.addEvents({ 
15849         /**
15850              * @event built
15851              * Fires when this the componnt is built
15852              * @param {Roo.XComponent} c the component
15853              */
15854         'built' : true
15855         
15856     });
15857     this.region = this.region || 'center'; // default..
15858     Roo.XComponent.register(this);
15859     this.modules = false;
15860     this.el = false; // where the layout goes..
15861     
15862     
15863 }
15864 Roo.extend(Roo.XComponent, Roo.util.Observable, {
15865     /**
15866      * @property el
15867      * The created element (with Roo.factory())
15868      * @type {Roo.Layout}
15869      */
15870     el  : false,
15871     
15872     /**
15873      * @property el
15874      * for BC  - use el in new code
15875      * @type {Roo.Layout}
15876      */
15877     panel : false,
15878     
15879     /**
15880      * @property layout
15881      * for BC  - use el in new code
15882      * @type {Roo.Layout}
15883      */
15884     layout : false,
15885     
15886      /**
15887      * @cfg {Function|boolean} disabled
15888      * If this module is disabled by some rule, return true from the funtion
15889      */
15890     disabled : false,
15891     
15892     /**
15893      * @cfg {String} parent 
15894      * Name of parent element which it get xtype added to..
15895      */
15896     parent: false,
15897     
15898     /**
15899      * @cfg {String} order
15900      * Used to set the order in which elements are created (usefull for multiple tabs)
15901      */
15902     
15903     order : false,
15904     /**
15905      * @cfg {String} name
15906      * String to display while loading.
15907      */
15908     name : false,
15909     /**
15910      * @cfg {String} region
15911      * Region to render component to (defaults to center)
15912      */
15913     region : 'center',
15914     
15915     /**
15916      * @cfg {Array} items
15917      * A single item array - the first element is the root of the tree..
15918      * It's done this way to stay compatible with the Xtype system...
15919      */
15920     items : false,
15921     
15922     /**
15923      * @property _tree
15924      * The method that retuns the tree of parts that make up this compoennt 
15925      * @type {function}
15926      */
15927     _tree  : false,
15928     
15929      /**
15930      * render
15931      * render element to dom or tree
15932      * @param {Roo.Element|String|DomElement} optional render to if parent is not set.
15933      */
15934     
15935     render : function(el)
15936     {
15937         
15938         el = el || false;
15939         var hp = this.parent ? 1 : 0;
15940         Roo.log(this);
15941         
15942         if (!el && typeof(this.parent) == 'string' && this.parent.substring(0,1) == '#') {
15943             // if parent is a '#.....' string, then let's use that..
15944             var ename = this.parent.substr(1);
15945             this.parent = false;
15946             Roo.log(ename);
15947             switch (ename) {
15948                 case 'bootstrap-body' :
15949                     if (typeof(Roo.bootstrap.Body) != 'undefined') {
15950                         this.parent = { el :  new  Roo.bootstrap.Body() };
15951                         Roo.log("setting el to doc body");
15952                          
15953                     } else {
15954                         throw "Container is bootstrap body, but Roo.bootstrap.Body is not defined";
15955                     }
15956                     break;
15957                 case 'bootstrap':
15958                     this.parent = { el : true};
15959                     // fall through
15960                 default:
15961                     el = Roo.get(ename);
15962                     break;
15963             }
15964                 
15965             
15966             if (!el && !this.parent) {
15967                 Roo.log("Warning - element can not be found :#" + ename );
15968                 return;
15969             }
15970         }
15971         Roo.log("EL:");Roo.log(el);
15972         Roo.log("this.parent.el:");Roo.log(this.parent.el);
15973         
15974         var tree = this._tree ? this._tree() : this.tree();
15975
15976         // altertive root elements ??? - we need a better way to indicate these.
15977         var is_alt = (typeof(Roo.bootstrap) != 'undefined' && tree.xns == Roo.bootstrap) ||
15978                         (typeof(Roo.mailer) != 'undefined' && tree.xns == Roo.mailer) ;
15979         
15980         if (!this.parent && is_alt) {
15981             //el = Roo.get(document.body);
15982             this.parent = { el : true };
15983         }
15984             
15985             
15986         
15987         if (!this.parent) {
15988             
15989             Roo.log("no parent - creating one");
15990             
15991             el = el ? Roo.get(el) : false;      
15992             
15993             // it's a top level one..
15994             this.parent =  {
15995                 el : new Roo.BorderLayout(el || document.body, {
15996                 
15997                      center: {
15998                          titlebar: false,
15999                          autoScroll:false,
16000                          closeOnTab: true,
16001                          tabPosition: 'top',
16002                           //resizeTabs: true,
16003                          alwaysShowTabs: el && hp? false :  true,
16004                          hideTabs: el || !hp ? true :  false,
16005                          minTabWidth: 140
16006                      }
16007                  })
16008             }
16009         }
16010         
16011         if (!this.parent.el) {
16012                 // probably an old style ctor, which has been disabled.
16013                 return;
16014
16015         }
16016                 // The 'tree' method is  '_tree now' 
16017             
16018         tree.region = tree.region || this.region;
16019         
16020         if (this.parent.el === true) {
16021             // bootstrap... - body..
16022             this.parent.el = Roo.factory(tree);
16023         }
16024         
16025         this.el = this.parent.el.addxtype(tree);
16026         this.fireEvent('built', this);
16027         
16028         this.panel = this.el;
16029         this.layout = this.panel.layout;
16030         this.parentLayout = this.parent.layout  || false;  
16031          
16032     }
16033     
16034 });
16035
16036 Roo.apply(Roo.XComponent, {
16037     /**
16038      * @property  hideProgress
16039      * true to disable the building progress bar.. usefull on single page renders.
16040      * @type Boolean
16041      */
16042     hideProgress : false,
16043     /**
16044      * @property  buildCompleted
16045      * True when the builder has completed building the interface.
16046      * @type Boolean
16047      */
16048     buildCompleted : false,
16049      
16050     /**
16051      * @property  topModule
16052      * the upper most module - uses document.element as it's constructor.
16053      * @type Object
16054      */
16055      
16056     topModule  : false,
16057       
16058     /**
16059      * @property  modules
16060      * array of modules to be created by registration system.
16061      * @type {Array} of Roo.XComponent
16062      */
16063     
16064     modules : [],
16065     /**
16066      * @property  elmodules
16067      * array of modules to be created by which use #ID 
16068      * @type {Array} of Roo.XComponent
16069      */
16070      
16071     elmodules : [],
16072
16073      /**
16074      * @property  build_from_html
16075      * Build elements from html - used by bootstrap HTML stuff 
16076      *    - this is cleared after build is completed
16077      * @type {boolean} true  (default false)
16078      */
16079      
16080     build_from_html : false,
16081
16082     /**
16083      * Register components to be built later.
16084      *
16085      * This solves the following issues
16086      * - Building is not done on page load, but after an authentication process has occured.
16087      * - Interface elements are registered on page load
16088      * - Parent Interface elements may not be loaded before child, so this handles that..
16089      * 
16090      *
16091      * example:
16092      * 
16093      * MyApp.register({
16094           order : '000001',
16095           module : 'Pman.Tab.projectMgr',
16096           region : 'center',
16097           parent : 'Pman.layout',
16098           disabled : false,  // or use a function..
16099         })
16100      
16101      * * @param {Object} details about module
16102      */
16103     register : function(obj) {
16104                 
16105         Roo.XComponent.event.fireEvent('register', obj);
16106         switch(typeof(obj.disabled) ) {
16107                 
16108             case 'undefined':
16109                 break;
16110             
16111             case 'function':
16112                 if ( obj.disabled() ) {
16113                         return;
16114                 }
16115                 break;
16116             
16117             default:
16118                 if (obj.disabled) {
16119                         return;
16120                 }
16121                 break;
16122         }
16123                 
16124         this.modules.push(obj);
16125          
16126     },
16127     /**
16128      * convert a string to an object..
16129      * eg. 'AAA.BBB' -> finds AAA.BBB
16130
16131      */
16132     
16133     toObject : function(str)
16134     {
16135         if (!str || typeof(str) == 'object') {
16136             return str;
16137         }
16138         if (str.substring(0,1) == '#') {
16139             return str;
16140         }
16141
16142         var ar = str.split('.');
16143         var rt, o;
16144         rt = ar.shift();
16145             /** eval:var:o */
16146         try {
16147             eval('if (typeof ' + rt + ' == "undefined"){ o = false;} o = ' + rt + ';');
16148         } catch (e) {
16149             throw "Module not found : " + str;
16150         }
16151         
16152         if (o === false) {
16153             throw "Module not found : " + str;
16154         }
16155         Roo.each(ar, function(e) {
16156             if (typeof(o[e]) == 'undefined') {
16157                 throw "Module not found : " + str;
16158             }
16159             o = o[e];
16160         });
16161         
16162         return o;
16163         
16164     },
16165     
16166     
16167     /**
16168      * move modules into their correct place in the tree..
16169      * 
16170      */
16171     preBuild : function ()
16172     {
16173         var _t = this;
16174         Roo.each(this.modules , function (obj)
16175         {
16176             Roo.XComponent.event.fireEvent('beforebuild', obj);
16177             
16178             var opar = obj.parent;
16179             try { 
16180                 obj.parent = this.toObject(opar);
16181             } catch(e) {
16182                 Roo.log("parent:toObject failed: " + e.toString());
16183                 return;
16184             }
16185             
16186             if (!obj.parent) {
16187                 Roo.debug && Roo.log("GOT top level module");
16188                 Roo.debug && Roo.log(obj);
16189                 obj.modules = new Roo.util.MixedCollection(false, 
16190                     function(o) { return o.order + '' }
16191                 );
16192                 this.topModule = obj;
16193                 return;
16194             }
16195                         // parent is a string (usually a dom element name..)
16196             if (typeof(obj.parent) == 'string') {
16197                 this.elmodules.push(obj);
16198                 return;
16199             }
16200             if (obj.parent.constructor != Roo.XComponent) {
16201                 Roo.log("Warning : Object Parent is not instance of XComponent:" + obj.name)
16202             }
16203             if (!obj.parent.modules) {
16204                 obj.parent.modules = new Roo.util.MixedCollection(false, 
16205                     function(o) { return o.order + '' }
16206                 );
16207             }
16208             if (obj.parent.disabled) {
16209                 obj.disabled = true;
16210             }
16211             obj.parent.modules.add(obj);
16212         }, this);
16213     },
16214     
16215      /**
16216      * make a list of modules to build.
16217      * @return {Array} list of modules. 
16218      */ 
16219     
16220     buildOrder : function()
16221     {
16222         var _this = this;
16223         var cmp = function(a,b) {   
16224             return String(a).toUpperCase() > String(b).toUpperCase() ? 1 : -1;
16225         };
16226         if ((!this.topModule || !this.topModule.modules) && !this.elmodules.length) {
16227             throw "No top level modules to build";
16228         }
16229         
16230         // make a flat list in order of modules to build.
16231         var mods = this.topModule ? [ this.topModule ] : [];
16232                 
16233         
16234         // elmodules (is a list of DOM based modules )
16235         Roo.each(this.elmodules, function(e) {
16236             mods.push(e);
16237             if (!this.topModule &&
16238                 typeof(e.parent) == 'string' &&
16239                 e.parent.substring(0,1) == '#' &&
16240                 Roo.get(e.parent.substr(1))
16241                ) {
16242                 
16243                 _this.topModule = e;
16244             }
16245             
16246         });
16247
16248         
16249         // add modules to their parents..
16250         var addMod = function(m) {
16251             Roo.debug && Roo.log("build Order: add: " + m.name);
16252                 
16253             mods.push(m);
16254             if (m.modules && !m.disabled) {
16255                 Roo.debug && Roo.log("build Order: " + m.modules.length + " child modules");
16256                 m.modules.keySort('ASC',  cmp );
16257                 Roo.debug && Roo.log("build Order: " + m.modules.length + " child modules (after sort)");
16258     
16259                 m.modules.each(addMod);
16260             } else {
16261                 Roo.debug && Roo.log("build Order: no child modules");
16262             }
16263             // not sure if this is used any more..
16264             if (m.finalize) {
16265                 m.finalize.name = m.name + " (clean up) ";
16266                 mods.push(m.finalize);
16267             }
16268             
16269         }
16270         if (this.topModule && this.topModule.modules) { 
16271             this.topModule.modules.keySort('ASC',  cmp );
16272             this.topModule.modules.each(addMod);
16273         } 
16274         return mods;
16275     },
16276     
16277      /**
16278      * Build the registered modules.
16279      * @param {Object} parent element.
16280      * @param {Function} optional method to call after module has been added.
16281      * 
16282      */ 
16283    
16284     build : function(opts) 
16285     {
16286         
16287         if (typeof(opts) != 'undefined') {
16288             Roo.apply(this,opts);
16289         }
16290         
16291         this.preBuild();
16292         var mods = this.buildOrder();
16293       
16294         //this.allmods = mods;
16295         //Roo.debug && Roo.log(mods);
16296         //return;
16297         if (!mods.length) { // should not happen
16298             throw "NO modules!!!";
16299         }
16300         
16301         
16302         var msg = "Building Interface...";
16303         // flash it up as modal - so we store the mask!?
16304         if (!this.hideProgress && Roo.MessageBox) {
16305             Roo.MessageBox.show({ title: 'loading' });
16306             Roo.MessageBox.show({
16307                title: "Please wait...",
16308                msg: msg,
16309                width:450,
16310                progress:true,
16311                closable:false,
16312                modal: false
16313               
16314             });
16315         }
16316         var total = mods.length;
16317         
16318         var _this = this;
16319         var progressRun = function() {
16320             if (!mods.length) {
16321                 Roo.debug && Roo.log('hide?');
16322                 if (!this.hideProgress && Roo.MessageBox) {
16323                     Roo.MessageBox.hide();
16324                 }
16325                 Roo.XComponent.build_from_html = false; // reset, so dialogs will be build from javascript
16326                 
16327                 Roo.XComponent.event.fireEvent('buildcomplete', _this.topModule);
16328                 
16329                 // THE END...
16330                 return false;   
16331             }
16332             
16333             var m = mods.shift();
16334             
16335             
16336             Roo.debug && Roo.log(m);
16337             // not sure if this is supported any more.. - modules that are are just function
16338             if (typeof(m) == 'function') { 
16339                 m.call(this);
16340                 return progressRun.defer(10, _this);
16341             } 
16342             
16343             
16344             msg = "Building Interface " + (total  - mods.length) + 
16345                     " of " + total + 
16346                     (m.name ? (' - ' + m.name) : '');
16347                         Roo.debug && Roo.log(msg);
16348             if (!this.hideProgress &&  Roo.MessageBox) { 
16349                 Roo.MessageBox.updateProgress(  (total  - mods.length)/total, msg  );
16350             }
16351             
16352          
16353             // is the module disabled?
16354             var disabled = (typeof(m.disabled) == 'function') ?
16355                 m.disabled.call(m.module.disabled) : m.disabled;    
16356             
16357             
16358             if (disabled) {
16359                 return progressRun(); // we do not update the display!
16360             }
16361             
16362             // now build 
16363             
16364                         
16365                         
16366             m.render();
16367             // it's 10 on top level, and 1 on others??? why...
16368             return progressRun.defer(10, _this);
16369              
16370         }
16371         progressRun.defer(1, _this);
16372      
16373         
16374         
16375     },
16376         
16377         
16378         /**
16379          * Event Object.
16380          *
16381          *
16382          */
16383         event: false, 
16384     /**
16385          * wrapper for event.on - aliased later..  
16386          * Typically use to register a event handler for register:
16387          *
16388          * eg. Roo.XComponent.on('register', function(comp) { comp.disable = true } );
16389          *
16390          */
16391     on : false
16392    
16393     
16394     
16395 });
16396
16397 Roo.XComponent.event = new Roo.util.Observable({
16398                 events : { 
16399                         /**
16400                          * @event register
16401                          * Fires when an Component is registered,
16402                          * set the disable property on the Component to stop registration.
16403                          * @param {Roo.XComponent} c the component being registerd.
16404                          * 
16405                          */
16406                         'register' : true,
16407             /**
16408                          * @event beforebuild
16409                          * Fires before each Component is built
16410                          * can be used to apply permissions.
16411                          * @param {Roo.XComponent} c the component being registerd.
16412                          * 
16413                          */
16414                         'beforebuild' : true,
16415                         /**
16416                          * @event buildcomplete
16417                          * Fires on the top level element when all elements have been built
16418                          * @param {Roo.XComponent} the top level component.
16419                          */
16420                         'buildcomplete' : true
16421                         
16422                 }
16423 });
16424
16425 Roo.XComponent.on = Roo.XComponent.event.on.createDelegate(Roo.XComponent.event); 
16426  /*
16427  * Based on:
16428  * Ext JS Library 1.1.1
16429  * Copyright(c) 2006-2007, Ext JS, LLC.
16430  *
16431  * Originally Released Under LGPL - original licence link has changed is not relivant.
16432  *
16433  * Fork - LGPL
16434  * <script type="text/javascript">
16435  */
16436
16437
16438
16439 /*
16440  * These classes are derivatives of the similarly named classes in the YUI Library.
16441  * The original license:
16442  * Copyright (c) 2006, Yahoo! Inc. All rights reserved.
16443  * Code licensed under the BSD License:
16444  * http://developer.yahoo.net/yui/license.txt
16445  */
16446
16447 (function() {
16448
16449 var Event=Roo.EventManager;
16450 var Dom=Roo.lib.Dom;
16451
16452 /**
16453  * @class Roo.dd.DragDrop
16454  * @extends Roo.util.Observable
16455  * Defines the interface and base operation of items that that can be
16456  * dragged or can be drop targets.  It was designed to be extended, overriding
16457  * the event handlers for startDrag, onDrag, onDragOver and onDragOut.
16458  * Up to three html elements can be associated with a DragDrop instance:
16459  * <ul>
16460  * <li>linked element: the element that is passed into the constructor.
16461  * This is the element which defines the boundaries for interaction with
16462  * other DragDrop objects.</li>
16463  * <li>handle element(s): The drag operation only occurs if the element that
16464  * was clicked matches a handle element.  By default this is the linked
16465  * element, but there are times that you will want only a portion of the
16466  * linked element to initiate the drag operation, and the setHandleElId()
16467  * method provides a way to define this.</li>
16468  * <li>drag element: this represents the element that would be moved along
16469  * with the cursor during a drag operation.  By default, this is the linked
16470  * element itself as in {@link Roo.dd.DD}.  setDragElId() lets you define
16471  * a separate element that would be moved, as in {@link Roo.dd.DDProxy}.
16472  * </li>
16473  * </ul>
16474  * This class should not be instantiated until the onload event to ensure that
16475  * the associated elements are available.
16476  * The following would define a DragDrop obj that would interact with any
16477  * other DragDrop obj in the "group1" group:
16478  * <pre>
16479  *  dd = new Roo.dd.DragDrop("div1", "group1");
16480  * </pre>
16481  * Since none of the event handlers have been implemented, nothing would
16482  * actually happen if you were to run the code above.  Normally you would
16483  * override this class or one of the default implementations, but you can
16484  * also override the methods you want on an instance of the class...
16485  * <pre>
16486  *  dd.onDragDrop = function(e, id) {
16487  *  &nbsp;&nbsp;alert("dd was dropped on " + id);
16488  *  }
16489  * </pre>
16490  * @constructor
16491  * @param {String} id of the element that is linked to this instance
16492  * @param {String} sGroup the group of related DragDrop objects
16493  * @param {object} config an object containing configurable attributes
16494  *                Valid properties for DragDrop:
16495  *                    padding, isTarget, maintainOffset, primaryButtonOnly
16496  */
16497 Roo.dd.DragDrop = function(id, sGroup, config) {
16498     if (id) {
16499         this.init(id, sGroup, config);
16500     }
16501     
16502 };
16503
16504 Roo.extend(Roo.dd.DragDrop, Roo.util.Observable , {
16505
16506     /**
16507      * The id of the element associated with this object.  This is what we
16508      * refer to as the "linked element" because the size and position of
16509      * this element is used to determine when the drag and drop objects have
16510      * interacted.
16511      * @property id
16512      * @type String
16513      */
16514     id: null,
16515
16516     /**
16517      * Configuration attributes passed into the constructor
16518      * @property config
16519      * @type object
16520      */
16521     config: null,
16522
16523     /**
16524      * The id of the element that will be dragged.  By default this is same
16525      * as the linked element , but could be changed to another element. Ex:
16526      * Roo.dd.DDProxy
16527      * @property dragElId
16528      * @type String
16529      * @private
16530      */
16531     dragElId: null,
16532
16533     /**
16534      * the id of the element that initiates the drag operation.  By default
16535      * this is the linked element, but could be changed to be a child of this
16536      * element.  This lets us do things like only starting the drag when the
16537      * header element within the linked html element is clicked.
16538      * @property handleElId
16539      * @type String
16540      * @private
16541      */
16542     handleElId: null,
16543
16544     /**
16545      * An associative array of HTML tags that will be ignored if clicked.
16546      * @property invalidHandleTypes
16547      * @type {string: string}
16548      */
16549     invalidHandleTypes: null,
16550
16551     /**
16552      * An associative array of ids for elements that will be ignored if clicked
16553      * @property invalidHandleIds
16554      * @type {string: string}
16555      */
16556     invalidHandleIds: null,
16557
16558     /**
16559      * An indexted array of css class names for elements that will be ignored
16560      * if clicked.
16561      * @property invalidHandleClasses
16562      * @type string[]
16563      */
16564     invalidHandleClasses: null,
16565
16566     /**
16567      * The linked element's absolute X position at the time the drag was
16568      * started
16569      * @property startPageX
16570      * @type int
16571      * @private
16572      */
16573     startPageX: 0,
16574
16575     /**
16576      * The linked element's absolute X position at the time the drag was
16577      * started
16578      * @property startPageY
16579      * @type int
16580      * @private
16581      */
16582     startPageY: 0,
16583
16584     /**
16585      * The group defines a logical collection of DragDrop objects that are
16586      * related.  Instances only get events when interacting with other
16587      * DragDrop object in the same group.  This lets us define multiple
16588      * groups using a single DragDrop subclass if we want.
16589      * @property groups
16590      * @type {string: string}
16591      */
16592     groups: null,
16593
16594     /**
16595      * Individual drag/drop instances can be locked.  This will prevent
16596      * onmousedown start drag.
16597      * @property locked
16598      * @type boolean
16599      * @private
16600      */
16601     locked: false,
16602
16603     /**
16604      * Lock this instance
16605      * @method lock
16606      */
16607     lock: function() { this.locked = true; },
16608
16609     /**
16610      * Unlock this instace
16611      * @method unlock
16612      */
16613     unlock: function() { this.locked = false; },
16614
16615     /**
16616      * By default, all insances can be a drop target.  This can be disabled by
16617      * setting isTarget to false.
16618      * @method isTarget
16619      * @type boolean
16620      */
16621     isTarget: true,
16622
16623     /**
16624      * The padding configured for this drag and drop object for calculating
16625      * the drop zone intersection with this object.
16626      * @method padding
16627      * @type int[]
16628      */
16629     padding: null,
16630
16631     /**
16632      * Cached reference to the linked element
16633      * @property _domRef
16634      * @private
16635      */
16636     _domRef: null,
16637
16638     /**
16639      * Internal typeof flag
16640      * @property __ygDragDrop
16641      * @private
16642      */
16643     __ygDragDrop: true,
16644
16645     /**
16646      * Set to true when horizontal contraints are applied
16647      * @property constrainX
16648      * @type boolean
16649      * @private
16650      */
16651     constrainX: false,
16652
16653     /**
16654      * Set to true when vertical contraints are applied
16655      * @property constrainY
16656      * @type boolean
16657      * @private
16658      */
16659     constrainY: false,
16660
16661     /**
16662      * The left constraint
16663      * @property minX
16664      * @type int
16665      * @private
16666      */
16667     minX: 0,
16668
16669     /**
16670      * The right constraint
16671      * @property maxX
16672      * @type int
16673      * @private
16674      */
16675     maxX: 0,
16676
16677     /**
16678      * The up constraint
16679      * @property minY
16680      * @type int
16681      * @type int
16682      * @private
16683      */
16684     minY: 0,
16685
16686     /**
16687      * The down constraint
16688      * @property maxY
16689      * @type int
16690      * @private
16691      */
16692     maxY: 0,
16693
16694     /**
16695      * Maintain offsets when we resetconstraints.  Set to true when you want
16696      * the position of the element relative to its parent to stay the same
16697      * when the page changes
16698      *
16699      * @property maintainOffset
16700      * @type boolean
16701      */
16702     maintainOffset: false,
16703
16704     /**
16705      * Array of pixel locations the element will snap to if we specified a
16706      * horizontal graduation/interval.  This array is generated automatically
16707      * when you define a tick interval.
16708      * @property xTicks
16709      * @type int[]
16710      */
16711     xTicks: null,
16712
16713     /**
16714      * Array of pixel locations the element will snap to if we specified a
16715      * vertical graduation/interval.  This array is generated automatically
16716      * when you define a tick interval.
16717      * @property yTicks
16718      * @type int[]
16719      */
16720     yTicks: null,
16721
16722     /**
16723      * By default the drag and drop instance will only respond to the primary
16724      * button click (left button for a right-handed mouse).  Set to true to
16725      * allow drag and drop to start with any mouse click that is propogated
16726      * by the browser
16727      * @property primaryButtonOnly
16728      * @type boolean
16729      */
16730     primaryButtonOnly: true,
16731
16732     /**
16733      * The availabe property is false until the linked dom element is accessible.
16734      * @property available
16735      * @type boolean
16736      */
16737     available: false,
16738
16739     /**
16740      * By default, drags can only be initiated if the mousedown occurs in the
16741      * region the linked element is.  This is done in part to work around a
16742      * bug in some browsers that mis-report the mousedown if the previous
16743      * mouseup happened outside of the window.  This property is set to true
16744      * if outer handles are defined.
16745      *
16746      * @property hasOuterHandles
16747      * @type boolean
16748      * @default false
16749      */
16750     hasOuterHandles: false,
16751
16752     /**
16753      * Code that executes immediately before the startDrag event
16754      * @method b4StartDrag
16755      * @private
16756      */
16757     b4StartDrag: function(x, y) { },
16758
16759     /**
16760      * Abstract method called after a drag/drop object is clicked
16761      * and the drag or mousedown time thresholds have beeen met.
16762      * @method startDrag
16763      * @param {int} X click location
16764      * @param {int} Y click location
16765      */
16766     startDrag: function(x, y) { /* override this */ },
16767
16768     /**
16769      * Code that executes immediately before the onDrag event
16770      * @method b4Drag
16771      * @private
16772      */
16773     b4Drag: function(e) { },
16774
16775     /**
16776      * Abstract method called during the onMouseMove event while dragging an
16777      * object.
16778      * @method onDrag
16779      * @param {Event} e the mousemove event
16780      */
16781     onDrag: function(e) { /* override this */ },
16782
16783     /**
16784      * Abstract method called when this element fist begins hovering over
16785      * another DragDrop obj
16786      * @method onDragEnter
16787      * @param {Event} e the mousemove event
16788      * @param {String|DragDrop[]} id In POINT mode, the element
16789      * id this is hovering over.  In INTERSECT mode, an array of one or more
16790      * dragdrop items being hovered over.
16791      */
16792     onDragEnter: function(e, id) { /* override this */ },
16793
16794     /**
16795      * Code that executes immediately before the onDragOver event
16796      * @method b4DragOver
16797      * @private
16798      */
16799     b4DragOver: function(e) { },
16800
16801     /**
16802      * Abstract method called when this element is hovering over another
16803      * DragDrop obj
16804      * @method onDragOver
16805      * @param {Event} e the mousemove event
16806      * @param {String|DragDrop[]} id In POINT mode, the element
16807      * id this is hovering over.  In INTERSECT mode, an array of dd items
16808      * being hovered over.
16809      */
16810     onDragOver: function(e, id) { /* override this */ },
16811
16812     /**
16813      * Code that executes immediately before the onDragOut event
16814      * @method b4DragOut
16815      * @private
16816      */
16817     b4DragOut: function(e) { },
16818
16819     /**
16820      * Abstract method called when we are no longer hovering over an element
16821      * @method onDragOut
16822      * @param {Event} e the mousemove event
16823      * @param {String|DragDrop[]} id In POINT mode, the element
16824      * id this was hovering over.  In INTERSECT mode, an array of dd items
16825      * that the mouse is no longer over.
16826      */
16827     onDragOut: function(e, id) { /* override this */ },
16828
16829     /**
16830      * Code that executes immediately before the onDragDrop event
16831      * @method b4DragDrop
16832      * @private
16833      */
16834     b4DragDrop: function(e) { },
16835
16836     /**
16837      * Abstract method called when this item is dropped on another DragDrop
16838      * obj
16839      * @method onDragDrop
16840      * @param {Event} e the mouseup event
16841      * @param {String|DragDrop[]} id In POINT mode, the element
16842      * id this was dropped on.  In INTERSECT mode, an array of dd items this
16843      * was dropped on.
16844      */
16845     onDragDrop: function(e, id) { /* override this */ },
16846
16847     /**
16848      * Abstract method called when this item is dropped on an area with no
16849      * drop target
16850      * @method onInvalidDrop
16851      * @param {Event} e the mouseup event
16852      */
16853     onInvalidDrop: function(e) { /* override this */ },
16854
16855     /**
16856      * Code that executes immediately before the endDrag event
16857      * @method b4EndDrag
16858      * @private
16859      */
16860     b4EndDrag: function(e) { },
16861
16862     /**
16863      * Fired when we are done dragging the object
16864      * @method endDrag
16865      * @param {Event} e the mouseup event
16866      */
16867     endDrag: function(e) { /* override this */ },
16868
16869     /**
16870      * Code executed immediately before the onMouseDown event
16871      * @method b4MouseDown
16872      * @param {Event} e the mousedown event
16873      * @private
16874      */
16875     b4MouseDown: function(e) {  },
16876
16877     /**
16878      * Event handler that fires when a drag/drop obj gets a mousedown
16879      * @method onMouseDown
16880      * @param {Event} e the mousedown event
16881      */
16882     onMouseDown: function(e) { /* override this */ },
16883
16884     /**
16885      * Event handler that fires when a drag/drop obj gets a mouseup
16886      * @method onMouseUp
16887      * @param {Event} e the mouseup event
16888      */
16889     onMouseUp: function(e) { /* override this */ },
16890
16891     /**
16892      * Override the onAvailable method to do what is needed after the initial
16893      * position was determined.
16894      * @method onAvailable
16895      */
16896     onAvailable: function () {
16897     },
16898
16899     /*
16900      * Provides default constraint padding to "constrainTo" elements (defaults to {left: 0, right:0, top:0, bottom:0}).
16901      * @type Object
16902      */
16903     defaultPadding : {left:0, right:0, top:0, bottom:0},
16904
16905     /*
16906      * Initializes the drag drop object's constraints to restrict movement to a certain element.
16907  *
16908  * Usage:
16909  <pre><code>
16910  var dd = new Roo.dd.DDProxy("dragDiv1", "proxytest",
16911                 { dragElId: "existingProxyDiv" });
16912  dd.startDrag = function(){
16913      this.constrainTo("parent-id");
16914  };
16915  </code></pre>
16916  * Or you can initalize it using the {@link Roo.Element} object:
16917  <pre><code>
16918  Roo.get("dragDiv1").initDDProxy("proxytest", {dragElId: "existingProxyDiv"}, {
16919      startDrag : function(){
16920          this.constrainTo("parent-id");
16921      }
16922  });
16923  </code></pre>
16924      * @param {String/HTMLElement/Element} constrainTo The element to constrain to.
16925      * @param {Object/Number} pad (optional) Pad provides a way to specify "padding" of the constraints,
16926      * and can be either a number for symmetrical padding (4 would be equal to {left:4, right:4, top:4, bottom:4}) or
16927      * an object containing the sides to pad. For example: {right:10, bottom:10}
16928      * @param {Boolean} inContent (optional) Constrain the draggable in the content box of the element (inside padding and borders)
16929      */
16930     constrainTo : function(constrainTo, pad, inContent){
16931         if(typeof pad == "number"){
16932             pad = {left: pad, right:pad, top:pad, bottom:pad};
16933         }
16934         pad = pad || this.defaultPadding;
16935         var b = Roo.get(this.getEl()).getBox();
16936         var ce = Roo.get(constrainTo);
16937         var s = ce.getScroll();
16938         var c, cd = ce.dom;
16939         if(cd == document.body){
16940             c = { x: s.left, y: s.top, width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
16941         }else{
16942             xy = ce.getXY();
16943             c = {x : xy[0]+s.left, y: xy[1]+s.top, width: cd.clientWidth, height: cd.clientHeight};
16944         }
16945
16946
16947         var topSpace = b.y - c.y;
16948         var leftSpace = b.x - c.x;
16949
16950         this.resetConstraints();
16951         this.setXConstraint(leftSpace - (pad.left||0), // left
16952                 c.width - leftSpace - b.width - (pad.right||0) //right
16953         );
16954         this.setYConstraint(topSpace - (pad.top||0), //top
16955                 c.height - topSpace - b.height - (pad.bottom||0) //bottom
16956         );
16957     },
16958
16959     /**
16960      * Returns a reference to the linked element
16961      * @method getEl
16962      * @return {HTMLElement} the html element
16963      */
16964     getEl: function() {
16965         if (!this._domRef) {
16966             this._domRef = Roo.getDom(this.id);
16967         }
16968
16969         return this._domRef;
16970     },
16971
16972     /**
16973      * Returns a reference to the actual element to drag.  By default this is
16974      * the same as the html element, but it can be assigned to another
16975      * element. An example of this can be found in Roo.dd.DDProxy
16976      * @method getDragEl
16977      * @return {HTMLElement} the html element
16978      */
16979     getDragEl: function() {
16980         return Roo.getDom(this.dragElId);
16981     },
16982
16983     /**
16984      * Sets up the DragDrop object.  Must be called in the constructor of any
16985      * Roo.dd.DragDrop subclass
16986      * @method init
16987      * @param id the id of the linked element
16988      * @param {String} sGroup the group of related items
16989      * @param {object} config configuration attributes
16990      */
16991     init: function(id, sGroup, config) {
16992         this.initTarget(id, sGroup, config);
16993         if (!Roo.isTouch) {
16994             Event.on(this.id, "mousedown", this.handleMouseDown, this);
16995         }
16996         Event.on(this.id, "touchstart", this.handleMouseDown, this);
16997         // Event.on(this.id, "selectstart", Event.preventDefault);
16998     },
16999
17000     /**
17001      * Initializes Targeting functionality only... the object does not
17002      * get a mousedown handler.
17003      * @method initTarget
17004      * @param id the id of the linked element
17005      * @param {String} sGroup the group of related items
17006      * @param {object} config configuration attributes
17007      */
17008     initTarget: function(id, sGroup, config) {
17009
17010         // configuration attributes
17011         this.config = config || {};
17012
17013         // create a local reference to the drag and drop manager
17014         this.DDM = Roo.dd.DDM;
17015         // initialize the groups array
17016         this.groups = {};
17017
17018         // assume that we have an element reference instead of an id if the
17019         // parameter is not a string
17020         if (typeof id !== "string") {
17021             id = Roo.id(id);
17022         }
17023
17024         // set the id
17025         this.id = id;
17026
17027         // add to an interaction group
17028         this.addToGroup((sGroup) ? sGroup : "default");
17029
17030         // We don't want to register this as the handle with the manager
17031         // so we just set the id rather than calling the setter.
17032         this.handleElId = id;
17033
17034         // the linked element is the element that gets dragged by default
17035         this.setDragElId(id);
17036
17037         // by default, clicked anchors will not start drag operations.
17038         this.invalidHandleTypes = { A: "A" };
17039         this.invalidHandleIds = {};
17040         this.invalidHandleClasses = [];
17041
17042         this.applyConfig();
17043
17044         this.handleOnAvailable();
17045     },
17046
17047     /**
17048      * Applies the configuration parameters that were passed into the constructor.
17049      * This is supposed to happen at each level through the inheritance chain.  So
17050      * a DDProxy implentation will execute apply config on DDProxy, DD, and
17051      * DragDrop in order to get all of the parameters that are available in
17052      * each object.
17053      * @method applyConfig
17054      */
17055     applyConfig: function() {
17056
17057         // configurable properties:
17058         //    padding, isTarget, maintainOffset, primaryButtonOnly
17059         this.padding           = this.config.padding || [0, 0, 0, 0];
17060         this.isTarget          = (this.config.isTarget !== false);
17061         this.maintainOffset    = (this.config.maintainOffset);
17062         this.primaryButtonOnly = (this.config.primaryButtonOnly !== false);
17063
17064     },
17065
17066     /**
17067      * Executed when the linked element is available
17068      * @method handleOnAvailable
17069      * @private
17070      */
17071     handleOnAvailable: function() {
17072         this.available = true;
17073         this.resetConstraints();
17074         this.onAvailable();
17075     },
17076
17077      /**
17078      * Configures the padding for the target zone in px.  Effectively expands
17079      * (or reduces) the virtual object size for targeting calculations.
17080      * Supports css-style shorthand; if only one parameter is passed, all sides
17081      * will have that padding, and if only two are passed, the top and bottom
17082      * will have the first param, the left and right the second.
17083      * @method setPadding
17084      * @param {int} iTop    Top pad
17085      * @param {int} iRight  Right pad
17086      * @param {int} iBot    Bot pad
17087      * @param {int} iLeft   Left pad
17088      */
17089     setPadding: function(iTop, iRight, iBot, iLeft) {
17090         // this.padding = [iLeft, iRight, iTop, iBot];
17091         if (!iRight && 0 !== iRight) {
17092             this.padding = [iTop, iTop, iTop, iTop];
17093         } else if (!iBot && 0 !== iBot) {
17094             this.padding = [iTop, iRight, iTop, iRight];
17095         } else {
17096             this.padding = [iTop, iRight, iBot, iLeft];
17097         }
17098     },
17099
17100     /**
17101      * Stores the initial placement of the linked element.
17102      * @method setInitialPosition
17103      * @param {int} diffX   the X offset, default 0
17104      * @param {int} diffY   the Y offset, default 0
17105      */
17106     setInitPosition: function(diffX, diffY) {
17107         var el = this.getEl();
17108
17109         if (!this.DDM.verifyEl(el)) {
17110             return;
17111         }
17112
17113         var dx = diffX || 0;
17114         var dy = diffY || 0;
17115
17116         var p = Dom.getXY( el );
17117
17118         this.initPageX = p[0] - dx;
17119         this.initPageY = p[1] - dy;
17120
17121         this.lastPageX = p[0];
17122         this.lastPageY = p[1];
17123
17124
17125         this.setStartPosition(p);
17126     },
17127
17128     /**
17129      * Sets the start position of the element.  This is set when the obj
17130      * is initialized, the reset when a drag is started.
17131      * @method setStartPosition
17132      * @param pos current position (from previous lookup)
17133      * @private
17134      */
17135     setStartPosition: function(pos) {
17136         var p = pos || Dom.getXY( this.getEl() );
17137         this.deltaSetXY = null;
17138
17139         this.startPageX = p[0];
17140         this.startPageY = p[1];
17141     },
17142
17143     /**
17144      * Add this instance to a group of related drag/drop objects.  All
17145      * instances belong to at least one group, and can belong to as many
17146      * groups as needed.
17147      * @method addToGroup
17148      * @param sGroup {string} the name of the group
17149      */
17150     addToGroup: function(sGroup) {
17151         this.groups[sGroup] = true;
17152         this.DDM.regDragDrop(this, sGroup);
17153     },
17154
17155     /**
17156      * Remove's this instance from the supplied interaction group
17157      * @method removeFromGroup
17158      * @param {string}  sGroup  The group to drop
17159      */
17160     removeFromGroup: function(sGroup) {
17161         if (this.groups[sGroup]) {
17162             delete this.groups[sGroup];
17163         }
17164
17165         this.DDM.removeDDFromGroup(this, sGroup);
17166     },
17167
17168     /**
17169      * Allows you to specify that an element other than the linked element
17170      * will be moved with the cursor during a drag
17171      * @method setDragElId
17172      * @param id {string} the id of the element that will be used to initiate the drag
17173      */
17174     setDragElId: function(id) {
17175         this.dragElId = id;
17176     },
17177
17178     /**
17179      * Allows you to specify a child of the linked element that should be
17180      * used to initiate the drag operation.  An example of this would be if
17181      * you have a content div with text and links.  Clicking anywhere in the
17182      * content area would normally start the drag operation.  Use this method
17183      * to specify that an element inside of the content div is the element
17184      * that starts the drag operation.
17185      * @method setHandleElId
17186      * @param id {string} the id of the element that will be used to
17187      * initiate the drag.
17188      */
17189     setHandleElId: function(id) {
17190         if (typeof id !== "string") {
17191             id = Roo.id(id);
17192         }
17193         this.handleElId = id;
17194         this.DDM.regHandle(this.id, id);
17195     },
17196
17197     /**
17198      * Allows you to set an element outside of the linked element as a drag
17199      * handle
17200      * @method setOuterHandleElId
17201      * @param id the id of the element that will be used to initiate the drag
17202      */
17203     setOuterHandleElId: function(id) {
17204         if (typeof id !== "string") {
17205             id = Roo.id(id);
17206         }
17207         Event.on(id, "mousedown",
17208                 this.handleMouseDown, this);
17209         this.setHandleElId(id);
17210
17211         this.hasOuterHandles = true;
17212     },
17213
17214     /**
17215      * Remove all drag and drop hooks for this element
17216      * @method unreg
17217      */
17218     unreg: function() {
17219         Event.un(this.id, "mousedown",
17220                 this.handleMouseDown);
17221         Event.un(this.id, "touchstart",
17222                 this.handleMouseDown);
17223         this._domRef = null;
17224         this.DDM._remove(this);
17225     },
17226
17227     destroy : function(){
17228         this.unreg();
17229     },
17230
17231     /**
17232      * Returns true if this instance is locked, or the drag drop mgr is locked
17233      * (meaning that all drag/drop is disabled on the page.)
17234      * @method isLocked
17235      * @return {boolean} true if this obj or all drag/drop is locked, else
17236      * false
17237      */
17238     isLocked: function() {
17239         return (this.DDM.isLocked() || this.locked);
17240     },
17241
17242     /**
17243      * Fired when this object is clicked
17244      * @method handleMouseDown
17245      * @param {Event} e
17246      * @param {Roo.dd.DragDrop} oDD the clicked dd object (this dd obj)
17247      * @private
17248      */
17249     handleMouseDown: function(e, oDD){
17250      
17251         if (!Roo.isTouch && this.primaryButtonOnly && e.button != 0) {
17252             //Roo.log('not touch/ button !=0');
17253             return;
17254         }
17255         if (e.browserEvent.touches && e.browserEvent.touches.length != 1) {
17256             return; // double touch..
17257         }
17258         
17259
17260         if (this.isLocked()) {
17261             //Roo.log('locked');
17262             return;
17263         }
17264
17265         this.DDM.refreshCache(this.groups);
17266 //        Roo.log([Roo.lib.Event.getPageX(e), Roo.lib.Event.getPageY(e)]);
17267         var pt = new Roo.lib.Point(Roo.lib.Event.getPageX(e), Roo.lib.Event.getPageY(e));
17268         if (!this.hasOuterHandles && !this.DDM.isOverTarget(pt, this) )  {
17269             //Roo.log('no outer handes or not over target');
17270                 // do nothing.
17271         } else {
17272 //            Roo.log('check validator');
17273             if (this.clickValidator(e)) {
17274 //                Roo.log('validate success');
17275                 // set the initial element position
17276                 this.setStartPosition();
17277
17278
17279                 this.b4MouseDown(e);
17280                 this.onMouseDown(e);
17281
17282                 this.DDM.handleMouseDown(e, this);
17283
17284                 this.DDM.stopEvent(e);
17285             } else {
17286
17287
17288             }
17289         }
17290     },
17291
17292     clickValidator: function(e) {
17293         var target = e.getTarget();
17294         return ( this.isValidHandleChild(target) &&
17295                     (this.id == this.handleElId ||
17296                         this.DDM.handleWasClicked(target, this.id)) );
17297     },
17298
17299     /**
17300      * Allows you to specify a tag name that should not start a drag operation
17301      * when clicked.  This is designed to facilitate embedding links within a
17302      * drag handle that do something other than start the drag.
17303      * @method addInvalidHandleType
17304      * @param {string} tagName the type of element to exclude
17305      */
17306     addInvalidHandleType: function(tagName) {
17307         var type = tagName.toUpperCase();
17308         this.invalidHandleTypes[type] = type;
17309     },
17310
17311     /**
17312      * Lets you to specify an element id for a child of a drag handle
17313      * that should not initiate a drag
17314      * @method addInvalidHandleId
17315      * @param {string} id the element id of the element you wish to ignore
17316      */
17317     addInvalidHandleId: function(id) {
17318         if (typeof id !== "string") {
17319             id = Roo.id(id);
17320         }
17321         this.invalidHandleIds[id] = id;
17322     },
17323
17324     /**
17325      * Lets you specify a css class of elements that will not initiate a drag
17326      * @method addInvalidHandleClass
17327      * @param {string} cssClass the class of the elements you wish to ignore
17328      */
17329     addInvalidHandleClass: function(cssClass) {
17330         this.invalidHandleClasses.push(cssClass);
17331     },
17332
17333     /**
17334      * Unsets an excluded tag name set by addInvalidHandleType
17335      * @method removeInvalidHandleType
17336      * @param {string} tagName the type of element to unexclude
17337      */
17338     removeInvalidHandleType: function(tagName) {
17339         var type = tagName.toUpperCase();
17340         // this.invalidHandleTypes[type] = null;
17341         delete this.invalidHandleTypes[type];
17342     },
17343
17344     /**
17345      * Unsets an invalid handle id
17346      * @method removeInvalidHandleId
17347      * @param {string} id the id of the element to re-enable
17348      */
17349     removeInvalidHandleId: function(id) {
17350         if (typeof id !== "string") {
17351             id = Roo.id(id);
17352         }
17353         delete this.invalidHandleIds[id];
17354     },
17355
17356     /**
17357      * Unsets an invalid css class
17358      * @method removeInvalidHandleClass
17359      * @param {string} cssClass the class of the element(s) you wish to
17360      * re-enable
17361      */
17362     removeInvalidHandleClass: function(cssClass) {
17363         for (var i=0, len=this.invalidHandleClasses.length; i<len; ++i) {
17364             if (this.invalidHandleClasses[i] == cssClass) {
17365                 delete this.invalidHandleClasses[i];
17366             }
17367         }
17368     },
17369
17370     /**
17371      * Checks the tag exclusion list to see if this click should be ignored
17372      * @method isValidHandleChild
17373      * @param {HTMLElement} node the HTMLElement to evaluate
17374      * @return {boolean} true if this is a valid tag type, false if not
17375      */
17376     isValidHandleChild: function(node) {
17377
17378         var valid = true;
17379         // var n = (node.nodeName == "#text") ? node.parentNode : node;
17380         var nodeName;
17381         try {
17382             nodeName = node.nodeName.toUpperCase();
17383         } catch(e) {
17384             nodeName = node.nodeName;
17385         }
17386         valid = valid && !this.invalidHandleTypes[nodeName];
17387         valid = valid && !this.invalidHandleIds[node.id];
17388
17389         for (var i=0, len=this.invalidHandleClasses.length; valid && i<len; ++i) {
17390             valid = !Dom.hasClass(node, this.invalidHandleClasses[i]);
17391         }
17392
17393
17394         return valid;
17395
17396     },
17397
17398     /**
17399      * Create the array of horizontal tick marks if an interval was specified
17400      * in setXConstraint().
17401      * @method setXTicks
17402      * @private
17403      */
17404     setXTicks: function(iStartX, iTickSize) {
17405         this.xTicks = [];
17406         this.xTickSize = iTickSize;
17407
17408         var tickMap = {};
17409
17410         for (var i = this.initPageX; i >= this.minX; i = i - iTickSize) {
17411             if (!tickMap[i]) {
17412                 this.xTicks[this.xTicks.length] = i;
17413                 tickMap[i] = true;
17414             }
17415         }
17416
17417         for (i = this.initPageX; i <= this.maxX; i = i + iTickSize) {
17418             if (!tickMap[i]) {
17419                 this.xTicks[this.xTicks.length] = i;
17420                 tickMap[i] = true;
17421             }
17422         }
17423
17424         this.xTicks.sort(this.DDM.numericSort) ;
17425     },
17426
17427     /**
17428      * Create the array of vertical tick marks if an interval was specified in
17429      * setYConstraint().
17430      * @method setYTicks
17431      * @private
17432      */
17433     setYTicks: function(iStartY, iTickSize) {
17434         this.yTicks = [];
17435         this.yTickSize = iTickSize;
17436
17437         var tickMap = {};
17438
17439         for (var i = this.initPageY; i >= this.minY; i = i - iTickSize) {
17440             if (!tickMap[i]) {
17441                 this.yTicks[this.yTicks.length] = i;
17442                 tickMap[i] = true;
17443             }
17444         }
17445
17446         for (i = this.initPageY; i <= this.maxY; i = i + iTickSize) {
17447             if (!tickMap[i]) {
17448                 this.yTicks[this.yTicks.length] = i;
17449                 tickMap[i] = true;
17450             }
17451         }
17452
17453         this.yTicks.sort(this.DDM.numericSort) ;
17454     },
17455
17456     /**
17457      * By default, the element can be dragged any place on the screen.  Use
17458      * this method to limit the horizontal travel of the element.  Pass in
17459      * 0,0 for the parameters if you want to lock the drag to the y axis.
17460      * @method setXConstraint
17461      * @param {int} iLeft the number of pixels the element can move to the left
17462      * @param {int} iRight the number of pixels the element can move to the
17463      * right
17464      * @param {int} iTickSize optional parameter for specifying that the
17465      * element
17466      * should move iTickSize pixels at a time.
17467      */
17468     setXConstraint: function(iLeft, iRight, iTickSize) {
17469         this.leftConstraint = iLeft;
17470         this.rightConstraint = iRight;
17471
17472         this.minX = this.initPageX - iLeft;
17473         this.maxX = this.initPageX + iRight;
17474         if (iTickSize) { this.setXTicks(this.initPageX, iTickSize); }
17475
17476         this.constrainX = true;
17477     },
17478
17479     /**
17480      * Clears any constraints applied to this instance.  Also clears ticks
17481      * since they can't exist independent of a constraint at this time.
17482      * @method clearConstraints
17483      */
17484     clearConstraints: function() {
17485         this.constrainX = false;
17486         this.constrainY = false;
17487         this.clearTicks();
17488     },
17489
17490     /**
17491      * Clears any tick interval defined for this instance
17492      * @method clearTicks
17493      */
17494     clearTicks: function() {
17495         this.xTicks = null;
17496         this.yTicks = null;
17497         this.xTickSize = 0;
17498         this.yTickSize = 0;
17499     },
17500
17501     /**
17502      * By default, the element can be dragged any place on the screen.  Set
17503      * this to limit the vertical travel of the element.  Pass in 0,0 for the
17504      * parameters if you want to lock the drag to the x axis.
17505      * @method setYConstraint
17506      * @param {int} iUp the number of pixels the element can move up
17507      * @param {int} iDown the number of pixels the element can move down
17508      * @param {int} iTickSize optional parameter for specifying that the
17509      * element should move iTickSize pixels at a time.
17510      */
17511     setYConstraint: function(iUp, iDown, iTickSize) {
17512         this.topConstraint = iUp;
17513         this.bottomConstraint = iDown;
17514
17515         this.minY = this.initPageY - iUp;
17516         this.maxY = this.initPageY + iDown;
17517         if (iTickSize) { this.setYTicks(this.initPageY, iTickSize); }
17518
17519         this.constrainY = true;
17520
17521     },
17522
17523     /**
17524      * resetConstraints must be called if you manually reposition a dd element.
17525      * @method resetConstraints
17526      * @param {boolean} maintainOffset
17527      */
17528     resetConstraints: function() {
17529
17530
17531         // Maintain offsets if necessary
17532         if (this.initPageX || this.initPageX === 0) {
17533             // figure out how much this thing has moved
17534             var dx = (this.maintainOffset) ? this.lastPageX - this.initPageX : 0;
17535             var dy = (this.maintainOffset) ? this.lastPageY - this.initPageY : 0;
17536
17537             this.setInitPosition(dx, dy);
17538
17539         // This is the first time we have detected the element's position
17540         } else {
17541             this.setInitPosition();
17542         }
17543
17544         if (this.constrainX) {
17545             this.setXConstraint( this.leftConstraint,
17546                                  this.rightConstraint,
17547                                  this.xTickSize        );
17548         }
17549
17550         if (this.constrainY) {
17551             this.setYConstraint( this.topConstraint,
17552                                  this.bottomConstraint,
17553                                  this.yTickSize         );
17554         }
17555     },
17556
17557     /**
17558      * Normally the drag element is moved pixel by pixel, but we can specify
17559      * that it move a number of pixels at a time.  This method resolves the
17560      * location when we have it set up like this.
17561      * @method getTick
17562      * @param {int} val where we want to place the object
17563      * @param {int[]} tickArray sorted array of valid points
17564      * @return {int} the closest tick
17565      * @private
17566      */
17567     getTick: function(val, tickArray) {
17568
17569         if (!tickArray) {
17570             // If tick interval is not defined, it is effectively 1 pixel,
17571             // so we return the value passed to us.
17572             return val;
17573         } else if (tickArray[0] >= val) {
17574             // The value is lower than the first tick, so we return the first
17575             // tick.
17576             return tickArray[0];
17577         } else {
17578             for (var i=0, len=tickArray.length; i<len; ++i) {
17579                 var next = i + 1;
17580                 if (tickArray[next] && tickArray[next] >= val) {
17581                     var diff1 = val - tickArray[i];
17582                     var diff2 = tickArray[next] - val;
17583                     return (diff2 > diff1) ? tickArray[i] : tickArray[next];
17584                 }
17585             }
17586
17587             // The value is larger than the last tick, so we return the last
17588             // tick.
17589             return tickArray[tickArray.length - 1];
17590         }
17591     },
17592
17593     /**
17594      * toString method
17595      * @method toString
17596      * @return {string} string representation of the dd obj
17597      */
17598     toString: function() {
17599         return ("DragDrop " + this.id);
17600     }
17601
17602 });
17603
17604 })();
17605 /*
17606  * Based on:
17607  * Ext JS Library 1.1.1
17608  * Copyright(c) 2006-2007, Ext JS, LLC.
17609  *
17610  * Originally Released Under LGPL - original licence link has changed is not relivant.
17611  *
17612  * Fork - LGPL
17613  * <script type="text/javascript">
17614  */
17615
17616
17617 /**
17618  * The drag and drop utility provides a framework for building drag and drop
17619  * applications.  In addition to enabling drag and drop for specific elements,
17620  * the drag and drop elements are tracked by the manager class, and the
17621  * interactions between the various elements are tracked during the drag and
17622  * the implementing code is notified about these important moments.
17623  */
17624
17625 // Only load the library once.  Rewriting the manager class would orphan
17626 // existing drag and drop instances.
17627 if (!Roo.dd.DragDropMgr) {
17628
17629 /**
17630  * @class Roo.dd.DragDropMgr
17631  * DragDropMgr is a singleton that tracks the element interaction for
17632  * all DragDrop items in the window.  Generally, you will not call
17633  * this class directly, but it does have helper methods that could
17634  * be useful in your DragDrop implementations.
17635  * @singleton
17636  */
17637 Roo.dd.DragDropMgr = function() {
17638
17639     var Event = Roo.EventManager;
17640
17641     return {
17642
17643         /**
17644          * Two dimensional Array of registered DragDrop objects.  The first
17645          * dimension is the DragDrop item group, the second the DragDrop
17646          * object.
17647          * @property ids
17648          * @type {string: string}
17649          * @private
17650          * @static
17651          */
17652         ids: {},
17653
17654         /**
17655          * Array of element ids defined as drag handles.  Used to determine
17656          * if the element that generated the mousedown event is actually the
17657          * handle and not the html element itself.
17658          * @property handleIds
17659          * @type {string: string}
17660          * @private
17661          * @static
17662          */
17663         handleIds: {},
17664
17665         /**
17666          * the DragDrop object that is currently being dragged
17667          * @property dragCurrent
17668          * @type DragDrop
17669          * @private
17670          * @static
17671          **/
17672         dragCurrent: null,
17673
17674         /**
17675          * the DragDrop object(s) that are being hovered over
17676          * @property dragOvers
17677          * @type Array
17678          * @private
17679          * @static
17680          */
17681         dragOvers: {},
17682
17683         /**
17684          * the X distance between the cursor and the object being dragged
17685          * @property deltaX
17686          * @type int
17687          * @private
17688          * @static
17689          */
17690         deltaX: 0,
17691
17692         /**
17693          * the Y distance between the cursor and the object being dragged
17694          * @property deltaY
17695          * @type int
17696          * @private
17697          * @static
17698          */
17699         deltaY: 0,
17700
17701         /**
17702          * Flag to determine if we should prevent the default behavior of the
17703          * events we define. By default this is true, but this can be set to
17704          * false if you need the default behavior (not recommended)
17705          * @property preventDefault
17706          * @type boolean
17707          * @static
17708          */
17709         preventDefault: true,
17710
17711         /**
17712          * Flag to determine if we should stop the propagation of the events
17713          * we generate. This is true by default but you may want to set it to
17714          * false if the html element contains other features that require the
17715          * mouse click.
17716          * @property stopPropagation
17717          * @type boolean
17718          * @static
17719          */
17720         stopPropagation: true,
17721
17722         /**
17723          * Internal flag that is set to true when drag and drop has been
17724          * intialized
17725          * @property initialized
17726          * @private
17727          * @static
17728          */
17729         initalized: false,
17730
17731         /**
17732          * All drag and drop can be disabled.
17733          * @property locked
17734          * @private
17735          * @static
17736          */
17737         locked: false,
17738
17739         /**
17740          * Called the first time an element is registered.
17741          * @method init
17742          * @private
17743          * @static
17744          */
17745         init: function() {
17746             this.initialized = true;
17747         },
17748
17749         /**
17750          * In point mode, drag and drop interaction is defined by the
17751          * location of the cursor during the drag/drop
17752          * @property POINT
17753          * @type int
17754          * @static
17755          */
17756         POINT: 0,
17757
17758         /**
17759          * In intersect mode, drag and drop interactio nis defined by the
17760          * overlap of two or more drag and drop objects.
17761          * @property INTERSECT
17762          * @type int
17763          * @static
17764          */
17765         INTERSECT: 1,
17766
17767         /**
17768          * The current drag and drop mode.  Default: POINT
17769          * @property mode
17770          * @type int
17771          * @static
17772          */
17773         mode: 0,
17774
17775         /**
17776          * Runs method on all drag and drop objects
17777          * @method _execOnAll
17778          * @private
17779          * @static
17780          */
17781         _execOnAll: function(sMethod, args) {
17782             for (var i in this.ids) {
17783                 for (var j in this.ids[i]) {
17784                     var oDD = this.ids[i][j];
17785                     if (! this.isTypeOfDD(oDD)) {
17786                         continue;
17787                     }
17788                     oDD[sMethod].apply(oDD, args);
17789                 }
17790             }
17791         },
17792
17793         /**
17794          * Drag and drop initialization.  Sets up the global event handlers
17795          * @method _onLoad
17796          * @private
17797          * @static
17798          */
17799         _onLoad: function() {
17800
17801             this.init();
17802
17803             if (!Roo.isTouch) {
17804                 Event.on(document, "mouseup",   this.handleMouseUp, this, true);
17805                 Event.on(document, "mousemove", this.handleMouseMove, this, true);
17806             }
17807             Event.on(document, "touchend",   this.handleMouseUp, this, true);
17808             Event.on(document, "touchmove", this.handleMouseMove, this, true);
17809             
17810             Event.on(window,   "unload",    this._onUnload, this, true);
17811             Event.on(window,   "resize",    this._onResize, this, true);
17812             // Event.on(window,   "mouseout",    this._test);
17813
17814         },
17815
17816         /**
17817          * Reset constraints on all drag and drop objs
17818          * @method _onResize
17819          * @private
17820          * @static
17821          */
17822         _onResize: function(e) {
17823             this._execOnAll("resetConstraints", []);
17824         },
17825
17826         /**
17827          * Lock all drag and drop functionality
17828          * @method lock
17829          * @static
17830          */
17831         lock: function() { this.locked = true; },
17832
17833         /**
17834          * Unlock all drag and drop functionality
17835          * @method unlock
17836          * @static
17837          */
17838         unlock: function() { this.locked = false; },
17839
17840         /**
17841          * Is drag and drop locked?
17842          * @method isLocked
17843          * @return {boolean} True if drag and drop is locked, false otherwise.
17844          * @static
17845          */
17846         isLocked: function() { return this.locked; },
17847
17848         /**
17849          * Location cache that is set for all drag drop objects when a drag is
17850          * initiated, cleared when the drag is finished.
17851          * @property locationCache
17852          * @private
17853          * @static
17854          */
17855         locationCache: {},
17856
17857         /**
17858          * Set useCache to false if you want to force object the lookup of each
17859          * drag and drop linked element constantly during a drag.
17860          * @property useCache
17861          * @type boolean
17862          * @static
17863          */
17864         useCache: true,
17865
17866         /**
17867          * The number of pixels that the mouse needs to move after the
17868          * mousedown before the drag is initiated.  Default=3;
17869          * @property clickPixelThresh
17870          * @type int
17871          * @static
17872          */
17873         clickPixelThresh: 3,
17874
17875         /**
17876          * The number of milliseconds after the mousedown event to initiate the
17877          * drag if we don't get a mouseup event. Default=1000
17878          * @property clickTimeThresh
17879          * @type int
17880          * @static
17881          */
17882         clickTimeThresh: 350,
17883
17884         /**
17885          * Flag that indicates that either the drag pixel threshold or the
17886          * mousdown time threshold has been met
17887          * @property dragThreshMet
17888          * @type boolean
17889          * @private
17890          * @static
17891          */
17892         dragThreshMet: false,
17893
17894         /**
17895          * Timeout used for the click time threshold
17896          * @property clickTimeout
17897          * @type Object
17898          * @private
17899          * @static
17900          */
17901         clickTimeout: null,
17902
17903         /**
17904          * The X position of the mousedown event stored for later use when a
17905          * drag threshold is met.
17906          * @property startX
17907          * @type int
17908          * @private
17909          * @static
17910          */
17911         startX: 0,
17912
17913         /**
17914          * The Y position of the mousedown event stored for later use when a
17915          * drag threshold is met.
17916          * @property startY
17917          * @type int
17918          * @private
17919          * @static
17920          */
17921         startY: 0,
17922
17923         /**
17924          * Each DragDrop instance must be registered with the DragDropMgr.
17925          * This is executed in DragDrop.init()
17926          * @method regDragDrop
17927          * @param {DragDrop} oDD the DragDrop object to register
17928          * @param {String} sGroup the name of the group this element belongs to
17929          * @static
17930          */
17931         regDragDrop: function(oDD, sGroup) {
17932             if (!this.initialized) { this.init(); }
17933
17934             if (!this.ids[sGroup]) {
17935                 this.ids[sGroup] = {};
17936             }
17937             this.ids[sGroup][oDD.id] = oDD;
17938         },
17939
17940         /**
17941          * Removes the supplied dd instance from the supplied group. Executed
17942          * by DragDrop.removeFromGroup, so don't call this function directly.
17943          * @method removeDDFromGroup
17944          * @private
17945          * @static
17946          */
17947         removeDDFromGroup: function(oDD, sGroup) {
17948             if (!this.ids[sGroup]) {
17949                 this.ids[sGroup] = {};
17950             }
17951
17952             var obj = this.ids[sGroup];
17953             if (obj && obj[oDD.id]) {
17954                 delete obj[oDD.id];
17955             }
17956         },
17957
17958         /**
17959          * Unregisters a drag and drop item.  This is executed in
17960          * DragDrop.unreg, use that method instead of calling this directly.
17961          * @method _remove
17962          * @private
17963          * @static
17964          */
17965         _remove: function(oDD) {
17966             for (var g in oDD.groups) {
17967                 if (g && this.ids[g][oDD.id]) {
17968                     delete this.ids[g][oDD.id];
17969                 }
17970             }
17971             delete this.handleIds[oDD.id];
17972         },
17973
17974         /**
17975          * Each DragDrop handle element must be registered.  This is done
17976          * automatically when executing DragDrop.setHandleElId()
17977          * @method regHandle
17978          * @param {String} sDDId the DragDrop id this element is a handle for
17979          * @param {String} sHandleId the id of the element that is the drag
17980          * handle
17981          * @static
17982          */
17983         regHandle: function(sDDId, sHandleId) {
17984             if (!this.handleIds[sDDId]) {
17985                 this.handleIds[sDDId] = {};
17986             }
17987             this.handleIds[sDDId][sHandleId] = sHandleId;
17988         },
17989
17990         /**
17991          * Utility function to determine if a given element has been
17992          * registered as a drag drop item.
17993          * @method isDragDrop
17994          * @param {String} id the element id to check
17995          * @return {boolean} true if this element is a DragDrop item,
17996          * false otherwise
17997          * @static
17998          */
17999         isDragDrop: function(id) {
18000             return ( this.getDDById(id) ) ? true : false;
18001         },
18002
18003         /**
18004          * Returns the drag and drop instances that are in all groups the
18005          * passed in instance belongs to.
18006          * @method getRelated
18007          * @param {DragDrop} p_oDD the obj to get related data for
18008          * @param {boolean} bTargetsOnly if true, only return targetable objs
18009          * @return {DragDrop[]} the related instances
18010          * @static
18011          */
18012         getRelated: function(p_oDD, bTargetsOnly) {
18013             var oDDs = [];
18014             for (var i in p_oDD.groups) {
18015                 for (j in this.ids[i]) {
18016                     var dd = this.ids[i][j];
18017                     if (! this.isTypeOfDD(dd)) {
18018                         continue;
18019                     }
18020                     if (!bTargetsOnly || dd.isTarget) {
18021                         oDDs[oDDs.length] = dd;
18022                     }
18023                 }
18024             }
18025
18026             return oDDs;
18027         },
18028
18029         /**
18030          * Returns true if the specified dd target is a legal target for
18031          * the specifice drag obj
18032          * @method isLegalTarget
18033          * @param {DragDrop} the drag obj
18034          * @param {DragDrop} the target
18035          * @return {boolean} true if the target is a legal target for the
18036          * dd obj
18037          * @static
18038          */
18039         isLegalTarget: function (oDD, oTargetDD) {
18040             var targets = this.getRelated(oDD, true);
18041             for (var i=0, len=targets.length;i<len;++i) {
18042                 if (targets[i].id == oTargetDD.id) {
18043                     return true;
18044                 }
18045             }
18046
18047             return false;
18048         },
18049
18050         /**
18051          * My goal is to be able to transparently determine if an object is
18052          * typeof DragDrop, and the exact subclass of DragDrop.  typeof
18053          * returns "object", oDD.constructor.toString() always returns
18054          * "DragDrop" and not the name of the subclass.  So for now it just
18055          * evaluates a well-known variable in DragDrop.
18056          * @method isTypeOfDD
18057          * @param {Object} the object to evaluate
18058          * @return {boolean} true if typeof oDD = DragDrop
18059          * @static
18060          */
18061         isTypeOfDD: function (oDD) {
18062             return (oDD && oDD.__ygDragDrop);
18063         },
18064
18065         /**
18066          * Utility function to determine if a given element has been
18067          * registered as a drag drop handle for the given Drag Drop object.
18068          * @method isHandle
18069          * @param {String} id the element id to check
18070          * @return {boolean} true if this element is a DragDrop handle, false
18071          * otherwise
18072          * @static
18073          */
18074         isHandle: function(sDDId, sHandleId) {
18075             return ( this.handleIds[sDDId] &&
18076                             this.handleIds[sDDId][sHandleId] );
18077         },
18078
18079         /**
18080          * Returns the DragDrop instance for a given id
18081          * @method getDDById
18082          * @param {String} id the id of the DragDrop object
18083          * @return {DragDrop} the drag drop object, null if it is not found
18084          * @static
18085          */
18086         getDDById: function(id) {
18087             for (var i in this.ids) {
18088                 if (this.ids[i][id]) {
18089                     return this.ids[i][id];
18090                 }
18091             }
18092             return null;
18093         },
18094
18095         /**
18096          * Fired after a registered DragDrop object gets the mousedown event.
18097          * Sets up the events required to track the object being dragged
18098          * @method handleMouseDown
18099          * @param {Event} e the event
18100          * @param oDD the DragDrop object being dragged
18101          * @private
18102          * @static
18103          */
18104         handleMouseDown: function(e, oDD) {
18105             if(Roo.QuickTips){
18106                 Roo.QuickTips.disable();
18107             }
18108             this.currentTarget = e.getTarget();
18109
18110             this.dragCurrent = oDD;
18111
18112             var el = oDD.getEl();
18113
18114             // track start position
18115             this.startX = e.getPageX();
18116             this.startY = e.getPageY();
18117
18118             this.deltaX = this.startX - el.offsetLeft;
18119             this.deltaY = this.startY - el.offsetTop;
18120
18121             this.dragThreshMet = false;
18122
18123             this.clickTimeout = setTimeout(
18124                     function() {
18125                         var DDM = Roo.dd.DDM;
18126                         DDM.startDrag(DDM.startX, DDM.startY);
18127                     },
18128                     this.clickTimeThresh );
18129         },
18130
18131         /**
18132          * Fired when either the drag pixel threshol or the mousedown hold
18133          * time threshold has been met.
18134          * @method startDrag
18135          * @param x {int} the X position of the original mousedown
18136          * @param y {int} the Y position of the original mousedown
18137          * @static
18138          */
18139         startDrag: function(x, y) {
18140             clearTimeout(this.clickTimeout);
18141             if (this.dragCurrent) {
18142                 this.dragCurrent.b4StartDrag(x, y);
18143                 this.dragCurrent.startDrag(x, y);
18144             }
18145             this.dragThreshMet = true;
18146         },
18147
18148         /**
18149          * Internal function to handle the mouseup event.  Will be invoked
18150          * from the context of the document.
18151          * @method handleMouseUp
18152          * @param {Event} e the event
18153          * @private
18154          * @static
18155          */
18156         handleMouseUp: function(e) {
18157
18158             if(Roo.QuickTips){
18159                 Roo.QuickTips.enable();
18160             }
18161             if (! this.dragCurrent) {
18162                 return;
18163             }
18164
18165             clearTimeout(this.clickTimeout);
18166
18167             if (this.dragThreshMet) {
18168                 this.fireEvents(e, true);
18169             } else {
18170             }
18171
18172             this.stopDrag(e);
18173
18174             this.stopEvent(e);
18175         },
18176
18177         /**
18178          * Utility to stop event propagation and event default, if these
18179          * features are turned on.
18180          * @method stopEvent
18181          * @param {Event} e the event as returned by this.getEvent()
18182          * @static
18183          */
18184         stopEvent: function(e){
18185             if(this.stopPropagation) {
18186                 e.stopPropagation();
18187             }
18188
18189             if (this.preventDefault) {
18190                 e.preventDefault();
18191             }
18192         },
18193
18194         /**
18195          * Internal function to clean up event handlers after the drag
18196          * operation is complete
18197          * @method stopDrag
18198          * @param {Event} e the event
18199          * @private
18200          * @static
18201          */
18202         stopDrag: function(e) {
18203             // Fire the drag end event for the item that was dragged
18204             if (this.dragCurrent) {
18205                 if (this.dragThreshMet) {
18206                     this.dragCurrent.b4EndDrag(e);
18207                     this.dragCurrent.endDrag(e);
18208                 }
18209
18210                 this.dragCurrent.onMouseUp(e);
18211             }
18212
18213             this.dragCurrent = null;
18214             this.dragOvers = {};
18215         },
18216
18217         /**
18218          * Internal function to handle the mousemove event.  Will be invoked
18219          * from the context of the html element.
18220          *
18221          * @TODO figure out what we can do about mouse events lost when the
18222          * user drags objects beyond the window boundary.  Currently we can
18223          * detect this in internet explorer by verifying that the mouse is
18224          * down during the mousemove event.  Firefox doesn't give us the
18225          * button state on the mousemove event.
18226          * @method handleMouseMove
18227          * @param {Event} e the event
18228          * @private
18229          * @static
18230          */
18231         handleMouseMove: function(e) {
18232             if (! this.dragCurrent) {
18233                 return true;
18234             }
18235
18236             // var button = e.which || e.button;
18237
18238             // check for IE mouseup outside of page boundary
18239             if (Roo.isIE && (e.button !== 0 && e.button !== 1 && e.button !== 2)) {
18240                 this.stopEvent(e);
18241                 return this.handleMouseUp(e);
18242             }
18243
18244             if (!this.dragThreshMet) {
18245                 var diffX = Math.abs(this.startX - e.getPageX());
18246                 var diffY = Math.abs(this.startY - e.getPageY());
18247                 if (diffX > this.clickPixelThresh ||
18248                             diffY > this.clickPixelThresh) {
18249                     this.startDrag(this.startX, this.startY);
18250                 }
18251             }
18252
18253             if (this.dragThreshMet) {
18254                 this.dragCurrent.b4Drag(e);
18255                 this.dragCurrent.onDrag(e);
18256                 if(!this.dragCurrent.moveOnly){
18257                     this.fireEvents(e, false);
18258                 }
18259             }
18260
18261             this.stopEvent(e);
18262
18263             return true;
18264         },
18265
18266         /**
18267          * Iterates over all of the DragDrop elements to find ones we are
18268          * hovering over or dropping on
18269          * @method fireEvents
18270          * @param {Event} e the event
18271          * @param {boolean} isDrop is this a drop op or a mouseover op?
18272          * @private
18273          * @static
18274          */
18275         fireEvents: function(e, isDrop) {
18276             var dc = this.dragCurrent;
18277
18278             // If the user did the mouse up outside of the window, we could
18279             // get here even though we have ended the drag.
18280             if (!dc || dc.isLocked()) {
18281                 return;
18282             }
18283
18284             var pt = e.getPoint();
18285
18286             // cache the previous dragOver array
18287             var oldOvers = [];
18288
18289             var outEvts   = [];
18290             var overEvts  = [];
18291             var dropEvts  = [];
18292             var enterEvts = [];
18293
18294             // Check to see if the object(s) we were hovering over is no longer
18295             // being hovered over so we can fire the onDragOut event
18296             for (var i in this.dragOvers) {
18297
18298                 var ddo = this.dragOvers[i];
18299
18300                 if (! this.isTypeOfDD(ddo)) {
18301                     continue;
18302                 }
18303
18304                 if (! this.isOverTarget(pt, ddo, this.mode)) {
18305                     outEvts.push( ddo );
18306                 }
18307
18308                 oldOvers[i] = true;
18309                 delete this.dragOvers[i];
18310             }
18311
18312             for (var sGroup in dc.groups) {
18313
18314                 if ("string" != typeof sGroup) {
18315                     continue;
18316                 }
18317
18318                 for (i in this.ids[sGroup]) {
18319                     var oDD = this.ids[sGroup][i];
18320                     if (! this.isTypeOfDD(oDD)) {
18321                         continue;
18322                     }
18323
18324                     if (oDD.isTarget && !oDD.isLocked() && oDD != dc) {
18325                         if (this.isOverTarget(pt, oDD, this.mode)) {
18326                             // look for drop interactions
18327                             if (isDrop) {
18328                                 dropEvts.push( oDD );
18329                             // look for drag enter and drag over interactions
18330                             } else {
18331
18332                                 // initial drag over: dragEnter fires
18333                                 if (!oldOvers[oDD.id]) {
18334                                     enterEvts.push( oDD );
18335                                 // subsequent drag overs: dragOver fires
18336                                 } else {
18337                                     overEvts.push( oDD );
18338                                 }
18339
18340                                 this.dragOvers[oDD.id] = oDD;
18341                             }
18342                         }
18343                     }
18344                 }
18345             }
18346
18347             if (this.mode) {
18348                 if (outEvts.length) {
18349                     dc.b4DragOut(e, outEvts);
18350                     dc.onDragOut(e, outEvts);
18351                 }
18352
18353                 if (enterEvts.length) {
18354                     dc.onDragEnter(e, enterEvts);
18355                 }
18356
18357                 if (overEvts.length) {
18358                     dc.b4DragOver(e, overEvts);
18359                     dc.onDragOver(e, overEvts);
18360                 }
18361
18362                 if (dropEvts.length) {
18363                     dc.b4DragDrop(e, dropEvts);
18364                     dc.onDragDrop(e, dropEvts);
18365                 }
18366
18367             } else {
18368                 // fire dragout events
18369                 var len = 0;
18370                 for (i=0, len=outEvts.length; i<len; ++i) {
18371                     dc.b4DragOut(e, outEvts[i].id);
18372                     dc.onDragOut(e, outEvts[i].id);
18373                 }
18374
18375                 // fire enter events
18376                 for (i=0,len=enterEvts.length; i<len; ++i) {
18377                     // dc.b4DragEnter(e, oDD.id);
18378                     dc.onDragEnter(e, enterEvts[i].id);
18379                 }
18380
18381                 // fire over events
18382                 for (i=0,len=overEvts.length; i<len; ++i) {
18383                     dc.b4DragOver(e, overEvts[i].id);
18384                     dc.onDragOver(e, overEvts[i].id);
18385                 }
18386
18387                 // fire drop events
18388                 for (i=0, len=dropEvts.length; i<len; ++i) {
18389                     dc.b4DragDrop(e, dropEvts[i].id);
18390                     dc.onDragDrop(e, dropEvts[i].id);
18391                 }
18392
18393             }
18394
18395             // notify about a drop that did not find a target
18396             if (isDrop && !dropEvts.length) {
18397                 dc.onInvalidDrop(e);
18398             }
18399
18400         },
18401
18402         /**
18403          * Helper function for getting the best match from the list of drag
18404          * and drop objects returned by the drag and drop events when we are
18405          * in INTERSECT mode.  It returns either the first object that the
18406          * cursor is over, or the object that has the greatest overlap with
18407          * the dragged element.
18408          * @method getBestMatch
18409          * @param  {DragDrop[]} dds The array of drag and drop objects
18410          * targeted
18411          * @return {DragDrop}       The best single match
18412          * @static
18413          */
18414         getBestMatch: function(dds) {
18415             var winner = null;
18416             // Return null if the input is not what we expect
18417             //if (!dds || !dds.length || dds.length == 0) {
18418                // winner = null;
18419             // If there is only one item, it wins
18420             //} else if (dds.length == 1) {
18421
18422             var len = dds.length;
18423
18424             if (len == 1) {
18425                 winner = dds[0];
18426             } else {
18427                 // Loop through the targeted items
18428                 for (var i=0; i<len; ++i) {
18429                     var dd = dds[i];
18430                     // If the cursor is over the object, it wins.  If the
18431                     // cursor is over multiple matches, the first one we come
18432                     // to wins.
18433                     if (dd.cursorIsOver) {
18434                         winner = dd;
18435                         break;
18436                     // Otherwise the object with the most overlap wins
18437                     } else {
18438                         if (!winner ||
18439                             winner.overlap.getArea() < dd.overlap.getArea()) {
18440                             winner = dd;
18441                         }
18442                     }
18443                 }
18444             }
18445
18446             return winner;
18447         },
18448
18449         /**
18450          * Refreshes the cache of the top-left and bottom-right points of the
18451          * drag and drop objects in the specified group(s).  This is in the
18452          * format that is stored in the drag and drop instance, so typical
18453          * usage is:
18454          * <code>
18455          * Roo.dd.DragDropMgr.refreshCache(ddinstance.groups);
18456          * </code>
18457          * Alternatively:
18458          * <code>
18459          * Roo.dd.DragDropMgr.refreshCache({group1:true, group2:true});
18460          * </code>
18461          * @TODO this really should be an indexed array.  Alternatively this
18462          * method could accept both.
18463          * @method refreshCache
18464          * @param {Object} groups an associative array of groups to refresh
18465          * @static
18466          */
18467         refreshCache: function(groups) {
18468             for (var sGroup in groups) {
18469                 if ("string" != typeof sGroup) {
18470                     continue;
18471                 }
18472                 for (var i in this.ids[sGroup]) {
18473                     var oDD = this.ids[sGroup][i];
18474
18475                     if (this.isTypeOfDD(oDD)) {
18476                     // if (this.isTypeOfDD(oDD) && oDD.isTarget) {
18477                         var loc = this.getLocation(oDD);
18478                         if (loc) {
18479                             this.locationCache[oDD.id] = loc;
18480                         } else {
18481                             delete this.locationCache[oDD.id];
18482                             // this will unregister the drag and drop object if
18483                             // the element is not in a usable state
18484                             // oDD.unreg();
18485                         }
18486                     }
18487                 }
18488             }
18489         },
18490
18491         /**
18492          * This checks to make sure an element exists and is in the DOM.  The
18493          * main purpose is to handle cases where innerHTML is used to remove
18494          * drag and drop objects from the DOM.  IE provides an 'unspecified
18495          * error' when trying to access the offsetParent of such an element
18496          * @method verifyEl
18497          * @param {HTMLElement} el the element to check
18498          * @return {boolean} true if the element looks usable
18499          * @static
18500          */
18501         verifyEl: function(el) {
18502             if (el) {
18503                 var parent;
18504                 if(Roo.isIE){
18505                     try{
18506                         parent = el.offsetParent;
18507                     }catch(e){}
18508                 }else{
18509                     parent = el.offsetParent;
18510                 }
18511                 if (parent) {
18512                     return true;
18513                 }
18514             }
18515
18516             return false;
18517         },
18518
18519         /**
18520          * Returns a Region object containing the drag and drop element's position
18521          * and size, including the padding configured for it
18522          * @method getLocation
18523          * @param {DragDrop} oDD the drag and drop object to get the
18524          *                       location for
18525          * @return {Roo.lib.Region} a Region object representing the total area
18526          *                             the element occupies, including any padding
18527          *                             the instance is configured for.
18528          * @static
18529          */
18530         getLocation: function(oDD) {
18531             if (! this.isTypeOfDD(oDD)) {
18532                 return null;
18533             }
18534
18535             var el = oDD.getEl(), pos, x1, x2, y1, y2, t, r, b, l;
18536
18537             try {
18538                 pos= Roo.lib.Dom.getXY(el);
18539             } catch (e) { }
18540
18541             if (!pos) {
18542                 return null;
18543             }
18544
18545             x1 = pos[0];
18546             x2 = x1 + el.offsetWidth;
18547             y1 = pos[1];
18548             y2 = y1 + el.offsetHeight;
18549
18550             t = y1 - oDD.padding[0];
18551             r = x2 + oDD.padding[1];
18552             b = y2 + oDD.padding[2];
18553             l = x1 - oDD.padding[3];
18554
18555             return new Roo.lib.Region( t, r, b, l );
18556         },
18557
18558         /**
18559          * Checks the cursor location to see if it over the target
18560          * @method isOverTarget
18561          * @param {Roo.lib.Point} pt The point to evaluate
18562          * @param {DragDrop} oTarget the DragDrop object we are inspecting
18563          * @return {boolean} true if the mouse is over the target
18564          * @private
18565          * @static
18566          */
18567         isOverTarget: function(pt, oTarget, intersect) {
18568             // use cache if available
18569             var loc = this.locationCache[oTarget.id];
18570             if (!loc || !this.useCache) {
18571                 loc = this.getLocation(oTarget);
18572                 this.locationCache[oTarget.id] = loc;
18573
18574             }
18575
18576             if (!loc) {
18577                 return false;
18578             }
18579
18580             oTarget.cursorIsOver = loc.contains( pt );
18581
18582             // DragDrop is using this as a sanity check for the initial mousedown
18583             // in this case we are done.  In POINT mode, if the drag obj has no
18584             // contraints, we are also done. Otherwise we need to evaluate the
18585             // location of the target as related to the actual location of the
18586             // dragged element.
18587             var dc = this.dragCurrent;
18588             if (!dc || !dc.getTargetCoord ||
18589                     (!intersect && !dc.constrainX && !dc.constrainY)) {
18590                 return oTarget.cursorIsOver;
18591             }
18592
18593             oTarget.overlap = null;
18594
18595             // Get the current location of the drag element, this is the
18596             // location of the mouse event less the delta that represents
18597             // where the original mousedown happened on the element.  We
18598             // need to consider constraints and ticks as well.
18599             var pos = dc.getTargetCoord(pt.x, pt.y);
18600
18601             var el = dc.getDragEl();
18602             var curRegion = new Roo.lib.Region( pos.y,
18603                                                    pos.x + el.offsetWidth,
18604                                                    pos.y + el.offsetHeight,
18605                                                    pos.x );
18606
18607             var overlap = curRegion.intersect(loc);
18608
18609             if (overlap) {
18610                 oTarget.overlap = overlap;
18611                 return (intersect) ? true : oTarget.cursorIsOver;
18612             } else {
18613                 return false;
18614             }
18615         },
18616
18617         /**
18618          * unload event handler
18619          * @method _onUnload
18620          * @private
18621          * @static
18622          */
18623         _onUnload: function(e, me) {
18624             Roo.dd.DragDropMgr.unregAll();
18625         },
18626
18627         /**
18628          * Cleans up the drag and drop events and objects.
18629          * @method unregAll
18630          * @private
18631          * @static
18632          */
18633         unregAll: function() {
18634
18635             if (this.dragCurrent) {
18636                 this.stopDrag();
18637                 this.dragCurrent = null;
18638             }
18639
18640             this._execOnAll("unreg", []);
18641
18642             for (i in this.elementCache) {
18643                 delete this.elementCache[i];
18644             }
18645
18646             this.elementCache = {};
18647             this.ids = {};
18648         },
18649
18650         /**
18651          * A cache of DOM elements
18652          * @property elementCache
18653          * @private
18654          * @static
18655          */
18656         elementCache: {},
18657
18658         /**
18659          * Get the wrapper for the DOM element specified
18660          * @method getElWrapper
18661          * @param {String} id the id of the element to get
18662          * @return {Roo.dd.DDM.ElementWrapper} the wrapped element
18663          * @private
18664          * @deprecated This wrapper isn't that useful
18665          * @static
18666          */
18667         getElWrapper: function(id) {
18668             var oWrapper = this.elementCache[id];
18669             if (!oWrapper || !oWrapper.el) {
18670                 oWrapper = this.elementCache[id] =
18671                     new this.ElementWrapper(Roo.getDom(id));
18672             }
18673             return oWrapper;
18674         },
18675
18676         /**
18677          * Returns the actual DOM element
18678          * @method getElement
18679          * @param {String} id the id of the elment to get
18680          * @return {Object} The element
18681          * @deprecated use Roo.getDom instead
18682          * @static
18683          */
18684         getElement: function(id) {
18685             return Roo.getDom(id);
18686         },
18687
18688         /**
18689          * Returns the style property for the DOM element (i.e.,
18690          * document.getElById(id).style)
18691          * @method getCss
18692          * @param {String} id the id of the elment to get
18693          * @return {Object} The style property of the element
18694          * @deprecated use Roo.getDom instead
18695          * @static
18696          */
18697         getCss: function(id) {
18698             var el = Roo.getDom(id);
18699             return (el) ? el.style : null;
18700         },
18701
18702         /**
18703          * Inner class for cached elements
18704          * @class DragDropMgr.ElementWrapper
18705          * @for DragDropMgr
18706          * @private
18707          * @deprecated
18708          */
18709         ElementWrapper: function(el) {
18710                 /**
18711                  * The element
18712                  * @property el
18713                  */
18714                 this.el = el || null;
18715                 /**
18716                  * The element id
18717                  * @property id
18718                  */
18719                 this.id = this.el && el.id;
18720                 /**
18721                  * A reference to the style property
18722                  * @property css
18723                  */
18724                 this.css = this.el && el.style;
18725             },
18726
18727         /**
18728          * Returns the X position of an html element
18729          * @method getPosX
18730          * @param el the element for which to get the position
18731          * @return {int} the X coordinate
18732          * @for DragDropMgr
18733          * @deprecated use Roo.lib.Dom.getX instead
18734          * @static
18735          */
18736         getPosX: function(el) {
18737             return Roo.lib.Dom.getX(el);
18738         },
18739
18740         /**
18741          * Returns the Y position of an html element
18742          * @method getPosY
18743          * @param el the element for which to get the position
18744          * @return {int} the Y coordinate
18745          * @deprecated use Roo.lib.Dom.getY instead
18746          * @static
18747          */
18748         getPosY: function(el) {
18749             return Roo.lib.Dom.getY(el);
18750         },
18751
18752         /**
18753          * Swap two nodes.  In IE, we use the native method, for others we
18754          * emulate the IE behavior
18755          * @method swapNode
18756          * @param n1 the first node to swap
18757          * @param n2 the other node to swap
18758          * @static
18759          */
18760         swapNode: function(n1, n2) {
18761             if (n1.swapNode) {
18762                 n1.swapNode(n2);
18763             } else {
18764                 var p = n2.parentNode;
18765                 var s = n2.nextSibling;
18766
18767                 if (s == n1) {
18768                     p.insertBefore(n1, n2);
18769                 } else if (n2 == n1.nextSibling) {
18770                     p.insertBefore(n2, n1);
18771                 } else {
18772                     n1.parentNode.replaceChild(n2, n1);
18773                     p.insertBefore(n1, s);
18774                 }
18775             }
18776         },
18777
18778         /**
18779          * Returns the current scroll position
18780          * @method getScroll
18781          * @private
18782          * @static
18783          */
18784         getScroll: function () {
18785             var t, l, dde=document.documentElement, db=document.body;
18786             if (dde && (dde.scrollTop || dde.scrollLeft)) {
18787                 t = dde.scrollTop;
18788                 l = dde.scrollLeft;
18789             } else if (db) {
18790                 t = db.scrollTop;
18791                 l = db.scrollLeft;
18792             } else {
18793
18794             }
18795             return { top: t, left: l };
18796         },
18797
18798         /**
18799          * Returns the specified element style property
18800          * @method getStyle
18801          * @param {HTMLElement} el          the element
18802          * @param {string}      styleProp   the style property
18803          * @return {string} The value of the style property
18804          * @deprecated use Roo.lib.Dom.getStyle
18805          * @static
18806          */
18807         getStyle: function(el, styleProp) {
18808             return Roo.fly(el).getStyle(styleProp);
18809         },
18810
18811         /**
18812          * Gets the scrollTop
18813          * @method getScrollTop
18814          * @return {int} the document's scrollTop
18815          * @static
18816          */
18817         getScrollTop: function () { return this.getScroll().top; },
18818
18819         /**
18820          * Gets the scrollLeft
18821          * @method getScrollLeft
18822          * @return {int} the document's scrollTop
18823          * @static
18824          */
18825         getScrollLeft: function () { return this.getScroll().left; },
18826
18827         /**
18828          * Sets the x/y position of an element to the location of the
18829          * target element.
18830          * @method moveToEl
18831          * @param {HTMLElement} moveEl      The element to move
18832          * @param {HTMLElement} targetEl    The position reference element
18833          * @static
18834          */
18835         moveToEl: function (moveEl, targetEl) {
18836             var aCoord = Roo.lib.Dom.getXY(targetEl);
18837             Roo.lib.Dom.setXY(moveEl, aCoord);
18838         },
18839
18840         /**
18841          * Numeric array sort function
18842          * @method numericSort
18843          * @static
18844          */
18845         numericSort: function(a, b) { return (a - b); },
18846
18847         /**
18848          * Internal counter
18849          * @property _timeoutCount
18850          * @private
18851          * @static
18852          */
18853         _timeoutCount: 0,
18854
18855         /**
18856          * Trying to make the load order less important.  Without this we get
18857          * an error if this file is loaded before the Event Utility.
18858          * @method _addListeners
18859          * @private
18860          * @static
18861          */
18862         _addListeners: function() {
18863             var DDM = Roo.dd.DDM;
18864             if ( Roo.lib.Event && document ) {
18865                 DDM._onLoad();
18866             } else {
18867                 if (DDM._timeoutCount > 2000) {
18868                 } else {
18869                     setTimeout(DDM._addListeners, 10);
18870                     if (document && document.body) {
18871                         DDM._timeoutCount += 1;
18872                     }
18873                 }
18874             }
18875         },
18876
18877         /**
18878          * Recursively searches the immediate parent and all child nodes for
18879          * the handle element in order to determine wheter or not it was
18880          * clicked.
18881          * @method handleWasClicked
18882          * @param node the html element to inspect
18883          * @static
18884          */
18885         handleWasClicked: function(node, id) {
18886             if (this.isHandle(id, node.id)) {
18887                 return true;
18888             } else {
18889                 // check to see if this is a text node child of the one we want
18890                 var p = node.parentNode;
18891
18892                 while (p) {
18893                     if (this.isHandle(id, p.id)) {
18894                         return true;
18895                     } else {
18896                         p = p.parentNode;
18897                     }
18898                 }
18899             }
18900
18901             return false;
18902         }
18903
18904     };
18905
18906 }();
18907
18908 // shorter alias, save a few bytes
18909 Roo.dd.DDM = Roo.dd.DragDropMgr;
18910 Roo.dd.DDM._addListeners();
18911
18912 }/*
18913  * Based on:
18914  * Ext JS Library 1.1.1
18915  * Copyright(c) 2006-2007, Ext JS, LLC.
18916  *
18917  * Originally Released Under LGPL - original licence link has changed is not relivant.
18918  *
18919  * Fork - LGPL
18920  * <script type="text/javascript">
18921  */
18922
18923 /**
18924  * @class Roo.dd.DD
18925  * A DragDrop implementation where the linked element follows the
18926  * mouse cursor during a drag.
18927  * @extends Roo.dd.DragDrop
18928  * @constructor
18929  * @param {String} id the id of the linked element
18930  * @param {String} sGroup the group of related DragDrop items
18931  * @param {object} config an object containing configurable attributes
18932  *                Valid properties for DD:
18933  *                    scroll
18934  */
18935 Roo.dd.DD = function(id, sGroup, config) {
18936     if (id) {
18937         this.init(id, sGroup, config);
18938     }
18939 };
18940
18941 Roo.extend(Roo.dd.DD, Roo.dd.DragDrop, {
18942
18943     /**
18944      * When set to true, the utility automatically tries to scroll the browser
18945      * window wehn a drag and drop element is dragged near the viewport boundary.
18946      * Defaults to true.
18947      * @property scroll
18948      * @type boolean
18949      */
18950     scroll: true,
18951
18952     /**
18953      * Sets the pointer offset to the distance between the linked element's top
18954      * left corner and the location the element was clicked
18955      * @method autoOffset
18956      * @param {int} iPageX the X coordinate of the click
18957      * @param {int} iPageY the Y coordinate of the click
18958      */
18959     autoOffset: function(iPageX, iPageY) {
18960         var x = iPageX - this.startPageX;
18961         var y = iPageY - this.startPageY;
18962         this.setDelta(x, y);
18963     },
18964
18965     /**
18966      * Sets the pointer offset.  You can call this directly to force the
18967      * offset to be in a particular location (e.g., pass in 0,0 to set it
18968      * to the center of the object)
18969      * @method setDelta
18970      * @param {int} iDeltaX the distance from the left
18971      * @param {int} iDeltaY the distance from the top
18972      */
18973     setDelta: function(iDeltaX, iDeltaY) {
18974         this.deltaX = iDeltaX;
18975         this.deltaY = iDeltaY;
18976     },
18977
18978     /**
18979      * Sets the drag element to the location of the mousedown or click event,
18980      * maintaining the cursor location relative to the location on the element
18981      * that was clicked.  Override this if you want to place the element in a
18982      * location other than where the cursor is.
18983      * @method setDragElPos
18984      * @param {int} iPageX the X coordinate of the mousedown or drag event
18985      * @param {int} iPageY the Y coordinate of the mousedown or drag event
18986      */
18987     setDragElPos: function(iPageX, iPageY) {
18988         // the first time we do this, we are going to check to make sure
18989         // the element has css positioning
18990
18991         var el = this.getDragEl();
18992         this.alignElWithMouse(el, iPageX, iPageY);
18993     },
18994
18995     /**
18996      * Sets the element to the location of the mousedown or click event,
18997      * maintaining the cursor location relative to the location on the element
18998      * that was clicked.  Override this if you want to place the element in a
18999      * location other than where the cursor is.
19000      * @method alignElWithMouse
19001      * @param {HTMLElement} el the element to move
19002      * @param {int} iPageX the X coordinate of the mousedown or drag event
19003      * @param {int} iPageY the Y coordinate of the mousedown or drag event
19004      */
19005     alignElWithMouse: function(el, iPageX, iPageY) {
19006         var oCoord = this.getTargetCoord(iPageX, iPageY);
19007         var fly = el.dom ? el : Roo.fly(el);
19008         if (!this.deltaSetXY) {
19009             var aCoord = [oCoord.x, oCoord.y];
19010             fly.setXY(aCoord);
19011             var newLeft = fly.getLeft(true);
19012             var newTop  = fly.getTop(true);
19013             this.deltaSetXY = [ newLeft - oCoord.x, newTop - oCoord.y ];
19014         } else {
19015             fly.setLeftTop(oCoord.x + this.deltaSetXY[0], oCoord.y + this.deltaSetXY[1]);
19016         }
19017
19018         this.cachePosition(oCoord.x, oCoord.y);
19019         this.autoScroll(oCoord.x, oCoord.y, el.offsetHeight, el.offsetWidth);
19020         return oCoord;
19021     },
19022
19023     /**
19024      * Saves the most recent position so that we can reset the constraints and
19025      * tick marks on-demand.  We need to know this so that we can calculate the
19026      * number of pixels the element is offset from its original position.
19027      * @method cachePosition
19028      * @param iPageX the current x position (optional, this just makes it so we
19029      * don't have to look it up again)
19030      * @param iPageY the current y position (optional, this just makes it so we
19031      * don't have to look it up again)
19032      */
19033     cachePosition: function(iPageX, iPageY) {
19034         if (iPageX) {
19035             this.lastPageX = iPageX;
19036             this.lastPageY = iPageY;
19037         } else {
19038             var aCoord = Roo.lib.Dom.getXY(this.getEl());
19039             this.lastPageX = aCoord[0];
19040             this.lastPageY = aCoord[1];
19041         }
19042     },
19043
19044     /**
19045      * Auto-scroll the window if the dragged object has been moved beyond the
19046      * visible window boundary.
19047      * @method autoScroll
19048      * @param {int} x the drag element's x position
19049      * @param {int} y the drag element's y position
19050      * @param {int} h the height of the drag element
19051      * @param {int} w the width of the drag element
19052      * @private
19053      */
19054     autoScroll: function(x, y, h, w) {
19055
19056         if (this.scroll) {
19057             // The client height
19058             var clientH = Roo.lib.Dom.getViewWidth();
19059
19060             // The client width
19061             var clientW = Roo.lib.Dom.getViewHeight();
19062
19063             // The amt scrolled down
19064             var st = this.DDM.getScrollTop();
19065
19066             // The amt scrolled right
19067             var sl = this.DDM.getScrollLeft();
19068
19069             // Location of the bottom of the element
19070             var bot = h + y;
19071
19072             // Location of the right of the element
19073             var right = w + x;
19074
19075             // The distance from the cursor to the bottom of the visible area,
19076             // adjusted so that we don't scroll if the cursor is beyond the
19077             // element drag constraints
19078             var toBot = (clientH + st - y - this.deltaY);
19079
19080             // The distance from the cursor to the right of the visible area
19081             var toRight = (clientW + sl - x - this.deltaX);
19082
19083
19084             // How close to the edge the cursor must be before we scroll
19085             // var thresh = (document.all) ? 100 : 40;
19086             var thresh = 40;
19087
19088             // How many pixels to scroll per autoscroll op.  This helps to reduce
19089             // clunky scrolling. IE is more sensitive about this ... it needs this
19090             // value to be higher.
19091             var scrAmt = (document.all) ? 80 : 30;
19092
19093             // Scroll down if we are near the bottom of the visible page and the
19094             // obj extends below the crease
19095             if ( bot > clientH && toBot < thresh ) {
19096                 window.scrollTo(sl, st + scrAmt);
19097             }
19098
19099             // Scroll up if the window is scrolled down and the top of the object
19100             // goes above the top border
19101             if ( y < st && st > 0 && y - st < thresh ) {
19102                 window.scrollTo(sl, st - scrAmt);
19103             }
19104
19105             // Scroll right if the obj is beyond the right border and the cursor is
19106             // near the border.
19107             if ( right > clientW && toRight < thresh ) {
19108                 window.scrollTo(sl + scrAmt, st);
19109             }
19110
19111             // Scroll left if the window has been scrolled to the right and the obj
19112             // extends past the left border
19113             if ( x < sl && sl > 0 && x - sl < thresh ) {
19114                 window.scrollTo(sl - scrAmt, st);
19115             }
19116         }
19117     },
19118
19119     /**
19120      * Finds the location the element should be placed if we want to move
19121      * it to where the mouse location less the click offset would place us.
19122      * @method getTargetCoord
19123      * @param {int} iPageX the X coordinate of the click
19124      * @param {int} iPageY the Y coordinate of the click
19125      * @return an object that contains the coordinates (Object.x and Object.y)
19126      * @private
19127      */
19128     getTargetCoord: function(iPageX, iPageY) {
19129
19130
19131         var x = iPageX - this.deltaX;
19132         var y = iPageY - this.deltaY;
19133
19134         if (this.constrainX) {
19135             if (x < this.minX) { x = this.minX; }
19136             if (x > this.maxX) { x = this.maxX; }
19137         }
19138
19139         if (this.constrainY) {
19140             if (y < this.minY) { y = this.minY; }
19141             if (y > this.maxY) { y = this.maxY; }
19142         }
19143
19144         x = this.getTick(x, this.xTicks);
19145         y = this.getTick(y, this.yTicks);
19146
19147
19148         return {x:x, y:y};
19149     },
19150
19151     /*
19152      * Sets up config options specific to this class. Overrides
19153      * Roo.dd.DragDrop, but all versions of this method through the
19154      * inheritance chain are called
19155      */
19156     applyConfig: function() {
19157         Roo.dd.DD.superclass.applyConfig.call(this);
19158         this.scroll = (this.config.scroll !== false);
19159     },
19160
19161     /*
19162      * Event that fires prior to the onMouseDown event.  Overrides
19163      * Roo.dd.DragDrop.
19164      */
19165     b4MouseDown: function(e) {
19166         // this.resetConstraints();
19167         this.autoOffset(e.getPageX(),
19168                             e.getPageY());
19169     },
19170
19171     /*
19172      * Event that fires prior to the onDrag event.  Overrides
19173      * Roo.dd.DragDrop.
19174      */
19175     b4Drag: function(e) {
19176         this.setDragElPos(e.getPageX(),
19177                             e.getPageY());
19178     },
19179
19180     toString: function() {
19181         return ("DD " + this.id);
19182     }
19183
19184     //////////////////////////////////////////////////////////////////////////
19185     // Debugging ygDragDrop events that can be overridden
19186     //////////////////////////////////////////////////////////////////////////
19187     /*
19188     startDrag: function(x, y) {
19189     },
19190
19191     onDrag: function(e) {
19192     },
19193
19194     onDragEnter: function(e, id) {
19195     },
19196
19197     onDragOver: function(e, id) {
19198     },
19199
19200     onDragOut: function(e, id) {
19201     },
19202
19203     onDragDrop: function(e, id) {
19204     },
19205
19206     endDrag: function(e) {
19207     }
19208
19209     */
19210
19211 });/*
19212  * Based on:
19213  * Ext JS Library 1.1.1
19214  * Copyright(c) 2006-2007, Ext JS, LLC.
19215  *
19216  * Originally Released Under LGPL - original licence link has changed is not relivant.
19217  *
19218  * Fork - LGPL
19219  * <script type="text/javascript">
19220  */
19221
19222 /**
19223  * @class Roo.dd.DDProxy
19224  * A DragDrop implementation that inserts an empty, bordered div into
19225  * the document that follows the cursor during drag operations.  At the time of
19226  * the click, the frame div is resized to the dimensions of the linked html
19227  * element, and moved to the exact location of the linked element.
19228  *
19229  * References to the "frame" element refer to the single proxy element that
19230  * was created to be dragged in place of all DDProxy elements on the
19231  * page.
19232  *
19233  * @extends Roo.dd.DD
19234  * @constructor
19235  * @param {String} id the id of the linked html element
19236  * @param {String} sGroup the group of related DragDrop objects
19237  * @param {object} config an object containing configurable attributes
19238  *                Valid properties for DDProxy in addition to those in DragDrop:
19239  *                   resizeFrame, centerFrame, dragElId
19240  */
19241 Roo.dd.DDProxy = function(id, sGroup, config) {
19242     if (id) {
19243         this.init(id, sGroup, config);
19244         this.initFrame();
19245     }
19246 };
19247
19248 /**
19249  * The default drag frame div id
19250  * @property Roo.dd.DDProxy.dragElId
19251  * @type String
19252  * @static
19253  */
19254 Roo.dd.DDProxy.dragElId = "ygddfdiv";
19255
19256 Roo.extend(Roo.dd.DDProxy, Roo.dd.DD, {
19257
19258     /**
19259      * By default we resize the drag frame to be the same size as the element
19260      * we want to drag (this is to get the frame effect).  We can turn it off
19261      * if we want a different behavior.
19262      * @property resizeFrame
19263      * @type boolean
19264      */
19265     resizeFrame: true,
19266
19267     /**
19268      * By default the frame is positioned exactly where the drag element is, so
19269      * we use the cursor offset provided by Roo.dd.DD.  Another option that works only if
19270      * you do not have constraints on the obj is to have the drag frame centered
19271      * around the cursor.  Set centerFrame to true for this effect.
19272      * @property centerFrame
19273      * @type boolean
19274      */
19275     centerFrame: false,
19276
19277     /**
19278      * Creates the proxy element if it does not yet exist
19279      * @method createFrame
19280      */
19281     createFrame: function() {
19282         var self = this;
19283         var body = document.body;
19284
19285         if (!body || !body.firstChild) {
19286             setTimeout( function() { self.createFrame(); }, 50 );
19287             return;
19288         }
19289
19290         var div = this.getDragEl();
19291
19292         if (!div) {
19293             div    = document.createElement("div");
19294             div.id = this.dragElId;
19295             var s  = div.style;
19296
19297             s.position   = "absolute";
19298             s.visibility = "hidden";
19299             s.cursor     = "move";
19300             s.border     = "2px solid #aaa";
19301             s.zIndex     = 999;
19302
19303             // appendChild can blow up IE if invoked prior to the window load event
19304             // while rendering a table.  It is possible there are other scenarios
19305             // that would cause this to happen as well.
19306             body.insertBefore(div, body.firstChild);
19307         }
19308     },
19309
19310     /**
19311      * Initialization for the drag frame element.  Must be called in the
19312      * constructor of all subclasses
19313      * @method initFrame
19314      */
19315     initFrame: function() {
19316         this.createFrame();
19317     },
19318
19319     applyConfig: function() {
19320         Roo.dd.DDProxy.superclass.applyConfig.call(this);
19321
19322         this.resizeFrame = (this.config.resizeFrame !== false);
19323         this.centerFrame = (this.config.centerFrame);
19324         this.setDragElId(this.config.dragElId || Roo.dd.DDProxy.dragElId);
19325     },
19326
19327     /**
19328      * Resizes the drag frame to the dimensions of the clicked object, positions
19329      * it over the object, and finally displays it
19330      * @method showFrame
19331      * @param {int} iPageX X click position
19332      * @param {int} iPageY Y click position
19333      * @private
19334      */
19335     showFrame: function(iPageX, iPageY) {
19336         var el = this.getEl();
19337         var dragEl = this.getDragEl();
19338         var s = dragEl.style;
19339
19340         this._resizeProxy();
19341
19342         if (this.centerFrame) {
19343             this.setDelta( Math.round(parseInt(s.width,  10)/2),
19344                            Math.round(parseInt(s.height, 10)/2) );
19345         }
19346
19347         this.setDragElPos(iPageX, iPageY);
19348
19349         Roo.fly(dragEl).show();
19350     },
19351
19352     /**
19353      * The proxy is automatically resized to the dimensions of the linked
19354      * element when a drag is initiated, unless resizeFrame is set to false
19355      * @method _resizeProxy
19356      * @private
19357      */
19358     _resizeProxy: function() {
19359         if (this.resizeFrame) {
19360             var el = this.getEl();
19361             Roo.fly(this.getDragEl()).setSize(el.offsetWidth, el.offsetHeight);
19362         }
19363     },
19364
19365     // overrides Roo.dd.DragDrop
19366     b4MouseDown: function(e) {
19367         var x = e.getPageX();
19368         var y = e.getPageY();
19369         this.autoOffset(x, y);
19370         this.setDragElPos(x, y);
19371     },
19372
19373     // overrides Roo.dd.DragDrop
19374     b4StartDrag: function(x, y) {
19375         // show the drag frame
19376         this.showFrame(x, y);
19377     },
19378
19379     // overrides Roo.dd.DragDrop
19380     b4EndDrag: function(e) {
19381         Roo.fly(this.getDragEl()).hide();
19382     },
19383
19384     // overrides Roo.dd.DragDrop
19385     // By default we try to move the element to the last location of the frame.
19386     // This is so that the default behavior mirrors that of Roo.dd.DD.
19387     endDrag: function(e) {
19388
19389         var lel = this.getEl();
19390         var del = this.getDragEl();
19391
19392         // Show the drag frame briefly so we can get its position
19393         del.style.visibility = "";
19394
19395         this.beforeMove();
19396         // Hide the linked element before the move to get around a Safari
19397         // rendering bug.
19398         lel.style.visibility = "hidden";
19399         Roo.dd.DDM.moveToEl(lel, del);
19400         del.style.visibility = "hidden";
19401         lel.style.visibility = "";
19402
19403         this.afterDrag();
19404     },
19405
19406     beforeMove : function(){
19407
19408     },
19409
19410     afterDrag : function(){
19411
19412     },
19413
19414     toString: function() {
19415         return ("DDProxy " + this.id);
19416     }
19417
19418 });
19419 /*
19420  * Based on:
19421  * Ext JS Library 1.1.1
19422  * Copyright(c) 2006-2007, Ext JS, LLC.
19423  *
19424  * Originally Released Under LGPL - original licence link has changed is not relivant.
19425  *
19426  * Fork - LGPL
19427  * <script type="text/javascript">
19428  */
19429
19430  /**
19431  * @class Roo.dd.DDTarget
19432  * A DragDrop implementation that does not move, but can be a drop
19433  * target.  You would get the same result by simply omitting implementation
19434  * for the event callbacks, but this way we reduce the processing cost of the
19435  * event listener and the callbacks.
19436  * @extends Roo.dd.DragDrop
19437  * @constructor
19438  * @param {String} id the id of the element that is a drop target
19439  * @param {String} sGroup the group of related DragDrop objects
19440  * @param {object} config an object containing configurable attributes
19441  *                 Valid properties for DDTarget in addition to those in
19442  *                 DragDrop:
19443  *                    none
19444  */
19445 Roo.dd.DDTarget = function(id, sGroup, config) {
19446     if (id) {
19447         this.initTarget(id, sGroup, config);
19448     }
19449     if (config.listeners || config.events) { 
19450        Roo.dd.DragDrop.superclass.constructor.call(this,  { 
19451             listeners : config.listeners || {}, 
19452             events : config.events || {} 
19453         });    
19454     }
19455 };
19456
19457 // Roo.dd.DDTarget.prototype = new Roo.dd.DragDrop();
19458 Roo.extend(Roo.dd.DDTarget, Roo.dd.DragDrop, {
19459     toString: function() {
19460         return ("DDTarget " + this.id);
19461     }
19462 });
19463 /*
19464  * Based on:
19465  * Ext JS Library 1.1.1
19466  * Copyright(c) 2006-2007, Ext JS, LLC.
19467  *
19468  * Originally Released Under LGPL - original licence link has changed is not relivant.
19469  *
19470  * Fork - LGPL
19471  * <script type="text/javascript">
19472  */
19473  
19474
19475 /**
19476  * @class Roo.dd.ScrollManager
19477  * Provides automatic scrolling of overflow regions in the page during drag operations.<br><br>
19478  * <b>Note: This class uses "Point Mode" and is untested in "Intersect Mode".</b>
19479  * @singleton
19480  */
19481 Roo.dd.ScrollManager = function(){
19482     var ddm = Roo.dd.DragDropMgr;
19483     var els = {};
19484     var dragEl = null;
19485     var proc = {};
19486     
19487     
19488     
19489     var onStop = function(e){
19490         dragEl = null;
19491         clearProc();
19492     };
19493     
19494     var triggerRefresh = function(){
19495         if(ddm.dragCurrent){
19496              ddm.refreshCache(ddm.dragCurrent.groups);
19497         }
19498     };
19499     
19500     var doScroll = function(){
19501         if(ddm.dragCurrent){
19502             var dds = Roo.dd.ScrollManager;
19503             if(!dds.animate){
19504                 if(proc.el.scroll(proc.dir, dds.increment)){
19505                     triggerRefresh();
19506                 }
19507             }else{
19508                 proc.el.scroll(proc.dir, dds.increment, true, dds.animDuration, triggerRefresh);
19509             }
19510         }
19511     };
19512     
19513     var clearProc = function(){
19514         if(proc.id){
19515             clearInterval(proc.id);
19516         }
19517         proc.id = 0;
19518         proc.el = null;
19519         proc.dir = "";
19520     };
19521     
19522     var startProc = function(el, dir){
19523          Roo.log('scroll startproc');
19524         clearProc();
19525         proc.el = el;
19526         proc.dir = dir;
19527         proc.id = setInterval(doScroll, Roo.dd.ScrollManager.frequency);
19528     };
19529     
19530     var onFire = function(e, isDrop){
19531        
19532         if(isDrop || !ddm.dragCurrent){ return; }
19533         var dds = Roo.dd.ScrollManager;
19534         if(!dragEl || dragEl != ddm.dragCurrent){
19535             dragEl = ddm.dragCurrent;
19536             // refresh regions on drag start
19537             dds.refreshCache();
19538         }
19539         
19540         var xy = Roo.lib.Event.getXY(e);
19541         var pt = new Roo.lib.Point(xy[0], xy[1]);
19542         for(var id in els){
19543             var el = els[id], r = el._region;
19544             if(r && r.contains(pt) && el.isScrollable()){
19545                 if(r.bottom - pt.y <= dds.thresh){
19546                     if(proc.el != el){
19547                         startProc(el, "down");
19548                     }
19549                     return;
19550                 }else if(r.right - pt.x <= dds.thresh){
19551                     if(proc.el != el){
19552                         startProc(el, "left");
19553                     }
19554                     return;
19555                 }else if(pt.y - r.top <= dds.thresh){
19556                     if(proc.el != el){
19557                         startProc(el, "up");
19558                     }
19559                     return;
19560                 }else if(pt.x - r.left <= dds.thresh){
19561                     if(proc.el != el){
19562                         startProc(el, "right");
19563                     }
19564                     return;
19565                 }
19566             }
19567         }
19568         clearProc();
19569     };
19570     
19571     ddm.fireEvents = ddm.fireEvents.createSequence(onFire, ddm);
19572     ddm.stopDrag = ddm.stopDrag.createSequence(onStop, ddm);
19573     
19574     return {
19575         /**
19576          * Registers new overflow element(s) to auto scroll
19577          * @param {String/HTMLElement/Element/Array} el The id of or the element to be scrolled or an array of either
19578          */
19579         register : function(el){
19580             if(el instanceof Array){
19581                 for(var i = 0, len = el.length; i < len; i++) {
19582                         this.register(el[i]);
19583                 }
19584             }else{
19585                 el = Roo.get(el);
19586                 els[el.id] = el;
19587             }
19588             Roo.dd.ScrollManager.els = els;
19589         },
19590         
19591         /**
19592          * Unregisters overflow element(s) so they are no longer scrolled
19593          * @param {String/HTMLElement/Element/Array} el The id of or the element to be removed or an array of either
19594          */
19595         unregister : function(el){
19596             if(el instanceof Array){
19597                 for(var i = 0, len = el.length; i < len; i++) {
19598                         this.unregister(el[i]);
19599                 }
19600             }else{
19601                 el = Roo.get(el);
19602                 delete els[el.id];
19603             }
19604         },
19605         
19606         /**
19607          * The number of pixels from the edge of a container the pointer needs to be to 
19608          * trigger scrolling (defaults to 25)
19609          * @type Number
19610          */
19611         thresh : 25,
19612         
19613         /**
19614          * The number of pixels to scroll in each scroll increment (defaults to 50)
19615          * @type Number
19616          */
19617         increment : 100,
19618         
19619         /**
19620          * The frequency of scrolls in milliseconds (defaults to 500)
19621          * @type Number
19622          */
19623         frequency : 500,
19624         
19625         /**
19626          * True to animate the scroll (defaults to true)
19627          * @type Boolean
19628          */
19629         animate: true,
19630         
19631         /**
19632          * The animation duration in seconds - 
19633          * MUST BE less than Roo.dd.ScrollManager.frequency! (defaults to .4)
19634          * @type Number
19635          */
19636         animDuration: .4,
19637         
19638         /**
19639          * Manually trigger a cache refresh.
19640          */
19641         refreshCache : function(){
19642             for(var id in els){
19643                 if(typeof els[id] == 'object'){ // for people extending the object prototype
19644                     els[id]._region = els[id].getRegion();
19645                 }
19646             }
19647         }
19648     };
19649 }();/*
19650  * Based on:
19651  * Ext JS Library 1.1.1
19652  * Copyright(c) 2006-2007, Ext JS, LLC.
19653  *
19654  * Originally Released Under LGPL - original licence link has changed is not relivant.
19655  *
19656  * Fork - LGPL
19657  * <script type="text/javascript">
19658  */
19659  
19660
19661 /**
19662  * @class Roo.dd.Registry
19663  * Provides easy access to all drag drop components that are registered on a page.  Items can be retrieved either
19664  * directly by DOM node id, or by passing in the drag drop event that occurred and looking up the event target.
19665  * @singleton
19666  */
19667 Roo.dd.Registry = function(){
19668     var elements = {}; 
19669     var handles = {}; 
19670     var autoIdSeed = 0;
19671
19672     var getId = function(el, autogen){
19673         if(typeof el == "string"){
19674             return el;
19675         }
19676         var id = el.id;
19677         if(!id && autogen !== false){
19678             id = "roodd-" + (++autoIdSeed);
19679             el.id = id;
19680         }
19681         return id;
19682     };
19683     
19684     return {
19685     /**
19686      * Register a drag drop element
19687      * @param {String|HTMLElement} element The id or DOM node to register
19688      * @param {Object} data (optional) A custom data object that will be passed between the elements that are involved
19689      * in drag drop operations.  You can populate this object with any arbitrary properties that your own code
19690      * knows how to interpret, plus there are some specific properties known to the Registry that should be
19691      * populated in the data object (if applicable):
19692      * <pre>
19693 Value      Description<br />
19694 ---------  ------------------------------------------<br />
19695 handles    Array of DOM nodes that trigger dragging<br />
19696            for the element being registered<br />
19697 isHandle   True if the element passed in triggers<br />
19698            dragging itself, else false
19699 </pre>
19700      */
19701         register : function(el, data){
19702             data = data || {};
19703             if(typeof el == "string"){
19704                 el = document.getElementById(el);
19705             }
19706             data.ddel = el;
19707             elements[getId(el)] = data;
19708             if(data.isHandle !== false){
19709                 handles[data.ddel.id] = data;
19710             }
19711             if(data.handles){
19712                 var hs = data.handles;
19713                 for(var i = 0, len = hs.length; i < len; i++){
19714                         handles[getId(hs[i])] = data;
19715                 }
19716             }
19717         },
19718
19719     /**
19720      * Unregister a drag drop element
19721      * @param {String|HTMLElement}  element The id or DOM node to unregister
19722      */
19723         unregister : function(el){
19724             var id = getId(el, false);
19725             var data = elements[id];
19726             if(data){
19727                 delete elements[id];
19728                 if(data.handles){
19729                     var hs = data.handles;
19730                     for(var i = 0, len = hs.length; i < len; i++){
19731                         delete handles[getId(hs[i], false)];
19732                     }
19733                 }
19734             }
19735         },
19736
19737     /**
19738      * Returns the handle registered for a DOM Node by id
19739      * @param {String|HTMLElement} id The DOM node or id to look up
19740      * @return {Object} handle The custom handle data
19741      */
19742         getHandle : function(id){
19743             if(typeof id != "string"){ // must be element?
19744                 id = id.id;
19745             }
19746             return handles[id];
19747         },
19748
19749     /**
19750      * Returns the handle that is registered for the DOM node that is the target of the event
19751      * @param {Event} e The event
19752      * @return {Object} handle The custom handle data
19753      */
19754         getHandleFromEvent : function(e){
19755             var t = Roo.lib.Event.getTarget(e);
19756             return t ? handles[t.id] : null;
19757         },
19758
19759     /**
19760      * Returns a custom data object that is registered for a DOM node by id
19761      * @param {String|HTMLElement} id The DOM node or id to look up
19762      * @return {Object} data The custom data
19763      */
19764         getTarget : function(id){
19765             if(typeof id != "string"){ // must be element?
19766                 id = id.id;
19767             }
19768             return elements[id];
19769         },
19770
19771     /**
19772      * Returns a custom data object that is registered for the DOM node that is the target of the event
19773      * @param {Event} e The event
19774      * @return {Object} data The custom data
19775      */
19776         getTargetFromEvent : function(e){
19777             var t = Roo.lib.Event.getTarget(e);
19778             return t ? elements[t.id] || handles[t.id] : null;
19779         }
19780     };
19781 }();/*
19782  * Based on:
19783  * Ext JS Library 1.1.1
19784  * Copyright(c) 2006-2007, Ext JS, LLC.
19785  *
19786  * Originally Released Under LGPL - original licence link has changed is not relivant.
19787  *
19788  * Fork - LGPL
19789  * <script type="text/javascript">
19790  */
19791  
19792
19793 /**
19794  * @class Roo.dd.StatusProxy
19795  * A specialized drag proxy that supports a drop status icon, {@link Roo.Layer} styles and auto-repair.  This is the
19796  * default drag proxy used by all Roo.dd components.
19797  * @constructor
19798  * @param {Object} config
19799  */
19800 Roo.dd.StatusProxy = function(config){
19801     Roo.apply(this, config);
19802     this.id = this.id || Roo.id();
19803     this.el = new Roo.Layer({
19804         dh: {
19805             id: this.id, tag: "div", cls: "x-dd-drag-proxy "+this.dropNotAllowed, children: [
19806                 {tag: "div", cls: "x-dd-drop-icon"},
19807                 {tag: "div", cls: "x-dd-drag-ghost"}
19808             ]
19809         }, 
19810         shadow: !config || config.shadow !== false
19811     });
19812     this.ghost = Roo.get(this.el.dom.childNodes[1]);
19813     this.dropStatus = this.dropNotAllowed;
19814 };
19815
19816 Roo.dd.StatusProxy.prototype = {
19817     /**
19818      * @cfg {String} dropAllowed
19819      * The CSS class to apply to the status element when drop is allowed (defaults to "x-dd-drop-ok").
19820      */
19821     dropAllowed : "x-dd-drop-ok",
19822     /**
19823      * @cfg {String} dropNotAllowed
19824      * The CSS class to apply to the status element when drop is not allowed (defaults to "x-dd-drop-nodrop").
19825      */
19826     dropNotAllowed : "x-dd-drop-nodrop",
19827
19828     /**
19829      * Updates the proxy's visual element to indicate the status of whether or not drop is allowed
19830      * over the current target element.
19831      * @param {String} cssClass The css class for the new drop status indicator image
19832      */
19833     setStatus : function(cssClass){
19834         cssClass = cssClass || this.dropNotAllowed;
19835         if(this.dropStatus != cssClass){
19836             this.el.replaceClass(this.dropStatus, cssClass);
19837             this.dropStatus = cssClass;
19838         }
19839     },
19840
19841     /**
19842      * Resets the status indicator to the default dropNotAllowed value
19843      * @param {Boolean} clearGhost True to also remove all content from the ghost, false to preserve it
19844      */
19845     reset : function(clearGhost){
19846         this.el.dom.className = "x-dd-drag-proxy " + this.dropNotAllowed;
19847         this.dropStatus = this.dropNotAllowed;
19848         if(clearGhost){
19849             this.ghost.update("");
19850         }
19851     },
19852
19853     /**
19854      * Updates the contents of the ghost element
19855      * @param {String} html The html that will replace the current innerHTML of the ghost element
19856      */
19857     update : function(html){
19858         if(typeof html == "string"){
19859             this.ghost.update(html);
19860         }else{
19861             this.ghost.update("");
19862             html.style.margin = "0";
19863             this.ghost.dom.appendChild(html);
19864         }
19865         // ensure float = none set?? cant remember why though.
19866         var el = this.ghost.dom.firstChild;
19867                 if(el){
19868                         Roo.fly(el).setStyle('float', 'none');
19869                 }
19870     },
19871     
19872     /**
19873      * Returns the underlying proxy {@link Roo.Layer}
19874      * @return {Roo.Layer} el
19875     */
19876     getEl : function(){
19877         return this.el;
19878     },
19879
19880     /**
19881      * Returns the ghost element
19882      * @return {Roo.Element} el
19883      */
19884     getGhost : function(){
19885         return this.ghost;
19886     },
19887
19888     /**
19889      * Hides the proxy
19890      * @param {Boolean} clear True to reset the status and clear the ghost contents, false to preserve them
19891      */
19892     hide : function(clear){
19893         this.el.hide();
19894         if(clear){
19895             this.reset(true);
19896         }
19897     },
19898
19899     /**
19900      * Stops the repair animation if it's currently running
19901      */
19902     stop : function(){
19903         if(this.anim && this.anim.isAnimated && this.anim.isAnimated()){
19904             this.anim.stop();
19905         }
19906     },
19907
19908     /**
19909      * Displays this proxy
19910      */
19911     show : function(){
19912         this.el.show();
19913     },
19914
19915     /**
19916      * Force the Layer to sync its shadow and shim positions to the element
19917      */
19918     sync : function(){
19919         this.el.sync();
19920     },
19921
19922     /**
19923      * Causes the proxy to return to its position of origin via an animation.  Should be called after an
19924      * invalid drop operation by the item being dragged.
19925      * @param {Array} xy The XY position of the element ([x, y])
19926      * @param {Function} callback The function to call after the repair is complete
19927      * @param {Object} scope The scope in which to execute the callback
19928      */
19929     repair : function(xy, callback, scope){
19930         this.callback = callback;
19931         this.scope = scope;
19932         if(xy && this.animRepair !== false){
19933             this.el.addClass("x-dd-drag-repair");
19934             this.el.hideUnders(true);
19935             this.anim = this.el.shift({
19936                 duration: this.repairDuration || .5,
19937                 easing: 'easeOut',
19938                 xy: xy,
19939                 stopFx: true,
19940                 callback: this.afterRepair,
19941                 scope: this
19942             });
19943         }else{
19944             this.afterRepair();
19945         }
19946     },
19947
19948     // private
19949     afterRepair : function(){
19950         this.hide(true);
19951         if(typeof this.callback == "function"){
19952             this.callback.call(this.scope || this);
19953         }
19954         this.callback = null;
19955         this.scope = null;
19956     }
19957 };/*
19958  * Based on:
19959  * Ext JS Library 1.1.1
19960  * Copyright(c) 2006-2007, Ext JS, LLC.
19961  *
19962  * Originally Released Under LGPL - original licence link has changed is not relivant.
19963  *
19964  * Fork - LGPL
19965  * <script type="text/javascript">
19966  */
19967
19968 /**
19969  * @class Roo.dd.DragSource
19970  * @extends Roo.dd.DDProxy
19971  * A simple class that provides the basic implementation needed to make any element draggable.
19972  * @constructor
19973  * @param {String/HTMLElement/Element} el The container element
19974  * @param {Object} config
19975  */
19976 Roo.dd.DragSource = function(el, config){
19977     this.el = Roo.get(el);
19978     this.dragData = {};
19979     
19980     Roo.apply(this, config);
19981     
19982     if(!this.proxy){
19983         this.proxy = new Roo.dd.StatusProxy();
19984     }
19985
19986     Roo.dd.DragSource.superclass.constructor.call(this, this.el.dom, this.ddGroup || this.group,
19987           {dragElId : this.proxy.id, resizeFrame: false, isTarget: false, scroll: this.scroll === true});
19988     
19989     this.dragging = false;
19990 };
19991
19992 Roo.extend(Roo.dd.DragSource, Roo.dd.DDProxy, {
19993     /**
19994      * @cfg {String} dropAllowed
19995      * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
19996      */
19997     dropAllowed : "x-dd-drop-ok",
19998     /**
19999      * @cfg {String} dropNotAllowed
20000      * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
20001      */
20002     dropNotAllowed : "x-dd-drop-nodrop",
20003
20004     /**
20005      * Returns the data object associated with this drag source
20006      * @return {Object} data An object containing arbitrary data
20007      */
20008     getDragData : function(e){
20009         return this.dragData;
20010     },
20011
20012     // private
20013     onDragEnter : function(e, id){
20014         var target = Roo.dd.DragDropMgr.getDDById(id);
20015         this.cachedTarget = target;
20016         if(this.beforeDragEnter(target, e, id) !== false){
20017             if(target.isNotifyTarget){
20018                 var status = target.notifyEnter(this, e, this.dragData);
20019                 this.proxy.setStatus(status);
20020             }else{
20021                 this.proxy.setStatus(this.dropAllowed);
20022             }
20023             
20024             if(this.afterDragEnter){
20025                 /**
20026                  * An empty function by default, but provided so that you can perform a custom action
20027                  * when the dragged item enters the drop target by providing an implementation.
20028                  * @param {Roo.dd.DragDrop} target The drop target
20029                  * @param {Event} e The event object
20030                  * @param {String} id The id of the dragged element
20031                  * @method afterDragEnter
20032                  */
20033                 this.afterDragEnter(target, e, id);
20034             }
20035         }
20036     },
20037
20038     /**
20039      * An empty function by default, but provided so that you can perform a custom action
20040      * before the dragged item enters the drop target and optionally cancel the onDragEnter.
20041      * @param {Roo.dd.DragDrop} target The drop target
20042      * @param {Event} e The event object
20043      * @param {String} id The id of the dragged element
20044      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
20045      */
20046     beforeDragEnter : function(target, e, id){
20047         return true;
20048     },
20049
20050     // private
20051     alignElWithMouse: function() {
20052         Roo.dd.DragSource.superclass.alignElWithMouse.apply(this, arguments);
20053         this.proxy.sync();
20054     },
20055
20056     // private
20057     onDragOver : function(e, id){
20058         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
20059         if(this.beforeDragOver(target, e, id) !== false){
20060             if(target.isNotifyTarget){
20061                 var status = target.notifyOver(this, e, this.dragData);
20062                 this.proxy.setStatus(status);
20063             }
20064
20065             if(this.afterDragOver){
20066                 /**
20067                  * An empty function by default, but provided so that you can perform a custom action
20068                  * while the dragged item is over the drop target by providing an implementation.
20069                  * @param {Roo.dd.DragDrop} target The drop target
20070                  * @param {Event} e The event object
20071                  * @param {String} id The id of the dragged element
20072                  * @method afterDragOver
20073                  */
20074                 this.afterDragOver(target, e, id);
20075             }
20076         }
20077     },
20078
20079     /**
20080      * An empty function by default, but provided so that you can perform a custom action
20081      * while the dragged item is over the drop target and optionally cancel the onDragOver.
20082      * @param {Roo.dd.DragDrop} target The drop target
20083      * @param {Event} e The event object
20084      * @param {String} id The id of the dragged element
20085      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
20086      */
20087     beforeDragOver : function(target, e, id){
20088         return true;
20089     },
20090
20091     // private
20092     onDragOut : function(e, id){
20093         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
20094         if(this.beforeDragOut(target, e, id) !== false){
20095             if(target.isNotifyTarget){
20096                 target.notifyOut(this, e, this.dragData);
20097             }
20098             this.proxy.reset();
20099             if(this.afterDragOut){
20100                 /**
20101                  * An empty function by default, but provided so that you can perform a custom action
20102                  * after the dragged item is dragged out of the target without dropping.
20103                  * @param {Roo.dd.DragDrop} target The drop target
20104                  * @param {Event} e The event object
20105                  * @param {String} id The id of the dragged element
20106                  * @method afterDragOut
20107                  */
20108                 this.afterDragOut(target, e, id);
20109             }
20110         }
20111         this.cachedTarget = null;
20112     },
20113
20114     /**
20115      * An empty function by default, but provided so that you can perform a custom action before the dragged
20116      * item is dragged out of the target without dropping, and optionally cancel the onDragOut.
20117      * @param {Roo.dd.DragDrop} target The drop target
20118      * @param {Event} e The event object
20119      * @param {String} id The id of the dragged element
20120      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
20121      */
20122     beforeDragOut : function(target, e, id){
20123         return true;
20124     },
20125     
20126     // private
20127     onDragDrop : function(e, id){
20128         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
20129         if(this.beforeDragDrop(target, e, id) !== false){
20130             if(target.isNotifyTarget){
20131                 if(target.notifyDrop(this, e, this.dragData)){ // valid drop?
20132                     this.onValidDrop(target, e, id);
20133                 }else{
20134                     this.onInvalidDrop(target, e, id);
20135                 }
20136             }else{
20137                 this.onValidDrop(target, e, id);
20138             }
20139             
20140             if(this.afterDragDrop){
20141                 /**
20142                  * An empty function by default, but provided so that you can perform a custom action
20143                  * after a valid drag drop has occurred by providing an implementation.
20144                  * @param {Roo.dd.DragDrop} target The drop target
20145                  * @param {Event} e The event object
20146                  * @param {String} id The id of the dropped element
20147                  * @method afterDragDrop
20148                  */
20149                 this.afterDragDrop(target, e, id);
20150             }
20151         }
20152         delete this.cachedTarget;
20153     },
20154
20155     /**
20156      * An empty function by default, but provided so that you can perform a custom action before the dragged
20157      * item is dropped onto the target and optionally cancel the onDragDrop.
20158      * @param {Roo.dd.DragDrop} target The drop target
20159      * @param {Event} e The event object
20160      * @param {String} id The id of the dragged element
20161      * @return {Boolean} isValid True if the drag drop event is valid, else false to cancel
20162      */
20163     beforeDragDrop : function(target, e, id){
20164         return true;
20165     },
20166
20167     // private
20168     onValidDrop : function(target, e, id){
20169         this.hideProxy();
20170         if(this.afterValidDrop){
20171             /**
20172              * An empty function by default, but provided so that you can perform a custom action
20173              * after a valid drop has occurred by providing an implementation.
20174              * @param {Object} target The target DD 
20175              * @param {Event} e The event object
20176              * @param {String} id The id of the dropped element
20177              * @method afterInvalidDrop
20178              */
20179             this.afterValidDrop(target, e, id);
20180         }
20181     },
20182
20183     // private
20184     getRepairXY : function(e, data){
20185         return this.el.getXY();  
20186     },
20187
20188     // private
20189     onInvalidDrop : function(target, e, id){
20190         this.beforeInvalidDrop(target, e, id);
20191         if(this.cachedTarget){
20192             if(this.cachedTarget.isNotifyTarget){
20193                 this.cachedTarget.notifyOut(this, e, this.dragData);
20194             }
20195             this.cacheTarget = null;
20196         }
20197         this.proxy.repair(this.getRepairXY(e, this.dragData), this.afterRepair, this);
20198
20199         if(this.afterInvalidDrop){
20200             /**
20201              * An empty function by default, but provided so that you can perform a custom action
20202              * after an invalid drop has occurred by providing an implementation.
20203              * @param {Event} e The event object
20204              * @param {String} id The id of the dropped element
20205              * @method afterInvalidDrop
20206              */
20207             this.afterInvalidDrop(e, id);
20208         }
20209     },
20210
20211     // private
20212     afterRepair : function(){
20213         if(Roo.enableFx){
20214             this.el.highlight(this.hlColor || "c3daf9");
20215         }
20216         this.dragging = false;
20217     },
20218
20219     /**
20220      * An empty function by default, but provided so that you can perform a custom action after an invalid
20221      * drop has occurred.
20222      * @param {Roo.dd.DragDrop} target The drop target
20223      * @param {Event} e The event object
20224      * @param {String} id The id of the dragged element
20225      * @return {Boolean} isValid True if the invalid drop should proceed, else false to cancel
20226      */
20227     beforeInvalidDrop : function(target, e, id){
20228         return true;
20229     },
20230
20231     // private
20232     handleMouseDown : function(e){
20233         if(this.dragging) {
20234             return;
20235         }
20236         var data = this.getDragData(e);
20237         if(data && this.onBeforeDrag(data, e) !== false){
20238             this.dragData = data;
20239             this.proxy.stop();
20240             Roo.dd.DragSource.superclass.handleMouseDown.apply(this, arguments);
20241         } 
20242     },
20243
20244     /**
20245      * An empty function by default, but provided so that you can perform a custom action before the initial
20246      * drag event begins and optionally cancel it.
20247      * @param {Object} data An object containing arbitrary data to be shared with drop targets
20248      * @param {Event} e The event object
20249      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
20250      */
20251     onBeforeDrag : function(data, e){
20252         return true;
20253     },
20254
20255     /**
20256      * An empty function by default, but provided so that you can perform a custom action once the initial
20257      * drag event has begun.  The drag cannot be canceled from this function.
20258      * @param {Number} x The x position of the click on the dragged object
20259      * @param {Number} y The y position of the click on the dragged object
20260      */
20261     onStartDrag : Roo.emptyFn,
20262
20263     // private - YUI override
20264     startDrag : function(x, y){
20265         this.proxy.reset();
20266         this.dragging = true;
20267         this.proxy.update("");
20268         this.onInitDrag(x, y);
20269         this.proxy.show();
20270     },
20271
20272     // private
20273     onInitDrag : function(x, y){
20274         var clone = this.el.dom.cloneNode(true);
20275         clone.id = Roo.id(); // prevent duplicate ids
20276         this.proxy.update(clone);
20277         this.onStartDrag(x, y);
20278         return true;
20279     },
20280
20281     /**
20282      * Returns the drag source's underlying {@link Roo.dd.StatusProxy}
20283      * @return {Roo.dd.StatusProxy} proxy The StatusProxy
20284      */
20285     getProxy : function(){
20286         return this.proxy;  
20287     },
20288
20289     /**
20290      * Hides the drag source's {@link Roo.dd.StatusProxy}
20291      */
20292     hideProxy : function(){
20293         this.proxy.hide();  
20294         this.proxy.reset(true);
20295         this.dragging = false;
20296     },
20297
20298     // private
20299     triggerCacheRefresh : function(){
20300         Roo.dd.DDM.refreshCache(this.groups);
20301     },
20302
20303     // private - override to prevent hiding
20304     b4EndDrag: function(e) {
20305     },
20306
20307     // private - override to prevent moving
20308     endDrag : function(e){
20309         this.onEndDrag(this.dragData, e);
20310     },
20311
20312     // private
20313     onEndDrag : function(data, e){
20314     },
20315     
20316     // private - pin to cursor
20317     autoOffset : function(x, y) {
20318         this.setDelta(-12, -20);
20319     }    
20320 });/*
20321  * Based on:
20322  * Ext JS Library 1.1.1
20323  * Copyright(c) 2006-2007, Ext JS, LLC.
20324  *
20325  * Originally Released Under LGPL - original licence link has changed is not relivant.
20326  *
20327  * Fork - LGPL
20328  * <script type="text/javascript">
20329  */
20330
20331
20332 /**
20333  * @class Roo.dd.DropTarget
20334  * @extends Roo.dd.DDTarget
20335  * A simple class that provides the basic implementation needed to make any element a drop target that can have
20336  * draggable items dropped onto it.  The drop has no effect until an implementation of notifyDrop is provided.
20337  * @constructor
20338  * @param {String/HTMLElement/Element} el The container element
20339  * @param {Object} config
20340  */
20341 Roo.dd.DropTarget = function(el, config){
20342     this.el = Roo.get(el);
20343     
20344     var listeners = false; ;
20345     if (config && config.listeners) {
20346         listeners= config.listeners;
20347         delete config.listeners;
20348     }
20349     Roo.apply(this, config);
20350     
20351     if(this.containerScroll){
20352         Roo.dd.ScrollManager.register(this.el);
20353     }
20354     this.addEvents( {
20355          /**
20356          * @scope Roo.dd.DropTarget
20357          */
20358          
20359          /**
20360          * @event enter
20361          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source is now over the
20362          * target.  This default implementation adds the CSS class specified by overClass (if any) to the drop element
20363          * and returns the dropAllowed config value.  This method should be overridden if drop validation is required.
20364          * 
20365          * IMPORTANT : it should set this.overClass and this.dropAllowed
20366          * 
20367          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
20368          * @param {Event} e The event
20369          * @param {Object} data An object containing arbitrary data supplied by the drag source
20370          */
20371         "enter" : true,
20372         
20373          /**
20374          * @event over
20375          * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the target.
20376          * This method will be called on every mouse movement while the drag source is over the drop target.
20377          * This default implementation simply returns the dropAllowed config value.
20378          * 
20379          * IMPORTANT : it should set this.dropAllowed
20380          * 
20381          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
20382          * @param {Event} e The event
20383          * @param {Object} data An object containing arbitrary data supplied by the drag source
20384          
20385          */
20386         "over" : true,
20387         /**
20388          * @event out
20389          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source has been dragged
20390          * out of the target without dropping.  This default implementation simply removes the CSS class specified by
20391          * overClass (if any) from the drop element.
20392          * 
20393          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
20394          * @param {Event} e The event
20395          * @param {Object} data An object containing arbitrary data supplied by the drag source
20396          */
20397          "out" : true,
20398          
20399         /**
20400          * @event drop
20401          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the dragged item has
20402          * been dropped on it.  This method has no default implementation and returns false, so you must provide an
20403          * implementation that does something to process the drop event and returns true so that the drag source's
20404          * repair action does not run.
20405          * 
20406          * IMPORTANT : it should set this.success
20407          * 
20408          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
20409          * @param {Event} e The event
20410          * @param {Object} data An object containing arbitrary data supplied by the drag source
20411         */
20412          "drop" : true
20413     });
20414             
20415      
20416     Roo.dd.DropTarget.superclass.constructor.call(  this, 
20417         this.el.dom, 
20418         this.ddGroup || this.group,
20419         {
20420             isTarget: true,
20421             listeners : listeners || {} 
20422            
20423         
20424         }
20425     );
20426
20427 };
20428
20429 Roo.extend(Roo.dd.DropTarget, Roo.dd.DDTarget, {
20430     /**
20431      * @cfg {String} overClass
20432      * The CSS class applied to the drop target element while the drag source is over it (defaults to "").
20433      */
20434      /**
20435      * @cfg {String} ddGroup
20436      * The drag drop group to handle drop events for
20437      */
20438      
20439     /**
20440      * @cfg {String} dropAllowed
20441      * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
20442      */
20443     dropAllowed : "x-dd-drop-ok",
20444     /**
20445      * @cfg {String} dropNotAllowed
20446      * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
20447      */
20448     dropNotAllowed : "x-dd-drop-nodrop",
20449     /**
20450      * @cfg {boolean} success
20451      * set this after drop listener.. 
20452      */
20453     success : false,
20454     /**
20455      * @cfg {boolean|String} valid true/false or string (ok-add/ok-sub/ok/nodrop)
20456      * if the drop point is valid for over/enter..
20457      */
20458     valid : false,
20459     // private
20460     isTarget : true,
20461
20462     // private
20463     isNotifyTarget : true,
20464     
20465     /**
20466      * @hide
20467      */
20468     notifyEnter : function(dd, e, data)
20469     {
20470         this.valid = true;
20471         this.fireEvent('enter', dd, e, data);
20472         if(this.overClass){
20473             this.el.addClass(this.overClass);
20474         }
20475         return typeof(this.valid) == 'string' ? 'x-dd-drop-' + this.valid : (
20476             this.valid ? this.dropAllowed : this.dropNotAllowed
20477         );
20478     },
20479
20480     /**
20481      * @hide
20482      */
20483     notifyOver : function(dd, e, data)
20484     {
20485         this.valid = true;
20486         this.fireEvent('over', dd, e, data);
20487         return typeof(this.valid) == 'string' ? 'x-dd-drop-' + this.valid : (
20488             this.valid ? this.dropAllowed : this.dropNotAllowed
20489         );
20490     },
20491
20492     /**
20493      * @hide
20494      */
20495     notifyOut : function(dd, e, data)
20496     {
20497         this.fireEvent('out', dd, e, data);
20498         if(this.overClass){
20499             this.el.removeClass(this.overClass);
20500         }
20501     },
20502
20503     /**
20504      * @hide
20505      */
20506     notifyDrop : function(dd, e, data)
20507     {
20508         this.success = false;
20509         this.fireEvent('drop', dd, e, data);
20510         return this.success;
20511     }
20512 });/*
20513  * Based on:
20514  * Ext JS Library 1.1.1
20515  * Copyright(c) 2006-2007, Ext JS, LLC.
20516  *
20517  * Originally Released Under LGPL - original licence link has changed is not relivant.
20518  *
20519  * Fork - LGPL
20520  * <script type="text/javascript">
20521  */
20522
20523
20524 /**
20525  * @class Roo.dd.DragZone
20526  * @extends Roo.dd.DragSource
20527  * This class provides a container DD instance that proxies for multiple child node sources.<br />
20528  * By default, this class requires that draggable child nodes are registered with {@link Roo.dd.Registry}.
20529  * @constructor
20530  * @param {String/HTMLElement/Element} el The container element
20531  * @param {Object} config
20532  */
20533 Roo.dd.DragZone = function(el, config){
20534     Roo.dd.DragZone.superclass.constructor.call(this, el, config);
20535     if(this.containerScroll){
20536         Roo.dd.ScrollManager.register(this.el);
20537     }
20538 };
20539
20540 Roo.extend(Roo.dd.DragZone, Roo.dd.DragSource, {
20541     /**
20542      * @cfg {Boolean} containerScroll True to register this container with the Scrollmanager
20543      * for auto scrolling during drag operations.
20544      */
20545     /**
20546      * @cfg {String} hlColor The color to use when visually highlighting the drag source in the afterRepair
20547      * method after a failed drop (defaults to "c3daf9" - light blue)
20548      */
20549
20550     /**
20551      * Called when a mousedown occurs in this container. Looks in {@link Roo.dd.Registry}
20552      * for a valid target to drag based on the mouse down. Override this method
20553      * to provide your own lookup logic (e.g. finding a child by class name). Make sure your returned
20554      * object has a "ddel" attribute (with an HTML Element) for other functions to work.
20555      * @param {EventObject} e The mouse down event
20556      * @return {Object} The dragData
20557      */
20558     getDragData : function(e){
20559         return Roo.dd.Registry.getHandleFromEvent(e);
20560     },
20561     
20562     /**
20563      * Called once drag threshold has been reached to initialize the proxy element. By default, it clones the
20564      * this.dragData.ddel
20565      * @param {Number} x The x position of the click on the dragged object
20566      * @param {Number} y The y position of the click on the dragged object
20567      * @return {Boolean} true to continue the drag, false to cancel
20568      */
20569     onInitDrag : function(x, y){
20570         this.proxy.update(this.dragData.ddel.cloneNode(true));
20571         this.onStartDrag(x, y);
20572         return true;
20573     },
20574     
20575     /**
20576      * Called after a repair of an invalid drop. By default, highlights this.dragData.ddel 
20577      */
20578     afterRepair : function(){
20579         if(Roo.enableFx){
20580             Roo.Element.fly(this.dragData.ddel).highlight(this.hlColor || "c3daf9");
20581         }
20582         this.dragging = false;
20583     },
20584
20585     /**
20586      * Called before a repair of an invalid drop to get the XY to animate to. By default returns
20587      * the XY of this.dragData.ddel
20588      * @param {EventObject} e The mouse up event
20589      * @return {Array} The xy location (e.g. [100, 200])
20590      */
20591     getRepairXY : function(e){
20592         return Roo.Element.fly(this.dragData.ddel).getXY();  
20593     }
20594 });/*
20595  * Based on:
20596  * Ext JS Library 1.1.1
20597  * Copyright(c) 2006-2007, Ext JS, LLC.
20598  *
20599  * Originally Released Under LGPL - original licence link has changed is not relivant.
20600  *
20601  * Fork - LGPL
20602  * <script type="text/javascript">
20603  */
20604 /**
20605  * @class Roo.dd.DropZone
20606  * @extends Roo.dd.DropTarget
20607  * This class provides a container DD instance that proxies for multiple child node targets.<br />
20608  * By default, this class requires that child nodes accepting drop are registered with {@link Roo.dd.Registry}.
20609  * @constructor
20610  * @param {String/HTMLElement/Element} el The container element
20611  * @param {Object} config
20612  */
20613 Roo.dd.DropZone = function(el, config){
20614     Roo.dd.DropZone.superclass.constructor.call(this, el, config);
20615 };
20616
20617 Roo.extend(Roo.dd.DropZone, Roo.dd.DropTarget, {
20618     /**
20619      * Returns a custom data object associated with the DOM node that is the target of the event.  By default
20620      * this looks up the event target in the {@link Roo.dd.Registry}, although you can override this method to
20621      * provide your own custom lookup.
20622      * @param {Event} e The event
20623      * @return {Object} data The custom data
20624      */
20625     getTargetFromEvent : function(e){
20626         return Roo.dd.Registry.getTargetFromEvent(e);
20627     },
20628
20629     /**
20630      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has entered a drop node
20631      * that it has registered.  This method has no default implementation and should be overridden to provide
20632      * node-specific processing if necessary.
20633      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from 
20634      * {@link #getTargetFromEvent} for this node)
20635      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
20636      * @param {Event} e The event
20637      * @param {Object} data An object containing arbitrary data supplied by the drag source
20638      */
20639     onNodeEnter : function(n, dd, e, data){
20640         
20641     },
20642
20643     /**
20644      * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is over a drop node
20645      * that it has registered.  The default implementation returns this.dropNotAllowed, so it should be
20646      * overridden to provide the proper feedback.
20647      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
20648      * {@link #getTargetFromEvent} for this node)
20649      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
20650      * @param {Event} e The event
20651      * @param {Object} data An object containing arbitrary data supplied by the drag source
20652      * @return {String} status The CSS class that communicates the drop status back to the source so that the
20653      * underlying {@link Roo.dd.StatusProxy} can be updated
20654      */
20655     onNodeOver : function(n, dd, e, data){
20656         return this.dropAllowed;
20657     },
20658
20659     /**
20660      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dragged out of
20661      * the drop node without dropping.  This method has no default implementation and should be overridden to provide
20662      * node-specific processing if necessary.
20663      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
20664      * {@link #getTargetFromEvent} for this node)
20665      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
20666      * @param {Event} e The event
20667      * @param {Object} data An object containing arbitrary data supplied by the drag source
20668      */
20669     onNodeOut : function(n, dd, e, data){
20670         
20671     },
20672
20673     /**
20674      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped onto
20675      * the drop node.  The default implementation returns false, so it should be overridden to provide the
20676      * appropriate processing of the drop event and return true so that the drag source's repair action does not run.
20677      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
20678      * {@link #getTargetFromEvent} for this node)
20679      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
20680      * @param {Event} e The event
20681      * @param {Object} data An object containing arbitrary data supplied by the drag source
20682      * @return {Boolean} True if the drop was valid, else false
20683      */
20684     onNodeDrop : function(n, dd, e, data){
20685         return false;
20686     },
20687
20688     /**
20689      * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is being dragged over it,
20690      * but not over any of its registered drop nodes.  The default implementation returns this.dropNotAllowed, so
20691      * it should be overridden to provide the proper feedback if necessary.
20692      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
20693      * @param {Event} e The event
20694      * @param {Object} data An object containing arbitrary data supplied by the drag source
20695      * @return {String} status The CSS class that communicates the drop status back to the source so that the
20696      * underlying {@link Roo.dd.StatusProxy} can be updated
20697      */
20698     onContainerOver : function(dd, e, data){
20699         return this.dropNotAllowed;
20700     },
20701
20702     /**
20703      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped on it,
20704      * but not on any of its registered drop nodes.  The default implementation returns false, so it should be
20705      * overridden to provide the appropriate processing of the drop event if you need the drop zone itself to
20706      * be able to accept drops.  It should return true when valid so that the drag source's repair action does not run.
20707      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
20708      * @param {Event} e The event
20709      * @param {Object} data An object containing arbitrary data supplied by the drag source
20710      * @return {Boolean} True if the drop was valid, else false
20711      */
20712     onContainerDrop : function(dd, e, data){
20713         return false;
20714     },
20715
20716     /**
20717      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source is now over
20718      * the zone.  The default implementation returns this.dropNotAllowed and expects that only registered drop
20719      * nodes can process drag drop operations, so if you need the drop zone itself to be able to process drops
20720      * you should override this method and provide a custom implementation.
20721      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
20722      * @param {Event} e The event
20723      * @param {Object} data An object containing arbitrary data supplied by the drag source
20724      * @return {String} status The CSS class that communicates the drop status back to the source so that the
20725      * underlying {@link Roo.dd.StatusProxy} can be updated
20726      */
20727     notifyEnter : function(dd, e, data){
20728         return this.dropNotAllowed;
20729     },
20730
20731     /**
20732      * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the drop zone.
20733      * This method will be called on every mouse movement while the drag source is over the drop zone.
20734      * It will call {@link #onNodeOver} while the drag source is over a registered node, and will also automatically
20735      * delegate to the appropriate node-specific methods as necessary when the drag source enters and exits
20736      * registered nodes ({@link #onNodeEnter}, {@link #onNodeOut}). If the drag source is not currently over a
20737      * registered node, it will call {@link #onContainerOver}.
20738      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
20739      * @param {Event} e The event
20740      * @param {Object} data An object containing arbitrary data supplied by the drag source
20741      * @return {String} status The CSS class that communicates the drop status back to the source so that the
20742      * underlying {@link Roo.dd.StatusProxy} can be updated
20743      */
20744     notifyOver : function(dd, e, data){
20745         var n = this.getTargetFromEvent(e);
20746         if(!n){ // not over valid drop target
20747             if(this.lastOverNode){
20748                 this.onNodeOut(this.lastOverNode, dd, e, data);
20749                 this.lastOverNode = null;
20750             }
20751             return this.onContainerOver(dd, e, data);
20752         }
20753         if(this.lastOverNode != n){
20754             if(this.lastOverNode){
20755                 this.onNodeOut(this.lastOverNode, dd, e, data);
20756             }
20757             this.onNodeEnter(n, dd, e, data);
20758             this.lastOverNode = n;
20759         }
20760         return this.onNodeOver(n, dd, e, data);
20761     },
20762
20763     /**
20764      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source has been dragged
20765      * out of the zone without dropping.  If the drag source is currently over a registered node, the notification
20766      * will be delegated to {@link #onNodeOut} for node-specific handling, otherwise it will be ignored.
20767      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
20768      * @param {Event} e The event
20769      * @param {Object} data An object containing arbitrary data supplied by the drag zone
20770      */
20771     notifyOut : function(dd, e, data){
20772         if(this.lastOverNode){
20773             this.onNodeOut(this.lastOverNode, dd, e, data);
20774             this.lastOverNode = null;
20775         }
20776     },
20777
20778     /**
20779      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the dragged item has
20780      * been dropped on it.  The drag zone will look up the target node based on the event passed in, and if there
20781      * is a node registered for that event, it will delegate to {@link #onNodeDrop} for node-specific handling,
20782      * otherwise it will call {@link #onContainerDrop}.
20783      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
20784      * @param {Event} e The event
20785      * @param {Object} data An object containing arbitrary data supplied by the drag source
20786      * @return {Boolean} True if the drop was valid, else false
20787      */
20788     notifyDrop : function(dd, e, data){
20789         if(this.lastOverNode){
20790             this.onNodeOut(this.lastOverNode, dd, e, data);
20791             this.lastOverNode = null;
20792         }
20793         var n = this.getTargetFromEvent(e);
20794         return n ?
20795             this.onNodeDrop(n, dd, e, data) :
20796             this.onContainerDrop(dd, e, data);
20797     },
20798
20799     // private
20800     triggerCacheRefresh : function(){
20801         Roo.dd.DDM.refreshCache(this.groups);
20802     }  
20803 });/*
20804  * Based on:
20805  * Ext JS Library 1.1.1
20806  * Copyright(c) 2006-2007, Ext JS, LLC.
20807  *
20808  * Originally Released Under LGPL - original licence link has changed is not relivant.
20809  *
20810  * Fork - LGPL
20811  * <script type="text/javascript">
20812  */
20813
20814
20815 /**
20816  * @class Roo.data.SortTypes
20817  * @singleton
20818  * Defines the default sorting (casting?) comparison functions used when sorting data.
20819  */
20820 Roo.data.SortTypes = {
20821     /**
20822      * Default sort that does nothing
20823      * @param {Mixed} s The value being converted
20824      * @return {Mixed} The comparison value
20825      */
20826     none : function(s){
20827         return s;
20828     },
20829     
20830     /**
20831      * The regular expression used to strip tags
20832      * @type {RegExp}
20833      * @property
20834      */
20835     stripTagsRE : /<\/?[^>]+>/gi,
20836     
20837     /**
20838      * Strips all HTML tags to sort on text only
20839      * @param {Mixed} s The value being converted
20840      * @return {String} The comparison value
20841      */
20842     asText : function(s){
20843         return String(s).replace(this.stripTagsRE, "");
20844     },
20845     
20846     /**
20847      * Strips all HTML tags to sort on text only - Case insensitive
20848      * @param {Mixed} s The value being converted
20849      * @return {String} The comparison value
20850      */
20851     asUCText : function(s){
20852         return String(s).toUpperCase().replace(this.stripTagsRE, "");
20853     },
20854     
20855     /**
20856      * Case insensitive string
20857      * @param {Mixed} s The value being converted
20858      * @return {String} The comparison value
20859      */
20860     asUCString : function(s) {
20861         return String(s).toUpperCase();
20862     },
20863     
20864     /**
20865      * Date sorting
20866      * @param {Mixed} s The value being converted
20867      * @return {Number} The comparison value
20868      */
20869     asDate : function(s) {
20870         if(!s){
20871             return 0;
20872         }
20873         if(s instanceof Date){
20874             return s.getTime();
20875         }
20876         return Date.parse(String(s));
20877     },
20878     
20879     /**
20880      * Float sorting
20881      * @param {Mixed} s The value being converted
20882      * @return {Float} The comparison value
20883      */
20884     asFloat : function(s) {
20885         var val = parseFloat(String(s).replace(/,/g, ""));
20886         if(isNaN(val)) val = 0;
20887         return val;
20888     },
20889     
20890     /**
20891      * Integer sorting
20892      * @param {Mixed} s The value being converted
20893      * @return {Number} The comparison value
20894      */
20895     asInt : function(s) {
20896         var val = parseInt(String(s).replace(/,/g, ""));
20897         if(isNaN(val)) val = 0;
20898         return val;
20899     }
20900 };/*
20901  * Based on:
20902  * Ext JS Library 1.1.1
20903  * Copyright(c) 2006-2007, Ext JS, LLC.
20904  *
20905  * Originally Released Under LGPL - original licence link has changed is not relivant.
20906  *
20907  * Fork - LGPL
20908  * <script type="text/javascript">
20909  */
20910
20911 /**
20912 * @class Roo.data.Record
20913  * Instances of this class encapsulate both record <em>definition</em> information, and record
20914  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
20915  * to access Records cached in an {@link Roo.data.Store} object.<br>
20916  * <p>
20917  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
20918  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
20919  * objects.<br>
20920  * <p>
20921  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
20922  * @constructor
20923  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
20924  * {@link #create}. The parameters are the same.
20925  * @param {Array} data An associative Array of data values keyed by the field name.
20926  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
20927  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
20928  * not specified an integer id is generated.
20929  */
20930 Roo.data.Record = function(data, id){
20931     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
20932     this.data = data;
20933 };
20934
20935 /**
20936  * Generate a constructor for a specific record layout.
20937  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
20938  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
20939  * Each field definition object may contain the following properties: <ul>
20940  * <li><b>name</b> : String<p style="margin-left:1em">The name by which the field is referenced within the Record. This is referenced by,
20941  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
20942  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
20943  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
20944  * is being used, then this is a string containing the javascript expression to reference the data relative to 
20945  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
20946  * to the data item relative to the record element. If the mapping expression is the same as the field name,
20947  * this may be omitted.</p></li>
20948  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
20949  * <ul><li>auto (Default, implies no conversion)</li>
20950  * <li>string</li>
20951  * <li>int</li>
20952  * <li>float</li>
20953  * <li>boolean</li>
20954  * <li>date</li></ul></p></li>
20955  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
20956  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
20957  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
20958  * by the Reader into an object that will be stored in the Record. It is passed the
20959  * following parameters:<ul>
20960  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
20961  * </ul></p></li>
20962  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
20963  * </ul>
20964  * <br>usage:<br><pre><code>
20965 var TopicRecord = Roo.data.Record.create(
20966     {name: 'title', mapping: 'topic_title'},
20967     {name: 'author', mapping: 'username'},
20968     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
20969     {name: 'lastPost', mapping: 'post_time', type: 'date'},
20970     {name: 'lastPoster', mapping: 'user2'},
20971     {name: 'excerpt', mapping: 'post_text'}
20972 );
20973
20974 var myNewRecord = new TopicRecord({
20975     title: 'Do my job please',
20976     author: 'noobie',
20977     totalPosts: 1,
20978     lastPost: new Date(),
20979     lastPoster: 'Animal',
20980     excerpt: 'No way dude!'
20981 });
20982 myStore.add(myNewRecord);
20983 </code></pre>
20984  * @method create
20985  * @static
20986  */
20987 Roo.data.Record.create = function(o){
20988     var f = function(){
20989         f.superclass.constructor.apply(this, arguments);
20990     };
20991     Roo.extend(f, Roo.data.Record);
20992     var p = f.prototype;
20993     p.fields = new Roo.util.MixedCollection(false, function(field){
20994         return field.name;
20995     });
20996     for(var i = 0, len = o.length; i < len; i++){
20997         p.fields.add(new Roo.data.Field(o[i]));
20998     }
20999     f.getField = function(name){
21000         return p.fields.get(name);  
21001     };
21002     return f;
21003 };
21004
21005 Roo.data.Record.AUTO_ID = 1000;
21006 Roo.data.Record.EDIT = 'edit';
21007 Roo.data.Record.REJECT = 'reject';
21008 Roo.data.Record.COMMIT = 'commit';
21009
21010 Roo.data.Record.prototype = {
21011     /**
21012      * Readonly flag - true if this record has been modified.
21013      * @type Boolean
21014      */
21015     dirty : false,
21016     editing : false,
21017     error: null,
21018     modified: null,
21019
21020     // private
21021     join : function(store){
21022         this.store = store;
21023     },
21024
21025     /**
21026      * Set the named field to the specified value.
21027      * @param {String} name The name of the field to set.
21028      * @param {Object} value The value to set the field to.
21029      */
21030     set : function(name, value){
21031         if(this.data[name] == value){
21032             return;
21033         }
21034         this.dirty = true;
21035         if(!this.modified){
21036             this.modified = {};
21037         }
21038         if(typeof this.modified[name] == 'undefined'){
21039             this.modified[name] = this.data[name];
21040         }
21041         this.data[name] = value;
21042         if(!this.editing && this.store){
21043             this.store.afterEdit(this);
21044         }       
21045     },
21046
21047     /**
21048      * Get the value of the named field.
21049      * @param {String} name The name of the field to get the value of.
21050      * @return {Object} The value of the field.
21051      */
21052     get : function(name){
21053         return this.data[name]; 
21054     },
21055
21056     // private
21057     beginEdit : function(){
21058         this.editing = true;
21059         this.modified = {}; 
21060     },
21061
21062     // private
21063     cancelEdit : function(){
21064         this.editing = false;
21065         delete this.modified;
21066     },
21067
21068     // private
21069     endEdit : function(){
21070         this.editing = false;
21071         if(this.dirty && this.store){
21072             this.store.afterEdit(this);
21073         }
21074     },
21075
21076     /**
21077      * Usually called by the {@link Roo.data.Store} which owns the Record.
21078      * Rejects all changes made to the Record since either creation, or the last commit operation.
21079      * Modified fields are reverted to their original values.
21080      * <p>
21081      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
21082      * of reject operations.
21083      */
21084     reject : function(){
21085         var m = this.modified;
21086         for(var n in m){
21087             if(typeof m[n] != "function"){
21088                 this.data[n] = m[n];
21089             }
21090         }
21091         this.dirty = false;
21092         delete this.modified;
21093         this.editing = false;
21094         if(this.store){
21095             this.store.afterReject(this);
21096         }
21097     },
21098
21099     /**
21100      * Usually called by the {@link Roo.data.Store} which owns the Record.
21101      * Commits all changes made to the Record since either creation, or the last commit operation.
21102      * <p>
21103      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
21104      * of commit operations.
21105      */
21106     commit : function(){
21107         this.dirty = false;
21108         delete this.modified;
21109         this.editing = false;
21110         if(this.store){
21111             this.store.afterCommit(this);
21112         }
21113     },
21114
21115     // private
21116     hasError : function(){
21117         return this.error != null;
21118     },
21119
21120     // private
21121     clearError : function(){
21122         this.error = null;
21123     },
21124
21125     /**
21126      * Creates a copy of this record.
21127      * @param {String} id (optional) A new record id if you don't want to use this record's id
21128      * @return {Record}
21129      */
21130     copy : function(newId) {
21131         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
21132     }
21133 };/*
21134  * Based on:
21135  * Ext JS Library 1.1.1
21136  * Copyright(c) 2006-2007, Ext JS, LLC.
21137  *
21138  * Originally Released Under LGPL - original licence link has changed is not relivant.
21139  *
21140  * Fork - LGPL
21141  * <script type="text/javascript">
21142  */
21143
21144
21145
21146 /**
21147  * @class Roo.data.Store
21148  * @extends Roo.util.Observable
21149  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
21150  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
21151  * <p>
21152  * A Store object uses an implementation of {@link Roo.data.DataProxy} to access a data object unless you call loadData() directly and pass in your data. The Store object
21153  * has no knowledge of the format of the data returned by the Proxy.<br>
21154  * <p>
21155  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
21156  * instances from the data object. These records are cached and made available through accessor functions.
21157  * @constructor
21158  * Creates a new Store.
21159  * @param {Object} config A config object containing the objects needed for the Store to access data,
21160  * and read the data into Records.
21161  */
21162 Roo.data.Store = function(config){
21163     this.data = new Roo.util.MixedCollection(false);
21164     this.data.getKey = function(o){
21165         return o.id;
21166     };
21167     this.baseParams = {};
21168     // private
21169     this.paramNames = {
21170         "start" : "start",
21171         "limit" : "limit",
21172         "sort" : "sort",
21173         "dir" : "dir",
21174         "multisort" : "_multisort"
21175     };
21176
21177     if(config && config.data){
21178         this.inlineData = config.data;
21179         delete config.data;
21180     }
21181
21182     Roo.apply(this, config);
21183     
21184     if(this.reader){ // reader passed
21185         this.reader = Roo.factory(this.reader, Roo.data);
21186         this.reader.xmodule = this.xmodule || false;
21187         if(!this.recordType){
21188             this.recordType = this.reader.recordType;
21189         }
21190         if(this.reader.onMetaChange){
21191             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
21192         }
21193     }
21194
21195     if(this.recordType){
21196         this.fields = this.recordType.prototype.fields;
21197     }
21198     this.modified = [];
21199
21200     this.addEvents({
21201         /**
21202          * @event datachanged
21203          * Fires when the data cache has changed, and a widget which is using this Store
21204          * as a Record cache should refresh its view.
21205          * @param {Store} this
21206          */
21207         datachanged : true,
21208         /**
21209          * @event metachange
21210          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
21211          * @param {Store} this
21212          * @param {Object} meta The JSON metadata
21213          */
21214         metachange : true,
21215         /**
21216          * @event add
21217          * Fires when Records have been added to the Store
21218          * @param {Store} this
21219          * @param {Roo.data.Record[]} records The array of Records added
21220          * @param {Number} index The index at which the record(s) were added
21221          */
21222         add : true,
21223         /**
21224          * @event remove
21225          * Fires when a Record has been removed from the Store
21226          * @param {Store} this
21227          * @param {Roo.data.Record} record The Record that was removed
21228          * @param {Number} index The index at which the record was removed
21229          */
21230         remove : true,
21231         /**
21232          * @event update
21233          * Fires when a Record has been updated
21234          * @param {Store} this
21235          * @param {Roo.data.Record} record The Record that was updated
21236          * @param {String} operation The update operation being performed.  Value may be one of:
21237          * <pre><code>
21238  Roo.data.Record.EDIT
21239  Roo.data.Record.REJECT
21240  Roo.data.Record.COMMIT
21241          * </code></pre>
21242          */
21243         update : true,
21244         /**
21245          * @event clear
21246          * Fires when the data cache has been cleared.
21247          * @param {Store} this
21248          */
21249         clear : true,
21250         /**
21251          * @event beforeload
21252          * Fires before a request is made for a new data object.  If the beforeload handler returns false
21253          * the load action will be canceled.
21254          * @param {Store} this
21255          * @param {Object} options The loading options that were specified (see {@link #load} for details)
21256          */
21257         beforeload : true,
21258         /**
21259          * @event beforeloadadd
21260          * Fires after a new set of Records has been loaded.
21261          * @param {Store} this
21262          * @param {Roo.data.Record[]} records The Records that were loaded
21263          * @param {Object} options The loading options that were specified (see {@link #load} for details)
21264          */
21265         beforeloadadd : true,
21266         /**
21267          * @event load
21268          * Fires after a new set of Records has been loaded, before they are added to the store.
21269          * @param {Store} this
21270          * @param {Roo.data.Record[]} records The Records that were loaded
21271          * @param {Object} options The loading options that were specified (see {@link #load} for details)
21272          * @params {Object} return from reader
21273          */
21274         load : true,
21275         /**
21276          * @event loadexception
21277          * Fires if an exception occurs in the Proxy during loading.
21278          * Called with the signature of the Proxy's "loadexception" event.
21279          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
21280          * 
21281          * @param {Proxy} 
21282          * @param {Object} return from JsonData.reader() - success, totalRecords, records
21283          * @param {Object} load options 
21284          * @param {Object} jsonData from your request (normally this contains the Exception)
21285          */
21286         loadexception : true
21287     });
21288     
21289     if(this.proxy){
21290         this.proxy = Roo.factory(this.proxy, Roo.data);
21291         this.proxy.xmodule = this.xmodule || false;
21292         this.relayEvents(this.proxy,  ["loadexception"]);
21293     }
21294     this.sortToggle = {};
21295     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
21296
21297     Roo.data.Store.superclass.constructor.call(this);
21298
21299     if(this.inlineData){
21300         this.loadData(this.inlineData);
21301         delete this.inlineData;
21302     }
21303 };
21304
21305 Roo.extend(Roo.data.Store, Roo.util.Observable, {
21306      /**
21307     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
21308     * without a remote query - used by combo/forms at present.
21309     */
21310     
21311     /**
21312     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
21313     */
21314     /**
21315     * @cfg {Array} data Inline data to be loaded when the store is initialized.
21316     */
21317     /**
21318     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
21319     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
21320     */
21321     /**
21322     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
21323     * on any HTTP request
21324     */
21325     /**
21326     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
21327     */
21328     /**
21329     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
21330     */
21331     multiSort: false,
21332     /**
21333     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
21334     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
21335     */
21336     remoteSort : false,
21337
21338     /**
21339     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
21340      * loaded or when a record is removed. (defaults to false).
21341     */
21342     pruneModifiedRecords : false,
21343
21344     // private
21345     lastOptions : null,
21346
21347     /**
21348      * Add Records to the Store and fires the add event.
21349      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
21350      */
21351     add : function(records){
21352         records = [].concat(records);
21353         for(var i = 0, len = records.length; i < len; i++){
21354             records[i].join(this);
21355         }
21356         var index = this.data.length;
21357         this.data.addAll(records);
21358         this.fireEvent("add", this, records, index);
21359     },
21360
21361     /**
21362      * Remove a Record from the Store and fires the remove event.
21363      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
21364      */
21365     remove : function(record){
21366         var index = this.data.indexOf(record);
21367         this.data.removeAt(index);
21368         if(this.pruneModifiedRecords){
21369             this.modified.remove(record);
21370         }
21371         this.fireEvent("remove", this, record, index);
21372     },
21373
21374     /**
21375      * Remove all Records from the Store and fires the clear event.
21376      */
21377     removeAll : function(){
21378         this.data.clear();
21379         if(this.pruneModifiedRecords){
21380             this.modified = [];
21381         }
21382         this.fireEvent("clear", this);
21383     },
21384
21385     /**
21386      * Inserts Records to the Store at the given index and fires the add event.
21387      * @param {Number} index The start index at which to insert the passed Records.
21388      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
21389      */
21390     insert : function(index, records){
21391         records = [].concat(records);
21392         for(var i = 0, len = records.length; i < len; i++){
21393             this.data.insert(index, records[i]);
21394             records[i].join(this);
21395         }
21396         this.fireEvent("add", this, records, index);
21397     },
21398
21399     /**
21400      * Get the index within the cache of the passed Record.
21401      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
21402      * @return {Number} The index of the passed Record. Returns -1 if not found.
21403      */
21404     indexOf : function(record){
21405         return this.data.indexOf(record);
21406     },
21407
21408     /**
21409      * Get the index within the cache of the Record with the passed id.
21410      * @param {String} id The id of the Record to find.
21411      * @return {Number} The index of the Record. Returns -1 if not found.
21412      */
21413     indexOfId : function(id){
21414         return this.data.indexOfKey(id);
21415     },
21416
21417     /**
21418      * Get the Record with the specified id.
21419      * @param {String} id The id of the Record to find.
21420      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
21421      */
21422     getById : function(id){
21423         return this.data.key(id);
21424     },
21425
21426     /**
21427      * Get the Record at the specified index.
21428      * @param {Number} index The index of the Record to find.
21429      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
21430      */
21431     getAt : function(index){
21432         return this.data.itemAt(index);
21433     },
21434
21435     /**
21436      * Returns a range of Records between specified indices.
21437      * @param {Number} startIndex (optional) The starting index (defaults to 0)
21438      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
21439      * @return {Roo.data.Record[]} An array of Records
21440      */
21441     getRange : function(start, end){
21442         return this.data.getRange(start, end);
21443     },
21444
21445     // private
21446     storeOptions : function(o){
21447         o = Roo.apply({}, o);
21448         delete o.callback;
21449         delete o.scope;
21450         this.lastOptions = o;
21451     },
21452
21453     /**
21454      * Loads the Record cache from the configured Proxy using the configured Reader.
21455      * <p>
21456      * If using remote paging, then the first load call must specify the <em>start</em>
21457      * and <em>limit</em> properties in the options.params property to establish the initial
21458      * position within the dataset, and the number of Records to cache on each read from the Proxy.
21459      * <p>
21460      * <strong>It is important to note that for remote data sources, loading is asynchronous,
21461      * and this call will return before the new data has been loaded. Perform any post-processing
21462      * in a callback function, or in a "load" event handler.</strong>
21463      * <p>
21464      * @param {Object} options An object containing properties which control loading options:<ul>
21465      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
21466      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
21467      * passed the following arguments:<ul>
21468      * <li>r : Roo.data.Record[]</li>
21469      * <li>options: Options object from the load call</li>
21470      * <li>success: Boolean success indicator</li></ul></li>
21471      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
21472      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
21473      * </ul>
21474      */
21475     load : function(options){
21476         options = options || {};
21477         if(this.fireEvent("beforeload", this, options) !== false){
21478             this.storeOptions(options);
21479             var p = Roo.apply(options.params || {}, this.baseParams);
21480             // if meta was not loaded from remote source.. try requesting it.
21481             if (!this.reader.metaFromRemote) {
21482                 p._requestMeta = 1;
21483             }
21484             if(this.sortInfo && this.remoteSort){
21485                 var pn = this.paramNames;
21486                 p[pn["sort"]] = this.sortInfo.field;
21487                 p[pn["dir"]] = this.sortInfo.direction;
21488             }
21489             if (this.multiSort) {
21490                 var pn = this.paramNames;
21491                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
21492             }
21493             
21494             this.proxy.load(p, this.reader, this.loadRecords, this, options);
21495         }
21496     },
21497
21498     /**
21499      * Reloads the Record cache from the configured Proxy using the configured Reader and
21500      * the options from the last load operation performed.
21501      * @param {Object} options (optional) An object containing properties which may override the options
21502      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
21503      * the most recently used options are reused).
21504      */
21505     reload : function(options){
21506         this.load(Roo.applyIf(options||{}, this.lastOptions));
21507     },
21508
21509     // private
21510     // Called as a callback by the Reader during a load operation.
21511     loadRecords : function(o, options, success){
21512         if(!o || success === false){
21513             if(success !== false){
21514                 this.fireEvent("load", this, [], options, o);
21515             }
21516             if(options.callback){
21517                 options.callback.call(options.scope || this, [], options, false);
21518             }
21519             return;
21520         }
21521         // if data returned failure - throw an exception.
21522         if (o.success === false) {
21523             // show a message if no listener is registered.
21524             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
21525                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
21526             }
21527             // loadmask wil be hooked into this..
21528             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
21529             return;
21530         }
21531         var r = o.records, t = o.totalRecords || r.length;
21532         
21533         this.fireEvent("beforeloadadd", this, r, options, o);
21534         
21535         if(!options || options.add !== true){
21536             if(this.pruneModifiedRecords){
21537                 this.modified = [];
21538             }
21539             for(var i = 0, len = r.length; i < len; i++){
21540                 r[i].join(this);
21541             }
21542             if(this.snapshot){
21543                 this.data = this.snapshot;
21544                 delete this.snapshot;
21545             }
21546             this.data.clear();
21547             this.data.addAll(r);
21548             this.totalLength = t;
21549             this.applySort();
21550             this.fireEvent("datachanged", this);
21551         }else{
21552             this.totalLength = Math.max(t, this.data.length+r.length);
21553             this.add(r);
21554         }
21555         this.fireEvent("load", this, r, options, o);
21556         if(options.callback){
21557             options.callback.call(options.scope || this, r, options, true);
21558         }
21559     },
21560
21561
21562     /**
21563      * Loads data from a passed data block. A Reader which understands the format of the data
21564      * must have been configured in the constructor.
21565      * @param {Object} data The data block from which to read the Records.  The format of the data expected
21566      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
21567      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
21568      */
21569     loadData : function(o, append){
21570         var r = this.reader.readRecords(o);
21571         this.loadRecords(r, {add: append}, true);
21572     },
21573
21574     /**
21575      * Gets the number of cached records.
21576      * <p>
21577      * <em>If using paging, this may not be the total size of the dataset. If the data object
21578      * used by the Reader contains the dataset size, then the getTotalCount() function returns
21579      * the data set size</em>
21580      */
21581     getCount : function(){
21582         return this.data.length || 0;
21583     },
21584
21585     /**
21586      * Gets the total number of records in the dataset as returned by the server.
21587      * <p>
21588      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
21589      * the dataset size</em>
21590      */
21591     getTotalCount : function(){
21592         return this.totalLength || 0;
21593     },
21594
21595     /**
21596      * Returns the sort state of the Store as an object with two properties:
21597      * <pre><code>
21598  field {String} The name of the field by which the Records are sorted
21599  direction {String} The sort order, "ASC" or "DESC"
21600      * </code></pre>
21601      */
21602     getSortState : function(){
21603         return this.sortInfo;
21604     },
21605
21606     // private
21607     applySort : function(){
21608         if(this.sortInfo && !this.remoteSort){
21609             var s = this.sortInfo, f = s.field;
21610             var st = this.fields.get(f).sortType;
21611             var fn = function(r1, r2){
21612                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
21613                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
21614             };
21615             this.data.sort(s.direction, fn);
21616             if(this.snapshot && this.snapshot != this.data){
21617                 this.snapshot.sort(s.direction, fn);
21618             }
21619         }
21620     },
21621
21622     /**
21623      * Sets the default sort column and order to be used by the next load operation.
21624      * @param {String} fieldName The name of the field to sort by.
21625      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
21626      */
21627     setDefaultSort : function(field, dir){
21628         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
21629     },
21630
21631     /**
21632      * Sort the Records.
21633      * If remote sorting is used, the sort is performed on the server, and the cache is
21634      * reloaded. If local sorting is used, the cache is sorted internally.
21635      * @param {String} fieldName The name of the field to sort by.
21636      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
21637      */
21638     sort : function(fieldName, dir){
21639         var f = this.fields.get(fieldName);
21640         if(!dir){
21641             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
21642             
21643             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
21644                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
21645             }else{
21646                 dir = f.sortDir;
21647             }
21648         }
21649         this.sortToggle[f.name] = dir;
21650         this.sortInfo = {field: f.name, direction: dir};
21651         if(!this.remoteSort){
21652             this.applySort();
21653             this.fireEvent("datachanged", this);
21654         }else{
21655             this.load(this.lastOptions);
21656         }
21657     },
21658
21659     /**
21660      * Calls the specified function for each of the Records in the cache.
21661      * @param {Function} fn The function to call. The Record is passed as the first parameter.
21662      * Returning <em>false</em> aborts and exits the iteration.
21663      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
21664      */
21665     each : function(fn, scope){
21666         this.data.each(fn, scope);
21667     },
21668
21669     /**
21670      * Gets all records modified since the last commit.  Modified records are persisted across load operations
21671      * (e.g., during paging).
21672      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
21673      */
21674     getModifiedRecords : function(){
21675         return this.modified;
21676     },
21677
21678     // private
21679     createFilterFn : function(property, value, anyMatch){
21680         if(!value.exec){ // not a regex
21681             value = String(value);
21682             if(value.length == 0){
21683                 return false;
21684             }
21685             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
21686         }
21687         return function(r){
21688             return value.test(r.data[property]);
21689         };
21690     },
21691
21692     /**
21693      * Sums the value of <i>property</i> for each record between start and end and returns the result.
21694      * @param {String} property A field on your records
21695      * @param {Number} start The record index to start at (defaults to 0)
21696      * @param {Number} end The last record index to include (defaults to length - 1)
21697      * @return {Number} The sum
21698      */
21699     sum : function(property, start, end){
21700         var rs = this.data.items, v = 0;
21701         start = start || 0;
21702         end = (end || end === 0) ? end : rs.length-1;
21703
21704         for(var i = start; i <= end; i++){
21705             v += (rs[i].data[property] || 0);
21706         }
21707         return v;
21708     },
21709
21710     /**
21711      * Filter the records by a specified property.
21712      * @param {String} field A field on your records
21713      * @param {String/RegExp} value Either a string that the field
21714      * should start with or a RegExp to test against the field
21715      * @param {Boolean} anyMatch True to match any part not just the beginning
21716      */
21717     filter : function(property, value, anyMatch){
21718         var fn = this.createFilterFn(property, value, anyMatch);
21719         return fn ? this.filterBy(fn) : this.clearFilter();
21720     },
21721
21722     /**
21723      * Filter by a function. The specified function will be called with each
21724      * record in this data source. If the function returns true the record is included,
21725      * otherwise it is filtered.
21726      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
21727      * @param {Object} scope (optional) The scope of the function (defaults to this)
21728      */
21729     filterBy : function(fn, scope){
21730         this.snapshot = this.snapshot || this.data;
21731         this.data = this.queryBy(fn, scope||this);
21732         this.fireEvent("datachanged", this);
21733     },
21734
21735     /**
21736      * Query the records by a specified property.
21737      * @param {String} field A field on your records
21738      * @param {String/RegExp} value Either a string that the field
21739      * should start with or a RegExp to test against the field
21740      * @param {Boolean} anyMatch True to match any part not just the beginning
21741      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
21742      */
21743     query : function(property, value, anyMatch){
21744         var fn = this.createFilterFn(property, value, anyMatch);
21745         return fn ? this.queryBy(fn) : this.data.clone();
21746     },
21747
21748     /**
21749      * Query by a function. The specified function will be called with each
21750      * record in this data source. If the function returns true the record is included
21751      * in the results.
21752      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
21753      * @param {Object} scope (optional) The scope of the function (defaults to this)
21754       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
21755      **/
21756     queryBy : function(fn, scope){
21757         var data = this.snapshot || this.data;
21758         return data.filterBy(fn, scope||this);
21759     },
21760
21761     /**
21762      * Collects unique values for a particular dataIndex from this store.
21763      * @param {String} dataIndex The property to collect
21764      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
21765      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
21766      * @return {Array} An array of the unique values
21767      **/
21768     collect : function(dataIndex, allowNull, bypassFilter){
21769         var d = (bypassFilter === true && this.snapshot) ?
21770                 this.snapshot.items : this.data.items;
21771         var v, sv, r = [], l = {};
21772         for(var i = 0, len = d.length; i < len; i++){
21773             v = d[i].data[dataIndex];
21774             sv = String(v);
21775             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
21776                 l[sv] = true;
21777                 r[r.length] = v;
21778             }
21779         }
21780         return r;
21781     },
21782
21783     /**
21784      * Revert to a view of the Record cache with no filtering applied.
21785      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
21786      */
21787     clearFilter : function(suppressEvent){
21788         if(this.snapshot && this.snapshot != this.data){
21789             this.data = this.snapshot;
21790             delete this.snapshot;
21791             if(suppressEvent !== true){
21792                 this.fireEvent("datachanged", this);
21793             }
21794         }
21795     },
21796
21797     // private
21798     afterEdit : function(record){
21799         if(this.modified.indexOf(record) == -1){
21800             this.modified.push(record);
21801         }
21802         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
21803     },
21804     
21805     // private
21806     afterReject : function(record){
21807         this.modified.remove(record);
21808         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
21809     },
21810
21811     // private
21812     afterCommit : function(record){
21813         this.modified.remove(record);
21814         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
21815     },
21816
21817     /**
21818      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
21819      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
21820      */
21821     commitChanges : function(){
21822         var m = this.modified.slice(0);
21823         this.modified = [];
21824         for(var i = 0, len = m.length; i < len; i++){
21825             m[i].commit();
21826         }
21827     },
21828
21829     /**
21830      * Cancel outstanding changes on all changed records.
21831      */
21832     rejectChanges : function(){
21833         var m = this.modified.slice(0);
21834         this.modified = [];
21835         for(var i = 0, len = m.length; i < len; i++){
21836             m[i].reject();
21837         }
21838     },
21839
21840     onMetaChange : function(meta, rtype, o){
21841         this.recordType = rtype;
21842         this.fields = rtype.prototype.fields;
21843         delete this.snapshot;
21844         this.sortInfo = meta.sortInfo || this.sortInfo;
21845         this.modified = [];
21846         this.fireEvent('metachange', this, this.reader.meta);
21847     },
21848     
21849     moveIndex : function(data, type)
21850     {
21851         var index = this.indexOf(data);
21852         
21853         var newIndex = index + type;
21854         
21855         this.remove(data);
21856         
21857         this.insert(newIndex, data);
21858         
21859     }
21860 });/*
21861  * Based on:
21862  * Ext JS Library 1.1.1
21863  * Copyright(c) 2006-2007, Ext JS, LLC.
21864  *
21865  * Originally Released Under LGPL - original licence link has changed is not relivant.
21866  *
21867  * Fork - LGPL
21868  * <script type="text/javascript">
21869  */
21870
21871 /**
21872  * @class Roo.data.SimpleStore
21873  * @extends Roo.data.Store
21874  * Small helper class to make creating Stores from Array data easier.
21875  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
21876  * @cfg {Array} fields An array of field definition objects, or field name strings.
21877  * @cfg {Array} data The multi-dimensional array of data
21878  * @constructor
21879  * @param {Object} config
21880  */
21881 Roo.data.SimpleStore = function(config){
21882     Roo.data.SimpleStore.superclass.constructor.call(this, {
21883         isLocal : true,
21884         reader: new Roo.data.ArrayReader({
21885                 id: config.id
21886             },
21887             Roo.data.Record.create(config.fields)
21888         ),
21889         proxy : new Roo.data.MemoryProxy(config.data)
21890     });
21891     this.load();
21892 };
21893 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
21894  * Based on:
21895  * Ext JS Library 1.1.1
21896  * Copyright(c) 2006-2007, Ext JS, LLC.
21897  *
21898  * Originally Released Under LGPL - original licence link has changed is not relivant.
21899  *
21900  * Fork - LGPL
21901  * <script type="text/javascript">
21902  */
21903
21904 /**
21905 /**
21906  * @extends Roo.data.Store
21907  * @class Roo.data.JsonStore
21908  * Small helper class to make creating Stores for JSON data easier. <br/>
21909 <pre><code>
21910 var store = new Roo.data.JsonStore({
21911     url: 'get-images.php',
21912     root: 'images',
21913     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
21914 });
21915 </code></pre>
21916  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
21917  * JsonReader and HttpProxy (unless inline data is provided).</b>
21918  * @cfg {Array} fields An array of field definition objects, or field name strings.
21919  * @constructor
21920  * @param {Object} config
21921  */
21922 Roo.data.JsonStore = function(c){
21923     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
21924         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
21925         reader: new Roo.data.JsonReader(c, c.fields)
21926     }));
21927 };
21928 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
21929  * Based on:
21930  * Ext JS Library 1.1.1
21931  * Copyright(c) 2006-2007, Ext JS, LLC.
21932  *
21933  * Originally Released Under LGPL - original licence link has changed is not relivant.
21934  *
21935  * Fork - LGPL
21936  * <script type="text/javascript">
21937  */
21938
21939  
21940 Roo.data.Field = function(config){
21941     if(typeof config == "string"){
21942         config = {name: config};
21943     }
21944     Roo.apply(this, config);
21945     
21946     if(!this.type){
21947         this.type = "auto";
21948     }
21949     
21950     var st = Roo.data.SortTypes;
21951     // named sortTypes are supported, here we look them up
21952     if(typeof this.sortType == "string"){
21953         this.sortType = st[this.sortType];
21954     }
21955     
21956     // set default sortType for strings and dates
21957     if(!this.sortType){
21958         switch(this.type){
21959             case "string":
21960                 this.sortType = st.asUCString;
21961                 break;
21962             case "date":
21963                 this.sortType = st.asDate;
21964                 break;
21965             default:
21966                 this.sortType = st.none;
21967         }
21968     }
21969
21970     // define once
21971     var stripRe = /[\$,%]/g;
21972
21973     // prebuilt conversion function for this field, instead of
21974     // switching every time we're reading a value
21975     if(!this.convert){
21976         var cv, dateFormat = this.dateFormat;
21977         switch(this.type){
21978             case "":
21979             case "auto":
21980             case undefined:
21981                 cv = function(v){ return v; };
21982                 break;
21983             case "string":
21984                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
21985                 break;
21986             case "int":
21987                 cv = function(v){
21988                     return v !== undefined && v !== null && v !== '' ?
21989                            parseInt(String(v).replace(stripRe, ""), 10) : '';
21990                     };
21991                 break;
21992             case "float":
21993                 cv = function(v){
21994                     return v !== undefined && v !== null && v !== '' ?
21995                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
21996                     };
21997                 break;
21998             case "bool":
21999             case "boolean":
22000                 cv = function(v){ return v === true || v === "true" || v == 1; };
22001                 break;
22002             case "date":
22003                 cv = function(v){
22004                     if(!v){
22005                         return '';
22006                     }
22007                     if(v instanceof Date){
22008                         return v;
22009                     }
22010                     if(dateFormat){
22011                         if(dateFormat == "timestamp"){
22012                             return new Date(v*1000);
22013                         }
22014                         return Date.parseDate(v, dateFormat);
22015                     }
22016                     var parsed = Date.parse(v);
22017                     return parsed ? new Date(parsed) : null;
22018                 };
22019              break;
22020             
22021         }
22022         this.convert = cv;
22023     }
22024 };
22025
22026 Roo.data.Field.prototype = {
22027     dateFormat: null,
22028     defaultValue: "",
22029     mapping: null,
22030     sortType : null,
22031     sortDir : "ASC"
22032 };/*
22033  * Based on:
22034  * Ext JS Library 1.1.1
22035  * Copyright(c) 2006-2007, Ext JS, LLC.
22036  *
22037  * Originally Released Under LGPL - original licence link has changed is not relivant.
22038  *
22039  * Fork - LGPL
22040  * <script type="text/javascript">
22041  */
22042  
22043 // Base class for reading structured data from a data source.  This class is intended to be
22044 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
22045
22046 /**
22047  * @class Roo.data.DataReader
22048  * Base class for reading structured data from a data source.  This class is intended to be
22049  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
22050  */
22051
22052 Roo.data.DataReader = function(meta, recordType){
22053     
22054     this.meta = meta;
22055     
22056     this.recordType = recordType instanceof Array ? 
22057         Roo.data.Record.create(recordType) : recordType;
22058 };
22059
22060 Roo.data.DataReader.prototype = {
22061      /**
22062      * Create an empty record
22063      * @param {Object} data (optional) - overlay some values
22064      * @return {Roo.data.Record} record created.
22065      */
22066     newRow :  function(d) {
22067         var da =  {};
22068         this.recordType.prototype.fields.each(function(c) {
22069             switch( c.type) {
22070                 case 'int' : da[c.name] = 0; break;
22071                 case 'date' : da[c.name] = new Date(); break;
22072                 case 'float' : da[c.name] = 0.0; break;
22073                 case 'boolean' : da[c.name] = false; break;
22074                 default : da[c.name] = ""; break;
22075             }
22076             
22077         });
22078         return new this.recordType(Roo.apply(da, d));
22079     }
22080     
22081 };/*
22082  * Based on:
22083  * Ext JS Library 1.1.1
22084  * Copyright(c) 2006-2007, Ext JS, LLC.
22085  *
22086  * Originally Released Under LGPL - original licence link has changed is not relivant.
22087  *
22088  * Fork - LGPL
22089  * <script type="text/javascript">
22090  */
22091
22092 /**
22093  * @class Roo.data.DataProxy
22094  * @extends Roo.data.Observable
22095  * This class is an abstract base class for implementations which provide retrieval of
22096  * unformatted data objects.<br>
22097  * <p>
22098  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
22099  * (of the appropriate type which knows how to parse the data object) to provide a block of
22100  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
22101  * <p>
22102  * Custom implementations must implement the load method as described in
22103  * {@link Roo.data.HttpProxy#load}.
22104  */
22105 Roo.data.DataProxy = function(){
22106     this.addEvents({
22107         /**
22108          * @event beforeload
22109          * Fires before a network request is made to retrieve a data object.
22110          * @param {Object} This DataProxy object.
22111          * @param {Object} params The params parameter to the load function.
22112          */
22113         beforeload : true,
22114         /**
22115          * @event load
22116          * Fires before the load method's callback is called.
22117          * @param {Object} This DataProxy object.
22118          * @param {Object} o The data object.
22119          * @param {Object} arg The callback argument object passed to the load function.
22120          */
22121         load : true,
22122         /**
22123          * @event loadexception
22124          * Fires if an Exception occurs during data retrieval.
22125          * @param {Object} This DataProxy object.
22126          * @param {Object} o The data object.
22127          * @param {Object} arg The callback argument object passed to the load function.
22128          * @param {Object} e The Exception.
22129          */
22130         loadexception : true
22131     });
22132     Roo.data.DataProxy.superclass.constructor.call(this);
22133 };
22134
22135 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
22136
22137     /**
22138      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
22139      */
22140 /*
22141  * Based on:
22142  * Ext JS Library 1.1.1
22143  * Copyright(c) 2006-2007, Ext JS, LLC.
22144  *
22145  * Originally Released Under LGPL - original licence link has changed is not relivant.
22146  *
22147  * Fork - LGPL
22148  * <script type="text/javascript">
22149  */
22150 /**
22151  * @class Roo.data.MemoryProxy
22152  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
22153  * to the Reader when its load method is called.
22154  * @constructor
22155  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
22156  */
22157 Roo.data.MemoryProxy = function(data){
22158     if (data.data) {
22159         data = data.data;
22160     }
22161     Roo.data.MemoryProxy.superclass.constructor.call(this);
22162     this.data = data;
22163 };
22164
22165 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
22166     /**
22167      * Load data from the requested source (in this case an in-memory
22168      * data object passed to the constructor), read the data object into
22169      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
22170      * process that block using the passed callback.
22171      * @param {Object} params This parameter is not used by the MemoryProxy class.
22172      * @param {Roo.data.DataReader} reader The Reader object which converts the data
22173      * object into a block of Roo.data.Records.
22174      * @param {Function} callback The function into which to pass the block of Roo.data.records.
22175      * The function must be passed <ul>
22176      * <li>The Record block object</li>
22177      * <li>The "arg" argument from the load function</li>
22178      * <li>A boolean success indicator</li>
22179      * </ul>
22180      * @param {Object} scope The scope in which to call the callback
22181      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
22182      */
22183     load : function(params, reader, callback, scope, arg){
22184         params = params || {};
22185         var result;
22186         try {
22187             result = reader.readRecords(this.data);
22188         }catch(e){
22189             this.fireEvent("loadexception", this, arg, null, e);
22190             callback.call(scope, null, arg, false);
22191             return;
22192         }
22193         callback.call(scope, result, arg, true);
22194     },
22195     
22196     // private
22197     update : function(params, records){
22198         
22199     }
22200 });/*
22201  * Based on:
22202  * Ext JS Library 1.1.1
22203  * Copyright(c) 2006-2007, Ext JS, LLC.
22204  *
22205  * Originally Released Under LGPL - original licence link has changed is not relivant.
22206  *
22207  * Fork - LGPL
22208  * <script type="text/javascript">
22209  */
22210 /**
22211  * @class Roo.data.HttpProxy
22212  * @extends Roo.data.DataProxy
22213  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
22214  * configured to reference a certain URL.<br><br>
22215  * <p>
22216  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
22217  * from which the running page was served.<br><br>
22218  * <p>
22219  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
22220  * <p>
22221  * Be aware that to enable the browser to parse an XML document, the server must set
22222  * the Content-Type header in the HTTP response to "text/xml".
22223  * @constructor
22224  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
22225  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
22226  * will be used to make the request.
22227  */
22228 Roo.data.HttpProxy = function(conn){
22229     Roo.data.HttpProxy.superclass.constructor.call(this);
22230     // is conn a conn config or a real conn?
22231     this.conn = conn;
22232     this.useAjax = !conn || !conn.events;
22233   
22234 };
22235
22236 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
22237     // thse are take from connection...
22238     
22239     /**
22240      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
22241      */
22242     /**
22243      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
22244      * extra parameters to each request made by this object. (defaults to undefined)
22245      */
22246     /**
22247      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
22248      *  to each request made by this object. (defaults to undefined)
22249      */
22250     /**
22251      * @cfg {String} method (Optional) The default HTTP method to be used for requests. (defaults to undefined; if not set but parms are present will use POST, otherwise GET)
22252      */
22253     /**
22254      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
22255      */
22256      /**
22257      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
22258      * @type Boolean
22259      */
22260   
22261
22262     /**
22263      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
22264      * @type Boolean
22265      */
22266     /**
22267      * Return the {@link Roo.data.Connection} object being used by this Proxy.
22268      * @return {Connection} The Connection object. This object may be used to subscribe to events on
22269      * a finer-grained basis than the DataProxy events.
22270      */
22271     getConnection : function(){
22272         return this.useAjax ? Roo.Ajax : this.conn;
22273     },
22274
22275     /**
22276      * Load data from the configured {@link Roo.data.Connection}, read the data object into
22277      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
22278      * process that block using the passed callback.
22279      * @param {Object} params An object containing properties which are to be used as HTTP parameters
22280      * for the request to the remote server.
22281      * @param {Roo.data.DataReader} reader The Reader object which converts the data
22282      * object into a block of Roo.data.Records.
22283      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
22284      * The function must be passed <ul>
22285      * <li>The Record block object</li>
22286      * <li>The "arg" argument from the load function</li>
22287      * <li>A boolean success indicator</li>
22288      * </ul>
22289      * @param {Object} scope The scope in which to call the callback
22290      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
22291      */
22292     load : function(params, reader, callback, scope, arg){
22293         if(this.fireEvent("beforeload", this, params) !== false){
22294             var  o = {
22295                 params : params || {},
22296                 request: {
22297                     callback : callback,
22298                     scope : scope,
22299                     arg : arg
22300                 },
22301                 reader: reader,
22302                 callback : this.loadResponse,
22303                 scope: this
22304             };
22305             if(this.useAjax){
22306                 Roo.applyIf(o, this.conn);
22307                 if(this.activeRequest){
22308                     Roo.Ajax.abort(this.activeRequest);
22309                 }
22310                 this.activeRequest = Roo.Ajax.request(o);
22311             }else{
22312                 this.conn.request(o);
22313             }
22314         }else{
22315             callback.call(scope||this, null, arg, false);
22316         }
22317     },
22318
22319     // private
22320     loadResponse : function(o, success, response){
22321         delete this.activeRequest;
22322         if(!success){
22323             this.fireEvent("loadexception", this, o, response);
22324             o.request.callback.call(o.request.scope, null, o.request.arg, false);
22325             return;
22326         }
22327         var result;
22328         try {
22329             result = o.reader.read(response);
22330         }catch(e){
22331             this.fireEvent("loadexception", this, o, response, e);
22332             o.request.callback.call(o.request.scope, null, o.request.arg, false);
22333             return;
22334         }
22335         
22336         this.fireEvent("load", this, o, o.request.arg);
22337         o.request.callback.call(o.request.scope, result, o.request.arg, true);
22338     },
22339
22340     // private
22341     update : function(dataSet){
22342
22343     },
22344
22345     // private
22346     updateResponse : function(dataSet){
22347
22348     }
22349 });/*
22350  * Based on:
22351  * Ext JS Library 1.1.1
22352  * Copyright(c) 2006-2007, Ext JS, LLC.
22353  *
22354  * Originally Released Under LGPL - original licence link has changed is not relivant.
22355  *
22356  * Fork - LGPL
22357  * <script type="text/javascript">
22358  */
22359
22360 /**
22361  * @class Roo.data.ScriptTagProxy
22362  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
22363  * other than the originating domain of the running page.<br><br>
22364  * <p>
22365  * <em>Note that if you are retrieving data from a page that is in a domain that is NOT the same as the originating domain
22366  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
22367  * <p>
22368  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
22369  * source code that is used as the source inside a &lt;script> tag.<br><br>
22370  * <p>
22371  * In order for the browser to process the returned data, the server must wrap the data object
22372  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
22373  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
22374  * depending on whether the callback name was passed:
22375  * <p>
22376  * <pre><code>
22377 boolean scriptTag = false;
22378 String cb = request.getParameter("callback");
22379 if (cb != null) {
22380     scriptTag = true;
22381     response.setContentType("text/javascript");
22382 } else {
22383     response.setContentType("application/x-json");
22384 }
22385 Writer out = response.getWriter();
22386 if (scriptTag) {
22387     out.write(cb + "(");
22388 }
22389 out.print(dataBlock.toJsonString());
22390 if (scriptTag) {
22391     out.write(");");
22392 }
22393 </pre></code>
22394  *
22395  * @constructor
22396  * @param {Object} config A configuration object.
22397  */
22398 Roo.data.ScriptTagProxy = function(config){
22399     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
22400     Roo.apply(this, config);
22401     this.head = document.getElementsByTagName("head")[0];
22402 };
22403
22404 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
22405
22406 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
22407     /**
22408      * @cfg {String} url The URL from which to request the data object.
22409      */
22410     /**
22411      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
22412      */
22413     timeout : 30000,
22414     /**
22415      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
22416      * the server the name of the callback function set up by the load call to process the returned data object.
22417      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
22418      * javascript output which calls this named function passing the data object as its only parameter.
22419      */
22420     callbackParam : "callback",
22421     /**
22422      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
22423      * name to the request.
22424      */
22425     nocache : true,
22426
22427     /**
22428      * Load data from the configured URL, read the data object into
22429      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
22430      * process that block using the passed callback.
22431      * @param {Object} params An object containing properties which are to be used as HTTP parameters
22432      * for the request to the remote server.
22433      * @param {Roo.data.DataReader} reader The Reader object which converts the data
22434      * object into a block of Roo.data.Records.
22435      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
22436      * The function must be passed <ul>
22437      * <li>The Record block object</li>
22438      * <li>The "arg" argument from the load function</li>
22439      * <li>A boolean success indicator</li>
22440      * </ul>
22441      * @param {Object} scope The scope in which to call the callback
22442      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
22443      */
22444     load : function(params, reader, callback, scope, arg){
22445         if(this.fireEvent("beforeload", this, params) !== false){
22446
22447             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
22448
22449             var url = this.url;
22450             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
22451             if(this.nocache){
22452                 url += "&_dc=" + (new Date().getTime());
22453             }
22454             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
22455             var trans = {
22456                 id : transId,
22457                 cb : "stcCallback"+transId,
22458                 scriptId : "stcScript"+transId,
22459                 params : params,
22460                 arg : arg,
22461                 url : url,
22462                 callback : callback,
22463                 scope : scope,
22464                 reader : reader
22465             };
22466             var conn = this;
22467
22468             window[trans.cb] = function(o){
22469                 conn.handleResponse(o, trans);
22470             };
22471
22472             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
22473
22474             if(this.autoAbort !== false){
22475                 this.abort();
22476             }
22477
22478             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
22479
22480             var script = document.createElement("script");
22481             script.setAttribute("src", url);
22482             script.setAttribute("type", "text/javascript");
22483             script.setAttribute("id", trans.scriptId);
22484             this.head.appendChild(script);
22485
22486             this.trans = trans;
22487         }else{
22488             callback.call(scope||this, null, arg, false);
22489         }
22490     },
22491
22492     // private
22493     isLoading : function(){
22494         return this.trans ? true : false;
22495     },
22496
22497     /**
22498      * Abort the current server request.
22499      */
22500     abort : function(){
22501         if(this.isLoading()){
22502             this.destroyTrans(this.trans);
22503         }
22504     },
22505
22506     // private
22507     destroyTrans : function(trans, isLoaded){
22508         this.head.removeChild(document.getElementById(trans.scriptId));
22509         clearTimeout(trans.timeoutId);
22510         if(isLoaded){
22511             window[trans.cb] = undefined;
22512             try{
22513                 delete window[trans.cb];
22514             }catch(e){}
22515         }else{
22516             // if hasn't been loaded, wait for load to remove it to prevent script error
22517             window[trans.cb] = function(){
22518                 window[trans.cb] = undefined;
22519                 try{
22520                     delete window[trans.cb];
22521                 }catch(e){}
22522             };
22523         }
22524     },
22525
22526     // private
22527     handleResponse : function(o, trans){
22528         this.trans = false;
22529         this.destroyTrans(trans, true);
22530         var result;
22531         try {
22532             result = trans.reader.readRecords(o);
22533         }catch(e){
22534             this.fireEvent("loadexception", this, o, trans.arg, e);
22535             trans.callback.call(trans.scope||window, null, trans.arg, false);
22536             return;
22537         }
22538         this.fireEvent("load", this, o, trans.arg);
22539         trans.callback.call(trans.scope||window, result, trans.arg, true);
22540     },
22541
22542     // private
22543     handleFailure : function(trans){
22544         this.trans = false;
22545         this.destroyTrans(trans, false);
22546         this.fireEvent("loadexception", this, null, trans.arg);
22547         trans.callback.call(trans.scope||window, null, trans.arg, false);
22548     }
22549 });/*
22550  * Based on:
22551  * Ext JS Library 1.1.1
22552  * Copyright(c) 2006-2007, Ext JS, LLC.
22553  *
22554  * Originally Released Under LGPL - original licence link has changed is not relivant.
22555  *
22556  * Fork - LGPL
22557  * <script type="text/javascript">
22558  */
22559
22560 /**
22561  * @class Roo.data.JsonReader
22562  * @extends Roo.data.DataReader
22563  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
22564  * based on mappings in a provided Roo.data.Record constructor.
22565  * 
22566  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
22567  * in the reply previously. 
22568  * 
22569  * <p>
22570  * Example code:
22571  * <pre><code>
22572 var RecordDef = Roo.data.Record.create([
22573     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
22574     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
22575 ]);
22576 var myReader = new Roo.data.JsonReader({
22577     totalProperty: "results",    // The property which contains the total dataset size (optional)
22578     root: "rows",                // The property which contains an Array of row objects
22579     id: "id"                     // The property within each row object that provides an ID for the record (optional)
22580 }, RecordDef);
22581 </code></pre>
22582  * <p>
22583  * This would consume a JSON file like this:
22584  * <pre><code>
22585 { 'results': 2, 'rows': [
22586     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
22587     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
22588 }
22589 </code></pre>
22590  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
22591  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
22592  * paged from the remote server.
22593  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
22594  * @cfg {String} root name of the property which contains the Array of row objects.
22595  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
22596  * @constructor
22597  * Create a new JsonReader
22598  * @param {Object} meta Metadata configuration options
22599  * @param {Object} recordType Either an Array of field definition objects,
22600  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
22601  */
22602 Roo.data.JsonReader = function(meta, recordType){
22603     
22604     meta = meta || {};
22605     // set some defaults:
22606     Roo.applyIf(meta, {
22607         totalProperty: 'total',
22608         successProperty : 'success',
22609         root : 'data',
22610         id : 'id'
22611     });
22612     
22613     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
22614 };
22615 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
22616     
22617     /**
22618      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
22619      * Used by Store query builder to append _requestMeta to params.
22620      * 
22621      */
22622     metaFromRemote : false,
22623     /**
22624      * This method is only used by a DataProxy which has retrieved data from a remote server.
22625      * @param {Object} response The XHR object which contains the JSON data in its responseText.
22626      * @return {Object} data A data block which is used by an Roo.data.Store object as
22627      * a cache of Roo.data.Records.
22628      */
22629     read : function(response){
22630         var json = response.responseText;
22631        
22632         var o = /* eval:var:o */ eval("("+json+")");
22633         if(!o) {
22634             throw {message: "JsonReader.read: Json object not found"};
22635         }
22636         
22637         if(o.metaData){
22638             
22639             delete this.ef;
22640             this.metaFromRemote = true;
22641             this.meta = o.metaData;
22642             this.recordType = Roo.data.Record.create(o.metaData.fields);
22643             this.onMetaChange(this.meta, this.recordType, o);
22644         }
22645         return this.readRecords(o);
22646     },
22647
22648     // private function a store will implement
22649     onMetaChange : function(meta, recordType, o){
22650
22651     },
22652
22653     /**
22654          * @ignore
22655          */
22656     simpleAccess: function(obj, subsc) {
22657         return obj[subsc];
22658     },
22659
22660         /**
22661          * @ignore
22662          */
22663     getJsonAccessor: function(){
22664         var re = /[\[\.]/;
22665         return function(expr) {
22666             try {
22667                 return(re.test(expr))
22668                     ? new Function("obj", "return obj." + expr)
22669                     : function(obj){
22670                         return obj[expr];
22671                     };
22672             } catch(e){}
22673             return Roo.emptyFn;
22674         };
22675     }(),
22676
22677     /**
22678      * Create a data block containing Roo.data.Records from an XML document.
22679      * @param {Object} o An object which contains an Array of row objects in the property specified
22680      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
22681      * which contains the total size of the dataset.
22682      * @return {Object} data A data block which is used by an Roo.data.Store object as
22683      * a cache of Roo.data.Records.
22684      */
22685     readRecords : function(o){
22686         /**
22687          * After any data loads, the raw JSON data is available for further custom processing.
22688          * @type Object
22689          */
22690         this.o = o;
22691         var s = this.meta, Record = this.recordType,
22692             f = Record.prototype.fields, fi = f.items, fl = f.length;
22693
22694 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
22695         if (!this.ef) {
22696             if(s.totalProperty) {
22697                     this.getTotal = this.getJsonAccessor(s.totalProperty);
22698                 }
22699                 if(s.successProperty) {
22700                     this.getSuccess = this.getJsonAccessor(s.successProperty);
22701                 }
22702                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
22703                 if (s.id) {
22704                         var g = this.getJsonAccessor(s.id);
22705                         this.getId = function(rec) {
22706                                 var r = g(rec);
22707                                 return (r === undefined || r === "") ? null : r;
22708                         };
22709                 } else {
22710                         this.getId = function(){return null;};
22711                 }
22712             this.ef = [];
22713             for(var jj = 0; jj < fl; jj++){
22714                 f = fi[jj];
22715                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
22716                 this.ef[jj] = this.getJsonAccessor(map);
22717             }
22718         }
22719
22720         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
22721         if(s.totalProperty){
22722             var vt = parseInt(this.getTotal(o), 10);
22723             if(!isNaN(vt)){
22724                 totalRecords = vt;
22725             }
22726         }
22727         if(s.successProperty){
22728             var vs = this.getSuccess(o);
22729             if(vs === false || vs === 'false'){
22730                 success = false;
22731             }
22732         }
22733         var records = [];
22734             for(var i = 0; i < c; i++){
22735                     var n = root[i];
22736                 var values = {};
22737                 var id = this.getId(n);
22738                 for(var j = 0; j < fl; j++){
22739                     f = fi[j];
22740                 var v = this.ef[j](n);
22741                 if (!f.convert) {
22742                     Roo.log('missing convert for ' + f.name);
22743                     Roo.log(f);
22744                     continue;
22745                 }
22746                 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
22747                 }
22748                 var record = new Record(values, id);
22749                 record.json = n;
22750                 records[i] = record;
22751             }
22752             return {
22753             raw : o,
22754                 success : success,
22755                 records : records,
22756                 totalRecords : totalRecords
22757             };
22758     }
22759 });/*
22760  * Based on:
22761  * Ext JS Library 1.1.1
22762  * Copyright(c) 2006-2007, Ext JS, LLC.
22763  *
22764  * Originally Released Under LGPL - original licence link has changed is not relivant.
22765  *
22766  * Fork - LGPL
22767  * <script type="text/javascript">
22768  */
22769
22770 /**
22771  * @class Roo.data.XmlReader
22772  * @extends Roo.data.DataReader
22773  * Data reader class to create an Array of {@link Roo.data.Record} objects from an XML document
22774  * based on mappings in a provided Roo.data.Record constructor.<br><br>
22775  * <p>
22776  * <em>Note that in order for the browser to parse a returned XML document, the Content-Type
22777  * header in the HTTP response must be set to "text/xml".</em>
22778  * <p>
22779  * Example code:
22780  * <pre><code>
22781 var RecordDef = Roo.data.Record.create([
22782    {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
22783    {name: 'occupation'}                 // This field will use "occupation" as the mapping.
22784 ]);
22785 var myReader = new Roo.data.XmlReader({
22786    totalRecords: "results", // The element which contains the total dataset size (optional)
22787    record: "row",           // The repeated element which contains row information
22788    id: "id"                 // The element within the row that provides an ID for the record (optional)
22789 }, RecordDef);
22790 </code></pre>
22791  * <p>
22792  * This would consume an XML file like this:
22793  * <pre><code>
22794 &lt;?xml?>
22795 &lt;dataset>
22796  &lt;results>2&lt;/results>
22797  &lt;row>
22798    &lt;id>1&lt;/id>
22799    &lt;name>Bill&lt;/name>
22800    &lt;occupation>Gardener&lt;/occupation>
22801  &lt;/row>
22802  &lt;row>
22803    &lt;id>2&lt;/id>
22804    &lt;name>Ben&lt;/name>
22805    &lt;occupation>Horticulturalist&lt;/occupation>
22806  &lt;/row>
22807 &lt;/dataset>
22808 </code></pre>
22809  * @cfg {String} totalRecords The DomQuery path from which to retrieve the total number of records
22810  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
22811  * paged from the remote server.
22812  * @cfg {String} record The DomQuery path to the repeated element which contains record information.
22813  * @cfg {String} success The DomQuery path to the success attribute used by forms.
22814  * @cfg {String} id The DomQuery path relative from the record element to the element that contains
22815  * a record identifier value.
22816  * @constructor
22817  * Create a new XmlReader
22818  * @param {Object} meta Metadata configuration options
22819  * @param {Mixed} recordType The definition of the data record type to produce.  This can be either a valid
22820  * Record subclass created with {@link Roo.data.Record#create}, or an array of objects with which to call
22821  * Roo.data.Record.create.  See the {@link Roo.data.Record} class for more details.
22822  */
22823 Roo.data.XmlReader = function(meta, recordType){
22824     meta = meta || {};
22825     Roo.data.XmlReader.superclass.constructor.call(this, meta, recordType||meta.fields);
22826 };
22827 Roo.extend(Roo.data.XmlReader, Roo.data.DataReader, {
22828     /**
22829      * This method is only used by a DataProxy which has retrieved data from a remote server.
22830          * @param {Object} response The XHR object which contains the parsed XML document.  The response is expected
22831          * to contain a method called 'responseXML' that returns an XML document object.
22832      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
22833      * a cache of Roo.data.Records.
22834      */
22835     read : function(response){
22836         var doc = response.responseXML;
22837         if(!doc) {
22838             throw {message: "XmlReader.read: XML Document not available"};
22839         }
22840         return this.readRecords(doc);
22841     },
22842
22843     /**
22844      * Create a data block containing Roo.data.Records from an XML document.
22845          * @param {Object} doc A parsed XML document.
22846      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
22847      * a cache of Roo.data.Records.
22848      */
22849     readRecords : function(doc){
22850         /**
22851          * After any data loads/reads, the raw XML Document is available for further custom processing.
22852          * @type XMLDocument
22853          */
22854         this.xmlData = doc;
22855         var root = doc.documentElement || doc;
22856         var q = Roo.DomQuery;
22857         var recordType = this.recordType, fields = recordType.prototype.fields;
22858         var sid = this.meta.id;
22859         var totalRecords = 0, success = true;
22860         if(this.meta.totalRecords){
22861             totalRecords = q.selectNumber(this.meta.totalRecords, root, 0);
22862         }
22863         
22864         if(this.meta.success){
22865             var sv = q.selectValue(this.meta.success, root, true);
22866             success = sv !== false && sv !== 'false';
22867         }
22868         var records = [];
22869         var ns = q.select(this.meta.record, root);
22870         for(var i = 0, len = ns.length; i < len; i++) {
22871                 var n = ns[i];
22872                 var values = {};
22873                 var id = sid ? q.selectValue(sid, n) : undefined;
22874                 for(var j = 0, jlen = fields.length; j < jlen; j++){
22875                     var f = fields.items[j];
22876                 var v = q.selectValue(f.mapping || f.name, n, f.defaultValue);
22877                     v = f.convert(v);
22878                     values[f.name] = v;
22879                 }
22880                 var record = new recordType(values, id);
22881                 record.node = n;
22882                 records[records.length] = record;
22883             }
22884
22885             return {
22886                 success : success,
22887                 records : records,
22888                 totalRecords : totalRecords || records.length
22889             };
22890     }
22891 });/*
22892  * Based on:
22893  * Ext JS Library 1.1.1
22894  * Copyright(c) 2006-2007, Ext JS, LLC.
22895  *
22896  * Originally Released Under LGPL - original licence link has changed is not relivant.
22897  *
22898  * Fork - LGPL
22899  * <script type="text/javascript">
22900  */
22901
22902 /**
22903  * @class Roo.data.ArrayReader
22904  * @extends Roo.data.DataReader
22905  * Data reader class to create an Array of Roo.data.Record objects from an Array.
22906  * Each element of that Array represents a row of data fields. The
22907  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
22908  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
22909  * <p>
22910  * Example code:.
22911  * <pre><code>
22912 var RecordDef = Roo.data.Record.create([
22913     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
22914     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
22915 ]);
22916 var myReader = new Roo.data.ArrayReader({
22917     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
22918 }, RecordDef);
22919 </code></pre>
22920  * <p>
22921  * This would consume an Array like this:
22922  * <pre><code>
22923 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
22924   </code></pre>
22925  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
22926  * @constructor
22927  * Create a new JsonReader
22928  * @param {Object} meta Metadata configuration options.
22929  * @param {Object} recordType Either an Array of field definition objects
22930  * as specified to {@link Roo.data.Record#create},
22931  * or an {@link Roo.data.Record} object
22932  * created using {@link Roo.data.Record#create}.
22933  */
22934 Roo.data.ArrayReader = function(meta, recordType){
22935     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
22936 };
22937
22938 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
22939     /**
22940      * Create a data block containing Roo.data.Records from an XML document.
22941      * @param {Object} o An Array of row objects which represents the dataset.
22942      * @return {Object} data A data block which is used by an Roo.data.Store object as
22943      * a cache of Roo.data.Records.
22944      */
22945     readRecords : function(o){
22946         var sid = this.meta ? this.meta.id : null;
22947         var recordType = this.recordType, fields = recordType.prototype.fields;
22948         var records = [];
22949         var root = o;
22950             for(var i = 0; i < root.length; i++){
22951                     var n = root[i];
22952                 var values = {};
22953                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
22954                 for(var j = 0, jlen = fields.length; j < jlen; j++){
22955                 var f = fields.items[j];
22956                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
22957                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
22958                 v = f.convert(v);
22959                 values[f.name] = v;
22960             }
22961                 var record = new recordType(values, id);
22962                 record.json = n;
22963                 records[records.length] = record;
22964             }
22965             return {
22966                 records : records,
22967                 totalRecords : records.length
22968             };
22969     }
22970 });/*
22971  * Based on:
22972  * Ext JS Library 1.1.1
22973  * Copyright(c) 2006-2007, Ext JS, LLC.
22974  *
22975  * Originally Released Under LGPL - original licence link has changed is not relivant.
22976  *
22977  * Fork - LGPL
22978  * <script type="text/javascript">
22979  */
22980
22981
22982 /**
22983  * @class Roo.data.Tree
22984  * @extends Roo.util.Observable
22985  * Represents a tree data structure and bubbles all the events for its nodes. The nodes
22986  * in the tree have most standard DOM functionality.
22987  * @constructor
22988  * @param {Node} root (optional) The root node
22989  */
22990 Roo.data.Tree = function(root){
22991    this.nodeHash = {};
22992    /**
22993     * The root node for this tree
22994     * @type Node
22995     */
22996    this.root = null;
22997    if(root){
22998        this.setRootNode(root);
22999    }
23000    this.addEvents({
23001        /**
23002         * @event append
23003         * Fires when a new child node is appended to a node in this tree.
23004         * @param {Tree} tree The owner tree
23005         * @param {Node} parent The parent node
23006         * @param {Node} node The newly appended node
23007         * @param {Number} index The index of the newly appended node
23008         */
23009        "append" : true,
23010        /**
23011         * @event remove
23012         * Fires when a child node is removed from a node in this tree.
23013         * @param {Tree} tree The owner tree
23014         * @param {Node} parent The parent node
23015         * @param {Node} node The child node removed
23016         */
23017        "remove" : true,
23018        /**
23019         * @event move
23020         * Fires when a node is moved to a new location in the tree
23021         * @param {Tree} tree The owner tree
23022         * @param {Node} node The node moved
23023         * @param {Node} oldParent The old parent of this node
23024         * @param {Node} newParent The new parent of this node
23025         * @param {Number} index The index it was moved to
23026         */
23027        "move" : true,
23028        /**
23029         * @event insert
23030         * Fires when a new child node is inserted in a node in this tree.
23031         * @param {Tree} tree The owner tree
23032         * @param {Node} parent The parent node
23033         * @param {Node} node The child node inserted
23034         * @param {Node} refNode The child node the node was inserted before
23035         */
23036        "insert" : true,
23037        /**
23038         * @event beforeappend
23039         * Fires before a new child is appended to a node in this tree, return false to cancel the append.
23040         * @param {Tree} tree The owner tree
23041         * @param {Node} parent The parent node
23042         * @param {Node} node The child node to be appended
23043         */
23044        "beforeappend" : true,
23045        /**
23046         * @event beforeremove
23047         * Fires before a child is removed from a node in this tree, return false to cancel the remove.
23048         * @param {Tree} tree The owner tree
23049         * @param {Node} parent The parent node
23050         * @param {Node} node The child node to be removed
23051         */
23052        "beforeremove" : true,
23053        /**
23054         * @event beforemove
23055         * Fires before a node is moved to a new location in the tree. Return false to cancel the move.
23056         * @param {Tree} tree The owner tree
23057         * @param {Node} node The node being moved
23058         * @param {Node} oldParent The parent of the node
23059         * @param {Node} newParent The new parent the node is moving to
23060         * @param {Number} index The index it is being moved to
23061         */
23062        "beforemove" : true,
23063        /**
23064         * @event beforeinsert
23065         * Fires before a new child is inserted in a node in this tree, return false to cancel the insert.
23066         * @param {Tree} tree The owner tree
23067         * @param {Node} parent The parent node
23068         * @param {Node} node The child node to be inserted
23069         * @param {Node} refNode The child node the node is being inserted before
23070         */
23071        "beforeinsert" : true
23072    });
23073
23074     Roo.data.Tree.superclass.constructor.call(this);
23075 };
23076
23077 Roo.extend(Roo.data.Tree, Roo.util.Observable, {
23078     pathSeparator: "/",
23079
23080     proxyNodeEvent : function(){
23081         return this.fireEvent.apply(this, arguments);
23082     },
23083
23084     /**
23085      * Returns the root node for this tree.
23086      * @return {Node}
23087      */
23088     getRootNode : function(){
23089         return this.root;
23090     },
23091
23092     /**
23093      * Sets the root node for this tree.
23094      * @param {Node} node
23095      * @return {Node}
23096      */
23097     setRootNode : function(node){
23098         this.root = node;
23099         node.ownerTree = this;
23100         node.isRoot = true;
23101         this.registerNode(node);
23102         return node;
23103     },
23104
23105     /**
23106      * Gets a node in this tree by its id.
23107      * @param {String} id
23108      * @return {Node}
23109      */
23110     getNodeById : function(id){
23111         return this.nodeHash[id];
23112     },
23113
23114     registerNode : function(node){
23115         this.nodeHash[node.id] = node;
23116     },
23117
23118     unregisterNode : function(node){
23119         delete this.nodeHash[node.id];
23120     },
23121
23122     toString : function(){
23123         return "[Tree"+(this.id?" "+this.id:"")+"]";
23124     }
23125 });
23126
23127 /**
23128  * @class Roo.data.Node
23129  * @extends Roo.util.Observable
23130  * @cfg {Boolean} leaf true if this node is a leaf and does not have children
23131  * @cfg {String} id The id for this node. If one is not specified, one is generated.
23132  * @constructor
23133  * @param {Object} attributes The attributes/config for the node
23134  */
23135 Roo.data.Node = function(attributes){
23136     /**
23137      * The attributes supplied for the node. You can use this property to access any custom attributes you supplied.
23138      * @type {Object}
23139      */
23140     this.attributes = attributes || {};
23141     this.leaf = this.attributes.leaf;
23142     /**
23143      * The node id. @type String
23144      */
23145     this.id = this.attributes.id;
23146     if(!this.id){
23147         this.id = Roo.id(null, "ynode-");
23148         this.attributes.id = this.id;
23149     }
23150      
23151     
23152     /**
23153      * All child nodes of this node. @type Array
23154      */
23155     this.childNodes = [];
23156     if(!this.childNodes.indexOf){ // indexOf is a must
23157         this.childNodes.indexOf = function(o){
23158             for(var i = 0, len = this.length; i < len; i++){
23159                 if(this[i] == o) {
23160                     return i;
23161                 }
23162             }
23163             return -1;
23164         };
23165     }
23166     /**
23167      * The parent node for this node. @type Node
23168      */
23169     this.parentNode = null;
23170     /**
23171      * The first direct child node of this node, or null if this node has no child nodes. @type Node
23172      */
23173     this.firstChild = null;
23174     /**
23175      * The last direct child node of this node, or null if this node has no child nodes. @type Node
23176      */
23177     this.lastChild = null;
23178     /**
23179      * The node immediately preceding this node in the tree, or null if there is no sibling node. @type Node
23180      */
23181     this.previousSibling = null;
23182     /**
23183      * The node immediately following this node in the tree, or null if there is no sibling node. @type Node
23184      */
23185     this.nextSibling = null;
23186
23187     this.addEvents({
23188        /**
23189         * @event append
23190         * Fires when a new child node is appended
23191         * @param {Tree} tree The owner tree
23192         * @param {Node} this This node
23193         * @param {Node} node The newly appended node
23194         * @param {Number} index The index of the newly appended node
23195         */
23196        "append" : true,
23197        /**
23198         * @event remove
23199         * Fires when a child node is removed
23200         * @param {Tree} tree The owner tree
23201         * @param {Node} this This node
23202         * @param {Node} node The removed node
23203         */
23204        "remove" : true,
23205        /**
23206         * @event move
23207         * Fires when this node is moved to a new location in the tree
23208         * @param {Tree} tree The owner tree
23209         * @param {Node} this This node
23210         * @param {Node} oldParent The old parent of this node
23211         * @param {Node} newParent The new parent of this node
23212         * @param {Number} index The index it was moved to
23213         */
23214        "move" : true,
23215        /**
23216         * @event insert
23217         * Fires when a new child node is inserted.
23218         * @param {Tree} tree The owner tree
23219         * @param {Node} this This node
23220         * @param {Node} node The child node inserted
23221         * @param {Node} refNode The child node the node was inserted before
23222         */
23223        "insert" : true,
23224        /**
23225         * @event beforeappend
23226         * Fires before a new child is appended, return false to cancel the append.
23227         * @param {Tree} tree The owner tree
23228         * @param {Node} this This node
23229         * @param {Node} node The child node to be appended
23230         */
23231        "beforeappend" : true,
23232        /**
23233         * @event beforeremove
23234         * Fires before a child is removed, return false to cancel the remove.
23235         * @param {Tree} tree The owner tree
23236         * @param {Node} this This node
23237         * @param {Node} node The child node to be removed
23238         */
23239        "beforeremove" : true,
23240        /**
23241         * @event beforemove
23242         * Fires before this node is moved to a new location in the tree. Return false to cancel the move.
23243         * @param {Tree} tree The owner tree
23244         * @param {Node} this This node
23245         * @param {Node} oldParent The parent of this node
23246         * @param {Node} newParent The new parent this node is moving to
23247         * @param {Number} index The index it is being moved to
23248         */
23249        "beforemove" : true,
23250        /**
23251         * @event beforeinsert
23252         * Fires before a new child is inserted, return false to cancel the insert.
23253         * @param {Tree} tree The owner tree
23254         * @param {Node} this This node
23255         * @param {Node} node The child node to be inserted
23256         * @param {Node} refNode The child node the node is being inserted before
23257         */
23258        "beforeinsert" : true
23259    });
23260     this.listeners = this.attributes.listeners;
23261     Roo.data.Node.superclass.constructor.call(this);
23262 };
23263
23264 Roo.extend(Roo.data.Node, Roo.util.Observable, {
23265     fireEvent : function(evtName){
23266         // first do standard event for this node
23267         if(Roo.data.Node.superclass.fireEvent.apply(this, arguments) === false){
23268             return false;
23269         }
23270         // then bubble it up to the tree if the event wasn't cancelled
23271         var ot = this.getOwnerTree();
23272         if(ot){
23273             if(ot.proxyNodeEvent.apply(ot, arguments) === false){
23274                 return false;
23275             }
23276         }
23277         return true;
23278     },
23279
23280     /**
23281      * Returns true if this node is a leaf
23282      * @return {Boolean}
23283      */
23284     isLeaf : function(){
23285         return this.leaf === true;
23286     },
23287
23288     // private
23289     setFirstChild : function(node){
23290         this.firstChild = node;
23291     },
23292
23293     //private
23294     setLastChild : function(node){
23295         this.lastChild = node;
23296     },
23297
23298
23299     /**
23300      * Returns true if this node is the last child of its parent
23301      * @return {Boolean}
23302      */
23303     isLast : function(){
23304        return (!this.parentNode ? true : this.parentNode.lastChild == this);
23305     },
23306
23307     /**
23308      * Returns true if this node is the first child of its parent
23309      * @return {Boolean}
23310      */
23311     isFirst : function(){
23312        return (!this.parentNode ? true : this.parentNode.firstChild == this);
23313     },
23314
23315     hasChildNodes : function(){
23316         return !this.isLeaf() && this.childNodes.length > 0;
23317     },
23318
23319     /**
23320      * Insert node(s) as the last child node of this node.
23321      * @param {Node/Array} node The node or Array of nodes to append
23322      * @return {Node} The appended node if single append, or null if an array was passed
23323      */
23324     appendChild : function(node){
23325         var multi = false;
23326         if(node instanceof Array){
23327             multi = node;
23328         }else if(arguments.length > 1){
23329             multi = arguments;
23330         }
23331         // if passed an array or multiple args do them one by one
23332         if(multi){
23333             for(var i = 0, len = multi.length; i < len; i++) {
23334                 this.appendChild(multi[i]);
23335             }
23336         }else{
23337             if(this.fireEvent("beforeappend", this.ownerTree, this, node) === false){
23338                 return false;
23339             }
23340             var index = this.childNodes.length;
23341             var oldParent = node.parentNode;
23342             // it's a move, make sure we move it cleanly
23343             if(oldParent){
23344                 if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index) === false){
23345                     return false;
23346                 }
23347                 oldParent.removeChild(node);
23348             }
23349             index = this.childNodes.length;
23350             if(index == 0){
23351                 this.setFirstChild(node);
23352             }
23353             this.childNodes.push(node);
23354             node.parentNode = this;
23355             var ps = this.childNodes[index-1];
23356             if(ps){
23357                 node.previousSibling = ps;
23358                 ps.nextSibling = node;
23359             }else{
23360                 node.previousSibling = null;
23361             }
23362             node.nextSibling = null;
23363             this.setLastChild(node);
23364             node.setOwnerTree(this.getOwnerTree());
23365             this.fireEvent("append", this.ownerTree, this, node, index);
23366             if(oldParent){
23367                 node.fireEvent("move", this.ownerTree, node, oldParent, this, index);
23368             }
23369             return node;
23370         }
23371     },
23372
23373     /**
23374      * Removes a child node from this node.
23375      * @param {Node} node The node to remove
23376      * @return {Node} The removed node
23377      */
23378     removeChild : function(node){
23379         var index = this.childNodes.indexOf(node);
23380         if(index == -1){
23381             return false;
23382         }
23383         if(this.fireEvent("beforeremove", this.ownerTree, this, node) === false){
23384             return false;
23385         }
23386
23387         // remove it from childNodes collection
23388         this.childNodes.splice(index, 1);
23389
23390         // update siblings
23391         if(node.previousSibling){
23392             node.previousSibling.nextSibling = node.nextSibling;
23393         }
23394         if(node.nextSibling){
23395             node.nextSibling.previousSibling = node.previousSibling;
23396         }
23397
23398         // update child refs
23399         if(this.firstChild == node){
23400             this.setFirstChild(node.nextSibling);
23401         }
23402         if(this.lastChild == node){
23403             this.setLastChild(node.previousSibling);
23404         }
23405
23406         node.setOwnerTree(null);
23407         // clear any references from the node
23408         node.parentNode = null;
23409         node.previousSibling = null;
23410         node.nextSibling = null;
23411         this.fireEvent("remove", this.ownerTree, this, node);
23412         return node;
23413     },
23414
23415     /**
23416      * Inserts the first node before the second node in this nodes childNodes collection.
23417      * @param {Node} node The node to insert
23418      * @param {Node} refNode The node to insert before (if null the node is appended)
23419      * @return {Node} The inserted node
23420      */
23421     insertBefore : function(node, refNode){
23422         if(!refNode){ // like standard Dom, refNode can be null for append
23423             return this.appendChild(node);
23424         }
23425         // nothing to do
23426         if(node == refNode){
23427             return false;
23428         }
23429
23430         if(this.fireEvent("beforeinsert", this.ownerTree, this, node, refNode) === false){
23431             return false;
23432         }
23433         var index = this.childNodes.indexOf(refNode);
23434         var oldParent = node.parentNode;
23435         var refIndex = index;
23436
23437         // when moving internally, indexes will change after remove
23438         if(oldParent == this && this.childNodes.indexOf(node) < index){
23439             refIndex--;
23440         }
23441
23442         // it's a move, make sure we move it cleanly
23443         if(oldParent){
23444             if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index, refNode) === false){
23445                 return false;
23446             }
23447             oldParent.removeChild(node);
23448         }
23449         if(refIndex == 0){
23450             this.setFirstChild(node);
23451         }
23452         this.childNodes.splice(refIndex, 0, node);
23453         node.parentNode = this;
23454         var ps = this.childNodes[refIndex-1];
23455         if(ps){
23456             node.previousSibling = ps;
23457             ps.nextSibling = node;
23458         }else{
23459             node.previousSibling = null;
23460         }
23461         node.nextSibling = refNode;
23462         refNode.previousSibling = node;
23463         node.setOwnerTree(this.getOwnerTree());
23464         this.fireEvent("insert", this.ownerTree, this, node, refNode);
23465         if(oldParent){
23466             node.fireEvent("move", this.ownerTree, node, oldParent, this, refIndex, refNode);
23467         }
23468         return node;
23469     },
23470
23471     /**
23472      * Returns the child node at the specified index.
23473      * @param {Number} index
23474      * @return {Node}
23475      */
23476     item : function(index){
23477         return this.childNodes[index];
23478     },
23479
23480     /**
23481      * Replaces one child node in this node with another.
23482      * @param {Node} newChild The replacement node
23483      * @param {Node} oldChild The node to replace
23484      * @return {Node} The replaced node
23485      */
23486     replaceChild : function(newChild, oldChild){
23487         this.insertBefore(newChild, oldChild);
23488         this.removeChild(oldChild);
23489         return oldChild;
23490     },
23491
23492     /**
23493      * Returns the index of a child node
23494      * @param {Node} node
23495      * @return {Number} The index of the node or -1 if it was not found
23496      */
23497     indexOf : function(child){
23498         return this.childNodes.indexOf(child);
23499     },
23500
23501     /**
23502      * Returns the tree this node is in.
23503      * @return {Tree}
23504      */
23505     getOwnerTree : function(){
23506         // if it doesn't have one, look for one
23507         if(!this.ownerTree){
23508             var p = this;
23509             while(p){
23510                 if(p.ownerTree){
23511                     this.ownerTree = p.ownerTree;
23512                     break;
23513                 }
23514                 p = p.parentNode;
23515             }
23516         }
23517         return this.ownerTree;
23518     },
23519
23520     /**
23521      * Returns depth of this node (the root node has a depth of 0)
23522      * @return {Number}
23523      */
23524     getDepth : function(){
23525         var depth = 0;
23526         var p = this;
23527         while(p.parentNode){
23528             ++depth;
23529             p = p.parentNode;
23530         }
23531         return depth;
23532     },
23533
23534     // private
23535     setOwnerTree : function(tree){
23536         // if it's move, we need to update everyone
23537         if(tree != this.ownerTree){
23538             if(this.ownerTree){
23539                 this.ownerTree.unregisterNode(this);
23540             }
23541             this.ownerTree = tree;
23542             var cs = this.childNodes;
23543             for(var i = 0, len = cs.length; i < len; i++) {
23544                 cs[i].setOwnerTree(tree);
23545             }
23546             if(tree){
23547                 tree.registerNode(this);
23548             }
23549         }
23550     },
23551
23552     /**
23553      * Returns the path for this node. The path can be used to expand or select this node programmatically.
23554      * @param {String} attr (optional) The attr to use for the path (defaults to the node's id)
23555      * @return {String} The path
23556      */
23557     getPath : function(attr){
23558         attr = attr || "id";
23559         var p = this.parentNode;
23560         var b = [this.attributes[attr]];
23561         while(p){
23562             b.unshift(p.attributes[attr]);
23563             p = p.parentNode;
23564         }
23565         var sep = this.getOwnerTree().pathSeparator;
23566         return sep + b.join(sep);
23567     },
23568
23569     /**
23570      * Bubbles up the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
23571      * function call will be the scope provided or the current node. The arguments to the function
23572      * will be the args provided or the current node. If the function returns false at any point,
23573      * the bubble is stopped.
23574      * @param {Function} fn The function to call
23575      * @param {Object} scope (optional) The scope of the function (defaults to current node)
23576      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
23577      */
23578     bubble : function(fn, scope, args){
23579         var p = this;
23580         while(p){
23581             if(fn.call(scope || p, args || p) === false){
23582                 break;
23583             }
23584             p = p.parentNode;
23585         }
23586     },
23587
23588     /**
23589      * Cascades down the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
23590      * function call will be the scope provided or the current node. The arguments to the function
23591      * will be the args provided or the current node. If the function returns false at any point,
23592      * the cascade is stopped on that branch.
23593      * @param {Function} fn The function to call
23594      * @param {Object} scope (optional) The scope of the function (defaults to current node)
23595      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
23596      */
23597     cascade : function(fn, scope, args){
23598         if(fn.call(scope || this, args || this) !== false){
23599             var cs = this.childNodes;
23600             for(var i = 0, len = cs.length; i < len; i++) {
23601                 cs[i].cascade(fn, scope, args);
23602             }
23603         }
23604     },
23605
23606     /**
23607      * Interates the child nodes of this node, calling the specified function with each node. The scope (<i>this</i>) of
23608      * function call will be the scope provided or the current node. The arguments to the function
23609      * will be the args provided or the current node. If the function returns false at any point,
23610      * the iteration stops.
23611      * @param {Function} fn The function to call
23612      * @param {Object} scope (optional) The scope of the function (defaults to current node)
23613      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
23614      */
23615     eachChild : function(fn, scope, args){
23616         var cs = this.childNodes;
23617         for(var i = 0, len = cs.length; i < len; i++) {
23618                 if(fn.call(scope || this, args || cs[i]) === false){
23619                     break;
23620                 }
23621         }
23622     },
23623
23624     /**
23625      * Finds the first child that has the attribute with the specified value.
23626      * @param {String} attribute The attribute name
23627      * @param {Mixed} value The value to search for
23628      * @return {Node} The found child or null if none was found
23629      */
23630     findChild : function(attribute, value){
23631         var cs = this.childNodes;
23632         for(var i = 0, len = cs.length; i < len; i++) {
23633                 if(cs[i].attributes[attribute] == value){
23634                     return cs[i];
23635                 }
23636         }
23637         return null;
23638     },
23639
23640     /**
23641      * Finds the first child by a custom function. The child matches if the function passed
23642      * returns true.
23643      * @param {Function} fn
23644      * @param {Object} scope (optional)
23645      * @return {Node} The found child or null if none was found
23646      */
23647     findChildBy : function(fn, scope){
23648         var cs = this.childNodes;
23649         for(var i = 0, len = cs.length; i < len; i++) {
23650                 if(fn.call(scope||cs[i], cs[i]) === true){
23651                     return cs[i];
23652                 }
23653         }
23654         return null;
23655     },
23656
23657     /**
23658      * Sorts this nodes children using the supplied sort function
23659      * @param {Function} fn
23660      * @param {Object} scope (optional)
23661      */
23662     sort : function(fn, scope){
23663         var cs = this.childNodes;
23664         var len = cs.length;
23665         if(len > 0){
23666             var sortFn = scope ? function(){fn.apply(scope, arguments);} : fn;
23667             cs.sort(sortFn);
23668             for(var i = 0; i < len; i++){
23669                 var n = cs[i];
23670                 n.previousSibling = cs[i-1];
23671                 n.nextSibling = cs[i+1];
23672                 if(i == 0){
23673                     this.setFirstChild(n);
23674                 }
23675                 if(i == len-1){
23676                     this.setLastChild(n);
23677                 }
23678             }
23679         }
23680     },
23681
23682     /**
23683      * Returns true if this node is an ancestor (at any point) of the passed node.
23684      * @param {Node} node
23685      * @return {Boolean}
23686      */
23687     contains : function(node){
23688         return node.isAncestor(this);
23689     },
23690
23691     /**
23692      * Returns true if the passed node is an ancestor (at any point) of this node.
23693      * @param {Node} node
23694      * @return {Boolean}
23695      */
23696     isAncestor : function(node){
23697         var p = this.parentNode;
23698         while(p){
23699             if(p == node){
23700                 return true;
23701             }
23702             p = p.parentNode;
23703         }
23704         return false;
23705     },
23706
23707     toString : function(){
23708         return "[Node"+(this.id?" "+this.id:"")+"]";
23709     }
23710 });/*
23711  * Based on:
23712  * Ext JS Library 1.1.1
23713  * Copyright(c) 2006-2007, Ext JS, LLC.
23714  *
23715  * Originally Released Under LGPL - original licence link has changed is not relivant.
23716  *
23717  * Fork - LGPL
23718  * <script type="text/javascript">
23719  */
23720  (function(){ 
23721 /**
23722  * @class Roo.Layer
23723  * @extends Roo.Element
23724  * An extended {@link Roo.Element} object that supports a shadow and shim, constrain to viewport and
23725  * automatic maintaining of shadow/shim positions.
23726  * @cfg {Boolean} shim False to disable the iframe shim in browsers which need one (defaults to true)
23727  * @cfg {String/Boolean} shadow True to create a shadow element with default class "x-layer-shadow", or
23728  * you can pass a string with a CSS class name. False turns off the shadow.
23729  * @cfg {Object} dh DomHelper object config to create element with (defaults to {tag: "div", cls: "x-layer"}).
23730  * @cfg {Boolean} constrain False to disable constrain to viewport (defaults to true)
23731  * @cfg {String} cls CSS class to add to the element
23732  * @cfg {Number} zindex Starting z-index (defaults to 11000)
23733  * @cfg {Number} shadowOffset Number of pixels to offset the shadow (defaults to 3)
23734  * @constructor
23735  * @param {Object} config An object with config options.
23736  * @param {String/HTMLElement} existingEl (optional) Uses an existing DOM element. If the element is not found it creates it.
23737  */
23738
23739 Roo.Layer = function(config, existingEl){
23740     config = config || {};
23741     var dh = Roo.DomHelper;
23742     var cp = config.parentEl, pel = cp ? Roo.getDom(cp) : document.body;
23743     if(existingEl){
23744         this.dom = Roo.getDom(existingEl);
23745     }
23746     if(!this.dom){
23747         var o = config.dh || {tag: "div", cls: "x-layer"};
23748         this.dom = dh.append(pel, o);
23749     }
23750     if(config.cls){
23751         this.addClass(config.cls);
23752     }
23753     this.constrain = config.constrain !== false;
23754     this.visibilityMode = Roo.Element.VISIBILITY;
23755     if(config.id){
23756         this.id = this.dom.id = config.id;
23757     }else{
23758         this.id = Roo.id(this.dom);
23759     }
23760     this.zindex = config.zindex || this.getZIndex();
23761     this.position("absolute", this.zindex);
23762     if(config.shadow){
23763         this.shadowOffset = config.shadowOffset || 4;
23764         this.shadow = new Roo.Shadow({
23765             offset : this.shadowOffset,
23766             mode : config.shadow
23767         });
23768     }else{
23769         this.shadowOffset = 0;
23770     }
23771     this.useShim = config.shim !== false && Roo.useShims;
23772     this.useDisplay = config.useDisplay;
23773     this.hide();
23774 };
23775
23776 var supr = Roo.Element.prototype;
23777
23778 // shims are shared among layer to keep from having 100 iframes
23779 var shims = [];
23780
23781 Roo.extend(Roo.Layer, Roo.Element, {
23782
23783     getZIndex : function(){
23784         return this.zindex || parseInt(this.getStyle("z-index"), 10) || 11000;
23785     },
23786
23787     getShim : function(){
23788         if(!this.useShim){
23789             return null;
23790         }
23791         if(this.shim){
23792             return this.shim;
23793         }
23794         var shim = shims.shift();
23795         if(!shim){
23796             shim = this.createShim();
23797             shim.enableDisplayMode('block');
23798             shim.dom.style.display = 'none';
23799             shim.dom.style.visibility = 'visible';
23800         }
23801         var pn = this.dom.parentNode;
23802         if(shim.dom.parentNode != pn){
23803             pn.insertBefore(shim.dom, this.dom);
23804         }
23805         shim.setStyle('z-index', this.getZIndex()-2);
23806         this.shim = shim;
23807         return shim;
23808     },
23809
23810     hideShim : function(){
23811         if(this.shim){
23812             this.shim.setDisplayed(false);
23813             shims.push(this.shim);
23814             delete this.shim;
23815         }
23816     },
23817
23818     disableShadow : function(){
23819         if(this.shadow){
23820             this.shadowDisabled = true;
23821             this.shadow.hide();
23822             this.lastShadowOffset = this.shadowOffset;
23823             this.shadowOffset = 0;
23824         }
23825     },
23826
23827     enableShadow : function(show){
23828         if(this.shadow){
23829             this.shadowDisabled = false;
23830             this.shadowOffset = this.lastShadowOffset;
23831             delete this.lastShadowOffset;
23832             if(show){
23833                 this.sync(true);
23834             }
23835         }
23836     },
23837
23838     // private
23839     // this code can execute repeatedly in milliseconds (i.e. during a drag) so
23840     // code size was sacrificed for effeciency (e.g. no getBox/setBox, no XY calls)
23841     sync : function(doShow){
23842         var sw = this.shadow;
23843         if(!this.updating && this.isVisible() && (sw || this.useShim)){
23844             var sh = this.getShim();
23845
23846             var w = this.getWidth(),
23847                 h = this.getHeight();
23848
23849             var l = this.getLeft(true),
23850                 t = this.getTop(true);
23851
23852             if(sw && !this.shadowDisabled){
23853                 if(doShow && !sw.isVisible()){
23854                     sw.show(this);
23855                 }else{
23856                     sw.realign(l, t, w, h);
23857                 }
23858                 if(sh){
23859                     if(doShow){
23860                        sh.show();
23861                     }
23862                     // fit the shim behind the shadow, so it is shimmed too
23863                     var a = sw.adjusts, s = sh.dom.style;
23864                     s.left = (Math.min(l, l+a.l))+"px";
23865                     s.top = (Math.min(t, t+a.t))+"px";
23866                     s.width = (w+a.w)+"px";
23867                     s.height = (h+a.h)+"px";
23868                 }
23869             }else if(sh){
23870                 if(doShow){
23871                    sh.show();
23872                 }
23873                 sh.setSize(w, h);
23874                 sh.setLeftTop(l, t);
23875             }
23876             
23877         }
23878     },
23879
23880     // private
23881     destroy : function(){
23882         this.hideShim();
23883         if(this.shadow){
23884             this.shadow.hide();
23885         }
23886         this.removeAllListeners();
23887         var pn = this.dom.parentNode;
23888         if(pn){
23889             pn.removeChild(this.dom);
23890         }
23891         Roo.Element.uncache(this.id);
23892     },
23893
23894     remove : function(){
23895         this.destroy();
23896     },
23897
23898     // private
23899     beginUpdate : function(){
23900         this.updating = true;
23901     },
23902
23903     // private
23904     endUpdate : function(){
23905         this.updating = false;
23906         this.sync(true);
23907     },
23908
23909     // private
23910     hideUnders : function(negOffset){
23911         if(this.shadow){
23912             this.shadow.hide();
23913         }
23914         this.hideShim();
23915     },
23916
23917     // private
23918     constrainXY : function(){
23919         if(this.constrain){
23920             var vw = Roo.lib.Dom.getViewWidth(),
23921                 vh = Roo.lib.Dom.getViewHeight();
23922             var s = Roo.get(document).getScroll();
23923
23924             var xy = this.getXY();
23925             var x = xy[0], y = xy[1];   
23926             var w = this.dom.offsetWidth+this.shadowOffset, h = this.dom.offsetHeight+this.shadowOffset;
23927             // only move it if it needs it
23928             var moved = false;
23929             // first validate right/bottom
23930             if((x + w) > vw+s.left){
23931                 x = vw - w - this.shadowOffset;
23932                 moved = true;
23933             }
23934             if((y + h) > vh+s.top){
23935                 y = vh - h - this.shadowOffset;
23936                 moved = true;
23937             }
23938             // then make sure top/left isn't negative
23939             if(x < s.left){
23940                 x = s.left;
23941                 moved = true;
23942             }
23943             if(y < s.top){
23944                 y = s.top;
23945                 moved = true;
23946             }
23947             if(moved){
23948                 if(this.avoidY){
23949                     var ay = this.avoidY;
23950                     if(y <= ay && (y+h) >= ay){
23951                         y = ay-h-5;   
23952                     }
23953                 }
23954                 xy = [x, y];
23955                 this.storeXY(xy);
23956                 supr.setXY.call(this, xy);
23957                 this.sync();
23958             }
23959         }
23960     },
23961
23962     isVisible : function(){
23963         return this.visible;    
23964     },
23965
23966     // private
23967     showAction : function(){
23968         this.visible = true; // track visibility to prevent getStyle calls
23969         if(this.useDisplay === true){
23970             this.setDisplayed("");
23971         }else if(this.lastXY){
23972             supr.setXY.call(this, this.lastXY);
23973         }else if(this.lastLT){
23974             supr.setLeftTop.call(this, this.lastLT[0], this.lastLT[1]);
23975         }
23976     },
23977
23978     // private
23979     hideAction : function(){
23980         this.visible = false;
23981         if(this.useDisplay === true){
23982             this.setDisplayed(false);
23983         }else{
23984             this.setLeftTop(-10000,-10000);
23985         }
23986     },
23987
23988     // overridden Element method
23989     setVisible : function(v, a, d, c, e){
23990         if(v){
23991             this.showAction();
23992         }
23993         if(a && v){
23994             var cb = function(){
23995                 this.sync(true);
23996                 if(c){
23997                     c();
23998                 }
23999             }.createDelegate(this);
24000             supr.setVisible.call(this, true, true, d, cb, e);
24001         }else{
24002             if(!v){
24003                 this.hideUnders(true);
24004             }
24005             var cb = c;
24006             if(a){
24007                 cb = function(){
24008                     this.hideAction();
24009                     if(c){
24010                         c();
24011                     }
24012                 }.createDelegate(this);
24013             }
24014             supr.setVisible.call(this, v, a, d, cb, e);
24015             if(v){
24016                 this.sync(true);
24017             }else if(!a){
24018                 this.hideAction();
24019             }
24020         }
24021     },
24022
24023     storeXY : function(xy){
24024         delete this.lastLT;
24025         this.lastXY = xy;
24026     },
24027
24028     storeLeftTop : function(left, top){
24029         delete this.lastXY;
24030         this.lastLT = [left, top];
24031     },
24032
24033     // private
24034     beforeFx : function(){
24035         this.beforeAction();
24036         return Roo.Layer.superclass.beforeFx.apply(this, arguments);
24037     },
24038
24039     // private
24040     afterFx : function(){
24041         Roo.Layer.superclass.afterFx.apply(this, arguments);
24042         this.sync(this.isVisible());
24043     },
24044
24045     // private
24046     beforeAction : function(){
24047         if(!this.updating && this.shadow){
24048             this.shadow.hide();
24049         }
24050     },
24051
24052     // overridden Element method
24053     setLeft : function(left){
24054         this.storeLeftTop(left, this.getTop(true));
24055         supr.setLeft.apply(this, arguments);
24056         this.sync();
24057     },
24058
24059     setTop : function(top){
24060         this.storeLeftTop(this.getLeft(true), top);
24061         supr.setTop.apply(this, arguments);
24062         this.sync();
24063     },
24064
24065     setLeftTop : function(left, top){
24066         this.storeLeftTop(left, top);
24067         supr.setLeftTop.apply(this, arguments);
24068         this.sync();
24069     },
24070
24071     setXY : function(xy, a, d, c, e){
24072         this.fixDisplay();
24073         this.beforeAction();
24074         this.storeXY(xy);
24075         var cb = this.createCB(c);
24076         supr.setXY.call(this, xy, a, d, cb, e);
24077         if(!a){
24078             cb();
24079         }
24080     },
24081
24082     // private
24083     createCB : function(c){
24084         var el = this;
24085         return function(){
24086             el.constrainXY();
24087             el.sync(true);
24088             if(c){
24089                 c();
24090             }
24091         };
24092     },
24093
24094     // overridden Element method
24095     setX : function(x, a, d, c, e){
24096         this.setXY([x, this.getY()], a, d, c, e);
24097     },
24098
24099     // overridden Element method
24100     setY : function(y, a, d, c, e){
24101         this.setXY([this.getX(), y], a, d, c, e);
24102     },
24103
24104     // overridden Element method
24105     setSize : function(w, h, a, d, c, e){
24106         this.beforeAction();
24107         var cb = this.createCB(c);
24108         supr.setSize.call(this, w, h, a, d, cb, e);
24109         if(!a){
24110             cb();
24111         }
24112     },
24113
24114     // overridden Element method
24115     setWidth : function(w, a, d, c, e){
24116         this.beforeAction();
24117         var cb = this.createCB(c);
24118         supr.setWidth.call(this, w, a, d, cb, e);
24119         if(!a){
24120             cb();
24121         }
24122     },
24123
24124     // overridden Element method
24125     setHeight : function(h, a, d, c, e){
24126         this.beforeAction();
24127         var cb = this.createCB(c);
24128         supr.setHeight.call(this, h, a, d, cb, e);
24129         if(!a){
24130             cb();
24131         }
24132     },
24133
24134     // overridden Element method
24135     setBounds : function(x, y, w, h, a, d, c, e){
24136         this.beforeAction();
24137         var cb = this.createCB(c);
24138         if(!a){
24139             this.storeXY([x, y]);
24140             supr.setXY.call(this, [x, y]);
24141             supr.setSize.call(this, w, h, a, d, cb, e);
24142             cb();
24143         }else{
24144             supr.setBounds.call(this, x, y, w, h, a, d, cb, e);
24145         }
24146         return this;
24147     },
24148     
24149     /**
24150      * Sets the z-index of this layer and adjusts any shadow and shim z-indexes. The layer z-index is automatically
24151      * incremented by two more than the value passed in so that it always shows above any shadow or shim (the shadow
24152      * element, if any, will be assigned z-index + 1, and the shim element, if any, will be assigned the unmodified z-index).
24153      * @param {Number} zindex The new z-index to set
24154      * @return {this} The Layer
24155      */
24156     setZIndex : function(zindex){
24157         this.zindex = zindex;
24158         this.setStyle("z-index", zindex + 2);
24159         if(this.shadow){
24160             this.shadow.setZIndex(zindex + 1);
24161         }
24162         if(this.shim){
24163             this.shim.setStyle("z-index", zindex);
24164         }
24165     }
24166 });
24167 })();/*
24168  * Based on:
24169  * Ext JS Library 1.1.1
24170  * Copyright(c) 2006-2007, Ext JS, LLC.
24171  *
24172  * Originally Released Under LGPL - original licence link has changed is not relivant.
24173  *
24174  * Fork - LGPL
24175  * <script type="text/javascript">
24176  */
24177
24178
24179 /**
24180  * @class Roo.Shadow
24181  * Simple class that can provide a shadow effect for any element.  Note that the element MUST be absolutely positioned,
24182  * and the shadow does not provide any shimming.  This should be used only in simple cases -- for more advanced
24183  * functionality that can also provide the same shadow effect, see the {@link Roo.Layer} class.
24184  * @constructor
24185  * Create a new Shadow
24186  * @param {Object} config The config object
24187  */
24188 Roo.Shadow = function(config){
24189     Roo.apply(this, config);
24190     if(typeof this.mode != "string"){
24191         this.mode = this.defaultMode;
24192     }
24193     var o = this.offset, a = {h: 0};
24194     var rad = Math.floor(this.offset/2);
24195     switch(this.mode.toLowerCase()){ // all this hideous nonsense calculates the various offsets for shadows
24196         case "drop":
24197             a.w = 0;
24198             a.l = a.t = o;
24199             a.t -= 1;
24200             if(Roo.isIE){
24201                 a.l -= this.offset + rad;
24202                 a.t -= this.offset + rad;
24203                 a.w -= rad;
24204                 a.h -= rad;
24205                 a.t += 1;
24206             }
24207         break;
24208         case "sides":
24209             a.w = (o*2);
24210             a.l = -o;
24211             a.t = o-1;
24212             if(Roo.isIE){
24213                 a.l -= (this.offset - rad);
24214                 a.t -= this.offset + rad;
24215                 a.l += 1;
24216                 a.w -= (this.offset - rad)*2;
24217                 a.w -= rad + 1;
24218                 a.h -= 1;
24219             }
24220         break;
24221         case "frame":
24222             a.w = a.h = (o*2);
24223             a.l = a.t = -o;
24224             a.t += 1;
24225             a.h -= 2;
24226             if(Roo.isIE){
24227                 a.l -= (this.offset - rad);
24228                 a.t -= (this.offset - rad);
24229                 a.l += 1;
24230                 a.w -= (this.offset + rad + 1);
24231                 a.h -= (this.offset + rad);
24232                 a.h += 1;
24233             }
24234         break;
24235     };
24236
24237     this.adjusts = a;
24238 };
24239
24240 Roo.Shadow.prototype = {
24241     /**
24242      * @cfg {String} mode
24243      * The shadow display mode.  Supports the following options:<br />
24244      * sides: Shadow displays on both sides and bottom only<br />
24245      * frame: Shadow displays equally on all four sides<br />
24246      * drop: Traditional bottom-right drop shadow (default)
24247      */
24248     /**
24249      * @cfg {String} offset
24250      * The number of pixels to offset the shadow from the element (defaults to 4)
24251      */
24252     offset: 4,
24253
24254     // private
24255     defaultMode: "drop",
24256
24257     /**
24258      * Displays the shadow under the target element
24259      * @param {String/HTMLElement/Element} targetEl The id or element under which the shadow should display
24260      */
24261     show : function(target){
24262         target = Roo.get(target);
24263         if(!this.el){
24264             this.el = Roo.Shadow.Pool.pull();
24265             if(this.el.dom.nextSibling != target.dom){
24266                 this.el.insertBefore(target);
24267             }
24268         }
24269         this.el.setStyle("z-index", this.zIndex || parseInt(target.getStyle("z-index"), 10)-1);
24270         if(Roo.isIE){
24271             this.el.dom.style.filter="progid:DXImageTransform.Microsoft.alpha(opacity=50) progid:DXImageTransform.Microsoft.Blur(pixelradius="+(this.offset)+")";
24272         }
24273         this.realign(
24274             target.getLeft(true),
24275             target.getTop(true),
24276             target.getWidth(),
24277             target.getHeight()
24278         );
24279         this.el.dom.style.display = "block";
24280     },
24281
24282     /**
24283      * Returns true if the shadow is visible, else false
24284      */
24285     isVisible : function(){
24286         return this.el ? true : false;  
24287     },
24288
24289     /**
24290      * Direct alignment when values are already available. Show must be called at least once before
24291      * calling this method to ensure it is initialized.
24292      * @param {Number} left The target element left position
24293      * @param {Number} top The target element top position
24294      * @param {Number} width The target element width
24295      * @param {Number} height The target element height
24296      */
24297     realign : function(l, t, w, h){
24298         if(!this.el){
24299             return;
24300         }
24301         var a = this.adjusts, d = this.el.dom, s = d.style;
24302         var iea = 0;
24303         s.left = (l+a.l)+"px";
24304         s.top = (t+a.t)+"px";
24305         var sw = (w+a.w), sh = (h+a.h), sws = sw +"px", shs = sh + "px";
24306  
24307         if(s.width != sws || s.height != shs){
24308             s.width = sws;
24309             s.height = shs;
24310             if(!Roo.isIE){
24311                 var cn = d.childNodes;
24312                 var sww = Math.max(0, (sw-12))+"px";
24313                 cn[0].childNodes[1].style.width = sww;
24314                 cn[1].childNodes[1].style.width = sww;
24315                 cn[2].childNodes[1].style.width = sww;
24316                 cn[1].style.height = Math.max(0, (sh-12))+"px";
24317             }
24318         }
24319     },
24320
24321     /**
24322      * Hides this shadow
24323      */
24324     hide : function(){
24325         if(this.el){
24326             this.el.dom.style.display = "none";
24327             Roo.Shadow.Pool.push(this.el);
24328             delete this.el;
24329         }
24330     },
24331
24332     /**
24333      * Adjust the z-index of this shadow
24334      * @param {Number} zindex The new z-index
24335      */
24336     setZIndex : function(z){
24337         this.zIndex = z;
24338         if(this.el){
24339             this.el.setStyle("z-index", z);
24340         }
24341     }
24342 };
24343
24344 // Private utility class that manages the internal Shadow cache
24345 Roo.Shadow.Pool = function(){
24346     var p = [];
24347     var markup = Roo.isIE ?
24348                  '<div class="x-ie-shadow"></div>' :
24349                  '<div class="x-shadow"><div class="xst"><div class="xstl"></div><div class="xstc"></div><div class="xstr"></div></div><div class="xsc"><div class="xsml"></div><div class="xsmc"></div><div class="xsmr"></div></div><div class="xsb"><div class="xsbl"></div><div class="xsbc"></div><div class="xsbr"></div></div></div>';
24350     return {
24351         pull : function(){
24352             var sh = p.shift();
24353             if(!sh){
24354                 sh = Roo.get(Roo.DomHelper.insertHtml("beforeBegin", document.body.firstChild, markup));
24355                 sh.autoBoxAdjust = false;
24356             }
24357             return sh;
24358         },
24359
24360         push : function(sh){
24361             p.push(sh);
24362         }
24363     };
24364 }();/*
24365  * Based on:
24366  * Ext JS Library 1.1.1
24367  * Copyright(c) 2006-2007, Ext JS, LLC.
24368  *
24369  * Originally Released Under LGPL - original licence link has changed is not relivant.
24370  *
24371  * Fork - LGPL
24372  * <script type="text/javascript">
24373  */
24374
24375
24376 /**
24377  * @class Roo.SplitBar
24378  * @extends Roo.util.Observable
24379  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
24380  * <br><br>
24381  * Usage:
24382  * <pre><code>
24383 var split = new Roo.SplitBar("elementToDrag", "elementToSize",
24384                    Roo.SplitBar.HORIZONTAL, Roo.SplitBar.LEFT);
24385 split.setAdapter(new Roo.SplitBar.AbsoluteLayoutAdapter("container"));
24386 split.minSize = 100;
24387 split.maxSize = 600;
24388 split.animate = true;
24389 split.on('moved', splitterMoved);
24390 </code></pre>
24391  * @constructor
24392  * Create a new SplitBar
24393  * @param {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
24394  * @param {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
24395  * @param {Number} orientation (optional) Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
24396  * @param {Number} placement (optional) Either Roo.SplitBar.LEFT or Roo.SplitBar.RIGHT for horizontal or  
24397                         Roo.SplitBar.TOP or Roo.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
24398                         position of the SplitBar).
24399  */
24400 Roo.SplitBar = function(dragElement, resizingElement, orientation, placement, existingProxy){
24401     
24402     /** @private */
24403     this.el = Roo.get(dragElement, true);
24404     this.el.dom.unselectable = "on";
24405     /** @private */
24406     this.resizingEl = Roo.get(resizingElement, true);
24407
24408     /**
24409      * @private
24410      * The orientation of the split. Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
24411      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
24412      * @type Number
24413      */
24414     this.orientation = orientation || Roo.SplitBar.HORIZONTAL;
24415     
24416     /**
24417      * The minimum size of the resizing element. (Defaults to 0)
24418      * @type Number
24419      */
24420     this.minSize = 0;
24421     
24422     /**
24423      * The maximum size of the resizing element. (Defaults to 2000)
24424      * @type Number
24425      */
24426     this.maxSize = 2000;
24427     
24428     /**
24429      * Whether to animate the transition to the new size
24430      * @type Boolean
24431      */
24432     this.animate = false;
24433     
24434     /**
24435      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
24436      * @type Boolean
24437      */
24438     this.useShim = false;
24439     
24440     /** @private */
24441     this.shim = null;
24442     
24443     if(!existingProxy){
24444         /** @private */
24445         this.proxy = Roo.SplitBar.createProxy(this.orientation);
24446     }else{
24447         this.proxy = Roo.get(existingProxy).dom;
24448     }
24449     /** @private */
24450     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
24451     
24452     /** @private */
24453     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
24454     
24455     /** @private */
24456     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
24457     
24458     /** @private */
24459     this.dragSpecs = {};
24460     
24461     /**
24462      * @private The adapter to use to positon and resize elements
24463      */
24464     this.adapter = new Roo.SplitBar.BasicLayoutAdapter();
24465     this.adapter.init(this);
24466     
24467     if(this.orientation == Roo.SplitBar.HORIZONTAL){
24468         /** @private */
24469         this.placement = placement || (this.el.getX() > this.resizingEl.getX() ? Roo.SplitBar.LEFT : Roo.SplitBar.RIGHT);
24470         this.el.addClass("x-splitbar-h");
24471     }else{
24472         /** @private */
24473         this.placement = placement || (this.el.getY() > this.resizingEl.getY() ? Roo.SplitBar.TOP : Roo.SplitBar.BOTTOM);
24474         this.el.addClass("x-splitbar-v");
24475     }
24476     
24477     this.addEvents({
24478         /**
24479          * @event resize
24480          * Fires when the splitter is moved (alias for {@link #event-moved})
24481          * @param {Roo.SplitBar} this
24482          * @param {Number} newSize the new width or height
24483          */
24484         "resize" : true,
24485         /**
24486          * @event moved
24487          * Fires when the splitter is moved
24488          * @param {Roo.SplitBar} this
24489          * @param {Number} newSize the new width or height
24490          */
24491         "moved" : true,
24492         /**
24493          * @event beforeresize
24494          * Fires before the splitter is dragged
24495          * @param {Roo.SplitBar} this
24496          */
24497         "beforeresize" : true,
24498
24499         "beforeapply" : true
24500     });
24501
24502     Roo.util.Observable.call(this);
24503 };
24504
24505 Roo.extend(Roo.SplitBar, Roo.util.Observable, {
24506     onStartProxyDrag : function(x, y){
24507         this.fireEvent("beforeresize", this);
24508         if(!this.overlay){
24509             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "x-drag-overlay", html: "&#160;"}, true);
24510             o.unselectable();
24511             o.enableDisplayMode("block");
24512             // all splitbars share the same overlay
24513             Roo.SplitBar.prototype.overlay = o;
24514         }
24515         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
24516         this.overlay.show();
24517         Roo.get(this.proxy).setDisplayed("block");
24518         var size = this.adapter.getElementSize(this);
24519         this.activeMinSize = this.getMinimumSize();;
24520         this.activeMaxSize = this.getMaximumSize();;
24521         var c1 = size - this.activeMinSize;
24522         var c2 = Math.max(this.activeMaxSize - size, 0);
24523         if(this.orientation == Roo.SplitBar.HORIZONTAL){
24524             this.dd.resetConstraints();
24525             this.dd.setXConstraint(
24526                 this.placement == Roo.SplitBar.LEFT ? c1 : c2, 
24527                 this.placement == Roo.SplitBar.LEFT ? c2 : c1
24528             );
24529             this.dd.setYConstraint(0, 0);
24530         }else{
24531             this.dd.resetConstraints();
24532             this.dd.setXConstraint(0, 0);
24533             this.dd.setYConstraint(
24534                 this.placement == Roo.SplitBar.TOP ? c1 : c2, 
24535                 this.placement == Roo.SplitBar.TOP ? c2 : c1
24536             );
24537          }
24538         this.dragSpecs.startSize = size;
24539         this.dragSpecs.startPoint = [x, y];
24540         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
24541     },
24542     
24543     /** 
24544      * @private Called after the drag operation by the DDProxy
24545      */
24546     onEndProxyDrag : function(e){
24547         Roo.get(this.proxy).setDisplayed(false);
24548         var endPoint = Roo.lib.Event.getXY(e);
24549         if(this.overlay){
24550             this.overlay.hide();
24551         }
24552         var newSize;
24553         if(this.orientation == Roo.SplitBar.HORIZONTAL){
24554             newSize = this.dragSpecs.startSize + 
24555                 (this.placement == Roo.SplitBar.LEFT ?
24556                     endPoint[0] - this.dragSpecs.startPoint[0] :
24557                     this.dragSpecs.startPoint[0] - endPoint[0]
24558                 );
24559         }else{
24560             newSize = this.dragSpecs.startSize + 
24561                 (this.placement == Roo.SplitBar.TOP ?
24562                     endPoint[1] - this.dragSpecs.startPoint[1] :
24563                     this.dragSpecs.startPoint[1] - endPoint[1]
24564                 );
24565         }
24566         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
24567         if(newSize != this.dragSpecs.startSize){
24568             if(this.fireEvent('beforeapply', this, newSize) !== false){
24569                 this.adapter.setElementSize(this, newSize);
24570                 this.fireEvent("moved", this, newSize);
24571                 this.fireEvent("resize", this, newSize);
24572             }
24573         }
24574     },
24575     
24576     /**
24577      * Get the adapter this SplitBar uses
24578      * @return The adapter object
24579      */
24580     getAdapter : function(){
24581         return this.adapter;
24582     },
24583     
24584     /**
24585      * Set the adapter this SplitBar uses
24586      * @param {Object} adapter A SplitBar adapter object
24587      */
24588     setAdapter : function(adapter){
24589         this.adapter = adapter;
24590         this.adapter.init(this);
24591     },
24592     
24593     /**
24594      * Gets the minimum size for the resizing element
24595      * @return {Number} The minimum size
24596      */
24597     getMinimumSize : function(){
24598         return this.minSize;
24599     },
24600     
24601     /**
24602      * Sets the minimum size for the resizing element
24603      * @param {Number} minSize The minimum size
24604      */
24605     setMinimumSize : function(minSize){
24606         this.minSize = minSize;
24607     },
24608     
24609     /**
24610      * Gets the maximum size for the resizing element
24611      * @return {Number} The maximum size
24612      */
24613     getMaximumSize : function(){
24614         return this.maxSize;
24615     },
24616     
24617     /**
24618      * Sets the maximum size for the resizing element
24619      * @param {Number} maxSize The maximum size
24620      */
24621     setMaximumSize : function(maxSize){
24622         this.maxSize = maxSize;
24623     },
24624     
24625     /**
24626      * Sets the initialize size for the resizing element
24627      * @param {Number} size The initial size
24628      */
24629     setCurrentSize : function(size){
24630         var oldAnimate = this.animate;
24631         this.animate = false;
24632         this.adapter.setElementSize(this, size);
24633         this.animate = oldAnimate;
24634     },
24635     
24636     /**
24637      * Destroy this splitbar. 
24638      * @param {Boolean} removeEl True to remove the element
24639      */
24640     destroy : function(removeEl){
24641         if(this.shim){
24642             this.shim.remove();
24643         }
24644         this.dd.unreg();
24645         this.proxy.parentNode.removeChild(this.proxy);
24646         if(removeEl){
24647             this.el.remove();
24648         }
24649     }
24650 });
24651
24652 /**
24653  * @private static Create our own proxy element element. So it will be the same same size on all browsers, we won't use borders. Instead we use a background color.
24654  */
24655 Roo.SplitBar.createProxy = function(dir){
24656     var proxy = new Roo.Element(document.createElement("div"));
24657     proxy.unselectable();
24658     var cls = 'x-splitbar-proxy';
24659     proxy.addClass(cls + ' ' + (dir == Roo.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
24660     document.body.appendChild(proxy.dom);
24661     return proxy.dom;
24662 };
24663
24664 /** 
24665  * @class Roo.SplitBar.BasicLayoutAdapter
24666  * Default Adapter. It assumes the splitter and resizing element are not positioned
24667  * elements and only gets/sets the width of the element. Generally used for table based layouts.
24668  */
24669 Roo.SplitBar.BasicLayoutAdapter = function(){
24670 };
24671
24672 Roo.SplitBar.BasicLayoutAdapter.prototype = {
24673     // do nothing for now
24674     init : function(s){
24675     
24676     },
24677     /**
24678      * Called before drag operations to get the current size of the resizing element. 
24679      * @param {Roo.SplitBar} s The SplitBar using this adapter
24680      */
24681      getElementSize : function(s){
24682         if(s.orientation == Roo.SplitBar.HORIZONTAL){
24683             return s.resizingEl.getWidth();
24684         }else{
24685             return s.resizingEl.getHeight();
24686         }
24687     },
24688     
24689     /**
24690      * Called after drag operations to set the size of the resizing element.
24691      * @param {Roo.SplitBar} s The SplitBar using this adapter
24692      * @param {Number} newSize The new size to set
24693      * @param {Function} onComplete A function to be invoked when resizing is complete
24694      */
24695     setElementSize : function(s, newSize, onComplete){
24696         if(s.orientation == Roo.SplitBar.HORIZONTAL){
24697             if(!s.animate){
24698                 s.resizingEl.setWidth(newSize);
24699                 if(onComplete){
24700                     onComplete(s, newSize);
24701                 }
24702             }else{
24703                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
24704             }
24705         }else{
24706             
24707             if(!s.animate){
24708                 s.resizingEl.setHeight(newSize);
24709                 if(onComplete){
24710                     onComplete(s, newSize);
24711                 }
24712             }else{
24713                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
24714             }
24715         }
24716     }
24717 };
24718
24719 /** 
24720  *@class Roo.SplitBar.AbsoluteLayoutAdapter
24721  * @extends Roo.SplitBar.BasicLayoutAdapter
24722  * Adapter that  moves the splitter element to align with the resized sizing element. 
24723  * Used with an absolute positioned SplitBar.
24724  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
24725  * document.body, make sure you assign an id to the body element.
24726  */
24727 Roo.SplitBar.AbsoluteLayoutAdapter = function(container){
24728     this.basic = new Roo.SplitBar.BasicLayoutAdapter();
24729     this.container = Roo.get(container);
24730 };
24731
24732 Roo.SplitBar.AbsoluteLayoutAdapter.prototype = {
24733     init : function(s){
24734         this.basic.init(s);
24735     },
24736     
24737     getElementSize : function(s){
24738         return this.basic.getElementSize(s);
24739     },
24740     
24741     setElementSize : function(s, newSize, onComplete){
24742         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
24743     },
24744     
24745     moveSplitter : function(s){
24746         var yes = Roo.SplitBar;
24747         switch(s.placement){
24748             case yes.LEFT:
24749                 s.el.setX(s.resizingEl.getRight());
24750                 break;
24751             case yes.RIGHT:
24752                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
24753                 break;
24754             case yes.TOP:
24755                 s.el.setY(s.resizingEl.getBottom());
24756                 break;
24757             case yes.BOTTOM:
24758                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
24759                 break;
24760         }
24761     }
24762 };
24763
24764 /**
24765  * Orientation constant - Create a vertical SplitBar
24766  * @static
24767  * @type Number
24768  */
24769 Roo.SplitBar.VERTICAL = 1;
24770
24771 /**
24772  * Orientation constant - Create a horizontal SplitBar
24773  * @static
24774  * @type Number
24775  */
24776 Roo.SplitBar.HORIZONTAL = 2;
24777
24778 /**
24779  * Placement constant - The resizing element is to the left of the splitter element
24780  * @static
24781  * @type Number
24782  */
24783 Roo.SplitBar.LEFT = 1;
24784
24785 /**
24786  * Placement constant - The resizing element is to the right of the splitter element
24787  * @static
24788  * @type Number
24789  */
24790 Roo.SplitBar.RIGHT = 2;
24791
24792 /**
24793  * Placement constant - The resizing element is positioned above the splitter element
24794  * @static
24795  * @type Number
24796  */
24797 Roo.SplitBar.TOP = 3;
24798
24799 /**
24800  * Placement constant - The resizing element is positioned under splitter element
24801  * @static
24802  * @type Number
24803  */
24804 Roo.SplitBar.BOTTOM = 4;
24805 /*
24806  * Based on:
24807  * Ext JS Library 1.1.1
24808  * Copyright(c) 2006-2007, Ext JS, LLC.
24809  *
24810  * Originally Released Under LGPL - original licence link has changed is not relivant.
24811  *
24812  * Fork - LGPL
24813  * <script type="text/javascript">
24814  */
24815
24816 /**
24817  * @class Roo.View
24818  * @extends Roo.util.Observable
24819  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
24820  * This class also supports single and multi selection modes. <br>
24821  * Create a data model bound view:
24822  <pre><code>
24823  var store = new Roo.data.Store(...);
24824
24825  var view = new Roo.View({
24826     el : "my-element",
24827     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
24828  
24829     singleSelect: true,
24830     selectedClass: "ydataview-selected",
24831     store: store
24832  });
24833
24834  // listen for node click?
24835  view.on("click", function(vw, index, node, e){
24836  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
24837  });
24838
24839  // load XML data
24840  dataModel.load("foobar.xml");
24841  </code></pre>
24842  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
24843  * <br><br>
24844  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
24845  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
24846  * 
24847  * Note: old style constructor is still suported (container, template, config)
24848  * 
24849  * @constructor
24850  * Create a new View
24851  * @param {Object} config The config object
24852  * 
24853  */
24854 Roo.View = function(config, depreciated_tpl, depreciated_config){
24855     
24856     this.parent = false;
24857     
24858     if (typeof(depreciated_tpl) == 'undefined') {
24859         // new way.. - universal constructor.
24860         Roo.apply(this, config);
24861         this.el  = Roo.get(this.el);
24862     } else {
24863         // old format..
24864         this.el  = Roo.get(config);
24865         this.tpl = depreciated_tpl;
24866         Roo.apply(this, depreciated_config);
24867     }
24868     this.wrapEl  = this.el.wrap().wrap();
24869     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
24870     
24871     
24872     if(typeof(this.tpl) == "string"){
24873         this.tpl = new Roo.Template(this.tpl);
24874     } else {
24875         // support xtype ctors..
24876         this.tpl = new Roo.factory(this.tpl, Roo);
24877     }
24878     
24879     
24880     this.tpl.compile();
24881     
24882     /** @private */
24883     this.addEvents({
24884         /**
24885          * @event beforeclick
24886          * Fires before a click is processed. Returns false to cancel the default action.
24887          * @param {Roo.View} this
24888          * @param {Number} index The index of the target node
24889          * @param {HTMLElement} node The target node
24890          * @param {Roo.EventObject} e The raw event object
24891          */
24892             "beforeclick" : true,
24893         /**
24894          * @event click
24895          * Fires when a template node is clicked.
24896          * @param {Roo.View} this
24897          * @param {Number} index The index of the target node
24898          * @param {HTMLElement} node The target node
24899          * @param {Roo.EventObject} e The raw event object
24900          */
24901             "click" : true,
24902         /**
24903          * @event dblclick
24904          * Fires when a template node is double clicked.
24905          * @param {Roo.View} this
24906          * @param {Number} index The index of the target node
24907          * @param {HTMLElement} node The target node
24908          * @param {Roo.EventObject} e The raw event object
24909          */
24910             "dblclick" : true,
24911         /**
24912          * @event contextmenu
24913          * Fires when a template node is right clicked.
24914          * @param {Roo.View} this
24915          * @param {Number} index The index of the target node
24916          * @param {HTMLElement} node The target node
24917          * @param {Roo.EventObject} e The raw event object
24918          */
24919             "contextmenu" : true,
24920         /**
24921          * @event selectionchange
24922          * Fires when the selected nodes change.
24923          * @param {Roo.View} this
24924          * @param {Array} selections Array of the selected nodes
24925          */
24926             "selectionchange" : true,
24927     
24928         /**
24929          * @event beforeselect
24930          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
24931          * @param {Roo.View} this
24932          * @param {HTMLElement} node The node to be selected
24933          * @param {Array} selections Array of currently selected nodes
24934          */
24935             "beforeselect" : true,
24936         /**
24937          * @event preparedata
24938          * Fires on every row to render, to allow you to change the data.
24939          * @param {Roo.View} this
24940          * @param {Object} data to be rendered (change this)
24941          */
24942           "preparedata" : true
24943           
24944           
24945         });
24946
24947
24948
24949     this.el.on({
24950         "click": this.onClick,
24951         "dblclick": this.onDblClick,
24952         "contextmenu": this.onContextMenu,
24953         scope:this
24954     });
24955
24956     this.selections = [];
24957     this.nodes = [];
24958     this.cmp = new Roo.CompositeElementLite([]);
24959     if(this.store){
24960         this.store = Roo.factory(this.store, Roo.data);
24961         this.setStore(this.store, true);
24962     }
24963     
24964     if ( this.footer && this.footer.xtype) {
24965            
24966          var fctr = this.wrapEl.appendChild(document.createElement("div"));
24967         
24968         this.footer.dataSource = this.store
24969         this.footer.container = fctr;
24970         this.footer = Roo.factory(this.footer, Roo);
24971         fctr.insertFirst(this.el);
24972         
24973         // this is a bit insane - as the paging toolbar seems to detach the el..
24974 //        dom.parentNode.parentNode.parentNode
24975          // they get detached?
24976     }
24977     
24978     
24979     Roo.View.superclass.constructor.call(this);
24980     
24981     
24982 };
24983
24984 Roo.extend(Roo.View, Roo.util.Observable, {
24985     
24986      /**
24987      * @cfg {Roo.data.Store} store Data store to load data from.
24988      */
24989     store : false,
24990     
24991     /**
24992      * @cfg {String|Roo.Element} el The container element.
24993      */
24994     el : '',
24995     
24996     /**
24997      * @cfg {String|Roo.Template} tpl The template used by this View 
24998      */
24999     tpl : false,
25000     /**
25001      * @cfg {String} dataName the named area of the template to use as the data area
25002      *                          Works with domtemplates roo-name="name"
25003      */
25004     dataName: false,
25005     /**
25006      * @cfg {String} selectedClass The css class to add to selected nodes
25007      */
25008     selectedClass : "x-view-selected",
25009      /**
25010      * @cfg {String} emptyText The empty text to show when nothing is loaded.
25011      */
25012     emptyText : "",
25013     
25014     /**
25015      * @cfg {String} text to display on mask (default Loading)
25016      */
25017     mask : false,
25018     /**
25019      * @cfg {Boolean} multiSelect Allow multiple selection
25020      */
25021     multiSelect : false,
25022     /**
25023      * @cfg {Boolean} singleSelect Allow single selection
25024      */
25025     singleSelect:  false,
25026     
25027     /**
25028      * @cfg {Boolean} toggleSelect - selecting 
25029      */
25030     toggleSelect : false,
25031     
25032     /**
25033      * @cfg {Boolean} tickable - selecting 
25034      */
25035     tickable : false,
25036     
25037     /**
25038      * Returns the element this view is bound to.
25039      * @return {Roo.Element}
25040      */
25041     getEl : function(){
25042         return this.wrapEl;
25043     },
25044     
25045     
25046
25047     /**
25048      * Refreshes the view. - called by datachanged on the store. - do not call directly.
25049      */
25050     refresh : function(){
25051         Roo.log('refresh');
25052         var t = this.tpl;
25053         
25054         // if we are using something like 'domtemplate', then
25055         // the what gets used is:
25056         // t.applySubtemplate(NAME, data, wrapping data..)
25057         // the outer template then get' applied with
25058         //     the store 'extra data'
25059         // and the body get's added to the
25060         //      roo-name="data" node?
25061         //      <span class='roo-tpl-{name}'></span> ?????
25062         
25063         
25064         
25065         this.clearSelections();
25066         this.el.update("");
25067         var html = [];
25068         var records = this.store.getRange();
25069         if(records.length < 1) {
25070             
25071             // is this valid??  = should it render a template??
25072             
25073             this.el.update(this.emptyText);
25074             return;
25075         }
25076         var el = this.el;
25077         if (this.dataName) {
25078             this.el.update(t.apply(this.store.meta)); //????
25079             el = this.el.child('.roo-tpl-' + this.dataName);
25080         }
25081         
25082         for(var i = 0, len = records.length; i < len; i++){
25083             var data = this.prepareData(records[i].data, i, records[i]);
25084             this.fireEvent("preparedata", this, data, i, records[i]);
25085             
25086             var d = Roo.apply({}, data);
25087             
25088             if(this.tickable){
25089                 Roo.apply(d, {'roo-id' : Roo.id()});
25090                 
25091                 var _this = this;
25092             
25093                 Roo.each(this.parent.item, function(item){
25094                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
25095                         return;
25096                     }
25097                     Roo.apply(d, {'roo-data-checked' : 'checked'});
25098                 });
25099             }
25100             
25101             html[html.length] = Roo.util.Format.trim(
25102                 this.dataName ?
25103                     t.applySubtemplate(this.dataName, d, this.store.meta) :
25104                     t.apply(d)
25105             );
25106         }
25107         
25108         
25109         
25110         el.update(html.join(""));
25111         this.nodes = el.dom.childNodes;
25112         this.updateIndexes(0);
25113     },
25114     
25115
25116     /**
25117      * Function to override to reformat the data that is sent to
25118      * the template for each node.
25119      * DEPRICATED - use the preparedata event handler.
25120      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
25121      * a JSON object for an UpdateManager bound view).
25122      */
25123     prepareData : function(data, index, record)
25124     {
25125         this.fireEvent("preparedata", this, data, index, record);
25126         return data;
25127     },
25128
25129     onUpdate : function(ds, record){
25130          Roo.log('on update');   
25131         this.clearSelections();
25132         var index = this.store.indexOf(record);
25133         var n = this.nodes[index];
25134         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
25135         n.parentNode.removeChild(n);
25136         this.updateIndexes(index, index);
25137     },
25138
25139     
25140     
25141 // --------- FIXME     
25142     onAdd : function(ds, records, index)
25143     {
25144         Roo.log(['on Add', ds, records, index] );        
25145         this.clearSelections();
25146         if(this.nodes.length == 0){
25147             this.refresh();
25148             return;
25149         }
25150         var n = this.nodes[index];
25151         for(var i = 0, len = records.length; i < len; i++){
25152             var d = this.prepareData(records[i].data, i, records[i]);
25153             if(n){
25154                 this.tpl.insertBefore(n, d);
25155             }else{
25156                 
25157                 this.tpl.append(this.el, d);
25158             }
25159         }
25160         this.updateIndexes(index);
25161     },
25162
25163     onRemove : function(ds, record, index){
25164         Roo.log('onRemove');
25165         this.clearSelections();
25166         var el = this.dataName  ?
25167             this.el.child('.roo-tpl-' + this.dataName) :
25168             this.el; 
25169         
25170         el.dom.removeChild(this.nodes[index]);
25171         this.updateIndexes(index);
25172     },
25173
25174     /**
25175      * Refresh an individual node.
25176      * @param {Number} index
25177      */
25178     refreshNode : function(index){
25179         this.onUpdate(this.store, this.store.getAt(index));
25180     },
25181
25182     updateIndexes : function(startIndex, endIndex){
25183         var ns = this.nodes;
25184         startIndex = startIndex || 0;
25185         endIndex = endIndex || ns.length - 1;
25186         for(var i = startIndex; i <= endIndex; i++){
25187             ns[i].nodeIndex = i;
25188         }
25189     },
25190
25191     /**
25192      * Changes the data store this view uses and refresh the view.
25193      * @param {Store} store
25194      */
25195     setStore : function(store, initial){
25196         if(!initial && this.store){
25197             this.store.un("datachanged", this.refresh);
25198             this.store.un("add", this.onAdd);
25199             this.store.un("remove", this.onRemove);
25200             this.store.un("update", this.onUpdate);
25201             this.store.un("clear", this.refresh);
25202             this.store.un("beforeload", this.onBeforeLoad);
25203             this.store.un("load", this.onLoad);
25204             this.store.un("loadexception", this.onLoad);
25205         }
25206         if(store){
25207           
25208             store.on("datachanged", this.refresh, this);
25209             store.on("add", this.onAdd, this);
25210             store.on("remove", this.onRemove, this);
25211             store.on("update", this.onUpdate, this);
25212             store.on("clear", this.refresh, this);
25213             store.on("beforeload", this.onBeforeLoad, this);
25214             store.on("load", this.onLoad, this);
25215             store.on("loadexception", this.onLoad, this);
25216         }
25217         
25218         if(store){
25219             this.refresh();
25220         }
25221     },
25222     /**
25223      * onbeforeLoad - masks the loading area.
25224      *
25225      */
25226     onBeforeLoad : function(store,opts)
25227     {
25228          Roo.log('onBeforeLoad');   
25229         if (!opts.add) {
25230             this.el.update("");
25231         }
25232         this.el.mask(this.mask ? this.mask : "Loading" ); 
25233     },
25234     onLoad : function ()
25235     {
25236         this.el.unmask();
25237     },
25238     
25239
25240     /**
25241      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
25242      * @param {HTMLElement} node
25243      * @return {HTMLElement} The template node
25244      */
25245     findItemFromChild : function(node){
25246         var el = this.dataName  ?
25247             this.el.child('.roo-tpl-' + this.dataName,true) :
25248             this.el.dom; 
25249         
25250         if(!node || node.parentNode == el){
25251                     return node;
25252             }
25253             var p = node.parentNode;
25254             while(p && p != el){
25255             if(p.parentNode == el){
25256                 return p;
25257             }
25258             p = p.parentNode;
25259         }
25260             return null;
25261     },
25262
25263     /** @ignore */
25264     onClick : function(e){
25265         var item = this.findItemFromChild(e.getTarget());
25266         if(item){
25267             var index = this.indexOf(item);
25268             if(this.onItemClick(item, index, e) !== false){
25269                 this.fireEvent("click", this, index, item, e);
25270             }
25271         }else{
25272             this.clearSelections();
25273         }
25274     },
25275
25276     /** @ignore */
25277     onContextMenu : function(e){
25278         var item = this.findItemFromChild(e.getTarget());
25279         if(item){
25280             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
25281         }
25282     },
25283
25284     /** @ignore */
25285     onDblClick : function(e){
25286         var item = this.findItemFromChild(e.getTarget());
25287         if(item){
25288             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
25289         }
25290     },
25291
25292     onItemClick : function(item, index, e)
25293     {
25294         if(this.fireEvent("beforeclick", this, index, item, e) === false){
25295             return false;
25296         }
25297         if (this.toggleSelect) {
25298             var m = this.isSelected(item) ? 'unselect' : 'select';
25299             Roo.log(m);
25300             var _t = this;
25301             _t[m](item, true, false);
25302             return true;
25303         }
25304         if(this.multiSelect || this.singleSelect){
25305             if(this.multiSelect && e.shiftKey && this.lastSelection){
25306                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
25307             }else{
25308                 this.select(item, this.multiSelect && e.ctrlKey);
25309                 this.lastSelection = item;
25310             }
25311             
25312             if(!this.tickable){
25313                 e.preventDefault();
25314             }
25315             
25316         }
25317         return true;
25318     },
25319
25320     /**
25321      * Get the number of selected nodes.
25322      * @return {Number}
25323      */
25324     getSelectionCount : function(){
25325         return this.selections.length;
25326     },
25327
25328     /**
25329      * Get the currently selected nodes.
25330      * @return {Array} An array of HTMLElements
25331      */
25332     getSelectedNodes : function(){
25333         return this.selections;
25334     },
25335
25336     /**
25337      * Get the indexes of the selected nodes.
25338      * @return {Array}
25339      */
25340     getSelectedIndexes : function(){
25341         var indexes = [], s = this.selections;
25342         for(var i = 0, len = s.length; i < len; i++){
25343             indexes.push(s[i].nodeIndex);
25344         }
25345         return indexes;
25346     },
25347
25348     /**
25349      * Clear all selections
25350      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
25351      */
25352     clearSelections : function(suppressEvent){
25353         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
25354             this.cmp.elements = this.selections;
25355             this.cmp.removeClass(this.selectedClass);
25356             this.selections = [];
25357             if(!suppressEvent){
25358                 this.fireEvent("selectionchange", this, this.selections);
25359             }
25360         }
25361     },
25362
25363     /**
25364      * Returns true if the passed node is selected
25365      * @param {HTMLElement/Number} node The node or node index
25366      * @return {Boolean}
25367      */
25368     isSelected : function(node){
25369         var s = this.selections;
25370         if(s.length < 1){
25371             return false;
25372         }
25373         node = this.getNode(node);
25374         return s.indexOf(node) !== -1;
25375     },
25376
25377     /**
25378      * Selects nodes.
25379      * @param {Array/HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node, id of a template node or an array of any of those to select
25380      * @param {Boolean} keepExisting (optional) true to keep existing selections
25381      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
25382      */
25383     select : function(nodeInfo, keepExisting, suppressEvent){
25384         if(nodeInfo instanceof Array){
25385             if(!keepExisting){
25386                 this.clearSelections(true);
25387             }
25388             for(var i = 0, len = nodeInfo.length; i < len; i++){
25389                 this.select(nodeInfo[i], true, true);
25390             }
25391             return;
25392         } 
25393         var node = this.getNode(nodeInfo);
25394         if(!node || this.isSelected(node)){
25395             return; // already selected.
25396         }
25397         if(!keepExisting){
25398             this.clearSelections(true);
25399         }
25400         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
25401             Roo.fly(node).addClass(this.selectedClass);
25402             this.selections.push(node);
25403             if(!suppressEvent){
25404                 this.fireEvent("selectionchange", this, this.selections);
25405             }
25406         }
25407         
25408         
25409     },
25410       /**
25411      * Unselects nodes.
25412      * @param {Array/HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node, id of a template node or an array of any of those to select
25413      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
25414      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
25415      */
25416     unselect : function(nodeInfo, keepExisting, suppressEvent)
25417     {
25418         if(nodeInfo instanceof Array){
25419             Roo.each(this.selections, function(s) {
25420                 this.unselect(s, nodeInfo);
25421             }, this);
25422             return;
25423         }
25424         var node = this.getNode(nodeInfo);
25425         if(!node || !this.isSelected(node)){
25426             Roo.log("not selected");
25427             return; // not selected.
25428         }
25429         // fireevent???
25430         var ns = [];
25431         Roo.each(this.selections, function(s) {
25432             if (s == node ) {
25433                 Roo.fly(node).removeClass(this.selectedClass);
25434
25435                 return;
25436             }
25437             ns.push(s);
25438         },this);
25439         
25440         this.selections= ns;
25441         this.fireEvent("selectionchange", this, this.selections);
25442     },
25443
25444     /**
25445      * Gets a template node.
25446      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
25447      * @return {HTMLElement} The node or null if it wasn't found
25448      */
25449     getNode : function(nodeInfo){
25450         if(typeof nodeInfo == "string"){
25451             return document.getElementById(nodeInfo);
25452         }else if(typeof nodeInfo == "number"){
25453             return this.nodes[nodeInfo];
25454         }
25455         return nodeInfo;
25456     },
25457
25458     /**
25459      * Gets a range template nodes.
25460      * @param {Number} startIndex
25461      * @param {Number} endIndex
25462      * @return {Array} An array of nodes
25463      */
25464     getNodes : function(start, end){
25465         var ns = this.nodes;
25466         start = start || 0;
25467         end = typeof end == "undefined" ? ns.length - 1 : end;
25468         var nodes = [];
25469         if(start <= end){
25470             for(var i = start; i <= end; i++){
25471                 nodes.push(ns[i]);
25472             }
25473         } else{
25474             for(var i = start; i >= end; i--){
25475                 nodes.push(ns[i]);
25476             }
25477         }
25478         return nodes;
25479     },
25480
25481     /**
25482      * Finds the index of the passed node
25483      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
25484      * @return {Number} The index of the node or -1
25485      */
25486     indexOf : function(node){
25487         node = this.getNode(node);
25488         if(typeof node.nodeIndex == "number"){
25489             return node.nodeIndex;
25490         }
25491         var ns = this.nodes;
25492         for(var i = 0, len = ns.length; i < len; i++){
25493             if(ns[i] == node){
25494                 return i;
25495             }
25496         }
25497         return -1;
25498     }
25499 });
25500 /*
25501  * Based on:
25502  * Ext JS Library 1.1.1
25503  * Copyright(c) 2006-2007, Ext JS, LLC.
25504  *
25505  * Originally Released Under LGPL - original licence link has changed is not relivant.
25506  *
25507  * Fork - LGPL
25508  * <script type="text/javascript">
25509  */
25510
25511 /**
25512  * @class Roo.JsonView
25513  * @extends Roo.View
25514  * Shortcut class to create a JSON + {@link Roo.UpdateManager} template view. Usage:
25515 <pre><code>
25516 var view = new Roo.JsonView({
25517     container: "my-element",
25518     tpl: '&lt;div id="{id}"&gt;{foo} - {bar}&lt;/div&gt;', // auto create template
25519     multiSelect: true, 
25520     jsonRoot: "data" 
25521 });
25522
25523 // listen for node click?
25524 view.on("click", function(vw, index, node, e){
25525     alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
25526 });
25527
25528 // direct load of JSON data
25529 view.load("foobar.php");
25530
25531 // Example from my blog list
25532 var tpl = new Roo.Template(
25533     '&lt;div class="entry"&gt;' +
25534     '&lt;a class="entry-title" href="{link}"&gt;{title}&lt;/a&gt;' +
25535     "&lt;h4&gt;{date} by {author} | {comments} Comments&lt;/h4&gt;{description}" +
25536     "&lt;/div&gt;&lt;hr /&gt;"
25537 );
25538
25539 var moreView = new Roo.JsonView({
25540     container :  "entry-list", 
25541     template : tpl,
25542     jsonRoot: "posts"
25543 });
25544 moreView.on("beforerender", this.sortEntries, this);
25545 moreView.load({
25546     url: "/blog/get-posts.php",
25547     params: "allposts=true",
25548     text: "Loading Blog Entries..."
25549 });
25550 </code></pre>
25551
25552 * Note: old code is supported with arguments : (container, template, config)
25553
25554
25555  * @constructor
25556  * Create a new JsonView
25557  * 
25558  * @param {Object} config The config object
25559  * 
25560  */
25561 Roo.JsonView = function(config, depreciated_tpl, depreciated_config){
25562     
25563     
25564     Roo.JsonView.superclass.constructor.call(this, config, depreciated_tpl, depreciated_config);
25565
25566     var um = this.el.getUpdateManager();
25567     um.setRenderer(this);
25568     um.on("update", this.onLoad, this);
25569     um.on("failure", this.onLoadException, this);
25570
25571     /**
25572      * @event beforerender
25573      * Fires before rendering of the downloaded JSON data.
25574      * @param {Roo.JsonView} this
25575      * @param {Object} data The JSON data loaded
25576      */
25577     /**
25578      * @event load
25579      * Fires when data is loaded.
25580      * @param {Roo.JsonView} this
25581      * @param {Object} data The JSON data loaded
25582      * @param {Object} response The raw Connect response object
25583      */
25584     /**
25585      * @event loadexception
25586      * Fires when loading fails.
25587      * @param {Roo.JsonView} this
25588      * @param {Object} response The raw Connect response object
25589      */
25590     this.addEvents({
25591         'beforerender' : true,
25592         'load' : true,
25593         'loadexception' : true
25594     });
25595 };
25596 Roo.extend(Roo.JsonView, Roo.View, {
25597     /**
25598      * @type {String} The root property in the loaded JSON object that contains the data
25599      */
25600     jsonRoot : "",
25601
25602     /**
25603      * Refreshes the view.
25604      */
25605     refresh : function(){
25606         this.clearSelections();
25607         this.el.update("");
25608         var html = [];
25609         var o = this.jsonData;
25610         if(o && o.length > 0){
25611             for(var i = 0, len = o.length; i < len; i++){
25612                 var data = this.prepareData(o[i], i, o);
25613                 html[html.length] = this.tpl.apply(data);
25614             }
25615         }else{
25616             html.push(this.emptyText);
25617         }
25618         this.el.update(html.join(""));
25619         this.nodes = this.el.dom.childNodes;
25620         this.updateIndexes(0);
25621     },
25622
25623     /**
25624      * Performs an async HTTP request, and loads the JSON from the response. If <i>params</i> are specified it uses POST, otherwise it uses GET.
25625      * @param {Object/String/Function} url The URL for this request, or a function to call to get the URL, or a config object containing any of the following options:
25626      <pre><code>
25627      view.load({
25628          url: "your-url.php",
25629          params: {param1: "foo", param2: "bar"}, // or a URL encoded string
25630          callback: yourFunction,
25631          scope: yourObject, //(optional scope)
25632          discardUrl: false,
25633          nocache: false,
25634          text: "Loading...",
25635          timeout: 30,
25636          scripts: false
25637      });
25638      </code></pre>
25639      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
25640      * are respectively shorthand for <i>disableCaching</i>, <i>indicatorText</i>, and <i>loadScripts</i> and are used to set their associated property on this UpdateManager instance.
25641      * @param {String/Object} params (optional) The parameters to pass, as either a URL encoded string "param1=1&amp;param2=2" or an object {param1: 1, param2: 2}
25642      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
25643      * @param {Boolean} discardUrl (optional) By default when you execute an update the defaultUrl is changed to the last used URL. If true, it will not store the URL.
25644      */
25645     load : function(){
25646         var um = this.el.getUpdateManager();
25647         um.update.apply(um, arguments);
25648     },
25649
25650     render : function(el, response){
25651         this.clearSelections();
25652         this.el.update("");
25653         var o;
25654         try{
25655             o = Roo.util.JSON.decode(response.responseText);
25656             if(this.jsonRoot){
25657                 
25658                 o = o[this.jsonRoot];
25659             }
25660         } catch(e){
25661         }
25662         /**
25663          * The current JSON data or null
25664          */
25665         this.jsonData = o;
25666         this.beforeRender();
25667         this.refresh();
25668     },
25669
25670 /**
25671  * Get the number of records in the current JSON dataset
25672  * @return {Number}
25673  */
25674     getCount : function(){
25675         return this.jsonData ? this.jsonData.length : 0;
25676     },
25677
25678 /**
25679  * Returns the JSON object for the specified node(s)
25680  * @param {HTMLElement/Array} node The node or an array of nodes
25681  * @return {Object/Array} If you pass in an array, you get an array back, otherwise
25682  * you get the JSON object for the node
25683  */
25684     getNodeData : function(node){
25685         if(node instanceof Array){
25686             var data = [];
25687             for(var i = 0, len = node.length; i < len; i++){
25688                 data.push(this.getNodeData(node[i]));
25689             }
25690             return data;
25691         }
25692         return this.jsonData[this.indexOf(node)] || null;
25693     },
25694
25695     beforeRender : function(){
25696         this.snapshot = this.jsonData;
25697         if(this.sortInfo){
25698             this.sort.apply(this, this.sortInfo);
25699         }
25700         this.fireEvent("beforerender", this, this.jsonData);
25701     },
25702
25703     onLoad : function(el, o){
25704         this.fireEvent("load", this, this.jsonData, o);
25705     },
25706
25707     onLoadException : function(el, o){
25708         this.fireEvent("loadexception", this, o);
25709     },
25710
25711 /**
25712  * Filter the data by a specific property.
25713  * @param {String} property A property on your JSON objects
25714  * @param {String/RegExp} value Either string that the property values
25715  * should start with, or a RegExp to test against the property
25716  */
25717     filter : function(property, value){
25718         if(this.jsonData){
25719             var data = [];
25720             var ss = this.snapshot;
25721             if(typeof value == "string"){
25722                 var vlen = value.length;
25723                 if(vlen == 0){
25724                     this.clearFilter();
25725                     return;
25726                 }
25727                 value = value.toLowerCase();
25728                 for(var i = 0, len = ss.length; i < len; i++){
25729                     var o = ss[i];
25730                     if(o[property].substr(0, vlen).toLowerCase() == value){
25731                         data.push(o);
25732                     }
25733                 }
25734             } else if(value.exec){ // regex?
25735                 for(var i = 0, len = ss.length; i < len; i++){
25736                     var o = ss[i];
25737                     if(value.test(o[property])){
25738                         data.push(o);
25739                     }
25740                 }
25741             } else{
25742                 return;
25743             }
25744             this.jsonData = data;
25745             this.refresh();
25746         }
25747     },
25748
25749 /**
25750  * Filter by a function. The passed function will be called with each
25751  * object in the current dataset. If the function returns true the value is kept,
25752  * otherwise it is filtered.
25753  * @param {Function} fn
25754  * @param {Object} scope (optional) The scope of the function (defaults to this JsonView)
25755  */
25756     filterBy : function(fn, scope){
25757         if(this.jsonData){
25758             var data = [];
25759             var ss = this.snapshot;
25760             for(var i = 0, len = ss.length; i < len; i++){
25761                 var o = ss[i];
25762                 if(fn.call(scope || this, o)){
25763                     data.push(o);
25764                 }
25765             }
25766             this.jsonData = data;
25767             this.refresh();
25768         }
25769     },
25770
25771 /**
25772  * Clears the current filter.
25773  */
25774     clearFilter : function(){
25775         if(this.snapshot && this.jsonData != this.snapshot){
25776             this.jsonData = this.snapshot;
25777             this.refresh();
25778         }
25779     },
25780
25781
25782 /**
25783  * Sorts the data for this view and refreshes it.
25784  * @param {String} property A property on your JSON objects to sort on
25785  * @param {String} direction (optional) "desc" or "asc" (defaults to "asc")
25786  * @param {Function} sortType (optional) A function to call to convert the data to a sortable value.
25787  */
25788     sort : function(property, dir, sortType){
25789         this.sortInfo = Array.prototype.slice.call(arguments, 0);
25790         if(this.jsonData){
25791             var p = property;
25792             var dsc = dir && dir.toLowerCase() == "desc";
25793             var f = function(o1, o2){
25794                 var v1 = sortType ? sortType(o1[p]) : o1[p];
25795                 var v2 = sortType ? sortType(o2[p]) : o2[p];
25796                 ;
25797                 if(v1 < v2){
25798                     return dsc ? +1 : -1;
25799                 } else if(v1 > v2){
25800                     return dsc ? -1 : +1;
25801                 } else{
25802                     return 0;
25803                 }
25804             };
25805             this.jsonData.sort(f);
25806             this.refresh();
25807             if(this.jsonData != this.snapshot){
25808                 this.snapshot.sort(f);
25809             }
25810         }
25811     }
25812 });/*
25813  * Based on:
25814  * Ext JS Library 1.1.1
25815  * Copyright(c) 2006-2007, Ext JS, LLC.
25816  *
25817  * Originally Released Under LGPL - original licence link has changed is not relivant.
25818  *
25819  * Fork - LGPL
25820  * <script type="text/javascript">
25821  */
25822  
25823
25824 /**
25825  * @class Roo.ColorPalette
25826  * @extends Roo.Component
25827  * Simple color palette class for choosing colors.  The palette can be rendered to any container.<br />
25828  * Here's an example of typical usage:
25829  * <pre><code>
25830 var cp = new Roo.ColorPalette({value:'993300'});  // initial selected color
25831 cp.render('my-div');
25832
25833 cp.on('select', function(palette, selColor){
25834     // do something with selColor
25835 });
25836 </code></pre>
25837  * @constructor
25838  * Create a new ColorPalette
25839  * @param {Object} config The config object
25840  */
25841 Roo.ColorPalette = function(config){
25842     Roo.ColorPalette.superclass.constructor.call(this, config);
25843     this.addEvents({
25844         /**
25845              * @event select
25846              * Fires when a color is selected
25847              * @param {ColorPalette} this
25848              * @param {String} color The 6-digit color hex code (without the # symbol)
25849              */
25850         select: true
25851     });
25852
25853     if(this.handler){
25854         this.on("select", this.handler, this.scope, true);
25855     }
25856 };
25857 Roo.extend(Roo.ColorPalette, Roo.Component, {
25858     /**
25859      * @cfg {String} itemCls
25860      * The CSS class to apply to the containing element (defaults to "x-color-palette")
25861      */
25862     itemCls : "x-color-palette",
25863     /**
25864      * @cfg {String} value
25865      * The initial color to highlight (should be a valid 6-digit color hex code without the # symbol).  Note that
25866      * the hex codes are case-sensitive.
25867      */
25868     value : null,
25869     clickEvent:'click',
25870     // private
25871     ctype: "Roo.ColorPalette",
25872
25873     /**
25874      * @cfg {Boolean} allowReselect If set to true then reselecting a color that is already selected fires the selection event
25875      */
25876     allowReselect : false,
25877
25878     /**
25879      * <p>An array of 6-digit color hex code strings (without the # symbol).  This array can contain any number
25880      * of colors, and each hex code should be unique.  The width of the palette is controlled via CSS by adjusting
25881      * the width property of the 'x-color-palette' class (or assigning a custom class), so you can balance the number
25882      * of colors with the width setting until the box is symmetrical.</p>
25883      * <p>You can override individual colors if needed:</p>
25884      * <pre><code>
25885 var cp = new Roo.ColorPalette();
25886 cp.colors[0] = "FF0000";  // change the first box to red
25887 </code></pre>
25888
25889 Or you can provide a custom array of your own for complete control:
25890 <pre><code>
25891 var cp = new Roo.ColorPalette();
25892 cp.colors = ["000000", "993300", "333300"];
25893 </code></pre>
25894      * @type Array
25895      */
25896     colors : [
25897         "000000", "993300", "333300", "003300", "003366", "000080", "333399", "333333",
25898         "800000", "FF6600", "808000", "008000", "008080", "0000FF", "666699", "808080",
25899         "FF0000", "FF9900", "99CC00", "339966", "33CCCC", "3366FF", "800080", "969696",
25900         "FF00FF", "FFCC00", "FFFF00", "00FF00", "00FFFF", "00CCFF", "993366", "C0C0C0",
25901         "FF99CC", "FFCC99", "FFFF99", "CCFFCC", "CCFFFF", "99CCFF", "CC99FF", "FFFFFF"
25902     ],
25903
25904     // private
25905     onRender : function(container, position){
25906         var t = new Roo.MasterTemplate(
25907             '<tpl><a href="#" class="color-{0}" hidefocus="on"><em><span style="background:#{0}" unselectable="on">&#160;</span></em></a></tpl>'
25908         );
25909         var c = this.colors;
25910         for(var i = 0, len = c.length; i < len; i++){
25911             t.add([c[i]]);
25912         }
25913         var el = document.createElement("div");
25914         el.className = this.itemCls;
25915         t.overwrite(el);
25916         container.dom.insertBefore(el, position);
25917         this.el = Roo.get(el);
25918         this.el.on(this.clickEvent, this.handleClick,  this, {delegate: "a"});
25919         if(this.clickEvent != 'click'){
25920             this.el.on('click', Roo.emptyFn,  this, {delegate: "a", preventDefault:true});
25921         }
25922     },
25923
25924     // private
25925     afterRender : function(){
25926         Roo.ColorPalette.superclass.afterRender.call(this);
25927         if(this.value){
25928             var s = this.value;
25929             this.value = null;
25930             this.select(s);
25931         }
25932     },
25933
25934     // private
25935     handleClick : function(e, t){
25936         e.preventDefault();
25937         if(!this.disabled){
25938             var c = t.className.match(/(?:^|\s)color-(.{6})(?:\s|$)/)[1];
25939             this.select(c.toUpperCase());
25940         }
25941     },
25942
25943     /**
25944      * Selects the specified color in the palette (fires the select event)
25945      * @param {String} color A valid 6-digit color hex code (# will be stripped if included)
25946      */
25947     select : function(color){
25948         color = color.replace("#", "");
25949         if(color != this.value || this.allowReselect){
25950             var el = this.el;
25951             if(this.value){
25952                 el.child("a.color-"+this.value).removeClass("x-color-palette-sel");
25953             }
25954             el.child("a.color-"+color).addClass("x-color-palette-sel");
25955             this.value = color;
25956             this.fireEvent("select", this, color);
25957         }
25958     }
25959 });/*
25960  * Based on:
25961  * Ext JS Library 1.1.1
25962  * Copyright(c) 2006-2007, Ext JS, LLC.
25963  *
25964  * Originally Released Under LGPL - original licence link has changed is not relivant.
25965  *
25966  * Fork - LGPL
25967  * <script type="text/javascript">
25968  */
25969  
25970 /**
25971  * @class Roo.DatePicker
25972  * @extends Roo.Component
25973  * Simple date picker class.
25974  * @constructor
25975  * Create a new DatePicker
25976  * @param {Object} config The config object
25977  */
25978 Roo.DatePicker = function(config){
25979     Roo.DatePicker.superclass.constructor.call(this, config);
25980
25981     this.value = config && config.value ?
25982                  config.value.clearTime() : new Date().clearTime();
25983
25984     this.addEvents({
25985         /**
25986              * @event select
25987              * Fires when a date is selected
25988              * @param {DatePicker} this
25989              * @param {Date} date The selected date
25990              */
25991         'select': true,
25992         /**
25993              * @event monthchange
25994              * Fires when the displayed month changes 
25995              * @param {DatePicker} this
25996              * @param {Date} date The selected month
25997              */
25998         'monthchange': true
25999     });
26000
26001     if(this.handler){
26002         this.on("select", this.handler,  this.scope || this);
26003     }
26004     // build the disabledDatesRE
26005     if(!this.disabledDatesRE && this.disabledDates){
26006         var dd = this.disabledDates;
26007         var re = "(?:";
26008         for(var i = 0; i < dd.length; i++){
26009             re += dd[i];
26010             if(i != dd.length-1) re += "|";
26011         }
26012         this.disabledDatesRE = new RegExp(re + ")");
26013     }
26014 };
26015
26016 Roo.extend(Roo.DatePicker, Roo.Component, {
26017     /**
26018      * @cfg {String} todayText
26019      * The text to display on the button that selects the current date (defaults to "Today")
26020      */
26021     todayText : "Today",
26022     /**
26023      * @cfg {String} okText
26024      * The text to display on the ok button
26025      */
26026     okText : "&#160;OK&#160;", // &#160; to give the user extra clicking room
26027     /**
26028      * @cfg {String} cancelText
26029      * The text to display on the cancel button
26030      */
26031     cancelText : "Cancel",
26032     /**
26033      * @cfg {String} todayTip
26034      * The tooltip to display for the button that selects the current date (defaults to "{current date} (Spacebar)")
26035      */
26036     todayTip : "{0} (Spacebar)",
26037     /**
26038      * @cfg {Date} minDate
26039      * Minimum allowable date (JavaScript date object, defaults to null)
26040      */
26041     minDate : null,
26042     /**
26043      * @cfg {Date} maxDate
26044      * Maximum allowable date (JavaScript date object, defaults to null)
26045      */
26046     maxDate : null,
26047     /**
26048      * @cfg {String} minText
26049      * The error text to display if the minDate validation fails (defaults to "This date is before the minimum date")
26050      */
26051     minText : "This date is before the minimum date",
26052     /**
26053      * @cfg {String} maxText
26054      * The error text to display if the maxDate validation fails (defaults to "This date is after the maximum date")
26055      */
26056     maxText : "This date is after the maximum date",
26057     /**
26058      * @cfg {String} format
26059      * The default date format string which can be overriden for localization support.  The format must be
26060      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
26061      */
26062     format : "m/d/y",
26063     /**
26064      * @cfg {Array} disabledDays
26065      * An array of days to disable, 0-based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
26066      */
26067     disabledDays : null,
26068     /**
26069      * @cfg {String} disabledDaysText
26070      * The tooltip to display when the date falls on a disabled day (defaults to "")
26071      */
26072     disabledDaysText : "",
26073     /**
26074      * @cfg {RegExp} disabledDatesRE
26075      * JavaScript regular expression used to disable a pattern of dates (defaults to null)
26076      */
26077     disabledDatesRE : null,
26078     /**
26079      * @cfg {String} disabledDatesText
26080      * The tooltip text to display when the date falls on a disabled date (defaults to "")
26081      */
26082     disabledDatesText : "",
26083     /**
26084      * @cfg {Boolean} constrainToViewport
26085      * True to constrain the date picker to the viewport (defaults to true)
26086      */
26087     constrainToViewport : true,
26088     /**
26089      * @cfg {Array} monthNames
26090      * An array of textual month names which can be overriden for localization support (defaults to Date.monthNames)
26091      */
26092     monthNames : Date.monthNames,
26093     /**
26094      * @cfg {Array} dayNames
26095      * An array of textual day names which can be overriden for localization support (defaults to Date.dayNames)
26096      */
26097     dayNames : Date.dayNames,
26098     /**
26099      * @cfg {String} nextText
26100      * The next month navigation button tooltip (defaults to 'Next Month (Control+Right)')
26101      */
26102     nextText: 'Next Month (Control+Right)',
26103     /**
26104      * @cfg {String} prevText
26105      * The previous month navigation button tooltip (defaults to 'Previous Month (Control+Left)')
26106      */
26107     prevText: 'Previous Month (Control+Left)',
26108     /**
26109      * @cfg {String} monthYearText
26110      * The header month selector tooltip (defaults to 'Choose a month (Control+Up/Down to move years)')
26111      */
26112     monthYearText: 'Choose a month (Control+Up/Down to move years)',
26113     /**
26114      * @cfg {Number} startDay
26115      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
26116      */
26117     startDay : 0,
26118     /**
26119      * @cfg {Bool} showClear
26120      * Show a clear button (usefull for date form elements that can be blank.)
26121      */
26122     
26123     showClear: false,
26124     
26125     /**
26126      * Sets the value of the date field
26127      * @param {Date} value The date to set
26128      */
26129     setValue : function(value){
26130         var old = this.value;
26131         
26132         if (typeof(value) == 'string') {
26133          
26134             value = Date.parseDate(value, this.format);
26135         }
26136         if (!value) {
26137             value = new Date();
26138         }
26139         
26140         this.value = value.clearTime(true);
26141         if(this.el){
26142             this.update(this.value);
26143         }
26144     },
26145
26146     /**
26147      * Gets the current selected value of the date field
26148      * @return {Date} The selected date
26149      */
26150     getValue : function(){
26151         return this.value;
26152     },
26153
26154     // private
26155     focus : function(){
26156         if(this.el){
26157             this.update(this.activeDate);
26158         }
26159     },
26160
26161     // privateval
26162     onRender : function(container, position){
26163         
26164         var m = [
26165              '<table cellspacing="0">',
26166                 '<tr><td class="x-date-left"><a href="#" title="', this.prevText ,'">&#160;</a></td><td class="x-date-middle" align="center"></td><td class="x-date-right"><a href="#" title="', this.nextText ,'">&#160;</a></td></tr>',
26167                 '<tr><td colspan="3"><table class="x-date-inner" cellspacing="0"><thead><tr>'];
26168         var dn = this.dayNames;
26169         for(var i = 0; i < 7; i++){
26170             var d = this.startDay+i;
26171             if(d > 6){
26172                 d = d-7;
26173             }
26174             m.push("<th><span>", dn[d].substr(0,1), "</span></th>");
26175         }
26176         m[m.length] = "</tr></thead><tbody><tr>";
26177         for(var i = 0; i < 42; i++) {
26178             if(i % 7 == 0 && i != 0){
26179                 m[m.length] = "</tr><tr>";
26180             }
26181             m[m.length] = '<td><a href="#" hidefocus="on" class="x-date-date" tabIndex="1"><em><span></span></em></a></td>';
26182         }
26183         m[m.length] = '</tr></tbody></table></td></tr><tr>'+
26184             '<td colspan="3" class="x-date-bottom" align="center"></td></tr></table><div class="x-date-mp"></div>';
26185
26186         var el = document.createElement("div");
26187         el.className = "x-date-picker";
26188         el.innerHTML = m.join("");
26189
26190         container.dom.insertBefore(el, position);
26191
26192         this.el = Roo.get(el);
26193         this.eventEl = Roo.get(el.firstChild);
26194
26195         new Roo.util.ClickRepeater(this.el.child("td.x-date-left a"), {
26196             handler: this.showPrevMonth,
26197             scope: this,
26198             preventDefault:true,
26199             stopDefault:true
26200         });
26201
26202         new Roo.util.ClickRepeater(this.el.child("td.x-date-right a"), {
26203             handler: this.showNextMonth,
26204             scope: this,
26205             preventDefault:true,
26206             stopDefault:true
26207         });
26208
26209         this.eventEl.on("mousewheel", this.handleMouseWheel,  this);
26210
26211         this.monthPicker = this.el.down('div.x-date-mp');
26212         this.monthPicker.enableDisplayMode('block');
26213         
26214         var kn = new Roo.KeyNav(this.eventEl, {
26215             "left" : function(e){
26216                 e.ctrlKey ?
26217                     this.showPrevMonth() :
26218                     this.update(this.activeDate.add("d", -1));
26219             },
26220
26221             "right" : function(e){
26222                 e.ctrlKey ?
26223                     this.showNextMonth() :
26224                     this.update(this.activeDate.add("d", 1));
26225             },
26226
26227             "up" : function(e){
26228                 e.ctrlKey ?
26229                     this.showNextYear() :
26230                     this.update(this.activeDate.add("d", -7));
26231             },
26232
26233             "down" : function(e){
26234                 e.ctrlKey ?
26235                     this.showPrevYear() :
26236                     this.update(this.activeDate.add("d", 7));
26237             },
26238
26239             "pageUp" : function(e){
26240                 this.showNextMonth();
26241             },
26242
26243             "pageDown" : function(e){
26244                 this.showPrevMonth();
26245             },
26246
26247             "enter" : function(e){
26248                 e.stopPropagation();
26249                 return true;
26250             },
26251
26252             scope : this
26253         });
26254
26255         this.eventEl.on("click", this.handleDateClick,  this, {delegate: "a.x-date-date"});
26256
26257         this.eventEl.addKeyListener(Roo.EventObject.SPACE, this.selectToday,  this);
26258
26259         this.el.unselectable();
26260         
26261         this.cells = this.el.select("table.x-date-inner tbody td");
26262         this.textNodes = this.el.query("table.x-date-inner tbody span");
26263
26264         this.mbtn = new Roo.Button(this.el.child("td.x-date-middle", true), {
26265             text: "&#160;",
26266             tooltip: this.monthYearText
26267         });
26268
26269         this.mbtn.on('click', this.showMonthPicker, this);
26270         this.mbtn.el.child(this.mbtn.menuClassTarget).addClass("x-btn-with-menu");
26271
26272
26273         var today = (new Date()).dateFormat(this.format);
26274         
26275         var baseTb = new Roo.Toolbar(this.el.child("td.x-date-bottom", true));
26276         if (this.showClear) {
26277             baseTb.add( new Roo.Toolbar.Fill());
26278         }
26279         baseTb.add({
26280             text: String.format(this.todayText, today),
26281             tooltip: String.format(this.todayTip, today),
26282             handler: this.selectToday,
26283             scope: this
26284         });
26285         
26286         //var todayBtn = new Roo.Button(this.el.child("td.x-date-bottom", true), {
26287             
26288         //});
26289         if (this.showClear) {
26290             
26291             baseTb.add( new Roo.Toolbar.Fill());
26292             baseTb.add({
26293                 text: '&#160;',
26294                 cls: 'x-btn-icon x-btn-clear',
26295                 handler: function() {
26296                     //this.value = '';
26297                     this.fireEvent("select", this, '');
26298                 },
26299                 scope: this
26300             });
26301         }
26302         
26303         
26304         if(Roo.isIE){
26305             this.el.repaint();
26306         }
26307         this.update(this.value);
26308     },
26309
26310     createMonthPicker : function(){
26311         if(!this.monthPicker.dom.firstChild){
26312             var buf = ['<table border="0" cellspacing="0">'];
26313             for(var i = 0; i < 6; i++){
26314                 buf.push(
26315                     '<tr><td class="x-date-mp-month"><a href="#">', this.monthNames[i].substr(0, 3), '</a></td>',
26316                     '<td class="x-date-mp-month x-date-mp-sep"><a href="#">', this.monthNames[i+6].substr(0, 3), '</a></td>',
26317                     i == 0 ?
26318                     '<td class="x-date-mp-ybtn" align="center"><a class="x-date-mp-prev"></a></td><td class="x-date-mp-ybtn" align="center"><a class="x-date-mp-next"></a></td></tr>' :
26319                     '<td class="x-date-mp-year"><a href="#"></a></td><td class="x-date-mp-year"><a href="#"></a></td></tr>'
26320                 );
26321             }
26322             buf.push(
26323                 '<tr class="x-date-mp-btns"><td colspan="4"><button type="button" class="x-date-mp-ok">',
26324                     this.okText,
26325                     '</button><button type="button" class="x-date-mp-cancel">',
26326                     this.cancelText,
26327                     '</button></td></tr>',
26328                 '</table>'
26329             );
26330             this.monthPicker.update(buf.join(''));
26331             this.monthPicker.on('click', this.onMonthClick, this);
26332             this.monthPicker.on('dblclick', this.onMonthDblClick, this);
26333
26334             this.mpMonths = this.monthPicker.select('td.x-date-mp-month');
26335             this.mpYears = this.monthPicker.select('td.x-date-mp-year');
26336
26337             this.mpMonths.each(function(m, a, i){
26338                 i += 1;
26339                 if((i%2) == 0){
26340                     m.dom.xmonth = 5 + Math.round(i * .5);
26341                 }else{
26342                     m.dom.xmonth = Math.round((i-1) * .5);
26343                 }
26344             });
26345         }
26346     },
26347
26348     showMonthPicker : function(){
26349         this.createMonthPicker();
26350         var size = this.el.getSize();
26351         this.monthPicker.setSize(size);
26352         this.monthPicker.child('table').setSize(size);
26353
26354         this.mpSelMonth = (this.activeDate || this.value).getMonth();
26355         this.updateMPMonth(this.mpSelMonth);
26356         this.mpSelYear = (this.activeDate || this.value).getFullYear();
26357         this.updateMPYear(this.mpSelYear);
26358
26359         this.monthPicker.slideIn('t', {duration:.2});
26360     },
26361
26362     updateMPYear : function(y){
26363         this.mpyear = y;
26364         var ys = this.mpYears.elements;
26365         for(var i = 1; i <= 10; i++){
26366             var td = ys[i-1], y2;
26367             if((i%2) == 0){
26368                 y2 = y + Math.round(i * .5);
26369                 td.firstChild.innerHTML = y2;
26370                 td.xyear = y2;
26371             }else{
26372                 y2 = y - (5-Math.round(i * .5));
26373                 td.firstChild.innerHTML = y2;
26374                 td.xyear = y2;
26375             }
26376             this.mpYears.item(i-1)[y2 == this.mpSelYear ? 'addClass' : 'removeClass']('x-date-mp-sel');
26377         }
26378     },
26379
26380     updateMPMonth : function(sm){
26381         this.mpMonths.each(function(m, a, i){
26382             m[m.dom.xmonth == sm ? 'addClass' : 'removeClass']('x-date-mp-sel');
26383         });
26384     },
26385
26386     selectMPMonth: function(m){
26387         
26388     },
26389
26390     onMonthClick : function(e, t){
26391         e.stopEvent();
26392         var el = new Roo.Element(t), pn;
26393         if(el.is('button.x-date-mp-cancel')){
26394             this.hideMonthPicker();
26395         }
26396         else if(el.is('button.x-date-mp-ok')){
26397             this.update(new Date(this.mpSelYear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
26398             this.hideMonthPicker();
26399         }
26400         else if(pn = el.up('td.x-date-mp-month', 2)){
26401             this.mpMonths.removeClass('x-date-mp-sel');
26402             pn.addClass('x-date-mp-sel');
26403             this.mpSelMonth = pn.dom.xmonth;
26404         }
26405         else if(pn = el.up('td.x-date-mp-year', 2)){
26406             this.mpYears.removeClass('x-date-mp-sel');
26407             pn.addClass('x-date-mp-sel');
26408             this.mpSelYear = pn.dom.xyear;
26409         }
26410         else if(el.is('a.x-date-mp-prev')){
26411             this.updateMPYear(this.mpyear-10);
26412         }
26413         else if(el.is('a.x-date-mp-next')){
26414             this.updateMPYear(this.mpyear+10);
26415         }
26416     },
26417
26418     onMonthDblClick : function(e, t){
26419         e.stopEvent();
26420         var el = new Roo.Element(t), pn;
26421         if(pn = el.up('td.x-date-mp-month', 2)){
26422             this.update(new Date(this.mpSelYear, pn.dom.xmonth, (this.activeDate || this.value).getDate()));
26423             this.hideMonthPicker();
26424         }
26425         else if(pn = el.up('td.x-date-mp-year', 2)){
26426             this.update(new Date(pn.dom.xyear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
26427             this.hideMonthPicker();
26428         }
26429     },
26430
26431     hideMonthPicker : function(disableAnim){
26432         if(this.monthPicker){
26433             if(disableAnim === true){
26434                 this.monthPicker.hide();
26435             }else{
26436                 this.monthPicker.slideOut('t', {duration:.2});
26437             }
26438         }
26439     },
26440
26441     // private
26442     showPrevMonth : function(e){
26443         this.update(this.activeDate.add("mo", -1));
26444     },
26445
26446     // private
26447     showNextMonth : function(e){
26448         this.update(this.activeDate.add("mo", 1));
26449     },
26450
26451     // private
26452     showPrevYear : function(){
26453         this.update(this.activeDate.add("y", -1));
26454     },
26455
26456     // private
26457     showNextYear : function(){
26458         this.update(this.activeDate.add("y", 1));
26459     },
26460
26461     // private
26462     handleMouseWheel : function(e){
26463         var delta = e.getWheelDelta();
26464         if(delta > 0){
26465             this.showPrevMonth();
26466             e.stopEvent();
26467         } else if(delta < 0){
26468             this.showNextMonth();
26469             e.stopEvent();
26470         }
26471     },
26472
26473     // private
26474     handleDateClick : function(e, t){
26475         e.stopEvent();
26476         if(t.dateValue && !Roo.fly(t.parentNode).hasClass("x-date-disabled")){
26477             this.setValue(new Date(t.dateValue));
26478             this.fireEvent("select", this, this.value);
26479         }
26480     },
26481
26482     // private
26483     selectToday : function(){
26484         this.setValue(new Date().clearTime());
26485         this.fireEvent("select", this, this.value);
26486     },
26487
26488     // private
26489     update : function(date)
26490     {
26491         var vd = this.activeDate;
26492         this.activeDate = date;
26493         if(vd && this.el){
26494             var t = date.getTime();
26495             if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
26496                 this.cells.removeClass("x-date-selected");
26497                 this.cells.each(function(c){
26498                    if(c.dom.firstChild.dateValue == t){
26499                        c.addClass("x-date-selected");
26500                        setTimeout(function(){
26501                             try{c.dom.firstChild.focus();}catch(e){}
26502                        }, 50);
26503                        return false;
26504                    }
26505                 });
26506                 return;
26507             }
26508         }
26509         
26510         var days = date.getDaysInMonth();
26511         var firstOfMonth = date.getFirstDateOfMonth();
26512         var startingPos = firstOfMonth.getDay()-this.startDay;
26513
26514         if(startingPos <= this.startDay){
26515             startingPos += 7;
26516         }
26517
26518         var pm = date.add("mo", -1);
26519         var prevStart = pm.getDaysInMonth()-startingPos;
26520
26521         var cells = this.cells.elements;
26522         var textEls = this.textNodes;
26523         days += startingPos;
26524
26525         // convert everything to numbers so it's fast
26526         var day = 86400000;
26527         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
26528         var today = new Date().clearTime().getTime();
26529         var sel = date.clearTime().getTime();
26530         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
26531         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
26532         var ddMatch = this.disabledDatesRE;
26533         var ddText = this.disabledDatesText;
26534         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
26535         var ddaysText = this.disabledDaysText;
26536         var format = this.format;
26537
26538         var setCellClass = function(cal, cell){
26539             cell.title = "";
26540             var t = d.getTime();
26541             cell.firstChild.dateValue = t;
26542             if(t == today){
26543                 cell.className += " x-date-today";
26544                 cell.title = cal.todayText;
26545             }
26546             if(t == sel){
26547                 cell.className += " x-date-selected";
26548                 setTimeout(function(){
26549                     try{cell.firstChild.focus();}catch(e){}
26550                 }, 50);
26551             }
26552             // disabling
26553             if(t < min) {
26554                 cell.className = " x-date-disabled";
26555                 cell.title = cal.minText;
26556                 return;
26557             }
26558             if(t > max) {
26559                 cell.className = " x-date-disabled";
26560                 cell.title = cal.maxText;
26561                 return;
26562             }
26563             if(ddays){
26564                 if(ddays.indexOf(d.getDay()) != -1){
26565                     cell.title = ddaysText;
26566                     cell.className = " x-date-disabled";
26567                 }
26568             }
26569             if(ddMatch && format){
26570                 var fvalue = d.dateFormat(format);
26571                 if(ddMatch.test(fvalue)){
26572                     cell.title = ddText.replace("%0", fvalue);
26573                     cell.className = " x-date-disabled";
26574                 }
26575             }
26576         };
26577
26578         var i = 0;
26579         for(; i < startingPos; i++) {
26580             textEls[i].innerHTML = (++prevStart);
26581             d.setDate(d.getDate()+1);
26582             cells[i].className = "x-date-prevday";
26583             setCellClass(this, cells[i]);
26584         }
26585         for(; i < days; i++){
26586             intDay = i - startingPos + 1;
26587             textEls[i].innerHTML = (intDay);
26588             d.setDate(d.getDate()+1);
26589             cells[i].className = "x-date-active";
26590             setCellClass(this, cells[i]);
26591         }
26592         var extraDays = 0;
26593         for(; i < 42; i++) {
26594              textEls[i].innerHTML = (++extraDays);
26595              d.setDate(d.getDate()+1);
26596              cells[i].className = "x-date-nextday";
26597              setCellClass(this, cells[i]);
26598         }
26599
26600         this.mbtn.setText(this.monthNames[date.getMonth()] + " " + date.getFullYear());
26601         this.fireEvent('monthchange', this, date);
26602         
26603         if(!this.internalRender){
26604             var main = this.el.dom.firstChild;
26605             var w = main.offsetWidth;
26606             this.el.setWidth(w + this.el.getBorderWidth("lr"));
26607             Roo.fly(main).setWidth(w);
26608             this.internalRender = true;
26609             // opera does not respect the auto grow header center column
26610             // then, after it gets a width opera refuses to recalculate
26611             // without a second pass
26612             if(Roo.isOpera && !this.secondPass){
26613                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
26614                 this.secondPass = true;
26615                 this.update.defer(10, this, [date]);
26616             }
26617         }
26618         
26619         
26620     }
26621 });        /*
26622  * Based on:
26623  * Ext JS Library 1.1.1
26624  * Copyright(c) 2006-2007, Ext JS, LLC.
26625  *
26626  * Originally Released Under LGPL - original licence link has changed is not relivant.
26627  *
26628  * Fork - LGPL
26629  * <script type="text/javascript">
26630  */
26631 /**
26632  * @class Roo.TabPanel
26633  * @extends Roo.util.Observable
26634  * A lightweight tab container.
26635  * <br><br>
26636  * Usage:
26637  * <pre><code>
26638 // basic tabs 1, built from existing content
26639 var tabs = new Roo.TabPanel("tabs1");
26640 tabs.addTab("script", "View Script");
26641 tabs.addTab("markup", "View Markup");
26642 tabs.activate("script");
26643
26644 // more advanced tabs, built from javascript
26645 var jtabs = new Roo.TabPanel("jtabs");
26646 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
26647
26648 // set up the UpdateManager
26649 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
26650 var updater = tab2.getUpdateManager();
26651 updater.setDefaultUrl("ajax1.htm");
26652 tab2.on('activate', updater.refresh, updater, true);
26653
26654 // Use setUrl for Ajax loading
26655 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
26656 tab3.setUrl("ajax2.htm", null, true);
26657
26658 // Disabled tab
26659 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
26660 tab4.disable();
26661
26662 jtabs.activate("jtabs-1");
26663  * </code></pre>
26664  * @constructor
26665  * Create a new TabPanel.
26666  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
26667  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
26668  */
26669 Roo.TabPanel = function(container, config){
26670     /**
26671     * The container element for this TabPanel.
26672     * @type Roo.Element
26673     */
26674     this.el = Roo.get(container, true);
26675     if(config){
26676         if(typeof config == "boolean"){
26677             this.tabPosition = config ? "bottom" : "top";
26678         }else{
26679             Roo.apply(this, config);
26680         }
26681     }
26682     if(this.tabPosition == "bottom"){
26683         this.bodyEl = Roo.get(this.createBody(this.el.dom));
26684         this.el.addClass("x-tabs-bottom");
26685     }
26686     this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
26687     this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
26688     this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
26689     if(Roo.isIE){
26690         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
26691     }
26692     if(this.tabPosition != "bottom"){
26693         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
26694          * @type Roo.Element
26695          */
26696         this.bodyEl = Roo.get(this.createBody(this.el.dom));
26697         this.el.addClass("x-tabs-top");
26698     }
26699     this.items = [];
26700
26701     this.bodyEl.setStyle("position", "relative");
26702
26703     this.active = null;
26704     this.activateDelegate = this.activate.createDelegate(this);
26705
26706     this.addEvents({
26707         /**
26708          * @event tabchange
26709          * Fires when the active tab changes
26710          * @param {Roo.TabPanel} this
26711          * @param {Roo.TabPanelItem} activePanel The new active tab
26712          */
26713         "tabchange": true,
26714         /**
26715          * @event beforetabchange
26716          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
26717          * @param {Roo.TabPanel} this
26718          * @param {Object} e Set cancel to true on this object to cancel the tab change
26719          * @param {Roo.TabPanelItem} tab The tab being changed to
26720          */
26721         "beforetabchange" : true
26722     });
26723
26724     Roo.EventManager.onWindowResize(this.onResize, this);
26725     this.cpad = this.el.getPadding("lr");
26726     this.hiddenCount = 0;
26727
26728
26729     // toolbar on the tabbar support...
26730     if (this.toolbar) {
26731         var tcfg = this.toolbar;
26732         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
26733         this.toolbar = new Roo.Toolbar(tcfg);
26734         if (Roo.isSafari) {
26735             var tbl = tcfg.container.child('table', true);
26736             tbl.setAttribute('width', '100%');
26737         }
26738         
26739     }
26740    
26741
26742
26743     Roo.TabPanel.superclass.constructor.call(this);
26744 };
26745
26746 Roo.extend(Roo.TabPanel, Roo.util.Observable, {
26747     /*
26748      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
26749      */
26750     tabPosition : "top",
26751     /*
26752      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
26753      */
26754     currentTabWidth : 0,
26755     /*
26756      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
26757      */
26758     minTabWidth : 40,
26759     /*
26760      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
26761      */
26762     maxTabWidth : 250,
26763     /*
26764      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
26765      */
26766     preferredTabWidth : 175,
26767     /*
26768      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
26769      */
26770     resizeTabs : false,
26771     /*
26772      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
26773      */
26774     monitorResize : true,
26775     /*
26776      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
26777      */
26778     toolbar : false,
26779
26780     /**
26781      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
26782      * @param {String} id The id of the div to use <b>or create</b>
26783      * @param {String} text The text for the tab
26784      * @param {String} content (optional) Content to put in the TabPanelItem body
26785      * @param {Boolean} closable (optional) True to create a close icon on the tab
26786      * @return {Roo.TabPanelItem} The created TabPanelItem
26787      */
26788     addTab : function(id, text, content, closable){
26789         var item = new Roo.TabPanelItem(this, id, text, closable);
26790         this.addTabItem(item);
26791         if(content){
26792             item.setContent(content);
26793         }
26794         return item;
26795     },
26796
26797     /**
26798      * Returns the {@link Roo.TabPanelItem} with the specified id/index
26799      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
26800      * @return {Roo.TabPanelItem}
26801      */
26802     getTab : function(id){
26803         return this.items[id];
26804     },
26805
26806     /**
26807      * Hides the {@link Roo.TabPanelItem} with the specified id/index
26808      * @param {String/Number} id The id or index of the TabPanelItem to hide.
26809      */
26810     hideTab : function(id){
26811         var t = this.items[id];
26812         if(!t.isHidden()){
26813            t.setHidden(true);
26814            this.hiddenCount++;
26815            this.autoSizeTabs();
26816         }
26817     },
26818
26819     /**
26820      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
26821      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
26822      */
26823     unhideTab : function(id){
26824         var t = this.items[id];
26825         if(t.isHidden()){
26826            t.setHidden(false);
26827            this.hiddenCount--;
26828            this.autoSizeTabs();
26829         }
26830     },
26831
26832     /**
26833      * Adds an existing {@link Roo.TabPanelItem}.
26834      * @param {Roo.TabPanelItem} item The TabPanelItem to add
26835      */
26836     addTabItem : function(item){
26837         this.items[item.id] = item;
26838         this.items.push(item);
26839         if(this.resizeTabs){
26840            item.setWidth(this.currentTabWidth || this.preferredTabWidth);
26841            this.autoSizeTabs();
26842         }else{
26843             item.autoSize();
26844         }
26845     },
26846
26847     /**
26848      * Removes a {@link Roo.TabPanelItem}.
26849      * @param {String/Number} id The id or index of the TabPanelItem to remove.
26850      */
26851     removeTab : function(id){
26852         var items = this.items;
26853         var tab = items[id];
26854         if(!tab) { return; }
26855         var index = items.indexOf(tab);
26856         if(this.active == tab && items.length > 1){
26857             var newTab = this.getNextAvailable(index);
26858             if(newTab) {
26859                 newTab.activate();
26860             }
26861         }
26862         this.stripEl.dom.removeChild(tab.pnode.dom);
26863         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
26864             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
26865         }
26866         items.splice(index, 1);
26867         delete this.items[tab.id];
26868         tab.fireEvent("close", tab);
26869         tab.purgeListeners();
26870         this.autoSizeTabs();
26871     },
26872
26873     getNextAvailable : function(start){
26874         var items = this.items;
26875         var index = start;
26876         // look for a next tab that will slide over to
26877         // replace the one being removed
26878         while(index < items.length){
26879             var item = items[++index];
26880             if(item && !item.isHidden()){
26881                 return item;
26882             }
26883         }
26884         // if one isn't found select the previous tab (on the left)
26885         index = start;
26886         while(index >= 0){
26887             var item = items[--index];
26888             if(item && !item.isHidden()){
26889                 return item;
26890             }
26891         }
26892         return null;
26893     },
26894
26895     /**
26896      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
26897      * @param {String/Number} id The id or index of the TabPanelItem to disable.
26898      */
26899     disableTab : function(id){
26900         var tab = this.items[id];
26901         if(tab && this.active != tab){
26902             tab.disable();
26903         }
26904     },
26905
26906     /**
26907      * Enables a {@link Roo.TabPanelItem} that is disabled.
26908      * @param {String/Number} id The id or index of the TabPanelItem to enable.
26909      */
26910     enableTab : function(id){
26911         var tab = this.items[id];
26912         tab.enable();
26913     },
26914
26915     /**
26916      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
26917      * @param {String/Number} id The id or index of the TabPanelItem to activate.
26918      * @return {Roo.TabPanelItem} The TabPanelItem.
26919      */
26920     activate : function(id){
26921         var tab = this.items[id];
26922         if(!tab){
26923             return null;
26924         }
26925         if(tab == this.active || tab.disabled){
26926             return tab;
26927         }
26928         var e = {};
26929         this.fireEvent("beforetabchange", this, e, tab);
26930         if(e.cancel !== true && !tab.disabled){
26931             if(this.active){
26932                 this.active.hide();
26933             }
26934             this.active = this.items[id];
26935             this.active.show();
26936             this.fireEvent("tabchange", this, this.active);
26937         }
26938         return tab;
26939     },
26940
26941     /**
26942      * Gets the active {@link Roo.TabPanelItem}.
26943      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
26944      */
26945     getActiveTab : function(){
26946         return this.active;
26947     },
26948
26949     /**
26950      * Updates the tab body element to fit the height of the container element
26951      * for overflow scrolling
26952      * @param {Number} targetHeight (optional) Override the starting height from the elements height
26953      */
26954     syncHeight : function(targetHeight){
26955         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
26956         var bm = this.bodyEl.getMargins();
26957         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
26958         this.bodyEl.setHeight(newHeight);
26959         return newHeight;
26960     },
26961
26962     onResize : function(){
26963         if(this.monitorResize){
26964             this.autoSizeTabs();
26965         }
26966     },
26967
26968     /**
26969      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
26970      */
26971     beginUpdate : function(){
26972         this.updating = true;
26973     },
26974
26975     /**
26976      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
26977      */
26978     endUpdate : function(){
26979         this.updating = false;
26980         this.autoSizeTabs();
26981     },
26982
26983     /**
26984      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
26985      */
26986     autoSizeTabs : function(){
26987         var count = this.items.length;
26988         var vcount = count - this.hiddenCount;
26989         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) return;
26990         var w = Math.max(this.el.getWidth() - this.cpad, 10);
26991         var availWidth = Math.floor(w / vcount);
26992         var b = this.stripBody;
26993         if(b.getWidth() > w){
26994             var tabs = this.items;
26995             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
26996             if(availWidth < this.minTabWidth){
26997                 /*if(!this.sleft){    // incomplete scrolling code
26998                     this.createScrollButtons();
26999                 }
27000                 this.showScroll();
27001                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
27002             }
27003         }else{
27004             if(this.currentTabWidth < this.preferredTabWidth){
27005                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
27006             }
27007         }
27008     },
27009
27010     /**
27011      * Returns the number of tabs in this TabPanel.
27012      * @return {Number}
27013      */
27014      getCount : function(){
27015          return this.items.length;
27016      },
27017
27018     /**
27019      * Resizes all the tabs to the passed width
27020      * @param {Number} The new width
27021      */
27022     setTabWidth : function(width){
27023         this.currentTabWidth = width;
27024         for(var i = 0, len = this.items.length; i < len; i++) {
27025                 if(!this.items[i].isHidden())this.items[i].setWidth(width);
27026         }
27027     },
27028
27029     /**
27030      * Destroys this TabPanel
27031      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
27032      */
27033     destroy : function(removeEl){
27034         Roo.EventManager.removeResizeListener(this.onResize, this);
27035         for(var i = 0, len = this.items.length; i < len; i++){
27036             this.items[i].purgeListeners();
27037         }
27038         if(removeEl === true){
27039             this.el.update("");
27040             this.el.remove();
27041         }
27042     }
27043 });
27044
27045 /**
27046  * @class Roo.TabPanelItem
27047  * @extends Roo.util.Observable
27048  * Represents an individual item (tab plus body) in a TabPanel.
27049  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
27050  * @param {String} id The id of this TabPanelItem
27051  * @param {String} text The text for the tab of this TabPanelItem
27052  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
27053  */
27054 Roo.TabPanelItem = function(tabPanel, id, text, closable){
27055     /**
27056      * The {@link Roo.TabPanel} this TabPanelItem belongs to
27057      * @type Roo.TabPanel
27058      */
27059     this.tabPanel = tabPanel;
27060     /**
27061      * The id for this TabPanelItem
27062      * @type String
27063      */
27064     this.id = id;
27065     /** @private */
27066     this.disabled = false;
27067     /** @private */
27068     this.text = text;
27069     /** @private */
27070     this.loaded = false;
27071     this.closable = closable;
27072
27073     /**
27074      * The body element for this TabPanelItem.
27075      * @type Roo.Element
27076      */
27077     this.bodyEl = Roo.get(tabPanel.createItemBody(tabPanel.bodyEl.dom, id));
27078     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
27079     this.bodyEl.setStyle("display", "block");
27080     this.bodyEl.setStyle("zoom", "1");
27081     this.hideAction();
27082
27083     var els = tabPanel.createStripElements(tabPanel.stripEl.dom, text, closable);
27084     /** @private */
27085     this.el = Roo.get(els.el, true);
27086     this.inner = Roo.get(els.inner, true);
27087     this.textEl = Roo.get(this.el.dom.firstChild.firstChild.firstChild, true);
27088     this.pnode = Roo.get(els.el.parentNode, true);
27089     this.el.on("mousedown", this.onTabMouseDown, this);
27090     this.el.on("click", this.onTabClick, this);
27091     /** @private */
27092     if(closable){
27093         var c = Roo.get(els.close, true);
27094         c.dom.title = this.closeText;
27095         c.addClassOnOver("close-over");
27096         c.on("click", this.closeClick, this);
27097      }
27098
27099     this.addEvents({
27100          /**
27101          * @event activate
27102          * Fires when this tab becomes the active tab.
27103          * @param {Roo.TabPanel} tabPanel The parent TabPanel
27104          * @param {Roo.TabPanelItem} this
27105          */
27106         "activate": true,
27107         /**
27108          * @event beforeclose
27109          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
27110          * @param {Roo.TabPanelItem} this
27111          * @param {Object} e Set cancel to true on this object to cancel the close.
27112          */
27113         "beforeclose": true,
27114         /**
27115          * @event close
27116          * Fires when this tab is closed.
27117          * @param {Roo.TabPanelItem} this
27118          */
27119          "close": true,
27120         /**
27121          * @event deactivate
27122          * Fires when this tab is no longer the active tab.
27123          * @param {Roo.TabPanel} tabPanel The parent TabPanel
27124          * @param {Roo.TabPanelItem} this
27125          */
27126          "deactivate" : true
27127     });
27128     this.hidden = false;
27129
27130     Roo.TabPanelItem.superclass.constructor.call(this);
27131 };
27132
27133 Roo.extend(Roo.TabPanelItem, Roo.util.Observable, {
27134     purgeListeners : function(){
27135        Roo.util.Observable.prototype.purgeListeners.call(this);
27136        this.el.removeAllListeners();
27137     },
27138     /**
27139      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
27140      */
27141     show : function(){
27142         this.pnode.addClass("on");
27143         this.showAction();
27144         if(Roo.isOpera){
27145             this.tabPanel.stripWrap.repaint();
27146         }
27147         this.fireEvent("activate", this.tabPanel, this);
27148     },
27149
27150     /**
27151      * Returns true if this tab is the active tab.
27152      * @return {Boolean}
27153      */
27154     isActive : function(){
27155         return this.tabPanel.getActiveTab() == this;
27156     },
27157
27158     /**
27159      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
27160      */
27161     hide : function(){
27162         this.pnode.removeClass("on");
27163         this.hideAction();
27164         this.fireEvent("deactivate", this.tabPanel, this);
27165     },
27166
27167     hideAction : function(){
27168         this.bodyEl.hide();
27169         this.bodyEl.setStyle("position", "absolute");
27170         this.bodyEl.setLeft("-20000px");
27171         this.bodyEl.setTop("-20000px");
27172     },
27173
27174     showAction : function(){
27175         this.bodyEl.setStyle("position", "relative");
27176         this.bodyEl.setTop("");
27177         this.bodyEl.setLeft("");
27178         this.bodyEl.show();
27179     },
27180
27181     /**
27182      * Set the tooltip for the tab.
27183      * @param {String} tooltip The tab's tooltip
27184      */
27185     setTooltip : function(text){
27186         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
27187             this.textEl.dom.qtip = text;
27188             this.textEl.dom.removeAttribute('title');
27189         }else{
27190             this.textEl.dom.title = text;
27191         }
27192     },
27193
27194     onTabClick : function(e){
27195         e.preventDefault();
27196         this.tabPanel.activate(this.id);
27197     },
27198
27199     onTabMouseDown : function(e){
27200         e.preventDefault();
27201         this.tabPanel.activate(this.id);
27202     },
27203
27204     getWidth : function(){
27205         return this.inner.getWidth();
27206     },
27207
27208     setWidth : function(width){
27209         var iwidth = width - this.pnode.getPadding("lr");
27210         this.inner.setWidth(iwidth);
27211         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
27212         this.pnode.setWidth(width);
27213     },
27214
27215     /**
27216      * Show or hide the tab
27217      * @param {Boolean} hidden True to hide or false to show.
27218      */
27219     setHidden : function(hidden){
27220         this.hidden = hidden;
27221         this.pnode.setStyle("display", hidden ? "none" : "");
27222     },
27223
27224     /**
27225      * Returns true if this tab is "hidden"
27226      * @return {Boolean}
27227      */
27228     isHidden : function(){
27229         return this.hidden;
27230     },
27231
27232     /**
27233      * Returns the text for this tab
27234      * @return {String}
27235      */
27236     getText : function(){
27237         return this.text;
27238     },
27239
27240     autoSize : function(){
27241         //this.el.beginMeasure();
27242         this.textEl.setWidth(1);
27243         /*
27244          *  #2804 [new] Tabs in Roojs
27245          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
27246          */
27247         this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr") + 2);
27248         //this.el.endMeasure();
27249     },
27250
27251     /**
27252      * Sets the text for the tab (Note: this also sets the tooltip text)
27253      * @param {String} text The tab's text and tooltip
27254      */
27255     setText : function(text){
27256         this.text = text;
27257         this.textEl.update(text);
27258         this.setTooltip(text);
27259         if(!this.tabPanel.resizeTabs){
27260             this.autoSize();
27261         }
27262     },
27263     /**
27264      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
27265      */
27266     activate : function(){
27267         this.tabPanel.activate(this.id);
27268     },
27269
27270     /**
27271      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
27272      */
27273     disable : function(){
27274         if(this.tabPanel.active != this){
27275             this.disabled = true;
27276             this.pnode.addClass("disabled");
27277         }
27278     },
27279
27280     /**
27281      * Enables this TabPanelItem if it was previously disabled.
27282      */
27283     enable : function(){
27284         this.disabled = false;
27285         this.pnode.removeClass("disabled");
27286     },
27287
27288     /**
27289      * Sets the content for this TabPanelItem.
27290      * @param {String} content The content
27291      * @param {Boolean} loadScripts true to look for and load scripts
27292      */
27293     setContent : function(content, loadScripts){
27294         this.bodyEl.update(content, loadScripts);
27295     },
27296
27297     /**
27298      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
27299      * @return {Roo.UpdateManager} The UpdateManager
27300      */
27301     getUpdateManager : function(){
27302         return this.bodyEl.getUpdateManager();
27303     },
27304
27305     /**
27306      * Set a URL to be used to load the content for this TabPanelItem.
27307      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
27308      * @param {String/Object} params (optional) The string params for the update call or an object of the params. See {@link Roo.UpdateManager#update} for more details. (Defaults to null)
27309      * @param {Boolean} loadOnce (optional) Whether to only load the content once. If this is false it makes the Ajax call every time this TabPanelItem is activated. (Defaults to false)
27310      * @return {Roo.UpdateManager} The UpdateManager
27311      */
27312     setUrl : function(url, params, loadOnce){
27313         if(this.refreshDelegate){
27314             this.un('activate', this.refreshDelegate);
27315         }
27316         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
27317         this.on("activate", this.refreshDelegate);
27318         return this.bodyEl.getUpdateManager();
27319     },
27320
27321     /** @private */
27322     _handleRefresh : function(url, params, loadOnce){
27323         if(!loadOnce || !this.loaded){
27324             var updater = this.bodyEl.getUpdateManager();
27325             updater.update(url, params, this._setLoaded.createDelegate(this));
27326         }
27327     },
27328
27329     /**
27330      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
27331      *   Will fail silently if the setUrl method has not been called.
27332      *   This does not activate the panel, just updates its content.
27333      */
27334     refresh : function(){
27335         if(this.refreshDelegate){
27336            this.loaded = false;
27337            this.refreshDelegate();
27338         }
27339     },
27340
27341     /** @private */
27342     _setLoaded : function(){
27343         this.loaded = true;
27344     },
27345
27346     /** @private */
27347     closeClick : function(e){
27348         var o = {};
27349         e.stopEvent();
27350         this.fireEvent("beforeclose", this, o);
27351         if(o.cancel !== true){
27352             this.tabPanel.removeTab(this.id);
27353         }
27354     },
27355     /**
27356      * The text displayed in the tooltip for the close icon.
27357      * @type String
27358      */
27359     closeText : "Close this tab"
27360 });
27361
27362 /** @private */
27363 Roo.TabPanel.prototype.createStrip = function(container){
27364     var strip = document.createElement("div");
27365     strip.className = "x-tabs-wrap";
27366     container.appendChild(strip);
27367     return strip;
27368 };
27369 /** @private */
27370 Roo.TabPanel.prototype.createStripList = function(strip){
27371     // div wrapper for retard IE
27372     // returns the "tr" element.
27373     strip.innerHTML = '<div class="x-tabs-strip-wrap">'+
27374         '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
27375         '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
27376     return strip.firstChild.firstChild.firstChild.firstChild;
27377 };
27378 /** @private */
27379 Roo.TabPanel.prototype.createBody = function(container){
27380     var body = document.createElement("div");
27381     Roo.id(body, "tab-body");
27382     Roo.fly(body).addClass("x-tabs-body");
27383     container.appendChild(body);
27384     return body;
27385 };
27386 /** @private */
27387 Roo.TabPanel.prototype.createItemBody = function(bodyEl, id){
27388     var body = Roo.getDom(id);
27389     if(!body){
27390         body = document.createElement("div");
27391         body.id = id;
27392     }
27393     Roo.fly(body).addClass("x-tabs-item-body");
27394     bodyEl.insertBefore(body, bodyEl.firstChild);
27395     return body;
27396 };
27397 /** @private */
27398 Roo.TabPanel.prototype.createStripElements = function(stripEl, text, closable){
27399     var td = document.createElement("td");
27400     stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
27401     //stripEl.appendChild(td);
27402     if(closable){
27403         td.className = "x-tabs-closable";
27404         if(!this.closeTpl){
27405             this.closeTpl = new Roo.Template(
27406                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
27407                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
27408                '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
27409             );
27410         }
27411         var el = this.closeTpl.overwrite(td, {"text": text});
27412         var close = el.getElementsByTagName("div")[0];
27413         var inner = el.getElementsByTagName("em")[0];
27414         return {"el": el, "close": close, "inner": inner};
27415     } else {
27416         if(!this.tabTpl){
27417             this.tabTpl = new Roo.Template(
27418                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
27419                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
27420             );
27421         }
27422         var el = this.tabTpl.overwrite(td, {"text": text});
27423         var inner = el.getElementsByTagName("em")[0];
27424         return {"el": el, "inner": inner};
27425     }
27426 };/*
27427  * Based on:
27428  * Ext JS Library 1.1.1
27429  * Copyright(c) 2006-2007, Ext JS, LLC.
27430  *
27431  * Originally Released Under LGPL - original licence link has changed is not relivant.
27432  *
27433  * Fork - LGPL
27434  * <script type="text/javascript">
27435  */
27436
27437 /**
27438  * @class Roo.Button
27439  * @extends Roo.util.Observable
27440  * Simple Button class
27441  * @cfg {String} text The button text
27442  * @cfg {String} icon The path to an image to display in the button (the image will be set as the background-image
27443  * CSS property of the button by default, so if you want a mixed icon/text button, set cls:"x-btn-text-icon")
27444  * @cfg {Function} handler A function called when the button is clicked (can be used instead of click event)
27445  * @cfg {Object} scope The scope of the handler
27446  * @cfg {Number} minWidth The minimum width for this button (used to give a set of buttons a common width)
27447  * @cfg {String/Object} tooltip The tooltip for the button - can be a string or QuickTips config object
27448  * @cfg {Boolean} hidden True to start hidden (defaults to false)
27449  * @cfg {Boolean} disabled True to start disabled (defaults to false)
27450  * @cfg {Boolean} pressed True to start pressed (only if enableToggle = true)
27451  * @cfg {String} toggleGroup The group this toggle button is a member of (only 1 per group can be pressed, only
27452    applies if enableToggle = true)
27453  * @cfg {String/HTMLElement/Element} renderTo The element to append the button to
27454  * @cfg {Boolean/Object} repeat True to repeat fire the click event while the mouse is down. This can also be
27455   an {@link Roo.util.ClickRepeater} config object (defaults to false).
27456  * @constructor
27457  * Create a new button
27458  * @param {Object} config The config object
27459  */
27460 Roo.Button = function(renderTo, config)
27461 {
27462     if (!config) {
27463         config = renderTo;
27464         renderTo = config.renderTo || false;
27465     }
27466     
27467     Roo.apply(this, config);
27468     this.addEvents({
27469         /**
27470              * @event click
27471              * Fires when this button is clicked
27472              * @param {Button} this
27473              * @param {EventObject} e The click event
27474              */
27475             "click" : true,
27476         /**
27477              * @event toggle
27478              * Fires when the "pressed" state of this button changes (only if enableToggle = true)
27479              * @param {Button} this
27480              * @param {Boolean} pressed
27481              */
27482             "toggle" : true,
27483         /**
27484              * @event mouseover
27485              * Fires when the mouse hovers over the button
27486              * @param {Button} this
27487              * @param {Event} e The event object
27488              */
27489         'mouseover' : true,
27490         /**
27491              * @event mouseout
27492              * Fires when the mouse exits the button
27493              * @param {Button} this
27494              * @param {Event} e The event object
27495              */
27496         'mouseout': true,
27497          /**
27498              * @event render
27499              * Fires when the button is rendered
27500              * @param {Button} this
27501              */
27502         'render': true
27503     });
27504     if(this.menu){
27505         this.menu = Roo.menu.MenuMgr.get(this.menu);
27506     }
27507     // register listeners first!!  - so render can be captured..
27508     Roo.util.Observable.call(this);
27509     if(renderTo){
27510         this.render(renderTo);
27511     }
27512     
27513   
27514 };
27515
27516 Roo.extend(Roo.Button, Roo.util.Observable, {
27517     /**
27518      * 
27519      */
27520     
27521     /**
27522      * Read-only. True if this button is hidden
27523      * @type Boolean
27524      */
27525     hidden : false,
27526     /**
27527      * Read-only. True if this button is disabled
27528      * @type Boolean
27529      */
27530     disabled : false,
27531     /**
27532      * Read-only. True if this button is pressed (only if enableToggle = true)
27533      * @type Boolean
27534      */
27535     pressed : false,
27536
27537     /**
27538      * @cfg {Number} tabIndex 
27539      * The DOM tabIndex for this button (defaults to undefined)
27540      */
27541     tabIndex : undefined,
27542
27543     /**
27544      * @cfg {Boolean} enableToggle
27545      * True to enable pressed/not pressed toggling (defaults to false)
27546      */
27547     enableToggle: false,
27548     /**
27549      * @cfg {Mixed} menu
27550      * Standard menu attribute consisting of a reference to a menu object, a menu id or a menu config blob (defaults to undefined).
27551      */
27552     menu : undefined,
27553     /**
27554      * @cfg {String} menuAlign
27555      * The position to align the menu to (see {@link Roo.Element#alignTo} for more details, defaults to 'tl-bl?').
27556      */
27557     menuAlign : "tl-bl?",
27558
27559     /**
27560      * @cfg {String} iconCls
27561      * A css class which sets a background image to be used as the icon for this button (defaults to undefined).
27562      */
27563     iconCls : undefined,
27564     /**
27565      * @cfg {String} type
27566      * The button's type, corresponding to the DOM input element type attribute.  Either "submit," "reset" or "button" (default).
27567      */
27568     type : 'button',
27569
27570     // private
27571     menuClassTarget: 'tr',
27572
27573     /**
27574      * @cfg {String} clickEvent
27575      * The type of event to map to the button's event handler (defaults to 'click')
27576      */
27577     clickEvent : 'click',
27578
27579     /**
27580      * @cfg {Boolean} handleMouseEvents
27581      * False to disable visual cues on mouseover, mouseout and mousedown (defaults to true)
27582      */
27583     handleMouseEvents : true,
27584
27585     /**
27586      * @cfg {String} tooltipType
27587      * The type of tooltip to use. Either "qtip" (default) for QuickTips or "title" for title attribute.
27588      */
27589     tooltipType : 'qtip',
27590
27591     /**
27592      * @cfg {String} cls
27593      * A CSS class to apply to the button's main element.
27594      */
27595     
27596     /**
27597      * @cfg {Roo.Template} template (Optional)
27598      * An {@link Roo.Template} with which to create the Button's main element. This Template must
27599      * contain numeric substitution parameter 0 if it is to display the tRoo property. Changing the template could
27600      * require code modifications if required elements (e.g. a button) aren't present.
27601      */
27602
27603     // private
27604     render : function(renderTo){
27605         var btn;
27606         if(this.hideParent){
27607             this.parentEl = Roo.get(renderTo);
27608         }
27609         if(!this.dhconfig){
27610             if(!this.template){
27611                 if(!Roo.Button.buttonTemplate){
27612                     // hideous table template
27613                     Roo.Button.buttonTemplate = new Roo.Template(
27614                         '<table border="0" cellpadding="0" cellspacing="0" class="x-btn-wrap"><tbody><tr>',
27615                         '<td class="x-btn-left"><i>&#160;</i></td><td class="x-btn-center"><em unselectable="on"><button class="x-btn-text" type="{1}">{0}</button></em></td><td class="x-btn-right"><i>&#160;</i></td>',
27616                         "</tr></tbody></table>");
27617                 }
27618                 this.template = Roo.Button.buttonTemplate;
27619             }
27620             btn = this.template.append(renderTo, [this.text || '&#160;', this.type], true);
27621             var btnEl = btn.child("button:first");
27622             btnEl.on('focus', this.onFocus, this);
27623             btnEl.on('blur', this.onBlur, this);
27624             if(this.cls){
27625                 btn.addClass(this.cls);
27626             }
27627             if(this.icon){
27628                 btnEl.setStyle('background-image', 'url(' +this.icon +')');
27629             }
27630             if(this.iconCls){
27631                 btnEl.addClass(this.iconCls);
27632                 if(!this.cls){
27633                     btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
27634                 }
27635             }
27636             if(this.tabIndex !== undefined){
27637                 btnEl.dom.tabIndex = this.tabIndex;
27638             }
27639             if(this.tooltip){
27640                 if(typeof this.tooltip == 'object'){
27641                     Roo.QuickTips.tips(Roo.apply({
27642                           target: btnEl.id
27643                     }, this.tooltip));
27644                 } else {
27645                     btnEl.dom[this.tooltipType] = this.tooltip;
27646                 }
27647             }
27648         }else{
27649             btn = Roo.DomHelper.append(Roo.get(renderTo).dom, this.dhconfig, true);
27650         }
27651         this.el = btn;
27652         if(this.id){
27653             this.el.dom.id = this.el.id = this.id;
27654         }
27655         if(this.menu){
27656             this.el.child(this.menuClassTarget).addClass("x-btn-with-menu");
27657             this.menu.on("show", this.onMenuShow, this);
27658             this.menu.on("hide", this.onMenuHide, this);
27659         }
27660         btn.addClass("x-btn");
27661         if(Roo.isIE && !Roo.isIE7){
27662             this.autoWidth.defer(1, this);
27663         }else{
27664             this.autoWidth();
27665         }
27666         if(this.handleMouseEvents){
27667             btn.on("mouseover", this.onMouseOver, this);
27668             btn.on("mouseout", this.onMouseOut, this);
27669             btn.on("mousedown", this.onMouseDown, this);
27670         }
27671         btn.on(this.clickEvent, this.onClick, this);
27672         //btn.on("mouseup", this.onMouseUp, this);
27673         if(this.hidden){
27674             this.hide();
27675         }
27676         if(this.disabled){
27677             this.disable();
27678         }
27679         Roo.ButtonToggleMgr.register(this);
27680         if(this.pressed){
27681             this.el.addClass("x-btn-pressed");
27682         }
27683         if(this.repeat){
27684             var repeater = new Roo.util.ClickRepeater(btn,
27685                 typeof this.repeat == "object" ? this.repeat : {}
27686             );
27687             repeater.on("click", this.onClick,  this);
27688         }
27689         
27690         this.fireEvent('render', this);
27691         
27692     },
27693     /**
27694      * Returns the button's underlying element
27695      * @return {Roo.Element} The element
27696      */
27697     getEl : function(){
27698         return this.el;  
27699     },
27700     
27701     /**
27702      * Destroys this Button and removes any listeners.
27703      */
27704     destroy : function(){
27705         Roo.ButtonToggleMgr.unregister(this);
27706         this.el.removeAllListeners();
27707         this.purgeListeners();
27708         this.el.remove();
27709     },
27710
27711     // private
27712     autoWidth : function(){
27713         if(this.el){
27714             this.el.setWidth("auto");
27715             if(Roo.isIE7 && Roo.isStrict){
27716                 var ib = this.el.child('button');
27717                 if(ib && ib.getWidth() > 20){
27718                     ib.clip();
27719                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
27720                 }
27721             }
27722             if(this.minWidth){
27723                 if(this.hidden){
27724                     this.el.beginMeasure();
27725                 }
27726                 if(this.el.getWidth() < this.minWidth){
27727                     this.el.setWidth(this.minWidth);
27728                 }
27729                 if(this.hidden){
27730                     this.el.endMeasure();
27731                 }
27732             }
27733         }
27734     },
27735
27736     /**
27737      * Assigns this button's click handler
27738      * @param {Function} handler The function to call when the button is clicked
27739      * @param {Object} scope (optional) Scope for the function passed in
27740      */
27741     setHandler : function(handler, scope){
27742         this.handler = handler;
27743         this.scope = scope;  
27744     },
27745     
27746     /**
27747      * Sets this button's text
27748      * @param {String} text The button text
27749      */
27750     setText : function(text){
27751         this.text = text;
27752         if(this.el){
27753             this.el.child("td.x-btn-center button.x-btn-text").update(text);
27754         }
27755         this.autoWidth();
27756     },
27757     
27758     /**
27759      * Gets the text for this button
27760      * @return {String} The button text
27761      */
27762     getText : function(){
27763         return this.text;  
27764     },
27765     
27766     /**
27767      * Show this button
27768      */
27769     show: function(){
27770         this.hidden = false;
27771         if(this.el){
27772             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "");
27773         }
27774     },
27775     
27776     /**
27777      * Hide this button
27778      */
27779     hide: function(){
27780         this.hidden = true;
27781         if(this.el){
27782             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "none");
27783         }
27784     },
27785     
27786     /**
27787      * Convenience function for boolean show/hide
27788      * @param {Boolean} visible True to show, false to hide
27789      */
27790     setVisible: function(visible){
27791         if(visible) {
27792             this.show();
27793         }else{
27794             this.hide();
27795         }
27796     },
27797     
27798     /**
27799      * If a state it passed, it becomes the pressed state otherwise the current state is toggled.
27800      * @param {Boolean} state (optional) Force a particular state
27801      */
27802     toggle : function(state){
27803         state = state === undefined ? !this.pressed : state;
27804         if(state != this.pressed){
27805             if(state){
27806                 this.el.addClass("x-btn-pressed");
27807                 this.pressed = true;
27808                 this.fireEvent("toggle", this, true);
27809             }else{
27810                 this.el.removeClass("x-btn-pressed");
27811                 this.pressed = false;
27812                 this.fireEvent("toggle", this, false);
27813             }
27814             if(this.toggleHandler){
27815                 this.toggleHandler.call(this.scope || this, this, state);
27816             }
27817         }
27818     },
27819     
27820     /**
27821      * Focus the button
27822      */
27823     focus : function(){
27824         this.el.child('button:first').focus();
27825     },
27826     
27827     /**
27828      * Disable this button
27829      */
27830     disable : function(){
27831         if(this.el){
27832             this.el.addClass("x-btn-disabled");
27833         }
27834         this.disabled = true;
27835     },
27836     
27837     /**
27838      * Enable this button
27839      */
27840     enable : function(){
27841         if(this.el){
27842             this.el.removeClass("x-btn-disabled");
27843         }
27844         this.disabled = false;
27845     },
27846
27847     /**
27848      * Convenience function for boolean enable/disable
27849      * @param {Boolean} enabled True to enable, false to disable
27850      */
27851     setDisabled : function(v){
27852         this[v !== true ? "enable" : "disable"]();
27853     },
27854
27855     // private
27856     onClick : function(e){
27857         if(e){
27858             e.preventDefault();
27859         }
27860         if(e.button != 0){
27861             return;
27862         }
27863         if(!this.disabled){
27864             if(this.enableToggle){
27865                 this.toggle();
27866             }
27867             if(this.menu && !this.menu.isVisible()){
27868                 this.menu.show(this.el, this.menuAlign);
27869             }
27870             this.fireEvent("click", this, e);
27871             if(this.handler){
27872                 this.el.removeClass("x-btn-over");
27873                 this.handler.call(this.scope || this, this, e);
27874             }
27875         }
27876     },
27877     // private
27878     onMouseOver : function(e){
27879         if(!this.disabled){
27880             this.el.addClass("x-btn-over");
27881             this.fireEvent('mouseover', this, e);
27882         }
27883     },
27884     // private
27885     onMouseOut : function(e){
27886         if(!e.within(this.el,  true)){
27887             this.el.removeClass("x-btn-over");
27888             this.fireEvent('mouseout', this, e);
27889         }
27890     },
27891     // private
27892     onFocus : function(e){
27893         if(!this.disabled){
27894             this.el.addClass("x-btn-focus");
27895         }
27896     },
27897     // private
27898     onBlur : function(e){
27899         this.el.removeClass("x-btn-focus");
27900     },
27901     // private
27902     onMouseDown : function(e){
27903         if(!this.disabled && e.button == 0){
27904             this.el.addClass("x-btn-click");
27905             Roo.get(document).on('mouseup', this.onMouseUp, this);
27906         }
27907     },
27908     // private
27909     onMouseUp : function(e){
27910         if(e.button == 0){
27911             this.el.removeClass("x-btn-click");
27912             Roo.get(document).un('mouseup', this.onMouseUp, this);
27913         }
27914     },
27915     // private
27916     onMenuShow : function(e){
27917         this.el.addClass("x-btn-menu-active");
27918     },
27919     // private
27920     onMenuHide : function(e){
27921         this.el.removeClass("x-btn-menu-active");
27922     }   
27923 });
27924
27925 // Private utility class used by Button
27926 Roo.ButtonToggleMgr = function(){
27927    var groups = {};
27928    
27929    function toggleGroup(btn, state){
27930        if(state){
27931            var g = groups[btn.toggleGroup];
27932            for(var i = 0, l = g.length; i < l; i++){
27933                if(g[i] != btn){
27934                    g[i].toggle(false);
27935                }
27936            }
27937        }
27938    }
27939    
27940    return {
27941        register : function(btn){
27942            if(!btn.toggleGroup){
27943                return;
27944            }
27945            var g = groups[btn.toggleGroup];
27946            if(!g){
27947                g = groups[btn.toggleGroup] = [];
27948            }
27949            g.push(btn);
27950            btn.on("toggle", toggleGroup);
27951        },
27952        
27953        unregister : function(btn){
27954            if(!btn.toggleGroup){
27955                return;
27956            }
27957            var g = groups[btn.toggleGroup];
27958            if(g){
27959                g.remove(btn);
27960                btn.un("toggle", toggleGroup);
27961            }
27962        }
27963    };
27964 }();/*
27965  * Based on:
27966  * Ext JS Library 1.1.1
27967  * Copyright(c) 2006-2007, Ext JS, LLC.
27968  *
27969  * Originally Released Under LGPL - original licence link has changed is not relivant.
27970  *
27971  * Fork - LGPL
27972  * <script type="text/javascript">
27973  */
27974  
27975 /**
27976  * @class Roo.SplitButton
27977  * @extends Roo.Button
27978  * A split button that provides a built-in dropdown arrow that can fire an event separately from the default
27979  * click event of the button.  Typically this would be used to display a dropdown menu that provides additional
27980  * options to the primary button action, but any custom handler can provide the arrowclick implementation.
27981  * @cfg {Function} arrowHandler A function called when the arrow button is clicked (can be used instead of click event)
27982  * @cfg {String} arrowTooltip The title attribute of the arrow
27983  * @constructor
27984  * Create a new menu button
27985  * @param {String/HTMLElement/Element} renderTo The element to append the button to
27986  * @param {Object} config The config object
27987  */
27988 Roo.SplitButton = function(renderTo, config){
27989     Roo.SplitButton.superclass.constructor.call(this, renderTo, config);
27990     /**
27991      * @event arrowclick
27992      * Fires when this button's arrow is clicked
27993      * @param {SplitButton} this
27994      * @param {EventObject} e The click event
27995      */
27996     this.addEvents({"arrowclick":true});
27997 };
27998
27999 Roo.extend(Roo.SplitButton, Roo.Button, {
28000     render : function(renderTo){
28001         // this is one sweet looking template!
28002         var tpl = new Roo.Template(
28003             '<table cellspacing="0" class="x-btn-menu-wrap x-btn"><tr><td>',
28004             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-text-wrap"><tbody>',
28005             '<tr><td class="x-btn-left"><i>&#160;</i></td><td class="x-btn-center"><button class="x-btn-text" type="{1}">{0}</button></td></tr>',
28006             "</tbody></table></td><td>",
28007             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-arrow-wrap"><tbody>',
28008             '<tr><td class="x-btn-center"><button class="x-btn-menu-arrow-el" type="button">&#160;</button></td><td class="x-btn-right"><i>&#160;</i></td></tr>',
28009             "</tbody></table></td></tr></table>"
28010         );
28011         var btn = tpl.append(renderTo, [this.text, this.type], true);
28012         var btnEl = btn.child("button");
28013         if(this.cls){
28014             btn.addClass(this.cls);
28015         }
28016         if(this.icon){
28017             btnEl.setStyle('background-image', 'url(' +this.icon +')');
28018         }
28019         if(this.iconCls){
28020             btnEl.addClass(this.iconCls);
28021             if(!this.cls){
28022                 btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
28023             }
28024         }
28025         this.el = btn;
28026         if(this.handleMouseEvents){
28027             btn.on("mouseover", this.onMouseOver, this);
28028             btn.on("mouseout", this.onMouseOut, this);
28029             btn.on("mousedown", this.onMouseDown, this);
28030             btn.on("mouseup", this.onMouseUp, this);
28031         }
28032         btn.on(this.clickEvent, this.onClick, this);
28033         if(this.tooltip){
28034             if(typeof this.tooltip == 'object'){
28035                 Roo.QuickTips.tips(Roo.apply({
28036                       target: btnEl.id
28037                 }, this.tooltip));
28038             } else {
28039                 btnEl.dom[this.tooltipType] = this.tooltip;
28040             }
28041         }
28042         if(this.arrowTooltip){
28043             btn.child("button:nth(2)").dom[this.tooltipType] = this.arrowTooltip;
28044         }
28045         if(this.hidden){
28046             this.hide();
28047         }
28048         if(this.disabled){
28049             this.disable();
28050         }
28051         if(this.pressed){
28052             this.el.addClass("x-btn-pressed");
28053         }
28054         if(Roo.isIE && !Roo.isIE7){
28055             this.autoWidth.defer(1, this);
28056         }else{
28057             this.autoWidth();
28058         }
28059         if(this.menu){
28060             this.menu.on("show", this.onMenuShow, this);
28061             this.menu.on("hide", this.onMenuHide, this);
28062         }
28063         this.fireEvent('render', this);
28064     },
28065
28066     // private
28067     autoWidth : function(){
28068         if(this.el){
28069             var tbl = this.el.child("table:first");
28070             var tbl2 = this.el.child("table:last");
28071             this.el.setWidth("auto");
28072             tbl.setWidth("auto");
28073             if(Roo.isIE7 && Roo.isStrict){
28074                 var ib = this.el.child('button:first');
28075                 if(ib && ib.getWidth() > 20){
28076                     ib.clip();
28077                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
28078                 }
28079             }
28080             if(this.minWidth){
28081                 if(this.hidden){
28082                     this.el.beginMeasure();
28083                 }
28084                 if((tbl.getWidth()+tbl2.getWidth()) < this.minWidth){
28085                     tbl.setWidth(this.minWidth-tbl2.getWidth());
28086                 }
28087                 if(this.hidden){
28088                     this.el.endMeasure();
28089                 }
28090             }
28091             this.el.setWidth(tbl.getWidth()+tbl2.getWidth());
28092         } 
28093     },
28094     /**
28095      * Sets this button's click handler
28096      * @param {Function} handler The function to call when the button is clicked
28097      * @param {Object} scope (optional) Scope for the function passed above
28098      */
28099     setHandler : function(handler, scope){
28100         this.handler = handler;
28101         this.scope = scope;  
28102     },
28103     
28104     /**
28105      * Sets this button's arrow click handler
28106      * @param {Function} handler The function to call when the arrow is clicked
28107      * @param {Object} scope (optional) Scope for the function passed above
28108      */
28109     setArrowHandler : function(handler, scope){
28110         this.arrowHandler = handler;
28111         this.scope = scope;  
28112     },
28113     
28114     /**
28115      * Focus the button
28116      */
28117     focus : function(){
28118         if(this.el){
28119             this.el.child("button:first").focus();
28120         }
28121     },
28122
28123     // private
28124     onClick : function(e){
28125         e.preventDefault();
28126         if(!this.disabled){
28127             if(e.getTarget(".x-btn-menu-arrow-wrap")){
28128                 if(this.menu && !this.menu.isVisible()){
28129                     this.menu.show(this.el, this.menuAlign);
28130                 }
28131                 this.fireEvent("arrowclick", this, e);
28132                 if(this.arrowHandler){
28133                     this.arrowHandler.call(this.scope || this, this, e);
28134                 }
28135             }else{
28136                 this.fireEvent("click", this, e);
28137                 if(this.handler){
28138                     this.handler.call(this.scope || this, this, e);
28139                 }
28140             }
28141         }
28142     },
28143     // private
28144     onMouseDown : function(e){
28145         if(!this.disabled){
28146             Roo.fly(e.getTarget("table")).addClass("x-btn-click");
28147         }
28148     },
28149     // private
28150     onMouseUp : function(e){
28151         Roo.fly(e.getTarget("table")).removeClass("x-btn-click");
28152     }   
28153 });
28154
28155
28156 // backwards compat
28157 Roo.MenuButton = Roo.SplitButton;/*
28158  * Based on:
28159  * Ext JS Library 1.1.1
28160  * Copyright(c) 2006-2007, Ext JS, LLC.
28161  *
28162  * Originally Released Under LGPL - original licence link has changed is not relivant.
28163  *
28164  * Fork - LGPL
28165  * <script type="text/javascript">
28166  */
28167
28168 /**
28169  * @class Roo.Toolbar
28170  * Basic Toolbar class.
28171  * @constructor
28172  * Creates a new Toolbar
28173  * @param {Object} container The config object
28174  */ 
28175 Roo.Toolbar = function(container, buttons, config)
28176 {
28177     /// old consturctor format still supported..
28178     if(container instanceof Array){ // omit the container for later rendering
28179         buttons = container;
28180         config = buttons;
28181         container = null;
28182     }
28183     if (typeof(container) == 'object' && container.xtype) {
28184         config = container;
28185         container = config.container;
28186         buttons = config.buttons || []; // not really - use items!!
28187     }
28188     var xitems = [];
28189     if (config && config.items) {
28190         xitems = config.items;
28191         delete config.items;
28192     }
28193     Roo.apply(this, config);
28194     this.buttons = buttons;
28195     
28196     if(container){
28197         this.render(container);
28198     }
28199     this.xitems = xitems;
28200     Roo.each(xitems, function(b) {
28201         this.add(b);
28202     }, this);
28203     
28204 };
28205
28206 Roo.Toolbar.prototype = {
28207     /**
28208      * @cfg {Array} items
28209      * array of button configs or elements to add (will be converted to a MixedCollection)
28210      */
28211     
28212     /**
28213      * @cfg {String/HTMLElement/Element} container
28214      * The id or element that will contain the toolbar
28215      */
28216     // private
28217     render : function(ct){
28218         this.el = Roo.get(ct);
28219         if(this.cls){
28220             this.el.addClass(this.cls);
28221         }
28222         // using a table allows for vertical alignment
28223         // 100% width is needed by Safari...
28224         this.el.update('<div class="x-toolbar x-small-editor"><table cellspacing="0"><tr></tr></table></div>');
28225         this.tr = this.el.child("tr", true);
28226         var autoId = 0;
28227         this.items = new Roo.util.MixedCollection(false, function(o){
28228             return o.id || ("item" + (++autoId));
28229         });
28230         if(this.buttons){
28231             this.add.apply(this, this.buttons);
28232             delete this.buttons;
28233         }
28234     },
28235
28236     /**
28237      * Adds element(s) to the toolbar -- this function takes a variable number of 
28238      * arguments of mixed type and adds them to the toolbar.
28239      * @param {Mixed} arg1 The following types of arguments are all valid:<br />
28240      * <ul>
28241      * <li>{@link Roo.Toolbar.Button} config: A valid button config object (equivalent to {@link #addButton})</li>
28242      * <li>HtmlElement: Any standard HTML element (equivalent to {@link #addElement})</li>
28243      * <li>Field: Any form field (equivalent to {@link #addField})</li>
28244      * <li>Item: Any subclass of {@link Roo.Toolbar.Item} (equivalent to {@link #addItem})</li>
28245      * <li>String: Any generic string (gets wrapped in a {@link Roo.Toolbar.TextItem}, equivalent to {@link #addText}).
28246      * Note that there are a few special strings that are treated differently as explained nRoo.</li>
28247      * <li>'separator' or '-': Creates a separator element (equivalent to {@link #addSeparator})</li>
28248      * <li>' ': Creates a spacer element (equivalent to {@link #addSpacer})</li>
28249      * <li>'->': Creates a fill element (equivalent to {@link #addFill})</li>
28250      * </ul>
28251      * @param {Mixed} arg2
28252      * @param {Mixed} etc.
28253      */
28254     add : function(){
28255         var a = arguments, l = a.length;
28256         for(var i = 0; i < l; i++){
28257             this._add(a[i]);
28258         }
28259     },
28260     // private..
28261     _add : function(el) {
28262         
28263         if (el.xtype) {
28264             el = Roo.factory(el, typeof(Roo.Toolbar[el.xtype]) == 'undefined' ? Roo.form : Roo.Toolbar);
28265         }
28266         
28267         if (el.applyTo){ // some kind of form field
28268             return this.addField(el);
28269         } 
28270         if (el.render){ // some kind of Toolbar.Item
28271             return this.addItem(el);
28272         }
28273         if (typeof el == "string"){ // string
28274             if(el == "separator" || el == "-"){
28275                 return this.addSeparator();
28276             }
28277             if (el == " "){
28278                 return this.addSpacer();
28279             }
28280             if(el == "->"){
28281                 return this.addFill();
28282             }
28283             return this.addText(el);
28284             
28285         }
28286         if(el.tagName){ // element
28287             return this.addElement(el);
28288         }
28289         if(typeof el == "object"){ // must be button config?
28290             return this.addButton(el);
28291         }
28292         // and now what?!?!
28293         return false;
28294         
28295     },
28296     
28297     /**
28298      * Add an Xtype element
28299      * @param {Object} xtype Xtype Object
28300      * @return {Object} created Object
28301      */
28302     addxtype : function(e){
28303         return this.add(e);  
28304     },
28305     
28306     /**
28307      * Returns the Element for this toolbar.
28308      * @return {Roo.Element}
28309      */
28310     getEl : function(){
28311         return this.el;  
28312     },
28313     
28314     /**
28315      * Adds a separator
28316      * @return {Roo.Toolbar.Item} The separator item
28317      */
28318     addSeparator : function(){
28319         return this.addItem(new Roo.Toolbar.Separator());
28320     },
28321
28322     /**
28323      * Adds a spacer element
28324      * @return {Roo.Toolbar.Spacer} The spacer item
28325      */
28326     addSpacer : function(){
28327         return this.addItem(new Roo.Toolbar.Spacer());
28328     },
28329
28330     /**
28331      * Adds a fill element that forces subsequent additions to the right side of the toolbar
28332      * @return {Roo.Toolbar.Fill} The fill item
28333      */
28334     addFill : function(){
28335         return this.addItem(new Roo.Toolbar.Fill());
28336     },
28337
28338     /**
28339      * Adds any standard HTML element to the toolbar
28340      * @param {String/HTMLElement/Element} el The element or id of the element to add
28341      * @return {Roo.Toolbar.Item} The element's item
28342      */
28343     addElement : function(el){
28344         return this.addItem(new Roo.Toolbar.Item(el));
28345     },
28346     /**
28347      * Collection of items on the toolbar.. (only Toolbar Items, so use fields to retrieve fields)
28348      * @type Roo.util.MixedCollection  
28349      */
28350     items : false,
28351      
28352     /**
28353      * Adds any Toolbar.Item or subclass
28354      * @param {Roo.Toolbar.Item} item
28355      * @return {Roo.Toolbar.Item} The item
28356      */
28357     addItem : function(item){
28358         var td = this.nextBlock();
28359         item.render(td);
28360         this.items.add(item);
28361         return item;
28362     },
28363     
28364     /**
28365      * Adds a button (or buttons). See {@link Roo.Toolbar.Button} for more info on the config.
28366      * @param {Object/Array} config A button config or array of configs
28367      * @return {Roo.Toolbar.Button/Array}
28368      */
28369     addButton : function(config){
28370         if(config instanceof Array){
28371             var buttons = [];
28372             for(var i = 0, len = config.length; i < len; i++) {
28373                 buttons.push(this.addButton(config[i]));
28374             }
28375             return buttons;
28376         }
28377         var b = config;
28378         if(!(config instanceof Roo.Toolbar.Button)){
28379             b = config.split ?
28380                 new Roo.Toolbar.SplitButton(config) :
28381                 new Roo.Toolbar.Button(config);
28382         }
28383         var td = this.nextBlock();
28384         b.render(td);
28385         this.items.add(b);
28386         return b;
28387     },
28388     
28389     /**
28390      * Adds text to the toolbar
28391      * @param {String} text The text to add
28392      * @return {Roo.Toolbar.Item} The element's item
28393      */
28394     addText : function(text){
28395         return this.addItem(new Roo.Toolbar.TextItem(text));
28396     },
28397     
28398     /**
28399      * Inserts any {@link Roo.Toolbar.Item}/{@link Roo.Toolbar.Button} at the specified index.
28400      * @param {Number} index The index where the item is to be inserted
28401      * @param {Object/Roo.Toolbar.Item/Roo.Toolbar.Button (may be Array)} item The button, or button config object to be inserted.
28402      * @return {Roo.Toolbar.Button/Item}
28403      */
28404     insertButton : function(index, item){
28405         if(item instanceof Array){
28406             var buttons = [];
28407             for(var i = 0, len = item.length; i < len; i++) {
28408                buttons.push(this.insertButton(index + i, item[i]));
28409             }
28410             return buttons;
28411         }
28412         if (!(item instanceof Roo.Toolbar.Button)){
28413            item = new Roo.Toolbar.Button(item);
28414         }
28415         var td = document.createElement("td");
28416         this.tr.insertBefore(td, this.tr.childNodes[index]);
28417         item.render(td);
28418         this.items.insert(index, item);
28419         return item;
28420     },
28421     
28422     /**
28423      * Adds a new element to the toolbar from the passed {@link Roo.DomHelper} config.
28424      * @param {Object} config
28425      * @return {Roo.Toolbar.Item} The element's item
28426      */
28427     addDom : function(config, returnEl){
28428         var td = this.nextBlock();
28429         Roo.DomHelper.overwrite(td, config);
28430         var ti = new Roo.Toolbar.Item(td.firstChild);
28431         ti.render(td);
28432         this.items.add(ti);
28433         return ti;
28434     },
28435
28436     /**
28437      * Collection of fields on the toolbar.. usefull for quering (value is false if there are no fields)
28438      * @type Roo.util.MixedCollection  
28439      */
28440     fields : false,
28441     
28442     /**
28443      * Adds a dynamically rendered Roo.form field (TextField, ComboBox, etc).
28444      * Note: the field should not have been rendered yet. For a field that has already been
28445      * rendered, use {@link #addElement}.
28446      * @param {Roo.form.Field} field
28447      * @return {Roo.ToolbarItem}
28448      */
28449      
28450       
28451     addField : function(field) {
28452         if (!this.fields) {
28453             var autoId = 0;
28454             this.fields = new Roo.util.MixedCollection(false, function(o){
28455                 return o.id || ("item" + (++autoId));
28456             });
28457
28458         }
28459         
28460         var td = this.nextBlock();
28461         field.render(td);
28462         var ti = new Roo.Toolbar.Item(td.firstChild);
28463         ti.render(td);
28464         this.items.add(ti);
28465         this.fields.add(field);
28466         return ti;
28467     },
28468     /**
28469      * Hide the toolbar
28470      * @method hide
28471      */
28472      
28473       
28474     hide : function()
28475     {
28476         this.el.child('div').setVisibilityMode(Roo.Element.DISPLAY);
28477         this.el.child('div').hide();
28478     },
28479     /**
28480      * Show the toolbar
28481      * @method show
28482      */
28483     show : function()
28484     {
28485         this.el.child('div').show();
28486     },
28487       
28488     // private
28489     nextBlock : function(){
28490         var td = document.createElement("td");
28491         this.tr.appendChild(td);
28492         return td;
28493     },
28494
28495     // private
28496     destroy : function(){
28497         if(this.items){ // rendered?
28498             Roo.destroy.apply(Roo, this.items.items);
28499         }
28500         if(this.fields){ // rendered?
28501             Roo.destroy.apply(Roo, this.fields.items);
28502         }
28503         Roo.Element.uncache(this.el, this.tr);
28504     }
28505 };
28506
28507 /**
28508  * @class Roo.Toolbar.Item
28509  * The base class that other classes should extend in order to get some basic common toolbar item functionality.
28510  * @constructor
28511  * Creates a new Item
28512  * @param {HTMLElement} el 
28513  */
28514 Roo.Toolbar.Item = function(el){
28515     this.el = Roo.getDom(el);
28516     this.id = Roo.id(this.el);
28517     this.hidden = false;
28518 };
28519
28520 Roo.Toolbar.Item.prototype = {
28521     
28522     /**
28523      * Get this item's HTML Element
28524      * @return {HTMLElement}
28525      */
28526     getEl : function(){
28527        return this.el;  
28528     },
28529
28530     // private
28531     render : function(td){
28532         this.td = td;
28533         td.appendChild(this.el);
28534     },
28535     
28536     /**
28537      * Removes and destroys this item.
28538      */
28539     destroy : function(){
28540         this.td.parentNode.removeChild(this.td);
28541     },
28542     
28543     /**
28544      * Shows this item.
28545      */
28546     show: function(){
28547         this.hidden = false;
28548         this.td.style.display = "";
28549     },
28550     
28551     /**
28552      * Hides this item.
28553      */
28554     hide: function(){
28555         this.hidden = true;
28556         this.td.style.display = "none";
28557     },
28558     
28559     /**
28560      * Convenience function for boolean show/hide.
28561      * @param {Boolean} visible true to show/false to hide
28562      */
28563     setVisible: function(visible){
28564         if(visible) {
28565             this.show();
28566         }else{
28567             this.hide();
28568         }
28569     },
28570     
28571     /**
28572      * Try to focus this item.
28573      */
28574     focus : function(){
28575         Roo.fly(this.el).focus();
28576     },
28577     
28578     /**
28579      * Disables this item.
28580      */
28581     disable : function(){
28582         Roo.fly(this.td).addClass("x-item-disabled");
28583         this.disabled = true;
28584         this.el.disabled = true;
28585     },
28586     
28587     /**
28588      * Enables this item.
28589      */
28590     enable : function(){
28591         Roo.fly(this.td).removeClass("x-item-disabled");
28592         this.disabled = false;
28593         this.el.disabled = false;
28594     }
28595 };
28596
28597
28598 /**
28599  * @class Roo.Toolbar.Separator
28600  * @extends Roo.Toolbar.Item
28601  * A simple toolbar separator class
28602  * @constructor
28603  * Creates a new Separator
28604  */
28605 Roo.Toolbar.Separator = function(){
28606     var s = document.createElement("span");
28607     s.className = "ytb-sep";
28608     Roo.Toolbar.Separator.superclass.constructor.call(this, s);
28609 };
28610 Roo.extend(Roo.Toolbar.Separator, Roo.Toolbar.Item, {
28611     enable:Roo.emptyFn,
28612     disable:Roo.emptyFn,
28613     focus:Roo.emptyFn
28614 });
28615
28616 /**
28617  * @class Roo.Toolbar.Spacer
28618  * @extends Roo.Toolbar.Item
28619  * A simple element that adds extra horizontal space to a toolbar.
28620  * @constructor
28621  * Creates a new Spacer
28622  */
28623 Roo.Toolbar.Spacer = function(){
28624     var s = document.createElement("div");
28625     s.className = "ytb-spacer";
28626     Roo.Toolbar.Spacer.superclass.constructor.call(this, s);
28627 };
28628 Roo.extend(Roo.Toolbar.Spacer, Roo.Toolbar.Item, {
28629     enable:Roo.emptyFn,
28630     disable:Roo.emptyFn,
28631     focus:Roo.emptyFn
28632 });
28633
28634 /**
28635  * @class Roo.Toolbar.Fill
28636  * @extends Roo.Toolbar.Spacer
28637  * A simple element that adds a greedy (100% width) horizontal space to a toolbar.
28638  * @constructor
28639  * Creates a new Spacer
28640  */
28641 Roo.Toolbar.Fill = Roo.extend(Roo.Toolbar.Spacer, {
28642     // private
28643     render : function(td){
28644         td.style.width = '100%';
28645         Roo.Toolbar.Fill.superclass.render.call(this, td);
28646     }
28647 });
28648
28649 /**
28650  * @class Roo.Toolbar.TextItem
28651  * @extends Roo.Toolbar.Item
28652  * A simple class that renders text directly into a toolbar.
28653  * @constructor
28654  * Creates a new TextItem
28655  * @param {String} text
28656  */
28657 Roo.Toolbar.TextItem = function(text){
28658     if (typeof(text) == 'object') {
28659         text = text.text;
28660     }
28661     var s = document.createElement("span");
28662     s.className = "ytb-text";
28663     s.innerHTML = text;
28664     Roo.Toolbar.TextItem.superclass.constructor.call(this, s);
28665 };
28666 Roo.extend(Roo.Toolbar.TextItem, Roo.Toolbar.Item, {
28667     enable:Roo.emptyFn,
28668     disable:Roo.emptyFn,
28669     focus:Roo.emptyFn
28670 });
28671
28672 /**
28673  * @class Roo.Toolbar.Button
28674  * @extends Roo.Button
28675  * A button that renders into a toolbar.
28676  * @constructor
28677  * Creates a new Button
28678  * @param {Object} config A standard {@link Roo.Button} config object
28679  */
28680 Roo.Toolbar.Button = function(config){
28681     Roo.Toolbar.Button.superclass.constructor.call(this, null, config);
28682 };
28683 Roo.extend(Roo.Toolbar.Button, Roo.Button, {
28684     render : function(td){
28685         this.td = td;
28686         Roo.Toolbar.Button.superclass.render.call(this, td);
28687     },
28688     
28689     /**
28690      * Removes and destroys this button
28691      */
28692     destroy : function(){
28693         Roo.Toolbar.Button.superclass.destroy.call(this);
28694         this.td.parentNode.removeChild(this.td);
28695     },
28696     
28697     /**
28698      * Shows this button
28699      */
28700     show: function(){
28701         this.hidden = false;
28702         this.td.style.display = "";
28703     },
28704     
28705     /**
28706      * Hides this button
28707      */
28708     hide: function(){
28709         this.hidden = true;
28710         this.td.style.display = "none";
28711     },
28712
28713     /**
28714      * Disables this item
28715      */
28716     disable : function(){
28717         Roo.fly(this.td).addClass("x-item-disabled");
28718         this.disabled = true;
28719     },
28720
28721     /**
28722      * Enables this item
28723      */
28724     enable : function(){
28725         Roo.fly(this.td).removeClass("x-item-disabled");
28726         this.disabled = false;
28727     }
28728 });
28729 // backwards compat
28730 Roo.ToolbarButton = Roo.Toolbar.Button;
28731
28732 /**
28733  * @class Roo.Toolbar.SplitButton
28734  * @extends Roo.SplitButton
28735  * A menu button that renders into a toolbar.
28736  * @constructor
28737  * Creates a new SplitButton
28738  * @param {Object} config A standard {@link Roo.SplitButton} config object
28739  */
28740 Roo.Toolbar.SplitButton = function(config){
28741     Roo.Toolbar.SplitButton.superclass.constructor.call(this, null, config);
28742 };
28743 Roo.extend(Roo.Toolbar.SplitButton, Roo.SplitButton, {
28744     render : function(td){
28745         this.td = td;
28746         Roo.Toolbar.SplitButton.superclass.render.call(this, td);
28747     },
28748     
28749     /**
28750      * Removes and destroys this button
28751      */
28752     destroy : function(){
28753         Roo.Toolbar.SplitButton.superclass.destroy.call(this);
28754         this.td.parentNode.removeChild(this.td);
28755     },
28756     
28757     /**
28758      * Shows this button
28759      */
28760     show: function(){
28761         this.hidden = false;
28762         this.td.style.display = "";
28763     },
28764     
28765     /**
28766      * Hides this button
28767      */
28768     hide: function(){
28769         this.hidden = true;
28770         this.td.style.display = "none";
28771     }
28772 });
28773
28774 // backwards compat
28775 Roo.Toolbar.MenuButton = Roo.Toolbar.SplitButton;/*
28776  * Based on:
28777  * Ext JS Library 1.1.1
28778  * Copyright(c) 2006-2007, Ext JS, LLC.
28779  *
28780  * Originally Released Under LGPL - original licence link has changed is not relivant.
28781  *
28782  * Fork - LGPL
28783  * <script type="text/javascript">
28784  */
28785  
28786 /**
28787  * @class Roo.PagingToolbar
28788  * @extends Roo.Toolbar
28789  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
28790  * @constructor
28791  * Create a new PagingToolbar
28792  * @param {Object} config The config object
28793  */
28794 Roo.PagingToolbar = function(el, ds, config)
28795 {
28796     // old args format still supported... - xtype is prefered..
28797     if (typeof(el) == 'object' && el.xtype) {
28798         // created from xtype...
28799         config = el;
28800         ds = el.dataSource;
28801         el = config.container;
28802     }
28803     var items = [];
28804     if (config.items) {
28805         items = config.items;
28806         config.items = [];
28807     }
28808     
28809     Roo.PagingToolbar.superclass.constructor.call(this, el, null, config);
28810     this.ds = ds;
28811     this.cursor = 0;
28812     this.renderButtons(this.el);
28813     this.bind(ds);
28814     
28815     // supprot items array.
28816    
28817     Roo.each(items, function(e) {
28818         this.add(Roo.factory(e));
28819     },this);
28820     
28821 };
28822
28823 Roo.extend(Roo.PagingToolbar, Roo.Toolbar, {
28824     /**
28825      * @cfg {Roo.data.Store} dataSource
28826      * The underlying data store providing the paged data
28827      */
28828     /**
28829      * @cfg {String/HTMLElement/Element} container
28830      * container The id or element that will contain the toolbar
28831      */
28832     /**
28833      * @cfg {Boolean} displayInfo
28834      * True to display the displayMsg (defaults to false)
28835      */
28836     /**
28837      * @cfg {Number} pageSize
28838      * The number of records to display per page (defaults to 20)
28839      */
28840     pageSize: 20,
28841     /**
28842      * @cfg {String} displayMsg
28843      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
28844      */
28845     displayMsg : 'Displaying {0} - {1} of {2}',
28846     /**
28847      * @cfg {String} emptyMsg
28848      * The message to display when no records are found (defaults to "No data to display")
28849      */
28850     emptyMsg : 'No data to display',
28851     /**
28852      * Customizable piece of the default paging text (defaults to "Page")
28853      * @type String
28854      */
28855     beforePageText : "Page",
28856     /**
28857      * Customizable piece of the default paging text (defaults to "of %0")
28858      * @type String
28859      */
28860     afterPageText : "of {0}",
28861     /**
28862      * Customizable piece of the default paging text (defaults to "First Page")
28863      * @type String
28864      */
28865     firstText : "First Page",
28866     /**
28867      * Customizable piece of the default paging text (defaults to "Previous Page")
28868      * @type String
28869      */
28870     prevText : "Previous Page",
28871     /**
28872      * Customizable piece of the default paging text (defaults to "Next Page")
28873      * @type String
28874      */
28875     nextText : "Next Page",
28876     /**
28877      * Customizable piece of the default paging text (defaults to "Last Page")
28878      * @type String
28879      */
28880     lastText : "Last Page",
28881     /**
28882      * Customizable piece of the default paging text (defaults to "Refresh")
28883      * @type String
28884      */
28885     refreshText : "Refresh",
28886
28887     // private
28888     renderButtons : function(el){
28889         Roo.PagingToolbar.superclass.render.call(this, el);
28890         this.first = this.addButton({
28891             tooltip: this.firstText,
28892             cls: "x-btn-icon x-grid-page-first",
28893             disabled: true,
28894             handler: this.onClick.createDelegate(this, ["first"])
28895         });
28896         this.prev = this.addButton({
28897             tooltip: this.prevText,
28898             cls: "x-btn-icon x-grid-page-prev",
28899             disabled: true,
28900             handler: this.onClick.createDelegate(this, ["prev"])
28901         });
28902         //this.addSeparator();
28903         this.add(this.beforePageText);
28904         this.field = Roo.get(this.addDom({
28905            tag: "input",
28906            type: "text",
28907            size: "3",
28908            value: "1",
28909            cls: "x-grid-page-number"
28910         }).el);
28911         this.field.on("keydown", this.onPagingKeydown, this);
28912         this.field.on("focus", function(){this.dom.select();});
28913         this.afterTextEl = this.addText(String.format(this.afterPageText, 1));
28914         this.field.setHeight(18);
28915         //this.addSeparator();
28916         this.next = this.addButton({
28917             tooltip: this.nextText,
28918             cls: "x-btn-icon x-grid-page-next",
28919             disabled: true,
28920             handler: this.onClick.createDelegate(this, ["next"])
28921         });
28922         this.last = this.addButton({
28923             tooltip: this.lastText,
28924             cls: "x-btn-icon x-grid-page-last",
28925             disabled: true,
28926             handler: this.onClick.createDelegate(this, ["last"])
28927         });
28928         //this.addSeparator();
28929         this.loading = this.addButton({
28930             tooltip: this.refreshText,
28931             cls: "x-btn-icon x-grid-loading",
28932             handler: this.onClick.createDelegate(this, ["refresh"])
28933         });
28934
28935         if(this.displayInfo){
28936             this.displayEl = Roo.fly(this.el.dom.firstChild).createChild({cls:'x-paging-info'});
28937         }
28938     },
28939
28940     // private
28941     updateInfo : function(){
28942         if(this.displayEl){
28943             var count = this.ds.getCount();
28944             var msg = count == 0 ?
28945                 this.emptyMsg :
28946                 String.format(
28947                     this.displayMsg,
28948                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
28949                 );
28950             this.displayEl.update(msg);
28951         }
28952     },
28953
28954     // private
28955     onLoad : function(ds, r, o){
28956        this.cursor = o.params ? o.params.start : 0;
28957        var d = this.getPageData(), ap = d.activePage, ps = d.pages;
28958
28959        this.afterTextEl.el.innerHTML = String.format(this.afterPageText, d.pages);
28960        this.field.dom.value = ap;
28961        this.first.setDisabled(ap == 1);
28962        this.prev.setDisabled(ap == 1);
28963        this.next.setDisabled(ap == ps);
28964        this.last.setDisabled(ap == ps);
28965        this.loading.enable();
28966        this.updateInfo();
28967     },
28968
28969     // private
28970     getPageData : function(){
28971         var total = this.ds.getTotalCount();
28972         return {
28973             total : total,
28974             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
28975             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
28976         };
28977     },
28978
28979     // private
28980     onLoadError : function(){
28981         this.loading.enable();
28982     },
28983
28984     // private
28985     onPagingKeydown : function(e){
28986         var k = e.getKey();
28987         var d = this.getPageData();
28988         if(k == e.RETURN){
28989             var v = this.field.dom.value, pageNum;
28990             if(!v || isNaN(pageNum = parseInt(v, 10))){
28991                 this.field.dom.value = d.activePage;
28992                 return;
28993             }
28994             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
28995             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
28996             e.stopEvent();
28997         }
28998         else if(k == e.HOME || (k == e.UP && e.ctrlKey) || (k == e.PAGEUP && e.ctrlKey) || (k == e.RIGHT && e.ctrlKey) || k == e.END || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey))
28999         {
29000           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
29001           this.field.dom.value = pageNum;
29002           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
29003           e.stopEvent();
29004         }
29005         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
29006         {
29007           var v = this.field.dom.value, pageNum; 
29008           var increment = (e.shiftKey) ? 10 : 1;
29009           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
29010             increment *= -1;
29011           if(!v || isNaN(pageNum = parseInt(v, 10))) {
29012             this.field.dom.value = d.activePage;
29013             return;
29014           }
29015           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
29016           {
29017             this.field.dom.value = parseInt(v, 10) + increment;
29018             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
29019             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
29020           }
29021           e.stopEvent();
29022         }
29023     },
29024
29025     // private
29026     beforeLoad : function(){
29027         if(this.loading){
29028             this.loading.disable();
29029         }
29030     },
29031
29032     // private
29033     onClick : function(which){
29034         var ds = this.ds;
29035         switch(which){
29036             case "first":
29037                 ds.load({params:{start: 0, limit: this.pageSize}});
29038             break;
29039             case "prev":
29040                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
29041             break;
29042             case "next":
29043                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
29044             break;
29045             case "last":
29046                 var total = ds.getTotalCount();
29047                 var extra = total % this.pageSize;
29048                 var lastStart = extra ? (total - extra) : total-this.pageSize;
29049                 ds.load({params:{start: lastStart, limit: this.pageSize}});
29050             break;
29051             case "refresh":
29052                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
29053             break;
29054         }
29055     },
29056
29057     /**
29058      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
29059      * @param {Roo.data.Store} store The data store to unbind
29060      */
29061     unbind : function(ds){
29062         ds.un("beforeload", this.beforeLoad, this);
29063         ds.un("load", this.onLoad, this);
29064         ds.un("loadexception", this.onLoadError, this);
29065         ds.un("remove", this.updateInfo, this);
29066         ds.un("add", this.updateInfo, this);
29067         this.ds = undefined;
29068     },
29069
29070     /**
29071      * Binds the paging toolbar to the specified {@link Roo.data.Store}
29072      * @param {Roo.data.Store} store The data store to bind
29073      */
29074     bind : function(ds){
29075         ds.on("beforeload", this.beforeLoad, this);
29076         ds.on("load", this.onLoad, this);
29077         ds.on("loadexception", this.onLoadError, this);
29078         ds.on("remove", this.updateInfo, this);
29079         ds.on("add", this.updateInfo, this);
29080         this.ds = ds;
29081     }
29082 });/*
29083  * Based on:
29084  * Ext JS Library 1.1.1
29085  * Copyright(c) 2006-2007, Ext JS, LLC.
29086  *
29087  * Originally Released Under LGPL - original licence link has changed is not relivant.
29088  *
29089  * Fork - LGPL
29090  * <script type="text/javascript">
29091  */
29092
29093 /**
29094  * @class Roo.Resizable
29095  * @extends Roo.util.Observable
29096  * <p>Applies drag handles to an element to make it resizable. The drag handles are inserted into the element
29097  * and positioned absolute. Some elements, such as a textarea or image, don't support this. To overcome that, you can wrap
29098  * the textarea in a div and set "resizeChild" to true (or to the id of the element), <b>or</b> set wrap:true in your config and
29099  * the element will be wrapped for you automatically.</p>
29100  * <p>Here is the list of valid resize handles:</p>
29101  * <pre>
29102 Value   Description
29103 ------  -------------------
29104  'n'     north
29105  's'     south
29106  'e'     east
29107  'w'     west
29108  'nw'    northwest
29109  'sw'    southwest
29110  'se'    southeast
29111  'ne'    northeast
29112  'hd'    horizontal drag
29113  'all'   all
29114 </pre>
29115  * <p>Here's an example showing the creation of a typical Resizable:</p>
29116  * <pre><code>
29117 var resizer = new Roo.Resizable("element-id", {
29118     handles: 'all',
29119     minWidth: 200,
29120     minHeight: 100,
29121     maxWidth: 500,
29122     maxHeight: 400,
29123     pinned: true
29124 });
29125 resizer.on("resize", myHandler);
29126 </code></pre>
29127  * <p>To hide a particular handle, set its display to none in CSS, or through script:<br>
29128  * resizer.east.setDisplayed(false);</p>
29129  * @cfg {Boolean/String/Element} resizeChild True to resize the first child, or id/element to resize (defaults to false)
29130  * @cfg {Array/String} adjustments String "auto" or an array [width, height] with values to be <b>added</b> to the
29131  * resize operation's new size (defaults to [0, 0])
29132  * @cfg {Number} minWidth The minimum width for the element (defaults to 5)
29133  * @cfg {Number} minHeight The minimum height for the element (defaults to 5)
29134  * @cfg {Number} maxWidth The maximum width for the element (defaults to 10000)
29135  * @cfg {Number} maxHeight The maximum height for the element (defaults to 10000)
29136  * @cfg {Boolean} enabled False to disable resizing (defaults to true)
29137  * @cfg {Boolean} wrap True to wrap an element with a div if needed (required for textareas and images, defaults to false)
29138  * @cfg {Number} width The width of the element in pixels (defaults to null)
29139  * @cfg {Number} height The height of the element in pixels (defaults to null)
29140  * @cfg {Boolean} animate True to animate the resize (not compatible with dynamic sizing, defaults to false)
29141  * @cfg {Number} duration Animation duration if animate = true (defaults to .35)
29142  * @cfg {Boolean} dynamic True to resize the element while dragging instead of using a proxy (defaults to false)
29143  * @cfg {String} handles String consisting of the resize handles to display (defaults to undefined)
29144  * @cfg {Boolean} multiDirectional <b>Deprecated</b>.  The old style of adding multi-direction resize handles, deprecated
29145  * in favor of the handles config option (defaults to false)
29146  * @cfg {Boolean} disableTrackOver True to disable mouse tracking. This is only applied at config time. (defaults to false)
29147  * @cfg {String} easing Animation easing if animate = true (defaults to 'easingOutStrong')
29148  * @cfg {Number} widthIncrement The increment to snap the width resize in pixels (dynamic must be true, defaults to 0)
29149  * @cfg {Number} heightIncrement The increment to snap the height resize in pixels (dynamic must be true, defaults to 0)
29150  * @cfg {Boolean} pinned True to ensure that the resize handles are always visible, false to display them only when the
29151  * user mouses over the resizable borders. This is only applied at config time. (defaults to false)
29152  * @cfg {Boolean} preserveRatio True to preserve the original ratio between height and width during resize (defaults to false)
29153  * @cfg {Boolean} transparent True for transparent handles. This is only applied at config time. (defaults to false)
29154  * @cfg {Number} minX The minimum allowed page X for the element (only used for west resizing, defaults to 0)
29155  * @cfg {Number} minY The minimum allowed page Y for the element (only used for north resizing, defaults to 0)
29156  * @cfg {Boolean} draggable Convenience to initialize drag drop (defaults to false)
29157  * @constructor
29158  * Create a new resizable component
29159  * @param {String/HTMLElement/Roo.Element} el The id or element to resize
29160  * @param {Object} config configuration options
29161   */
29162 Roo.Resizable = function(el, config)
29163 {
29164     this.el = Roo.get(el);
29165
29166     if(config && config.wrap){
29167         config.resizeChild = this.el;
29168         this.el = this.el.wrap(typeof config.wrap == "object" ? config.wrap : {cls:"xresizable-wrap"});
29169         this.el.id = this.el.dom.id = config.resizeChild.id + "-rzwrap";
29170         this.el.setStyle("overflow", "hidden");
29171         this.el.setPositioning(config.resizeChild.getPositioning());
29172         config.resizeChild.clearPositioning();
29173         if(!config.width || !config.height){
29174             var csize = config.resizeChild.getSize();
29175             this.el.setSize(csize.width, csize.height);
29176         }
29177         if(config.pinned && !config.adjustments){
29178             config.adjustments = "auto";
29179         }
29180     }
29181
29182     this.proxy = this.el.createProxy({tag: "div", cls: "x-resizable-proxy", id: this.el.id + "-rzproxy"});
29183     this.proxy.unselectable();
29184     this.proxy.enableDisplayMode('block');
29185
29186     Roo.apply(this, config);
29187
29188     if(this.pinned){
29189         this.disableTrackOver = true;
29190         this.el.addClass("x-resizable-pinned");
29191     }
29192     // if the element isn't positioned, make it relative
29193     var position = this.el.getStyle("position");
29194     if(position != "absolute" && position != "fixed"){
29195         this.el.setStyle("position", "relative");
29196     }
29197     if(!this.handles){ // no handles passed, must be legacy style
29198         this.handles = 's,e,se';
29199         if(this.multiDirectional){
29200             this.handles += ',n,w';
29201         }
29202     }
29203     if(this.handles == "all"){
29204         this.handles = "n s e w ne nw se sw";
29205     }
29206     var hs = this.handles.split(/\s*?[,;]\s*?| /);
29207     var ps = Roo.Resizable.positions;
29208     for(var i = 0, len = hs.length; i < len; i++){
29209         if(hs[i] && ps[hs[i]]){
29210             var pos = ps[hs[i]];
29211             this[pos] = new Roo.Resizable.Handle(this, pos, this.disableTrackOver, this.transparent);
29212         }
29213     }
29214     // legacy
29215     this.corner = this.southeast;
29216     
29217     // updateBox = the box can move..
29218     if(this.handles.indexOf("n") != -1 || this.handles.indexOf("w") != -1 || this.handles.indexOf("hd") != -1) {
29219         this.updateBox = true;
29220     }
29221
29222     this.activeHandle = null;
29223
29224     if(this.resizeChild){
29225         if(typeof this.resizeChild == "boolean"){
29226             this.resizeChild = Roo.get(this.el.dom.firstChild, true);
29227         }else{
29228             this.resizeChild = Roo.get(this.resizeChild, true);
29229         }
29230     }
29231     
29232     if(this.adjustments == "auto"){
29233         var rc = this.resizeChild;
29234         var hw = this.west, he = this.east, hn = this.north, hs = this.south;
29235         if(rc && (hw || hn)){
29236             rc.position("relative");
29237             rc.setLeft(hw ? hw.el.getWidth() : 0);
29238             rc.setTop(hn ? hn.el.getHeight() : 0);
29239         }
29240         this.adjustments = [
29241             (he ? -he.el.getWidth() : 0) + (hw ? -hw.el.getWidth() : 0),
29242             (hn ? -hn.el.getHeight() : 0) + (hs ? -hs.el.getHeight() : 0) -1
29243         ];
29244     }
29245
29246     if(this.draggable){
29247         this.dd = this.dynamic ?
29248             this.el.initDD(null) : this.el.initDDProxy(null, {dragElId: this.proxy.id});
29249         this.dd.setHandleElId(this.resizeChild ? this.resizeChild.id : this.el.id);
29250     }
29251
29252     // public events
29253     this.addEvents({
29254         /**
29255          * @event beforeresize
29256          * Fired before resize is allowed. Set enabled to false to cancel resize.
29257          * @param {Roo.Resizable} this
29258          * @param {Roo.EventObject} e The mousedown event
29259          */
29260         "beforeresize" : true,
29261         /**
29262          * @event resizing
29263          * Fired a resizing.
29264          * @param {Roo.Resizable} this
29265          * @param {Number} x The new x position
29266          * @param {Number} y The new y position
29267          * @param {Number} w The new w width
29268          * @param {Number} h The new h hight
29269          * @param {Roo.EventObject} e The mouseup event
29270          */
29271         "resizing" : true,
29272         /**
29273          * @event resize
29274          * Fired after a resize.
29275          * @param {Roo.Resizable} this
29276          * @param {Number} width The new width
29277          * @param {Number} height The new height
29278          * @param {Roo.EventObject} e The mouseup event
29279          */
29280         "resize" : true
29281     });
29282
29283     if(this.width !== null && this.height !== null){
29284         this.resizeTo(this.width, this.height);
29285     }else{
29286         this.updateChildSize();
29287     }
29288     if(Roo.isIE){
29289         this.el.dom.style.zoom = 1;
29290     }
29291     Roo.Resizable.superclass.constructor.call(this);
29292 };
29293
29294 Roo.extend(Roo.Resizable, Roo.util.Observable, {
29295         resizeChild : false,
29296         adjustments : [0, 0],
29297         minWidth : 5,
29298         minHeight : 5,
29299         maxWidth : 10000,
29300         maxHeight : 10000,
29301         enabled : true,
29302         animate : false,
29303         duration : .35,
29304         dynamic : false,
29305         handles : false,
29306         multiDirectional : false,
29307         disableTrackOver : false,
29308         easing : 'easeOutStrong',
29309         widthIncrement : 0,
29310         heightIncrement : 0,
29311         pinned : false,
29312         width : null,
29313         height : null,
29314         preserveRatio : false,
29315         transparent: false,
29316         minX: 0,
29317         minY: 0,
29318         draggable: false,
29319
29320         /**
29321          * @cfg {String/HTMLElement/Element} constrainTo Constrain the resize to a particular element
29322          */
29323         constrainTo: undefined,
29324         /**
29325          * @cfg {Roo.lib.Region} resizeRegion Constrain the resize to a particular region
29326          */
29327         resizeRegion: undefined,
29328
29329
29330     /**
29331      * Perform a manual resize
29332      * @param {Number} width
29333      * @param {Number} height
29334      */
29335     resizeTo : function(width, height){
29336         this.el.setSize(width, height);
29337         this.updateChildSize();
29338         this.fireEvent("resize", this, width, height, null);
29339     },
29340
29341     // private
29342     startSizing : function(e, handle){
29343         this.fireEvent("beforeresize", this, e);
29344         if(this.enabled){ // 2nd enabled check in case disabled before beforeresize handler
29345
29346             if(!this.overlay){
29347                 this.overlay = this.el.createProxy({tag: "div", cls: "x-resizable-overlay", html: "&#160;"});
29348                 this.overlay.unselectable();
29349                 this.overlay.enableDisplayMode("block");
29350                 this.overlay.on("mousemove", this.onMouseMove, this);
29351                 this.overlay.on("mouseup", this.onMouseUp, this);
29352             }
29353             this.overlay.setStyle("cursor", handle.el.getStyle("cursor"));
29354
29355             this.resizing = true;
29356             this.startBox = this.el.getBox();
29357             this.startPoint = e.getXY();
29358             this.offsets = [(this.startBox.x + this.startBox.width) - this.startPoint[0],
29359                             (this.startBox.y + this.startBox.height) - this.startPoint[1]];
29360
29361             this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
29362             this.overlay.show();
29363
29364             if(this.constrainTo) {
29365                 var ct = Roo.get(this.constrainTo);
29366                 this.resizeRegion = ct.getRegion().adjust(
29367                     ct.getFrameWidth('t'),
29368                     ct.getFrameWidth('l'),
29369                     -ct.getFrameWidth('b'),
29370                     -ct.getFrameWidth('r')
29371                 );
29372             }
29373
29374             this.proxy.setStyle('visibility', 'hidden'); // workaround display none
29375             this.proxy.show();
29376             this.proxy.setBox(this.startBox);
29377             if(!this.dynamic){
29378                 this.proxy.setStyle('visibility', 'visible');
29379             }
29380         }
29381     },
29382
29383     // private
29384     onMouseDown : function(handle, e){
29385         if(this.enabled){
29386             e.stopEvent();
29387             this.activeHandle = handle;
29388             this.startSizing(e, handle);
29389         }
29390     },
29391
29392     // private
29393     onMouseUp : function(e){
29394         var size = this.resizeElement();
29395         this.resizing = false;
29396         this.handleOut();
29397         this.overlay.hide();
29398         this.proxy.hide();
29399         this.fireEvent("resize", this, size.width, size.height, e);
29400     },
29401
29402     // private
29403     updateChildSize : function(){
29404         
29405         if(this.resizeChild){
29406             var el = this.el;
29407             var child = this.resizeChild;
29408             var adj = this.adjustments;
29409             if(el.dom.offsetWidth){
29410                 var b = el.getSize(true);
29411                 child.setSize(b.width+adj[0], b.height+adj[1]);
29412             }
29413             // Second call here for IE
29414             // The first call enables instant resizing and
29415             // the second call corrects scroll bars if they
29416             // exist
29417             if(Roo.isIE){
29418                 setTimeout(function(){
29419                     if(el.dom.offsetWidth){
29420                         var b = el.getSize(true);
29421                         child.setSize(b.width+adj[0], b.height+adj[1]);
29422                     }
29423                 }, 10);
29424             }
29425         }
29426     },
29427
29428     // private
29429     snap : function(value, inc, min){
29430         if(!inc || !value) return value;
29431         var newValue = value;
29432         var m = value % inc;
29433         if(m > 0){
29434             if(m > (inc/2)){
29435                 newValue = value + (inc-m);
29436             }else{
29437                 newValue = value - m;
29438             }
29439         }
29440         return Math.max(min, newValue);
29441     },
29442
29443     // private
29444     resizeElement : function(){
29445         var box = this.proxy.getBox();
29446         if(this.updateBox){
29447             this.el.setBox(box, false, this.animate, this.duration, null, this.easing);
29448         }else{
29449             this.el.setSize(box.width, box.height, this.animate, this.duration, null, this.easing);
29450         }
29451         this.updateChildSize();
29452         if(!this.dynamic){
29453             this.proxy.hide();
29454         }
29455         return box;
29456     },
29457
29458     // private
29459     constrain : function(v, diff, m, mx){
29460         if(v - diff < m){
29461             diff = v - m;
29462         }else if(v - diff > mx){
29463             diff = mx - v;
29464         }
29465         return diff;
29466     },
29467
29468     // private
29469     onMouseMove : function(e){
29470         
29471         if(this.enabled){
29472             try{// try catch so if something goes wrong the user doesn't get hung
29473
29474             if(this.resizeRegion && !this.resizeRegion.contains(e.getPoint())) {
29475                 return;
29476             }
29477
29478             //var curXY = this.startPoint;
29479             var curSize = this.curSize || this.startBox;
29480             var x = this.startBox.x, y = this.startBox.y;
29481             var ox = x, oy = y;
29482             var w = curSize.width, h = curSize.height;
29483             var ow = w, oh = h;
29484             var mw = this.minWidth, mh = this.minHeight;
29485             var mxw = this.maxWidth, mxh = this.maxHeight;
29486             var wi = this.widthIncrement;
29487             var hi = this.heightIncrement;
29488
29489             var eventXY = e.getXY();
29490             var diffX = -(this.startPoint[0] - Math.max(this.minX, eventXY[0]));
29491             var diffY = -(this.startPoint[1] - Math.max(this.minY, eventXY[1]));
29492
29493             var pos = this.activeHandle.position;
29494
29495             switch(pos){
29496                 case "east":
29497                     w += diffX;
29498                     w = Math.min(Math.max(mw, w), mxw);
29499                     break;
29500              
29501                 case "south":
29502                     h += diffY;
29503                     h = Math.min(Math.max(mh, h), mxh);
29504                     break;
29505                 case "southeast":
29506                     w += diffX;
29507                     h += diffY;
29508                     w = Math.min(Math.max(mw, w), mxw);
29509                     h = Math.min(Math.max(mh, h), mxh);
29510                     break;
29511                 case "north":
29512                     diffY = this.constrain(h, diffY, mh, mxh);
29513                     y += diffY;
29514                     h -= diffY;
29515                     break;
29516                 case "hdrag":
29517                     
29518                     if (wi) {
29519                         var adiffX = Math.abs(diffX);
29520                         var sub = (adiffX % wi); // how much 
29521                         if (sub > (wi/2)) { // far enough to snap
29522                             diffX = (diffX > 0) ? diffX-sub + wi : diffX+sub - wi;
29523                         } else {
29524                             // remove difference.. 
29525                             diffX = (diffX > 0) ? diffX-sub : diffX+sub;
29526                         }
29527                     }
29528                     x += diffX;
29529                     x = Math.max(this.minX, x);
29530                     break;
29531                 case "west":
29532                     diffX = this.constrain(w, diffX, mw, mxw);
29533                     x += diffX;
29534                     w -= diffX;
29535                     break;
29536                 case "northeast":
29537                     w += diffX;
29538                     w = Math.min(Math.max(mw, w), mxw);
29539                     diffY = this.constrain(h, diffY, mh, mxh);
29540                     y += diffY;
29541                     h -= diffY;
29542                     break;
29543                 case "northwest":
29544                     diffX = this.constrain(w, diffX, mw, mxw);
29545                     diffY = this.constrain(h, diffY, mh, mxh);
29546                     y += diffY;
29547                     h -= diffY;
29548                     x += diffX;
29549                     w -= diffX;
29550                     break;
29551                case "southwest":
29552                     diffX = this.constrain(w, diffX, mw, mxw);
29553                     h += diffY;
29554                     h = Math.min(Math.max(mh, h), mxh);
29555                     x += diffX;
29556                     w -= diffX;
29557                     break;
29558             }
29559
29560             var sw = this.snap(w, wi, mw);
29561             var sh = this.snap(h, hi, mh);
29562             if(sw != w || sh != h){
29563                 switch(pos){
29564                     case "northeast":
29565                         y -= sh - h;
29566                     break;
29567                     case "north":
29568                         y -= sh - h;
29569                         break;
29570                     case "southwest":
29571                         x -= sw - w;
29572                     break;
29573                     case "west":
29574                         x -= sw - w;
29575                         break;
29576                     case "northwest":
29577                         x -= sw - w;
29578                         y -= sh - h;
29579                     break;
29580                 }
29581                 w = sw;
29582                 h = sh;
29583             }
29584
29585             if(this.preserveRatio){
29586                 switch(pos){
29587                     case "southeast":
29588                     case "east":
29589                         h = oh * (w/ow);
29590                         h = Math.min(Math.max(mh, h), mxh);
29591                         w = ow * (h/oh);
29592                        break;
29593                     case "south":
29594                         w = ow * (h/oh);
29595                         w = Math.min(Math.max(mw, w), mxw);
29596                         h = oh * (w/ow);
29597                         break;
29598                     case "northeast":
29599                         w = ow * (h/oh);
29600                         w = Math.min(Math.max(mw, w), mxw);
29601                         h = oh * (w/ow);
29602                     break;
29603                     case "north":
29604                         var tw = w;
29605                         w = ow * (h/oh);
29606                         w = Math.min(Math.max(mw, w), mxw);
29607                         h = oh * (w/ow);
29608                         x += (tw - w) / 2;
29609                         break;
29610                     case "southwest":
29611                         h = oh * (w/ow);
29612                         h = Math.min(Math.max(mh, h), mxh);
29613                         var tw = w;
29614                         w = ow * (h/oh);
29615                         x += tw - w;
29616                         break;
29617                     case "west":
29618                         var th = h;
29619                         h = oh * (w/ow);
29620                         h = Math.min(Math.max(mh, h), mxh);
29621                         y += (th - h) / 2;
29622                         var tw = w;
29623                         w = ow * (h/oh);
29624                         x += tw - w;
29625                        break;
29626                     case "northwest":
29627                         var tw = w;
29628                         var th = h;
29629                         h = oh * (w/ow);
29630                         h = Math.min(Math.max(mh, h), mxh);
29631                         w = ow * (h/oh);
29632                         y += th - h;
29633                         x += tw - w;
29634                        break;
29635
29636                 }
29637             }
29638             if (pos == 'hdrag') {
29639                 w = ow;
29640             }
29641             this.proxy.setBounds(x, y, w, h);
29642             if(this.dynamic){
29643                 this.resizeElement();
29644             }
29645             }catch(e){}
29646         }
29647         this.fireEvent("resizing", this, x, y, w, h, e);
29648     },
29649
29650     // private
29651     handleOver : function(){
29652         if(this.enabled){
29653             this.el.addClass("x-resizable-over");
29654         }
29655     },
29656
29657     // private
29658     handleOut : function(){
29659         if(!this.resizing){
29660             this.el.removeClass("x-resizable-over");
29661         }
29662     },
29663
29664     /**
29665      * Returns the element this component is bound to.
29666      * @return {Roo.Element}
29667      */
29668     getEl : function(){
29669         return this.el;
29670     },
29671
29672     /**
29673      * Returns the resizeChild element (or null).
29674      * @return {Roo.Element}
29675      */
29676     getResizeChild : function(){
29677         return this.resizeChild;
29678     },
29679     groupHandler : function()
29680     {
29681         
29682     },
29683     /**
29684      * Destroys this resizable. If the element was wrapped and
29685      * removeEl is not true then the element remains.
29686      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
29687      */
29688     destroy : function(removeEl){
29689         this.proxy.remove();
29690         if(this.overlay){
29691             this.overlay.removeAllListeners();
29692             this.overlay.remove();
29693         }
29694         var ps = Roo.Resizable.positions;
29695         for(var k in ps){
29696             if(typeof ps[k] != "function" && this[ps[k]]){
29697                 var h = this[ps[k]];
29698                 h.el.removeAllListeners();
29699                 h.el.remove();
29700             }
29701         }
29702         if(removeEl){
29703             this.el.update("");
29704             this.el.remove();
29705         }
29706     }
29707 });
29708
29709 // private
29710 // hash to map config positions to true positions
29711 Roo.Resizable.positions = {
29712     n: "north", s: "south", e: "east", w: "west", se: "southeast", sw: "southwest", nw: "northwest", ne: "northeast", 
29713     hd: "hdrag"
29714 };
29715
29716 // private
29717 Roo.Resizable.Handle = function(rz, pos, disableTrackOver, transparent){
29718     if(!this.tpl){
29719         // only initialize the template if resizable is used
29720         var tpl = Roo.DomHelper.createTemplate(
29721             {tag: "div", cls: "x-resizable-handle x-resizable-handle-{0}"}
29722         );
29723         tpl.compile();
29724         Roo.Resizable.Handle.prototype.tpl = tpl;
29725     }
29726     this.position = pos;
29727     this.rz = rz;
29728     // show north drag fro topdra
29729     var handlepos = pos == 'hdrag' ? 'north' : pos;
29730     
29731     this.el = this.tpl.append(rz.el.dom, [handlepos], true);
29732     if (pos == 'hdrag') {
29733         this.el.setStyle('cursor', 'pointer');
29734     }
29735     this.el.unselectable();
29736     if(transparent){
29737         this.el.setOpacity(0);
29738     }
29739     this.el.on("mousedown", this.onMouseDown, this);
29740     if(!disableTrackOver){
29741         this.el.on("mouseover", this.onMouseOver, this);
29742         this.el.on("mouseout", this.onMouseOut, this);
29743     }
29744 };
29745
29746 // private
29747 Roo.Resizable.Handle.prototype = {
29748     afterResize : function(rz){
29749         Roo.log('after?');
29750         // do nothing
29751     },
29752     // private
29753     onMouseDown : function(e){
29754         this.rz.onMouseDown(this, e);
29755     },
29756     // private
29757     onMouseOver : function(e){
29758         this.rz.handleOver(this, e);
29759     },
29760     // private
29761     onMouseOut : function(e){
29762         this.rz.handleOut(this, e);
29763     }
29764 };/*
29765  * Based on:
29766  * Ext JS Library 1.1.1
29767  * Copyright(c) 2006-2007, Ext JS, LLC.
29768  *
29769  * Originally Released Under LGPL - original licence link has changed is not relivant.
29770  *
29771  * Fork - LGPL
29772  * <script type="text/javascript">
29773  */
29774
29775 /**
29776  * @class Roo.Editor
29777  * @extends Roo.Component
29778  * A base editor field that handles displaying/hiding on demand and has some built-in sizing and event handling logic.
29779  * @constructor
29780  * Create a new Editor
29781  * @param {Roo.form.Field} field The Field object (or descendant)
29782  * @param {Object} config The config object
29783  */
29784 Roo.Editor = function(field, config){
29785     Roo.Editor.superclass.constructor.call(this, config);
29786     this.field = field;
29787     this.addEvents({
29788         /**
29789              * @event beforestartedit
29790              * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
29791              * false from the handler of this event.
29792              * @param {Editor} this
29793              * @param {Roo.Element} boundEl The underlying element bound to this editor
29794              * @param {Mixed} value The field value being set
29795              */
29796         "beforestartedit" : true,
29797         /**
29798              * @event startedit
29799              * Fires when this editor is displayed
29800              * @param {Roo.Element} boundEl The underlying element bound to this editor
29801              * @param {Mixed} value The starting field value
29802              */
29803         "startedit" : true,
29804         /**
29805              * @event beforecomplete
29806              * Fires after a change has been made to the field, but before the change is reflected in the underlying
29807              * field.  Saving the change to the field can be canceled by returning false from the handler of this event.
29808              * Note that if the value has not changed and ignoreNoChange = true, the editing will still end but this
29809              * event will not fire since no edit actually occurred.
29810              * @param {Editor} this
29811              * @param {Mixed} value The current field value
29812              * @param {Mixed} startValue The original field value
29813              */
29814         "beforecomplete" : true,
29815         /**
29816              * @event complete
29817              * Fires after editing is complete and any changed value has been written to the underlying field.
29818              * @param {Editor} this
29819              * @param {Mixed} value The current field value
29820              * @param {Mixed} startValue The original field value
29821              */
29822         "complete" : true,
29823         /**
29824          * @event specialkey
29825          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
29826          * {@link Roo.EventObject#getKey} to determine which key was pressed.
29827          * @param {Roo.form.Field} this
29828          * @param {Roo.EventObject} e The event object
29829          */
29830         "specialkey" : true
29831     });
29832 };
29833
29834 Roo.extend(Roo.Editor, Roo.Component, {
29835     /**
29836      * @cfg {Boolean/String} autosize
29837      * True for the editor to automatically adopt the size of the underlying field, "width" to adopt the width only,
29838      * or "height" to adopt the height only (defaults to false)
29839      */
29840     /**
29841      * @cfg {Boolean} revertInvalid
29842      * True to automatically revert the field value and cancel the edit when the user completes an edit and the field
29843      * validation fails (defaults to true)
29844      */
29845     /**
29846      * @cfg {Boolean} ignoreNoChange
29847      * True to skip the the edit completion process (no save, no events fired) if the user completes an edit and
29848      * the value has not changed (defaults to false).  Applies only to string values - edits for other data types
29849      * will never be ignored.
29850      */
29851     /**
29852      * @cfg {Boolean} hideEl
29853      * False to keep the bound element visible while the editor is displayed (defaults to true)
29854      */
29855     /**
29856      * @cfg {Mixed} value
29857      * The data value of the underlying field (defaults to "")
29858      */
29859     value : "",
29860     /**
29861      * @cfg {String} alignment
29862      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "c-c?").
29863      */
29864     alignment: "c-c?",
29865     /**
29866      * @cfg {Boolean/String} shadow "sides" for sides/bottom only, "frame" for 4-way shadow, and "drop"
29867      * for bottom-right shadow (defaults to "frame")
29868      */
29869     shadow : "frame",
29870     /**
29871      * @cfg {Boolean} constrain True to constrain the editor to the viewport
29872      */
29873     constrain : false,
29874     /**
29875      * @cfg {Boolean} completeOnEnter True to complete the edit when the enter key is pressed (defaults to false)
29876      */
29877     completeOnEnter : false,
29878     /**
29879      * @cfg {Boolean} cancelOnEsc True to cancel the edit when the escape key is pressed (defaults to false)
29880      */
29881     cancelOnEsc : false,
29882     /**
29883      * @cfg {Boolean} updateEl True to update the innerHTML of the bound element when the update completes (defaults to false)
29884      */
29885     updateEl : false,
29886
29887     // private
29888     onRender : function(ct, position){
29889         this.el = new Roo.Layer({
29890             shadow: this.shadow,
29891             cls: "x-editor",
29892             parentEl : ct,
29893             shim : this.shim,
29894             shadowOffset:4,
29895             id: this.id,
29896             constrain: this.constrain
29897         });
29898         this.el.setStyle("overflow", Roo.isGecko ? "auto" : "hidden");
29899         if(this.field.msgTarget != 'title'){
29900             this.field.msgTarget = 'qtip';
29901         }
29902         this.field.render(this.el);
29903         if(Roo.isGecko){
29904             this.field.el.dom.setAttribute('autocomplete', 'off');
29905         }
29906         this.field.on("specialkey", this.onSpecialKey, this);
29907         if(this.swallowKeys){
29908             this.field.el.swallowEvent(['keydown','keypress']);
29909         }
29910         this.field.show();
29911         this.field.on("blur", this.onBlur, this);
29912         if(this.field.grow){
29913             this.field.on("autosize", this.el.sync,  this.el, {delay:1});
29914         }
29915     },
29916
29917     onSpecialKey : function(field, e)
29918     {
29919         //Roo.log('editor onSpecialKey');
29920         if(this.completeOnEnter && e.getKey() == e.ENTER){
29921             e.stopEvent();
29922             this.completeEdit();
29923             return;
29924         }
29925         // do not fire special key otherwise it might hide close the editor...
29926         if(e.getKey() == e.ENTER){    
29927             return;
29928         }
29929         if(this.cancelOnEsc && e.getKey() == e.ESC){
29930             this.cancelEdit();
29931             return;
29932         } 
29933         this.fireEvent('specialkey', field, e);
29934     
29935     },
29936
29937     /**
29938      * Starts the editing process and shows the editor.
29939      * @param {String/HTMLElement/Element} el The element to edit
29940      * @param {String} value (optional) A value to initialize the editor with. If a value is not provided, it defaults
29941       * to the innerHTML of el.
29942      */
29943     startEdit : function(el, value){
29944         if(this.editing){
29945             this.completeEdit();
29946         }
29947         this.boundEl = Roo.get(el);
29948         var v = value !== undefined ? value : this.boundEl.dom.innerHTML;
29949         if(!this.rendered){
29950             this.render(this.parentEl || document.body);
29951         }
29952         if(this.fireEvent("beforestartedit", this, this.boundEl, v) === false){
29953             return;
29954         }
29955         this.startValue = v;
29956         this.field.setValue(v);
29957         if(this.autoSize){
29958             var sz = this.boundEl.getSize();
29959             switch(this.autoSize){
29960                 case "width":
29961                 this.setSize(sz.width,  "");
29962                 break;
29963                 case "height":
29964                 this.setSize("",  sz.height);
29965                 break;
29966                 default:
29967                 this.setSize(sz.width,  sz.height);
29968             }
29969         }
29970         this.el.alignTo(this.boundEl, this.alignment);
29971         this.editing = true;
29972         if(Roo.QuickTips){
29973             Roo.QuickTips.disable();
29974         }
29975         this.show();
29976     },
29977
29978     /**
29979      * Sets the height and width of this editor.
29980      * @param {Number} width The new width
29981      * @param {Number} height The new height
29982      */
29983     setSize : function(w, h){
29984         this.field.setSize(w, h);
29985         if(this.el){
29986             this.el.sync();
29987         }
29988     },
29989
29990     /**
29991      * Realigns the editor to the bound field based on the current alignment config value.
29992      */
29993     realign : function(){
29994         this.el.alignTo(this.boundEl, this.alignment);
29995     },
29996
29997     /**
29998      * Ends the editing process, persists the changed value to the underlying field, and hides the editor.
29999      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after edit (defaults to false)
30000      */
30001     completeEdit : function(remainVisible){
30002         if(!this.editing){
30003             return;
30004         }
30005         var v = this.getValue();
30006         if(this.revertInvalid !== false && !this.field.isValid()){
30007             v = this.startValue;
30008             this.cancelEdit(true);
30009         }
30010         if(String(v) === String(this.startValue) && this.ignoreNoChange){
30011             this.editing = false;
30012             this.hide();
30013             return;
30014         }
30015         if(this.fireEvent("beforecomplete", this, v, this.startValue) !== false){
30016             this.editing = false;
30017             if(this.updateEl && this.boundEl){
30018                 this.boundEl.update(v);
30019             }
30020             if(remainVisible !== true){
30021                 this.hide();
30022             }
30023             this.fireEvent("complete", this, v, this.startValue);
30024         }
30025     },
30026
30027     // private
30028     onShow : function(){
30029         this.el.show();
30030         if(this.hideEl !== false){
30031             this.boundEl.hide();
30032         }
30033         this.field.show();
30034         if(Roo.isIE && !this.fixIEFocus){ // IE has problems with focusing the first time
30035             this.fixIEFocus = true;
30036             this.deferredFocus.defer(50, this);
30037         }else{
30038             this.field.focus();
30039         }
30040         this.fireEvent("startedit", this.boundEl, this.startValue);
30041     },
30042
30043     deferredFocus : function(){
30044         if(this.editing){
30045             this.field.focus();
30046         }
30047     },
30048
30049     /**
30050      * Cancels the editing process and hides the editor without persisting any changes.  The field value will be
30051      * reverted to the original starting value.
30052      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after
30053      * cancel (defaults to false)
30054      */
30055     cancelEdit : function(remainVisible){
30056         if(this.editing){
30057             this.setValue(this.startValue);
30058             if(remainVisible !== true){
30059                 this.hide();
30060             }
30061         }
30062     },
30063
30064     // private
30065     onBlur : function(){
30066         if(this.allowBlur !== true && this.editing){
30067             this.completeEdit();
30068         }
30069     },
30070
30071     // private
30072     onHide : function(){
30073         if(this.editing){
30074             this.completeEdit();
30075             return;
30076         }
30077         this.field.blur();
30078         if(this.field.collapse){
30079             this.field.collapse();
30080         }
30081         this.el.hide();
30082         if(this.hideEl !== false){
30083             this.boundEl.show();
30084         }
30085         if(Roo.QuickTips){
30086             Roo.QuickTips.enable();
30087         }
30088     },
30089
30090     /**
30091      * Sets the data value of the editor
30092      * @param {Mixed} value Any valid value supported by the underlying field
30093      */
30094     setValue : function(v){
30095         this.field.setValue(v);
30096     },
30097
30098     /**
30099      * Gets the data value of the editor
30100      * @return {Mixed} The data value
30101      */
30102     getValue : function(){
30103         return this.field.getValue();
30104     }
30105 });/*
30106  * Based on:
30107  * Ext JS Library 1.1.1
30108  * Copyright(c) 2006-2007, Ext JS, LLC.
30109  *
30110  * Originally Released Under LGPL - original licence link has changed is not relivant.
30111  *
30112  * Fork - LGPL
30113  * <script type="text/javascript">
30114  */
30115  
30116 /**
30117  * @class Roo.BasicDialog
30118  * @extends Roo.util.Observable
30119  * Lightweight Dialog Class.  The code below shows the creation of a typical dialog using existing HTML markup:
30120  * <pre><code>
30121 var dlg = new Roo.BasicDialog("my-dlg", {
30122     height: 200,
30123     width: 300,
30124     minHeight: 100,
30125     minWidth: 150,
30126     modal: true,
30127     proxyDrag: true,
30128     shadow: true
30129 });
30130 dlg.addKeyListener(27, dlg.hide, dlg); // ESC can also close the dialog
30131 dlg.addButton('OK', dlg.hide, dlg);    // Could call a save function instead of hiding
30132 dlg.addButton('Cancel', dlg.hide, dlg);
30133 dlg.show();
30134 </code></pre>
30135   <b>A Dialog should always be a direct child of the body element.</b>
30136  * @cfg {Boolean/DomHelper} autoCreate True to auto create from scratch, or using a DomHelper Object (defaults to false)
30137  * @cfg {String} title Default text to display in the title bar (defaults to null)
30138  * @cfg {Number} width Width of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
30139  * @cfg {Number} height Height of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
30140  * @cfg {Number} x The default left page coordinate of the dialog (defaults to center screen)
30141  * @cfg {Number} y The default top page coordinate of the dialog (defaults to center screen)
30142  * @cfg {String/Element} animateTarget Id or element from which the dialog should animate while opening
30143  * (defaults to null with no animation)
30144  * @cfg {Boolean} resizable False to disable manual dialog resizing (defaults to true)
30145  * @cfg {String} resizeHandles Which resize handles to display - see the {@link Roo.Resizable} handles config
30146  * property for valid values (defaults to 'all')
30147  * @cfg {Number} minHeight The minimum allowable height for a resizable dialog (defaults to 80)
30148  * @cfg {Number} minWidth The minimum allowable width for a resizable dialog (defaults to 200)
30149  * @cfg {Boolean} modal True to show the dialog modally, preventing user interaction with the rest of the page (defaults to false)
30150  * @cfg {Boolean} autoScroll True to allow the dialog body contents to overflow and display scrollbars (defaults to false)
30151  * @cfg {Boolean} closable False to remove the built-in top-right corner close button (defaults to true)
30152  * @cfg {Boolean} collapsible False to remove the built-in top-right corner collapse button (defaults to true)
30153  * @cfg {Boolean} constraintoviewport True to keep the dialog constrained within the visible viewport boundaries (defaults to true)
30154  * @cfg {Boolean} syncHeightBeforeShow True to cause the dimensions to be recalculated before the dialog is shown (defaults to false)
30155  * @cfg {Boolean} draggable False to disable dragging of the dialog within the viewport (defaults to true)
30156  * @cfg {Boolean} autoTabs If true, all elements with class 'x-dlg-tab' will get automatically converted to tabs (defaults to false)
30157  * @cfg {String} tabTag The tag name of tab elements, used when autoTabs = true (defaults to 'div')
30158  * @cfg {Boolean} proxyDrag True to drag a lightweight proxy element rather than the dialog itself, used when
30159  * draggable = true (defaults to false)
30160  * @cfg {Boolean} fixedcenter True to ensure that anytime the dialog is shown or resized it gets centered (defaults to false)
30161  * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
30162  * shadow (defaults to false)
30163  * @cfg {Number} shadowOffset The number of pixels to offset the shadow if displayed (defaults to 5)
30164  * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "right")
30165  * @cfg {Number} minButtonWidth Minimum width of all dialog buttons (defaults to 75)
30166  * @cfg {Array} buttons Array of buttons
30167  * @cfg {Boolean} shim True to create an iframe shim that prevents selects from showing through (defaults to false)
30168  * @constructor
30169  * Create a new BasicDialog.
30170  * @param {String/HTMLElement/Roo.Element} el The container element or DOM node, or its id
30171  * @param {Object} config Configuration options
30172  */
30173 Roo.BasicDialog = function(el, config){
30174     this.el = Roo.get(el);
30175     var dh = Roo.DomHelper;
30176     if(!this.el && config && config.autoCreate){
30177         if(typeof config.autoCreate == "object"){
30178             if(!config.autoCreate.id){
30179                 config.autoCreate.id = el;
30180             }
30181             this.el = dh.append(document.body,
30182                         config.autoCreate, true);
30183         }else{
30184             this.el = dh.append(document.body,
30185                         {tag: "div", id: el, style:'visibility:hidden;'}, true);
30186         }
30187     }
30188     el = this.el;
30189     el.setDisplayed(true);
30190     el.hide = this.hideAction;
30191     this.id = el.id;
30192     el.addClass("x-dlg");
30193
30194     Roo.apply(this, config);
30195
30196     this.proxy = el.createProxy("x-dlg-proxy");
30197     this.proxy.hide = this.hideAction;
30198     this.proxy.setOpacity(.5);
30199     this.proxy.hide();
30200
30201     if(config.width){
30202         el.setWidth(config.width);
30203     }
30204     if(config.height){
30205         el.setHeight(config.height);
30206     }
30207     this.size = el.getSize();
30208     if(typeof config.x != "undefined" && typeof config.y != "undefined"){
30209         this.xy = [config.x,config.y];
30210     }else{
30211         this.xy = el.getCenterXY(true);
30212     }
30213     /** The header element @type Roo.Element */
30214     this.header = el.child("> .x-dlg-hd");
30215     /** The body element @type Roo.Element */
30216     this.body = el.child("> .x-dlg-bd");
30217     /** The footer element @type Roo.Element */
30218     this.footer = el.child("> .x-dlg-ft");
30219
30220     if(!this.header){
30221         this.header = el.createChild({tag: "div", cls:"x-dlg-hd", html: "&#160;"}, this.body ? this.body.dom : null);
30222     }
30223     if(!this.body){
30224         this.body = el.createChild({tag: "div", cls:"x-dlg-bd"});
30225     }
30226
30227     this.header.unselectable();
30228     if(this.title){
30229         this.header.update(this.title);
30230     }
30231     // this element allows the dialog to be focused for keyboard event
30232     this.focusEl = el.createChild({tag: "a", href:"#", cls:"x-dlg-focus", tabIndex:"-1"});
30233     this.focusEl.swallowEvent("click", true);
30234
30235     this.header.wrap({cls:"x-dlg-hd-right"}).wrap({cls:"x-dlg-hd-left"}, true);
30236
30237     // wrap the body and footer for special rendering
30238     this.bwrap = this.body.wrap({tag: "div", cls:"x-dlg-dlg-body"});
30239     if(this.footer){
30240         this.bwrap.dom.appendChild(this.footer.dom);
30241     }
30242
30243     this.bg = this.el.createChild({
30244         tag: "div", cls:"x-dlg-bg",
30245         html: '<div class="x-dlg-bg-left"><div class="x-dlg-bg-right"><div class="x-dlg-bg-center">&#160;</div></div></div>'
30246     });
30247     this.centerBg = this.bg.child("div.x-dlg-bg-center");
30248
30249
30250     if(this.autoScroll !== false && !this.autoTabs){
30251         this.body.setStyle("overflow", "auto");
30252     }
30253
30254     this.toolbox = this.el.createChild({cls: "x-dlg-toolbox"});
30255
30256     if(this.closable !== false){
30257         this.el.addClass("x-dlg-closable");
30258         this.close = this.toolbox.createChild({cls:"x-dlg-close"});
30259         this.close.on("click", this.closeClick, this);
30260         this.close.addClassOnOver("x-dlg-close-over");
30261     }
30262     if(this.collapsible !== false){
30263         this.collapseBtn = this.toolbox.createChild({cls:"x-dlg-collapse"});
30264         this.collapseBtn.on("click", this.collapseClick, this);
30265         this.collapseBtn.addClassOnOver("x-dlg-collapse-over");
30266         this.header.on("dblclick", this.collapseClick, this);
30267     }
30268     if(this.resizable !== false){
30269         this.el.addClass("x-dlg-resizable");
30270         this.resizer = new Roo.Resizable(el, {
30271             minWidth: this.minWidth || 80,
30272             minHeight:this.minHeight || 80,
30273             handles: this.resizeHandles || "all",
30274             pinned: true
30275         });
30276         this.resizer.on("beforeresize", this.beforeResize, this);
30277         this.resizer.on("resize", this.onResize, this);
30278     }
30279     if(this.draggable !== false){
30280         el.addClass("x-dlg-draggable");
30281         if (!this.proxyDrag) {
30282             var dd = new Roo.dd.DD(el.dom.id, "WindowDrag");
30283         }
30284         else {
30285             var dd = new Roo.dd.DDProxy(el.dom.id, "WindowDrag", {dragElId: this.proxy.id});
30286         }
30287         dd.setHandleElId(this.header.id);
30288         dd.endDrag = this.endMove.createDelegate(this);
30289         dd.startDrag = this.startMove.createDelegate(this);
30290         dd.onDrag = this.onDrag.createDelegate(this);
30291         dd.scroll = false;
30292         this.dd = dd;
30293     }
30294     if(this.modal){
30295         this.mask = dh.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
30296         this.mask.enableDisplayMode("block");
30297         this.mask.hide();
30298         this.el.addClass("x-dlg-modal");
30299     }
30300     if(this.shadow){
30301         this.shadow = new Roo.Shadow({
30302             mode : typeof this.shadow == "string" ? this.shadow : "sides",
30303             offset : this.shadowOffset
30304         });
30305     }else{
30306         this.shadowOffset = 0;
30307     }
30308     if(Roo.useShims && this.shim !== false){
30309         this.shim = this.el.createShim();
30310         this.shim.hide = this.hideAction;
30311         this.shim.hide();
30312     }else{
30313         this.shim = false;
30314     }
30315     if(this.autoTabs){
30316         this.initTabs();
30317     }
30318     if (this.buttons) { 
30319         var bts= this.buttons;
30320         this.buttons = [];
30321         Roo.each(bts, function(b) {
30322             this.addButton(b);
30323         }, this);
30324     }
30325     
30326     
30327     this.addEvents({
30328         /**
30329          * @event keydown
30330          * Fires when a key is pressed
30331          * @param {Roo.BasicDialog} this
30332          * @param {Roo.EventObject} e
30333          */
30334         "keydown" : true,
30335         /**
30336          * @event move
30337          * Fires when this dialog is moved by the user.
30338          * @param {Roo.BasicDialog} this
30339          * @param {Number} x The new page X
30340          * @param {Number} y The new page Y
30341          */
30342         "move" : true,
30343         /**
30344          * @event resize
30345          * Fires when this dialog is resized by the user.
30346          * @param {Roo.BasicDialog} this
30347          * @param {Number} width The new width
30348          * @param {Number} height The new height
30349          */
30350         "resize" : true,
30351         /**
30352          * @event beforehide
30353          * Fires before this dialog is hidden.
30354          * @param {Roo.BasicDialog} this
30355          */
30356         "beforehide" : true,
30357         /**
30358          * @event hide
30359          * Fires when this dialog is hidden.
30360          * @param {Roo.BasicDialog} this
30361          */
30362         "hide" : true,
30363         /**
30364          * @event beforeshow
30365          * Fires before this dialog is shown.
30366          * @param {Roo.BasicDialog} this
30367          */
30368         "beforeshow" : true,
30369         /**
30370          * @event show
30371          * Fires when this dialog is shown.
30372          * @param {Roo.BasicDialog} this
30373          */
30374         "show" : true
30375     });
30376     el.on("keydown", this.onKeyDown, this);
30377     el.on("mousedown", this.toFront, this);
30378     Roo.EventManager.onWindowResize(this.adjustViewport, this, true);
30379     this.el.hide();
30380     Roo.DialogManager.register(this);
30381     Roo.BasicDialog.superclass.constructor.call(this);
30382 };
30383
30384 Roo.extend(Roo.BasicDialog, Roo.util.Observable, {
30385     shadowOffset: Roo.isIE ? 6 : 5,
30386     minHeight: 80,
30387     minWidth: 200,
30388     minButtonWidth: 75,
30389     defaultButton: null,
30390     buttonAlign: "right",
30391     tabTag: 'div',
30392     firstShow: true,
30393
30394     /**
30395      * Sets the dialog title text
30396      * @param {String} text The title text to display
30397      * @return {Roo.BasicDialog} this
30398      */
30399     setTitle : function(text){
30400         this.header.update(text);
30401         return this;
30402     },
30403
30404     // private
30405     closeClick : function(){
30406         this.hide();
30407     },
30408
30409     // private
30410     collapseClick : function(){
30411         this[this.collapsed ? "expand" : "collapse"]();
30412     },
30413
30414     /**
30415      * Collapses the dialog to its minimized state (only the title bar is visible).
30416      * Equivalent to the user clicking the collapse dialog button.
30417      */
30418     collapse : function(){
30419         if(!this.collapsed){
30420             this.collapsed = true;
30421             this.el.addClass("x-dlg-collapsed");
30422             this.restoreHeight = this.el.getHeight();
30423             this.resizeTo(this.el.getWidth(), this.header.getHeight());
30424         }
30425     },
30426
30427     /**
30428      * Expands a collapsed dialog back to its normal state.  Equivalent to the user
30429      * clicking the expand dialog button.
30430      */
30431     expand : function(){
30432         if(this.collapsed){
30433             this.collapsed = false;
30434             this.el.removeClass("x-dlg-collapsed");
30435             this.resizeTo(this.el.getWidth(), this.restoreHeight);
30436         }
30437     },
30438
30439     /**
30440      * Reinitializes the tabs component, clearing out old tabs and finding new ones.
30441      * @return {Roo.TabPanel} The tabs component
30442      */
30443     initTabs : function(){
30444         var tabs = this.getTabs();
30445         while(tabs.getTab(0)){
30446             tabs.removeTab(0);
30447         }
30448         this.el.select(this.tabTag+'.x-dlg-tab').each(function(el){
30449             var dom = el.dom;
30450             tabs.addTab(Roo.id(dom), dom.title);
30451             dom.title = "";
30452         });
30453         tabs.activate(0);
30454         return tabs;
30455     },
30456
30457     // private
30458     beforeResize : function(){
30459         this.resizer.minHeight = Math.max(this.minHeight, this.getHeaderFooterHeight(true)+40);
30460     },
30461
30462     // private
30463     onResize : function(){
30464         this.refreshSize();
30465         this.syncBodyHeight();
30466         this.adjustAssets();
30467         this.focus();
30468         this.fireEvent("resize", this, this.size.width, this.size.height);
30469     },
30470
30471     // private
30472     onKeyDown : function(e){
30473         if(this.isVisible()){
30474             this.fireEvent("keydown", this, e);
30475         }
30476     },
30477
30478     /**
30479      * Resizes the dialog.
30480      * @param {Number} width
30481      * @param {Number} height
30482      * @return {Roo.BasicDialog} this
30483      */
30484     resizeTo : function(width, height){
30485         this.el.setSize(width, height);
30486         this.size = {width: width, height: height};
30487         this.syncBodyHeight();
30488         if(this.fixedcenter){
30489             this.center();
30490         }
30491         if(this.isVisible()){
30492             this.constrainXY();
30493             this.adjustAssets();
30494         }
30495         this.fireEvent("resize", this, width, height);
30496         return this;
30497     },
30498
30499
30500     /**
30501      * Resizes the dialog to fit the specified content size.
30502      * @param {Number} width
30503      * @param {Number} height
30504      * @return {Roo.BasicDialog} this
30505      */
30506     setContentSize : function(w, h){
30507         h += this.getHeaderFooterHeight() + this.body.getMargins("tb");
30508         w += this.body.getMargins("lr") + this.bwrap.getMargins("lr") + this.centerBg.getPadding("lr");
30509         //if(!this.el.isBorderBox()){
30510             h +=  this.body.getPadding("tb") + this.bwrap.getBorderWidth("tb") + this.body.getBorderWidth("tb") + this.el.getBorderWidth("tb");
30511             w += this.body.getPadding("lr") + this.bwrap.getBorderWidth("lr") + this.body.getBorderWidth("lr") + this.bwrap.getPadding("lr") + this.el.getBorderWidth("lr");
30512         //}
30513         if(this.tabs){
30514             h += this.tabs.stripWrap.getHeight() + this.tabs.bodyEl.getMargins("tb") + this.tabs.bodyEl.getPadding("tb");
30515             w += this.tabs.bodyEl.getMargins("lr") + this.tabs.bodyEl.getPadding("lr");
30516         }
30517         this.resizeTo(w, h);
30518         return this;
30519     },
30520
30521     /**
30522      * Adds a key listener for when this dialog is displayed.  This allows you to hook in a function that will be
30523      * executed in response to a particular key being pressed while the dialog is active.
30524      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the following options:
30525      *                                  {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
30526      * @param {Function} fn The function to call
30527      * @param {Object} scope (optional) The scope of the function
30528      * @return {Roo.BasicDialog} this
30529      */
30530     addKeyListener : function(key, fn, scope){
30531         var keyCode, shift, ctrl, alt;
30532         if(typeof key == "object" && !(key instanceof Array)){
30533             keyCode = key["key"];
30534             shift = key["shift"];
30535             ctrl = key["ctrl"];
30536             alt = key["alt"];
30537         }else{
30538             keyCode = key;
30539         }
30540         var handler = function(dlg, e){
30541             if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) &&  (!alt || e.altKey)){
30542                 var k = e.getKey();
30543                 if(keyCode instanceof Array){
30544                     for(var i = 0, len = keyCode.length; i < len; i++){
30545                         if(keyCode[i] == k){
30546                           fn.call(scope || window, dlg, k, e);
30547                           return;
30548                         }
30549                     }
30550                 }else{
30551                     if(k == keyCode){
30552                         fn.call(scope || window, dlg, k, e);
30553                     }
30554                 }
30555             }
30556         };
30557         this.on("keydown", handler);
30558         return this;
30559     },
30560
30561     /**
30562      * Returns the TabPanel component (creates it if it doesn't exist).
30563      * Note: If you wish to simply check for the existence of tabs without creating them,
30564      * check for a null 'tabs' property.
30565      * @return {Roo.TabPanel} The tabs component
30566      */
30567     getTabs : function(){
30568         if(!this.tabs){
30569             this.el.addClass("x-dlg-auto-tabs");
30570             this.body.addClass(this.tabPosition == "bottom" ? "x-tabs-bottom" : "x-tabs-top");
30571             this.tabs = new Roo.TabPanel(this.body.dom, this.tabPosition == "bottom");
30572         }
30573         return this.tabs;
30574     },
30575
30576     /**
30577      * Adds a button to the footer section of the dialog.
30578      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
30579      * object or a valid Roo.DomHelper element config
30580      * @param {Function} handler The function called when the button is clicked
30581      * @param {Object} scope (optional) The scope of the handler function (accepts position as a property)
30582      * @return {Roo.Button} The new button
30583      */
30584     addButton : function(config, handler, scope){
30585         var dh = Roo.DomHelper;
30586         if(!this.footer){
30587             this.footer = dh.append(this.bwrap, {tag: "div", cls:"x-dlg-ft"}, true);
30588         }
30589         if(!this.btnContainer){
30590             var tb = this.footer.createChild({
30591
30592                 cls:"x-dlg-btns x-dlg-btns-"+this.buttonAlign,
30593                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
30594             }, null, true);
30595             this.btnContainer = tb.firstChild.firstChild.firstChild;
30596         }
30597         var bconfig = {
30598             handler: handler,
30599             scope: scope,
30600             minWidth: this.minButtonWidth,
30601             hideParent:true
30602         };
30603         if(typeof config == "string"){
30604             bconfig.text = config;
30605         }else{
30606             if(config.tag){
30607                 bconfig.dhconfig = config;
30608             }else{
30609                 Roo.apply(bconfig, config);
30610             }
30611         }
30612         var fc = false;
30613         if ((typeof(bconfig.position) != 'undefined') && bconfig.position < this.btnContainer.childNodes.length-1) {
30614             bconfig.position = Math.max(0, bconfig.position);
30615             fc = this.btnContainer.childNodes[bconfig.position];
30616         }
30617          
30618         var btn = new Roo.Button(
30619             fc ? 
30620                 this.btnContainer.insertBefore(document.createElement("td"),fc)
30621                 : this.btnContainer.appendChild(document.createElement("td")),
30622             //Roo.get(this.btnContainer).createChild( { tag: 'td'},  fc ),
30623             bconfig
30624         );
30625         this.syncBodyHeight();
30626         if(!this.buttons){
30627             /**
30628              * Array of all the buttons that have been added to this dialog via addButton
30629              * @type Array
30630              */
30631             this.buttons = [];
30632         }
30633         this.buttons.push(btn);
30634         return btn;
30635     },
30636
30637     /**
30638      * Sets the default button to be focused when the dialog is displayed.
30639      * @param {Roo.BasicDialog.Button} btn The button object returned by {@link #addButton}
30640      * @return {Roo.BasicDialog} this
30641      */
30642     setDefaultButton : function(btn){
30643         this.defaultButton = btn;
30644         return this;
30645     },
30646
30647     // private
30648     getHeaderFooterHeight : function(safe){
30649         var height = 0;
30650         if(this.header){
30651            height += this.header.getHeight();
30652         }
30653         if(this.footer){
30654            var fm = this.footer.getMargins();
30655             height += (this.footer.getHeight()+fm.top+fm.bottom);
30656         }
30657         height += this.bwrap.getPadding("tb")+this.bwrap.getBorderWidth("tb");
30658         height += this.centerBg.getPadding("tb");
30659         return height;
30660     },
30661
30662     // private
30663     syncBodyHeight : function()
30664     {
30665         var bd = this.body, // the text
30666             cb = this.centerBg, // wrapper around bottom.. but does not seem to be used..
30667             bw = this.bwrap;
30668         var height = this.size.height - this.getHeaderFooterHeight(false);
30669         bd.setHeight(height-bd.getMargins("tb"));
30670         var hh = this.header.getHeight();
30671         var h = this.size.height-hh;
30672         cb.setHeight(h);
30673         
30674         bw.setLeftTop(cb.getPadding("l"), hh+cb.getPadding("t"));
30675         bw.setHeight(h-cb.getPadding("tb"));
30676         
30677         bw.setWidth(this.el.getWidth(true)-cb.getPadding("lr"));
30678         bd.setWidth(bw.getWidth(true));
30679         if(this.tabs){
30680             this.tabs.syncHeight();
30681             if(Roo.isIE){
30682                 this.tabs.el.repaint();
30683             }
30684         }
30685     },
30686
30687     /**
30688      * Restores the previous state of the dialog if Roo.state is configured.
30689      * @return {Roo.BasicDialog} this
30690      */
30691     restoreState : function(){
30692         var box = Roo.state.Manager.get(this.stateId || (this.el.id + "-state"));
30693         if(box && box.width){
30694             this.xy = [box.x, box.y];
30695             this.resizeTo(box.width, box.height);
30696         }
30697         return this;
30698     },
30699
30700     // private
30701     beforeShow : function(){
30702         this.expand();
30703         if(this.fixedcenter){
30704             this.xy = this.el.getCenterXY(true);
30705         }
30706         if(this.modal){
30707             Roo.get(document.body).addClass("x-body-masked");
30708             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
30709             this.mask.show();
30710         }
30711         this.constrainXY();
30712     },
30713
30714     // private
30715     animShow : function(){
30716         var b = Roo.get(this.animateTarget).getBox();
30717         this.proxy.setSize(b.width, b.height);
30718         this.proxy.setLocation(b.x, b.y);
30719         this.proxy.show();
30720         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height,
30721                     true, .35, this.showEl.createDelegate(this));
30722     },
30723
30724     /**
30725      * Shows the dialog.
30726      * @param {String/HTMLElement/Roo.Element} animateTarget (optional) Reset the animation target
30727      * @return {Roo.BasicDialog} this
30728      */
30729     show : function(animateTarget){
30730         if (this.fireEvent("beforeshow", this) === false){
30731             return;
30732         }
30733         if(this.syncHeightBeforeShow){
30734             this.syncBodyHeight();
30735         }else if(this.firstShow){
30736             this.firstShow = false;
30737             this.syncBodyHeight(); // sync the height on the first show instead of in the constructor
30738         }
30739         this.animateTarget = animateTarget || this.animateTarget;
30740         if(!this.el.isVisible()){
30741             this.beforeShow();
30742             if(this.animateTarget && Roo.get(this.animateTarget)){
30743                 this.animShow();
30744             }else{
30745                 this.showEl();
30746             }
30747         }
30748         return this;
30749     },
30750
30751     // private
30752     showEl : function(){
30753         this.proxy.hide();
30754         this.el.setXY(this.xy);
30755         this.el.show();
30756         this.adjustAssets(true);
30757         this.toFront();
30758         this.focus();
30759         // IE peekaboo bug - fix found by Dave Fenwick
30760         if(Roo.isIE){
30761             this.el.repaint();
30762         }
30763         this.fireEvent("show", this);
30764     },
30765
30766     /**
30767      * Focuses the dialog.  If a defaultButton is set, it will receive focus, otherwise the
30768      * dialog itself will receive focus.
30769      */
30770     focus : function(){
30771         if(this.defaultButton){
30772             this.defaultButton.focus();
30773         }else{
30774             this.focusEl.focus();
30775         }
30776     },
30777
30778     // private
30779     constrainXY : function(){
30780         if(this.constraintoviewport !== false){
30781             if(!this.viewSize){
30782                 if(this.container){
30783                     var s = this.container.getSize();
30784                     this.viewSize = [s.width, s.height];
30785                 }else{
30786                     this.viewSize = [Roo.lib.Dom.getViewWidth(),Roo.lib.Dom.getViewHeight()];
30787                 }
30788             }
30789             var s = Roo.get(this.container||document).getScroll();
30790
30791             var x = this.xy[0], y = this.xy[1];
30792             var w = this.size.width, h = this.size.height;
30793             var vw = this.viewSize[0], vh = this.viewSize[1];
30794             // only move it if it needs it
30795             var moved = false;
30796             // first validate right/bottom
30797             if(x + w > vw+s.left){
30798                 x = vw - w;
30799                 moved = true;
30800             }
30801             if(y + h > vh+s.top){
30802                 y = vh - h;
30803                 moved = true;
30804             }
30805             // then make sure top/left isn't negative
30806             if(x < s.left){
30807                 x = s.left;
30808                 moved = true;
30809             }
30810             if(y < s.top){
30811                 y = s.top;
30812                 moved = true;
30813             }
30814             if(moved){
30815                 // cache xy
30816                 this.xy = [x, y];
30817                 if(this.isVisible()){
30818                     this.el.setLocation(x, y);
30819                     this.adjustAssets();
30820                 }
30821             }
30822         }
30823     },
30824
30825     // private
30826     onDrag : function(){
30827         if(!this.proxyDrag){
30828             this.xy = this.el.getXY();
30829             this.adjustAssets();
30830         }
30831     },
30832
30833     // private
30834     adjustAssets : function(doShow){
30835         var x = this.xy[0], y = this.xy[1];
30836         var w = this.size.width, h = this.size.height;
30837         if(doShow === true){
30838             if(this.shadow){
30839                 this.shadow.show(this.el);
30840             }
30841             if(this.shim){
30842                 this.shim.show();
30843             }
30844         }
30845         if(this.shadow && this.shadow.isVisible()){
30846             this.shadow.show(this.el);
30847         }
30848         if(this.shim && this.shim.isVisible()){
30849             this.shim.setBounds(x, y, w, h);
30850         }
30851     },
30852
30853     // private
30854     adjustViewport : function(w, h){
30855         if(!w || !h){
30856             w = Roo.lib.Dom.getViewWidth();
30857             h = Roo.lib.Dom.getViewHeight();
30858         }
30859         // cache the size
30860         this.viewSize = [w, h];
30861         if(this.modal && this.mask.isVisible()){
30862             this.mask.setSize(w, h); // first make sure the mask isn't causing overflow
30863             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
30864         }
30865         if(this.isVisible()){
30866             this.constrainXY();
30867         }
30868     },
30869
30870     /**
30871      * Destroys this dialog and all its supporting elements (including any tabs, shim,
30872      * shadow, proxy, mask, etc.)  Also removes all event listeners.
30873      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
30874      */
30875     destroy : function(removeEl){
30876         if(this.isVisible()){
30877             this.animateTarget = null;
30878             this.hide();
30879         }
30880         Roo.EventManager.removeResizeListener(this.adjustViewport, this);
30881         if(this.tabs){
30882             this.tabs.destroy(removeEl);
30883         }
30884         Roo.destroy(
30885              this.shim,
30886              this.proxy,
30887              this.resizer,
30888              this.close,
30889              this.mask
30890         );
30891         if(this.dd){
30892             this.dd.unreg();
30893         }
30894         if(this.buttons){
30895            for(var i = 0, len = this.buttons.length; i < len; i++){
30896                this.buttons[i].destroy();
30897            }
30898         }
30899         this.el.removeAllListeners();
30900         if(removeEl === true){
30901             this.el.update("");
30902             this.el.remove();
30903         }
30904         Roo.DialogManager.unregister(this);
30905     },
30906
30907     // private
30908     startMove : function(){
30909         if(this.proxyDrag){
30910             this.proxy.show();
30911         }
30912         if(this.constraintoviewport !== false){
30913             this.dd.constrainTo(document.body, {right: this.shadowOffset, bottom: this.shadowOffset});
30914         }
30915     },
30916
30917     // private
30918     endMove : function(){
30919         if(!this.proxyDrag){
30920             Roo.dd.DD.prototype.endDrag.apply(this.dd, arguments);
30921         }else{
30922             Roo.dd.DDProxy.prototype.endDrag.apply(this.dd, arguments);
30923             this.proxy.hide();
30924         }
30925         this.refreshSize();
30926         this.adjustAssets();
30927         this.focus();
30928         this.fireEvent("move", this, this.xy[0], this.xy[1]);
30929     },
30930
30931     /**
30932      * Brings this dialog to the front of any other visible dialogs
30933      * @return {Roo.BasicDialog} this
30934      */
30935     toFront : function(){
30936         Roo.DialogManager.bringToFront(this);
30937         return this;
30938     },
30939
30940     /**
30941      * Sends this dialog to the back (under) of any other visible dialogs
30942      * @return {Roo.BasicDialog} this
30943      */
30944     toBack : function(){
30945         Roo.DialogManager.sendToBack(this);
30946         return this;
30947     },
30948
30949     /**
30950      * Centers this dialog in the viewport
30951      * @return {Roo.BasicDialog} this
30952      */
30953     center : function(){
30954         var xy = this.el.getCenterXY(true);
30955         this.moveTo(xy[0], xy[1]);
30956         return this;
30957     },
30958
30959     /**
30960      * Moves the dialog's top-left corner to the specified point
30961      * @param {Number} x
30962      * @param {Number} y
30963      * @return {Roo.BasicDialog} this
30964      */
30965     moveTo : function(x, y){
30966         this.xy = [x,y];
30967         if(this.isVisible()){
30968             this.el.setXY(this.xy);
30969             this.adjustAssets();
30970         }
30971         return this;
30972     },
30973
30974     /**
30975      * Aligns the dialog to the specified element
30976      * @param {String/HTMLElement/Roo.Element} element The element to align to.
30977      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details).
30978      * @param {Array} offsets (optional) Offset the positioning by [x, y]
30979      * @return {Roo.BasicDialog} this
30980      */
30981     alignTo : function(element, position, offsets){
30982         this.xy = this.el.getAlignToXY(element, position, offsets);
30983         if(this.isVisible()){
30984             this.el.setXY(this.xy);
30985             this.adjustAssets();
30986         }
30987         return this;
30988     },
30989
30990     /**
30991      * Anchors an element to another element and realigns it when the window is resized.
30992      * @param {String/HTMLElement/Roo.Element} element The element to align to.
30993      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details)
30994      * @param {Array} offsets (optional) Offset the positioning by [x, y]
30995      * @param {Boolean/Number} monitorScroll (optional) true to monitor body scroll and reposition. If this parameter
30996      * is a number, it is used as the buffer delay (defaults to 50ms).
30997      * @return {Roo.BasicDialog} this
30998      */
30999     anchorTo : function(el, alignment, offsets, monitorScroll){
31000         var action = function(){
31001             this.alignTo(el, alignment, offsets);
31002         };
31003         Roo.EventManager.onWindowResize(action, this);
31004         var tm = typeof monitorScroll;
31005         if(tm != 'undefined'){
31006             Roo.EventManager.on(window, 'scroll', action, this,
31007                 {buffer: tm == 'number' ? monitorScroll : 50});
31008         }
31009         action.call(this);
31010         return this;
31011     },
31012
31013     /**
31014      * Returns true if the dialog is visible
31015      * @return {Boolean}
31016      */
31017     isVisible : function(){
31018         return this.el.isVisible();
31019     },
31020
31021     // private
31022     animHide : function(callback){
31023         var b = Roo.get(this.animateTarget).getBox();
31024         this.proxy.show();
31025         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height);
31026         this.el.hide();
31027         this.proxy.setBounds(b.x, b.y, b.width, b.height, true, .35,
31028                     this.hideEl.createDelegate(this, [callback]));
31029     },
31030
31031     /**
31032      * Hides the dialog.
31033      * @param {Function} callback (optional) Function to call when the dialog is hidden
31034      * @return {Roo.BasicDialog} this
31035      */
31036     hide : function(callback){
31037         if (this.fireEvent("beforehide", this) === false){
31038             return;
31039         }
31040         if(this.shadow){
31041             this.shadow.hide();
31042         }
31043         if(this.shim) {
31044           this.shim.hide();
31045         }
31046         // sometimes animateTarget seems to get set.. causing problems...
31047         // this just double checks..
31048         if(this.animateTarget && Roo.get(this.animateTarget)) {
31049            this.animHide(callback);
31050         }else{
31051             this.el.hide();
31052             this.hideEl(callback);
31053         }
31054         return this;
31055     },
31056
31057     // private
31058     hideEl : function(callback){
31059         this.proxy.hide();
31060         if(this.modal){
31061             this.mask.hide();
31062             Roo.get(document.body).removeClass("x-body-masked");
31063         }
31064         this.fireEvent("hide", this);
31065         if(typeof callback == "function"){
31066             callback();
31067         }
31068     },
31069
31070     // private
31071     hideAction : function(){
31072         this.setLeft("-10000px");
31073         this.setTop("-10000px");
31074         this.setStyle("visibility", "hidden");
31075     },
31076
31077     // private
31078     refreshSize : function(){
31079         this.size = this.el.getSize();
31080         this.xy = this.el.getXY();
31081         Roo.state.Manager.set(this.stateId || this.el.id + "-state", this.el.getBox());
31082     },
31083
31084     // private
31085     // z-index is managed by the DialogManager and may be overwritten at any time
31086     setZIndex : function(index){
31087         if(this.modal){
31088             this.mask.setStyle("z-index", index);
31089         }
31090         if(this.shim){
31091             this.shim.setStyle("z-index", ++index);
31092         }
31093         if(this.shadow){
31094             this.shadow.setZIndex(++index);
31095         }
31096         this.el.setStyle("z-index", ++index);
31097         if(this.proxy){
31098             this.proxy.setStyle("z-index", ++index);
31099         }
31100         if(this.resizer){
31101             this.resizer.proxy.setStyle("z-index", ++index);
31102         }
31103
31104         this.lastZIndex = index;
31105     },
31106
31107     /**
31108      * Returns the element for this dialog
31109      * @return {Roo.Element} The underlying dialog Element
31110      */
31111     getEl : function(){
31112         return this.el;
31113     }
31114 });
31115
31116 /**
31117  * @class Roo.DialogManager
31118  * Provides global access to BasicDialogs that have been created and
31119  * support for z-indexing (layering) multiple open dialogs.
31120  */
31121 Roo.DialogManager = function(){
31122     var list = {};
31123     var accessList = [];
31124     var front = null;
31125
31126     // private
31127     var sortDialogs = function(d1, d2){
31128         return (!d1._lastAccess || d1._lastAccess < d2._lastAccess) ? -1 : 1;
31129     };
31130
31131     // private
31132     var orderDialogs = function(){
31133         accessList.sort(sortDialogs);
31134         var seed = Roo.DialogManager.zseed;
31135         for(var i = 0, len = accessList.length; i < len; i++){
31136             var dlg = accessList[i];
31137             if(dlg){
31138                 dlg.setZIndex(seed + (i*10));
31139             }
31140         }
31141     };
31142
31143     return {
31144         /**
31145          * The starting z-index for BasicDialogs (defaults to 9000)
31146          * @type Number The z-index value
31147          */
31148         zseed : 9000,
31149
31150         // private
31151         register : function(dlg){
31152             list[dlg.id] = dlg;
31153             accessList.push(dlg);
31154         },
31155
31156         // private
31157         unregister : function(dlg){
31158             delete list[dlg.id];
31159             var i=0;
31160             var len=0;
31161             if(!accessList.indexOf){
31162                 for(  i = 0, len = accessList.length; i < len; i++){
31163                     if(accessList[i] == dlg){
31164                         accessList.splice(i, 1);
31165                         return;
31166                     }
31167                 }
31168             }else{
31169                  i = accessList.indexOf(dlg);
31170                 if(i != -1){
31171                     accessList.splice(i, 1);
31172                 }
31173             }
31174         },
31175
31176         /**
31177          * Gets a registered dialog by id
31178          * @param {String/Object} id The id of the dialog or a dialog
31179          * @return {Roo.BasicDialog} this
31180          */
31181         get : function(id){
31182             return typeof id == "object" ? id : list[id];
31183         },
31184
31185         /**
31186          * Brings the specified dialog to the front
31187          * @param {String/Object} dlg The id of the dialog or a dialog
31188          * @return {Roo.BasicDialog} this
31189          */
31190         bringToFront : function(dlg){
31191             dlg = this.get(dlg);
31192             if(dlg != front){
31193                 front = dlg;
31194                 dlg._lastAccess = new Date().getTime();
31195                 orderDialogs();
31196             }
31197             return dlg;
31198         },
31199
31200         /**
31201          * Sends the specified dialog to the back
31202          * @param {String/Object} dlg The id of the dialog or a dialog
31203          * @return {Roo.BasicDialog} this
31204          */
31205         sendToBack : function(dlg){
31206             dlg = this.get(dlg);
31207             dlg._lastAccess = -(new Date().getTime());
31208             orderDialogs();
31209             return dlg;
31210         },
31211
31212         /**
31213          * Hides all dialogs
31214          */
31215         hideAll : function(){
31216             for(var id in list){
31217                 if(list[id] && typeof list[id] != "function" && list[id].isVisible()){
31218                     list[id].hide();
31219                 }
31220             }
31221         }
31222     };
31223 }();
31224
31225 /**
31226  * @class Roo.LayoutDialog
31227  * @extends Roo.BasicDialog
31228  * Dialog which provides adjustments for working with a layout in a Dialog.
31229  * Add your necessary layout config options to the dialog's config.<br>
31230  * Example usage (including a nested layout):
31231  * <pre><code>
31232 if(!dialog){
31233     dialog = new Roo.LayoutDialog("download-dlg", {
31234         modal: true,
31235         width:600,
31236         height:450,
31237         shadow:true,
31238         minWidth:500,
31239         minHeight:350,
31240         autoTabs:true,
31241         proxyDrag:true,
31242         // layout config merges with the dialog config
31243         center:{
31244             tabPosition: "top",
31245             alwaysShowTabs: true
31246         }
31247     });
31248     dialog.addKeyListener(27, dialog.hide, dialog);
31249     dialog.setDefaultButton(dialog.addButton("Close", dialog.hide, dialog));
31250     dialog.addButton("Build It!", this.getDownload, this);
31251
31252     // we can even add nested layouts
31253     var innerLayout = new Roo.BorderLayout("dl-inner", {
31254         east: {
31255             initialSize: 200,
31256             autoScroll:true,
31257             split:true
31258         },
31259         center: {
31260             autoScroll:true
31261         }
31262     });
31263     innerLayout.beginUpdate();
31264     innerLayout.add("east", new Roo.ContentPanel("dl-details"));
31265     innerLayout.add("center", new Roo.ContentPanel("selection-panel"));
31266     innerLayout.endUpdate(true);
31267
31268     var layout = dialog.getLayout();
31269     layout.beginUpdate();
31270     layout.add("center", new Roo.ContentPanel("standard-panel",
31271                         {title: "Download the Source", fitToFrame:true}));
31272     layout.add("center", new Roo.NestedLayoutPanel(innerLayout,
31273                {title: "Build your own roo.js"}));
31274     layout.getRegion("center").showPanel(sp);
31275     layout.endUpdate();
31276 }
31277 </code></pre>
31278     * @constructor
31279     * @param {String/HTMLElement/Roo.Element} el The id of or container element, or config
31280     * @param {Object} config configuration options
31281   */
31282 Roo.LayoutDialog = function(el, cfg){
31283     
31284     var config=  cfg;
31285     if (typeof(cfg) == 'undefined') {
31286         config = Roo.apply({}, el);
31287         // not sure why we use documentElement here.. - it should always be body.
31288         // IE7 borks horribly if we use documentElement.
31289         // webkit also does not like documentElement - it creates a body element...
31290         el = Roo.get( document.body || document.documentElement ).createChild();
31291         //config.autoCreate = true;
31292     }
31293     
31294     
31295     config.autoTabs = false;
31296     Roo.LayoutDialog.superclass.constructor.call(this, el, config);
31297     this.body.setStyle({overflow:"hidden", position:"relative"});
31298     this.layout = new Roo.BorderLayout(this.body.dom, config);
31299     this.layout.monitorWindowResize = false;
31300     this.el.addClass("x-dlg-auto-layout");
31301     // fix case when center region overwrites center function
31302     this.center = Roo.BasicDialog.prototype.center;
31303     this.on("show", this.layout.layout, this.layout, true);
31304     if (config.items) {
31305         var xitems = config.items;
31306         delete config.items;
31307         Roo.each(xitems, this.addxtype, this);
31308     }
31309     
31310     
31311 };
31312 Roo.extend(Roo.LayoutDialog, Roo.BasicDialog, {
31313     /**
31314      * Ends update of the layout <strike>and resets display to none</strike>. Use standard beginUpdate/endUpdate on the layout.
31315      * @deprecated
31316      */
31317     endUpdate : function(){
31318         this.layout.endUpdate();
31319     },
31320
31321     /**
31322      * Begins an update of the layout <strike>and sets display to block and visibility to hidden</strike>. Use standard beginUpdate/endUpdate on the layout.
31323      *  @deprecated
31324      */
31325     beginUpdate : function(){
31326         this.layout.beginUpdate();
31327     },
31328
31329     /**
31330      * Get the BorderLayout for this dialog
31331      * @return {Roo.BorderLayout}
31332      */
31333     getLayout : function(){
31334         return this.layout;
31335     },
31336
31337     showEl : function(){
31338         Roo.LayoutDialog.superclass.showEl.apply(this, arguments);
31339         if(Roo.isIE7){
31340             this.layout.layout();
31341         }
31342     },
31343
31344     // private
31345     // Use the syncHeightBeforeShow config option to control this automatically
31346     syncBodyHeight : function(){
31347         Roo.LayoutDialog.superclass.syncBodyHeight.call(this);
31348         if(this.layout){this.layout.layout();}
31349     },
31350     
31351       /**
31352      * Add an xtype element (actually adds to the layout.)
31353      * @return {Object} xdata xtype object data.
31354      */
31355     
31356     addxtype : function(c) {
31357         return this.layout.addxtype(c);
31358     }
31359 });/*
31360  * Based on:
31361  * Ext JS Library 1.1.1
31362  * Copyright(c) 2006-2007, Ext JS, LLC.
31363  *
31364  * Originally Released Under LGPL - original licence link has changed is not relivant.
31365  *
31366  * Fork - LGPL
31367  * <script type="text/javascript">
31368  */
31369  
31370 /**
31371  * @class Roo.MessageBox
31372  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
31373  * Example usage:
31374  *<pre><code>
31375 // Basic alert:
31376 Roo.Msg.alert('Status', 'Changes saved successfully.');
31377
31378 // Prompt for user data:
31379 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
31380     if (btn == 'ok'){
31381         // process text value...
31382     }
31383 });
31384
31385 // Show a dialog using config options:
31386 Roo.Msg.show({
31387    title:'Save Changes?',
31388    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
31389    buttons: Roo.Msg.YESNOCANCEL,
31390    fn: processResult,
31391    animEl: 'elId'
31392 });
31393 </code></pre>
31394  * @singleton
31395  */
31396 Roo.MessageBox = function(){
31397     var dlg, opt, mask, waitTimer;
31398     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
31399     var buttons, activeTextEl, bwidth;
31400
31401     // private
31402     var handleButton = function(button){
31403         dlg.hide();
31404         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
31405     };
31406
31407     // private
31408     var handleHide = function(){
31409         if(opt && opt.cls){
31410             dlg.el.removeClass(opt.cls);
31411         }
31412         if(waitTimer){
31413             Roo.TaskMgr.stop(waitTimer);
31414             waitTimer = null;
31415         }
31416     };
31417
31418     // private
31419     var updateButtons = function(b){
31420         var width = 0;
31421         if(!b){
31422             buttons["ok"].hide();
31423             buttons["cancel"].hide();
31424             buttons["yes"].hide();
31425             buttons["no"].hide();
31426             dlg.footer.dom.style.display = 'none';
31427             return width;
31428         }
31429         dlg.footer.dom.style.display = '';
31430         for(var k in buttons){
31431             if(typeof buttons[k] != "function"){
31432                 if(b[k]){
31433                     buttons[k].show();
31434                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.MessageBox.buttonText[k]);
31435                     width += buttons[k].el.getWidth()+15;
31436                 }else{
31437                     buttons[k].hide();
31438                 }
31439             }
31440         }
31441         return width;
31442     };
31443
31444     // private
31445     var handleEsc = function(d, k, e){
31446         if(opt && opt.closable !== false){
31447             dlg.hide();
31448         }
31449         if(e){
31450             e.stopEvent();
31451         }
31452     };
31453
31454     return {
31455         /**
31456          * Returns a reference to the underlying {@link Roo.BasicDialog} element
31457          * @return {Roo.BasicDialog} The BasicDialog element
31458          */
31459         getDialog : function(){
31460            if(!dlg){
31461                 dlg = new Roo.BasicDialog("x-msg-box", {
31462                     autoCreate : true,
31463                     shadow: true,
31464                     draggable: true,
31465                     resizable:false,
31466                     constraintoviewport:false,
31467                     fixedcenter:true,
31468                     collapsible : false,
31469                     shim:true,
31470                     modal: true,
31471                     width:400, height:100,
31472                     buttonAlign:"center",
31473                     closeClick : function(){
31474                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
31475                             handleButton("no");
31476                         }else{
31477                             handleButton("cancel");
31478                         }
31479                     }
31480                 });
31481                 dlg.on("hide", handleHide);
31482                 mask = dlg.mask;
31483                 dlg.addKeyListener(27, handleEsc);
31484                 buttons = {};
31485                 var bt = this.buttonText;
31486                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
31487                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
31488                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
31489                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
31490                 bodyEl = dlg.body.createChild({
31491
31492                     html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" /><textarea class="roo-mb-textarea"></textarea><div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar">&#160;</div></div></div>'
31493                 });
31494                 msgEl = bodyEl.dom.firstChild;
31495                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
31496                 textboxEl.enableDisplayMode();
31497                 textboxEl.addKeyListener([10,13], function(){
31498                     if(dlg.isVisible() && opt && opt.buttons){
31499                         if(opt.buttons.ok){
31500                             handleButton("ok");
31501                         }else if(opt.buttons.yes){
31502                             handleButton("yes");
31503                         }
31504                     }
31505                 });
31506                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
31507                 textareaEl.enableDisplayMode();
31508                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
31509                 progressEl.enableDisplayMode();
31510                 var pf = progressEl.dom.firstChild;
31511                 if (pf) {
31512                     pp = Roo.get(pf.firstChild);
31513                     pp.setHeight(pf.offsetHeight);
31514                 }
31515                 
31516             }
31517             return dlg;
31518         },
31519
31520         /**
31521          * Updates the message box body text
31522          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
31523          * the XHTML-compliant non-breaking space character '&amp;#160;')
31524          * @return {Roo.MessageBox} This message box
31525          */
31526         updateText : function(text){
31527             if(!dlg.isVisible() && !opt.width){
31528                 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
31529             }
31530             msgEl.innerHTML = text || '&#160;';
31531       
31532             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
31533             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
31534             var w = Math.max(
31535                     Math.min(opt.width || cw , this.maxWidth), 
31536                     Math.max(opt.minWidth || this.minWidth, bwidth)
31537             );
31538             if(opt.prompt){
31539                 activeTextEl.setWidth(w);
31540             }
31541             if(dlg.isVisible()){
31542                 dlg.fixedcenter = false;
31543             }
31544             // to big, make it scroll. = But as usual stupid IE does not support
31545             // !important..
31546             
31547             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
31548                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
31549                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
31550             } else {
31551                 bodyEl.dom.style.height = '';
31552                 bodyEl.dom.style.overflowY = '';
31553             }
31554             if (cw > w) {
31555                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
31556             } else {
31557                 bodyEl.dom.style.overflowX = '';
31558             }
31559             
31560             dlg.setContentSize(w, bodyEl.getHeight());
31561             if(dlg.isVisible()){
31562                 dlg.fixedcenter = true;
31563             }
31564             return this;
31565         },
31566
31567         /**
31568          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
31569          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
31570          * @param {Number} value Any number between 0 and 1 (e.g., .5)
31571          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
31572          * @return {Roo.MessageBox} This message box
31573          */
31574         updateProgress : function(value, text){
31575             if(text){
31576                 this.updateText(text);
31577             }
31578             if (pp) { // weird bug on my firefox - for some reason this is not defined
31579                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
31580             }
31581             return this;
31582         },        
31583
31584         /**
31585          * Returns true if the message box is currently displayed
31586          * @return {Boolean} True if the message box is visible, else false
31587          */
31588         isVisible : function(){
31589             return dlg && dlg.isVisible();  
31590         },
31591
31592         /**
31593          * Hides the message box if it is displayed
31594          */
31595         hide : function(){
31596             if(this.isVisible()){
31597                 dlg.hide();
31598             }  
31599         },
31600
31601         /**
31602          * Displays a new message box, or reinitializes an existing message box, based on the config options
31603          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
31604          * The following config object properties are supported:
31605          * <pre>
31606 Property    Type             Description
31607 ----------  ---------------  ------------------------------------------------------------------------------------
31608 animEl            String/Element   An id or Element from which the message box should animate as it opens and
31609                                    closes (defaults to undefined)
31610 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
31611                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
31612 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
31613                                    progress and wait dialogs will ignore this property and always hide the
31614                                    close button as they can only be closed programmatically.
31615 cls               String           A custom CSS class to apply to the message box element
31616 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
31617                                    displayed (defaults to 75)
31618 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
31619                                    function will be btn (the name of the button that was clicked, if applicable,
31620                                    e.g. "ok"), and text (the value of the active text field, if applicable).
31621                                    Progress and wait dialogs will ignore this option since they do not respond to
31622                                    user actions and can only be closed programmatically, so any required function
31623                                    should be called by the same code after it closes the dialog.
31624 icon              String           A CSS class that provides a background image to be used as an icon for
31625                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
31626 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
31627 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
31628 modal             Boolean          False to allow user interaction with the page while the message box is
31629                                    displayed (defaults to true)
31630 msg               String           A string that will replace the existing message box body text (defaults
31631                                    to the XHTML-compliant non-breaking space character '&#160;')
31632 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
31633 progress          Boolean          True to display a progress bar (defaults to false)
31634 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
31635 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
31636 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
31637 title             String           The title text
31638 value             String           The string value to set into the active textbox element if displayed
31639 wait              Boolean          True to display a progress bar (defaults to false)
31640 width             Number           The width of the dialog in pixels
31641 </pre>
31642          *
31643          * Example usage:
31644          * <pre><code>
31645 Roo.Msg.show({
31646    title: 'Address',
31647    msg: 'Please enter your address:',
31648    width: 300,
31649    buttons: Roo.MessageBox.OKCANCEL,
31650    multiline: true,
31651    fn: saveAddress,
31652    animEl: 'addAddressBtn'
31653 });
31654 </code></pre>
31655          * @param {Object} config Configuration options
31656          * @return {Roo.MessageBox} This message box
31657          */
31658         show : function(options)
31659         {
31660             
31661             // this causes nightmares if you show one dialog after another
31662             // especially on callbacks..
31663              
31664             if(this.isVisible()){
31665                 
31666                 this.hide();
31667                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
31668                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
31669                 Roo.log("New Dialog Message:" +  options.msg )
31670                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
31671                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
31672                 
31673             }
31674             var d = this.getDialog();
31675             opt = options;
31676             d.setTitle(opt.title || "&#160;");
31677             d.close.setDisplayed(opt.closable !== false);
31678             activeTextEl = textboxEl;
31679             opt.prompt = opt.prompt || (opt.multiline ? true : false);
31680             if(opt.prompt){
31681                 if(opt.multiline){
31682                     textboxEl.hide();
31683                     textareaEl.show();
31684                     textareaEl.setHeight(typeof opt.multiline == "number" ?
31685                         opt.multiline : this.defaultTextHeight);
31686                     activeTextEl = textareaEl;
31687                 }else{
31688                     textboxEl.show();
31689                     textareaEl.hide();
31690                 }
31691             }else{
31692                 textboxEl.hide();
31693                 textareaEl.hide();
31694             }
31695             progressEl.setDisplayed(opt.progress === true);
31696             this.updateProgress(0);
31697             activeTextEl.dom.value = opt.value || "";
31698             if(opt.prompt){
31699                 dlg.setDefaultButton(activeTextEl);
31700             }else{
31701                 var bs = opt.buttons;
31702                 var db = null;
31703                 if(bs && bs.ok){
31704                     db = buttons["ok"];
31705                 }else if(bs && bs.yes){
31706                     db = buttons["yes"];
31707                 }
31708                 dlg.setDefaultButton(db);
31709             }
31710             bwidth = updateButtons(opt.buttons);
31711             this.updateText(opt.msg);
31712             if(opt.cls){
31713                 d.el.addClass(opt.cls);
31714             }
31715             d.proxyDrag = opt.proxyDrag === true;
31716             d.modal = opt.modal !== false;
31717             d.mask = opt.modal !== false ? mask : false;
31718             if(!d.isVisible()){
31719                 // force it to the end of the z-index stack so it gets a cursor in FF
31720                 document.body.appendChild(dlg.el.dom);
31721                 d.animateTarget = null;
31722                 d.show(options.animEl);
31723             }
31724             return this;
31725         },
31726
31727         /**
31728          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
31729          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
31730          * and closing the message box when the process is complete.
31731          * @param {String} title The title bar text
31732          * @param {String} msg The message box body text
31733          * @return {Roo.MessageBox} This message box
31734          */
31735         progress : function(title, msg){
31736             this.show({
31737                 title : title,
31738                 msg : msg,
31739                 buttons: false,
31740                 progress:true,
31741                 closable:false,
31742                 minWidth: this.minProgressWidth,
31743                 modal : true
31744             });
31745             return this;
31746         },
31747
31748         /**
31749          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
31750          * If a callback function is passed it will be called after the user clicks the button, and the
31751          * id of the button that was clicked will be passed as the only parameter to the callback
31752          * (could also be the top-right close button).
31753          * @param {String} title The title bar text
31754          * @param {String} msg The message box body text
31755          * @param {Function} fn (optional) The callback function invoked after the message box is closed
31756          * @param {Object} scope (optional) The scope of the callback function
31757          * @return {Roo.MessageBox} This message box
31758          */
31759         alert : function(title, msg, fn, scope){
31760             this.show({
31761                 title : title,
31762                 msg : msg,
31763                 buttons: this.OK,
31764                 fn: fn,
31765                 scope : scope,
31766                 modal : true
31767             });
31768             return this;
31769         },
31770
31771         /**
31772          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
31773          * interaction while waiting for a long-running process to complete that does not have defined intervals.
31774          * You are responsible for closing the message box when the process is complete.
31775          * @param {String} msg The message box body text
31776          * @param {String} title (optional) The title bar text
31777          * @return {Roo.MessageBox} This message box
31778          */
31779         wait : function(msg, title){
31780             this.show({
31781                 title : title,
31782                 msg : msg,
31783                 buttons: false,
31784                 closable:false,
31785                 progress:true,
31786                 modal:true,
31787                 width:300,
31788                 wait:true
31789             });
31790             waitTimer = Roo.TaskMgr.start({
31791                 run: function(i){
31792                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
31793                 },
31794                 interval: 1000
31795             });
31796             return this;
31797         },
31798
31799         /**
31800          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
31801          * If a callback function is passed it will be called after the user clicks either button, and the id of the
31802          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
31803          * @param {String} title The title bar text
31804          * @param {String} msg The message box body text
31805          * @param {Function} fn (optional) The callback function invoked after the message box is closed
31806          * @param {Object} scope (optional) The scope of the callback function
31807          * @return {Roo.MessageBox} This message box
31808          */
31809         confirm : function(title, msg, fn, scope){
31810             this.show({
31811                 title : title,
31812                 msg : msg,
31813                 buttons: this.YESNO,
31814                 fn: fn,
31815                 scope : scope,
31816                 modal : true
31817             });
31818             return this;
31819         },
31820
31821         /**
31822          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
31823          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
31824          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
31825          * (could also be the top-right close button) and the text that was entered will be passed as the two
31826          * parameters to the callback.
31827          * @param {String} title The title bar text
31828          * @param {String} msg The message box body text
31829          * @param {Function} fn (optional) The callback function invoked after the message box is closed
31830          * @param {Object} scope (optional) The scope of the callback function
31831          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
31832          * property, or the height in pixels to create the textbox (defaults to false / single-line)
31833          * @return {Roo.MessageBox} This message box
31834          */
31835         prompt : function(title, msg, fn, scope, multiline){
31836             this.show({
31837                 title : title,
31838                 msg : msg,
31839                 buttons: this.OKCANCEL,
31840                 fn: fn,
31841                 minWidth:250,
31842                 scope : scope,
31843                 prompt:true,
31844                 multiline: multiline,
31845                 modal : true
31846             });
31847             return this;
31848         },
31849
31850         /**
31851          * Button config that displays a single OK button
31852          * @type Object
31853          */
31854         OK : {ok:true},
31855         /**
31856          * Button config that displays Yes and No buttons
31857          * @type Object
31858          */
31859         YESNO : {yes:true, no:true},
31860         /**
31861          * Button config that displays OK and Cancel buttons
31862          * @type Object
31863          */
31864         OKCANCEL : {ok:true, cancel:true},
31865         /**
31866          * Button config that displays Yes, No and Cancel buttons
31867          * @type Object
31868          */
31869         YESNOCANCEL : {yes:true, no:true, cancel:true},
31870
31871         /**
31872          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
31873          * @type Number
31874          */
31875         defaultTextHeight : 75,
31876         /**
31877          * The maximum width in pixels of the message box (defaults to 600)
31878          * @type Number
31879          */
31880         maxWidth : 600,
31881         /**
31882          * The minimum width in pixels of the message box (defaults to 100)
31883          * @type Number
31884          */
31885         minWidth : 100,
31886         /**
31887          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
31888          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
31889          * @type Number
31890          */
31891         minProgressWidth : 250,
31892         /**
31893          * An object containing the default button text strings that can be overriden for localized language support.
31894          * Supported properties are: ok, cancel, yes and no.
31895          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
31896          * @type Object
31897          */
31898         buttonText : {
31899             ok : "OK",
31900             cancel : "Cancel",
31901             yes : "Yes",
31902             no : "No"
31903         }
31904     };
31905 }();
31906
31907 /**
31908  * Shorthand for {@link Roo.MessageBox}
31909  */
31910 Roo.Msg = Roo.MessageBox;/*
31911  * Based on:
31912  * Ext JS Library 1.1.1
31913  * Copyright(c) 2006-2007, Ext JS, LLC.
31914  *
31915  * Originally Released Under LGPL - original licence link has changed is not relivant.
31916  *
31917  * Fork - LGPL
31918  * <script type="text/javascript">
31919  */
31920 /**
31921  * @class Roo.QuickTips
31922  * Provides attractive and customizable tooltips for any element.
31923  * @singleton
31924  */
31925 Roo.QuickTips = function(){
31926     var el, tipBody, tipBodyText, tipTitle, tm, cfg, close, tagEls = {}, esc, removeCls = null, bdLeft, bdRight;
31927     var ce, bd, xy, dd;
31928     var visible = false, disabled = true, inited = false;
31929     var showProc = 1, hideProc = 1, dismissProc = 1, locks = [];
31930     
31931     var onOver = function(e){
31932         if(disabled){
31933             return;
31934         }
31935         var t = e.getTarget();
31936         if(!t || t.nodeType !== 1 || t == document || t == document.body){
31937             return;
31938         }
31939         if(ce && t == ce.el){
31940             clearTimeout(hideProc);
31941             return;
31942         }
31943         if(t && tagEls[t.id]){
31944             tagEls[t.id].el = t;
31945             showProc = show.defer(tm.showDelay, tm, [tagEls[t.id]]);
31946             return;
31947         }
31948         var ttp, et = Roo.fly(t);
31949         var ns = cfg.namespace;
31950         if(tm.interceptTitles && t.title){
31951             ttp = t.title;
31952             t.qtip = ttp;
31953             t.removeAttribute("title");
31954             e.preventDefault();
31955         }else{
31956             ttp = t.qtip || et.getAttributeNS(ns, cfg.attribute);
31957         }
31958         if(ttp){
31959             showProc = show.defer(tm.showDelay, tm, [{
31960                 el: t, 
31961                 text: ttp, 
31962                 width: et.getAttributeNS(ns, cfg.width),
31963                 autoHide: et.getAttributeNS(ns, cfg.hide) != "user",
31964                 title: et.getAttributeNS(ns, cfg.title),
31965                     cls: et.getAttributeNS(ns, cfg.cls)
31966             }]);
31967         }
31968     };
31969     
31970     var onOut = function(e){
31971         clearTimeout(showProc);
31972         var t = e.getTarget();
31973         if(t && ce && ce.el == t && (tm.autoHide && ce.autoHide !== false)){
31974             hideProc = setTimeout(hide, tm.hideDelay);
31975         }
31976     };
31977     
31978     var onMove = function(e){
31979         if(disabled){
31980             return;
31981         }
31982         xy = e.getXY();
31983         xy[1] += 18;
31984         if(tm.trackMouse && ce){
31985             el.setXY(xy);
31986         }
31987     };
31988     
31989     var onDown = function(e){
31990         clearTimeout(showProc);
31991         clearTimeout(hideProc);
31992         if(!e.within(el)){
31993             if(tm.hideOnClick){
31994                 hide();
31995                 tm.disable();
31996                 tm.enable.defer(100, tm);
31997             }
31998         }
31999     };
32000     
32001     var getPad = function(){
32002         return 2;//bdLeft.getPadding('l')+bdRight.getPadding('r');
32003     };
32004
32005     var show = function(o){
32006         if(disabled){
32007             return;
32008         }
32009         clearTimeout(dismissProc);
32010         ce = o;
32011         if(removeCls){ // in case manually hidden
32012             el.removeClass(removeCls);
32013             removeCls = null;
32014         }
32015         if(ce.cls){
32016             el.addClass(ce.cls);
32017             removeCls = ce.cls;
32018         }
32019         if(ce.title){
32020             tipTitle.update(ce.title);
32021             tipTitle.show();
32022         }else{
32023             tipTitle.update('');
32024             tipTitle.hide();
32025         }
32026         el.dom.style.width  = tm.maxWidth+'px';
32027         //tipBody.dom.style.width = '';
32028         tipBodyText.update(o.text);
32029         var p = getPad(), w = ce.width;
32030         if(!w){
32031             var td = tipBodyText.dom;
32032             var aw = Math.max(td.offsetWidth, td.clientWidth, td.scrollWidth);
32033             if(aw > tm.maxWidth){
32034                 w = tm.maxWidth;
32035             }else if(aw < tm.minWidth){
32036                 w = tm.minWidth;
32037             }else{
32038                 w = aw;
32039             }
32040         }
32041         //tipBody.setWidth(w);
32042         el.setWidth(parseInt(w, 10) + p);
32043         if(ce.autoHide === false){
32044             close.setDisplayed(true);
32045             if(dd){
32046                 dd.unlock();
32047             }
32048         }else{
32049             close.setDisplayed(false);
32050             if(dd){
32051                 dd.lock();
32052             }
32053         }
32054         if(xy){
32055             el.avoidY = xy[1]-18;
32056             el.setXY(xy);
32057         }
32058         if(tm.animate){
32059             el.setOpacity(.1);
32060             el.setStyle("visibility", "visible");
32061             el.fadeIn({callback: afterShow});
32062         }else{
32063             afterShow();
32064         }
32065     };
32066     
32067     var afterShow = function(){
32068         if(ce){
32069             el.show();
32070             esc.enable();
32071             if(tm.autoDismiss && ce.autoHide !== false){
32072                 dismissProc = setTimeout(hide, tm.autoDismissDelay);
32073             }
32074         }
32075     };
32076     
32077     var hide = function(noanim){
32078         clearTimeout(dismissProc);
32079         clearTimeout(hideProc);
32080         ce = null;
32081         if(el.isVisible()){
32082             esc.disable();
32083             if(noanim !== true && tm.animate){
32084                 el.fadeOut({callback: afterHide});
32085             }else{
32086                 afterHide();
32087             } 
32088         }
32089     };
32090     
32091     var afterHide = function(){
32092         el.hide();
32093         if(removeCls){
32094             el.removeClass(removeCls);
32095             removeCls = null;
32096         }
32097     };
32098     
32099     return {
32100         /**
32101         * @cfg {Number} minWidth
32102         * The minimum width of the quick tip (defaults to 40)
32103         */
32104        minWidth : 40,
32105         /**
32106         * @cfg {Number} maxWidth
32107         * The maximum width of the quick tip (defaults to 300)
32108         */
32109        maxWidth : 300,
32110         /**
32111         * @cfg {Boolean} interceptTitles
32112         * True to automatically use the element's DOM title value if available (defaults to false)
32113         */
32114        interceptTitles : false,
32115         /**
32116         * @cfg {Boolean} trackMouse
32117         * True to have the quick tip follow the mouse as it moves over the target element (defaults to false)
32118         */
32119        trackMouse : false,
32120         /**
32121         * @cfg {Boolean} hideOnClick
32122         * True to hide the quick tip if the user clicks anywhere in the document (defaults to true)
32123         */
32124        hideOnClick : true,
32125         /**
32126         * @cfg {Number} showDelay
32127         * Delay in milliseconds before the quick tip displays after the mouse enters the target element (defaults to 500)
32128         */
32129        showDelay : 500,
32130         /**
32131         * @cfg {Number} hideDelay
32132         * Delay in milliseconds before the quick tip hides when autoHide = true (defaults to 200)
32133         */
32134        hideDelay : 200,
32135         /**
32136         * @cfg {Boolean} autoHide
32137         * True to automatically hide the quick tip after the mouse exits the target element (defaults to true).
32138         * Used in conjunction with hideDelay.
32139         */
32140        autoHide : true,
32141         /**
32142         * @cfg {Boolean}
32143         * True to automatically hide the quick tip after a set period of time, regardless of the user's actions
32144         * (defaults to true).  Used in conjunction with autoDismissDelay.
32145         */
32146        autoDismiss : true,
32147         /**
32148         * @cfg {Number}
32149         * Delay in milliseconds before the quick tip hides when autoDismiss = true (defaults to 5000)
32150         */
32151        autoDismissDelay : 5000,
32152        /**
32153         * @cfg {Boolean} animate
32154         * True to turn on fade animation. Defaults to false (ClearType/scrollbar flicker issues in IE7).
32155         */
32156        animate : false,
32157
32158        /**
32159         * @cfg {String} title
32160         * Title text to display (defaults to '').  This can be any valid HTML markup.
32161         */
32162         title: '',
32163        /**
32164         * @cfg {String} text
32165         * Body text to display (defaults to '').  This can be any valid HTML markup.
32166         */
32167         text : '',
32168        /**
32169         * @cfg {String} cls
32170         * A CSS class to apply to the base quick tip element (defaults to '').
32171         */
32172         cls : '',
32173        /**
32174         * @cfg {Number} width
32175         * Width in pixels of the quick tip (defaults to auto).  Width will be ignored if it exceeds the bounds of
32176         * minWidth or maxWidth.
32177         */
32178         width : null,
32179
32180     /**
32181      * Initialize and enable QuickTips for first use.  This should be called once before the first attempt to access
32182      * or display QuickTips in a page.
32183      */
32184        init : function(){
32185           tm = Roo.QuickTips;
32186           cfg = tm.tagConfig;
32187           if(!inited){
32188               if(!Roo.isReady){ // allow calling of init() before onReady
32189                   Roo.onReady(Roo.QuickTips.init, Roo.QuickTips);
32190                   return;
32191               }
32192               el = new Roo.Layer({cls:"x-tip", shadow:"drop", shim: true, constrain:true, shadowOffset:4});
32193               el.fxDefaults = {stopFx: true};
32194               // maximum custom styling
32195               //el.update('<div class="x-tip-top-left"><div class="x-tip-top-right"><div class="x-tip-top"></div></div></div><div class="x-tip-bd-left"><div class="x-tip-bd-right"><div class="x-tip-bd"><div class="x-tip-close"></div><h3></h3><div class="x-tip-bd-inner"></div><div class="x-clear"></div></div></div></div><div class="x-tip-ft-left"><div class="x-tip-ft-right"><div class="x-tip-ft"></div></div></div>');
32196               el.update('<div class="x-tip-bd"><div class="x-tip-close"></div><h3></h3><div class="x-tip-bd-inner"></div><div class="x-clear"></div></div>');              
32197               tipTitle = el.child('h3');
32198               tipTitle.enableDisplayMode("block");
32199               tipBody = el.child('div.x-tip-bd');
32200               tipBodyText = el.child('div.x-tip-bd-inner');
32201               //bdLeft = el.child('div.x-tip-bd-left');
32202               //bdRight = el.child('div.x-tip-bd-right');
32203               close = el.child('div.x-tip-close');
32204               close.enableDisplayMode("block");
32205               close.on("click", hide);
32206               var d = Roo.get(document);
32207               d.on("mousedown", onDown);
32208               d.on("mouseover", onOver);
32209               d.on("mouseout", onOut);
32210               d.on("mousemove", onMove);
32211               esc = d.addKeyListener(27, hide);
32212               esc.disable();
32213               if(Roo.dd.DD){
32214                   dd = el.initDD("default", null, {
32215                       onDrag : function(){
32216                           el.sync();  
32217                       }
32218                   });
32219                   dd.setHandleElId(tipTitle.id);
32220                   dd.lock();
32221               }
32222               inited = true;
32223           }
32224           this.enable(); 
32225        },
32226
32227     /**
32228      * Configures a new quick tip instance and assigns it to a target element.  The following config options
32229      * are supported:
32230      * <pre>
32231 Property    Type                   Description
32232 ----------  ---------------------  ------------------------------------------------------------------------
32233 target      Element/String/Array   An Element, id or array of ids that this quick tip should be tied to
32234      * </ul>
32235      * @param {Object} config The config object
32236      */
32237        register : function(config){
32238            var cs = config instanceof Array ? config : arguments;
32239            for(var i = 0, len = cs.length; i < len; i++) {
32240                var c = cs[i];
32241                var target = c.target;
32242                if(target){
32243                    if(target instanceof Array){
32244                        for(var j = 0, jlen = target.length; j < jlen; j++){
32245                            tagEls[target[j]] = c;
32246                        }
32247                    }else{
32248                        tagEls[typeof target == 'string' ? target : Roo.id(target)] = c;
32249                    }
32250                }
32251            }
32252        },
32253
32254     /**
32255      * Removes this quick tip from its element and destroys it.
32256      * @param {String/HTMLElement/Element} el The element from which the quick tip is to be removed.
32257      */
32258        unregister : function(el){
32259            delete tagEls[Roo.id(el)];
32260        },
32261
32262     /**
32263      * Enable this quick tip.
32264      */
32265        enable : function(){
32266            if(inited && disabled){
32267                locks.pop();
32268                if(locks.length < 1){
32269                    disabled = false;
32270                }
32271            }
32272        },
32273
32274     /**
32275      * Disable this quick tip.
32276      */
32277        disable : function(){
32278           disabled = true;
32279           clearTimeout(showProc);
32280           clearTimeout(hideProc);
32281           clearTimeout(dismissProc);
32282           if(ce){
32283               hide(true);
32284           }
32285           locks.push(1);
32286        },
32287
32288     /**
32289      * Returns true if the quick tip is enabled, else false.
32290      */
32291        isEnabled : function(){
32292             return !disabled;
32293        },
32294
32295         // private
32296        tagConfig : {
32297            namespace : "ext",
32298            attribute : "qtip",
32299            width : "width",
32300            target : "target",
32301            title : "qtitle",
32302            hide : "hide",
32303            cls : "qclass"
32304        }
32305    };
32306 }();
32307
32308 // backwards compat
32309 Roo.QuickTips.tips = Roo.QuickTips.register;/*
32310  * Based on:
32311  * Ext JS Library 1.1.1
32312  * Copyright(c) 2006-2007, Ext JS, LLC.
32313  *
32314  * Originally Released Under LGPL - original licence link has changed is not relivant.
32315  *
32316  * Fork - LGPL
32317  * <script type="text/javascript">
32318  */
32319  
32320
32321 /**
32322  * @class Roo.tree.TreePanel
32323  * @extends Roo.data.Tree
32324
32325  * @cfg {Boolean} rootVisible false to hide the root node (defaults to true)
32326  * @cfg {Boolean} lines false to disable tree lines (defaults to true)
32327  * @cfg {Boolean} enableDD true to enable drag and drop
32328  * @cfg {Boolean} enableDrag true to enable just drag
32329  * @cfg {Boolean} enableDrop true to enable just drop
32330  * @cfg {Object} dragConfig Custom config to pass to the {@link Roo.tree.TreeDragZone} instance
32331  * @cfg {Object} dropConfig Custom config to pass to the {@link Roo.tree.TreeDropZone} instance
32332  * @cfg {String} ddGroup The DD group this TreePanel belongs to
32333  * @cfg {String} ddAppendOnly True if the tree should only allow append drops (use for trees which are sorted)
32334  * @cfg {Boolean} ddScroll true to enable YUI body scrolling
32335  * @cfg {Boolean} containerScroll true to register this container with ScrollManager
32336  * @cfg {Boolean} hlDrop false to disable node highlight on drop (defaults to the value of Roo.enableFx)
32337  * @cfg {String} hlColor The color of the node highlight (defaults to C3DAF9)
32338  * @cfg {Boolean} animate true to enable animated expand/collapse (defaults to the value of Roo.enableFx)
32339  * @cfg {Boolean} singleExpand true if only 1 node per branch may be expanded
32340  * @cfg {Boolean} selModel A tree selection model to use with this TreePanel (defaults to a {@link Roo.tree.DefaultSelectionModel})
32341  * @cfg {Boolean} loader A TreeLoader for use with this TreePanel
32342  * @cfg {Object|Roo.tree.TreeEditor} editor The TreeEditor or xtype data to display when clicked.
32343  * @cfg {String} pathSeparator The token used to separate sub-paths in path strings (defaults to '/')
32344  * @cfg {Function} renderer DEPRECATED - use TreeLoader:create event / Sets the rendering (formatting) function for the nodes. to return HTML markup for the tree view. The render function is called with  the following parameters:<ul><li>The {Object} The data for the node.</li></ul>
32345  * @cfg {Function} rendererTip DEPRECATED - use TreeLoader:create event / Sets the rendering (formatting) function for the nodes hovertip to return HTML markup for the tree view. The render function is called with  the following parameters:<ul><li>The {Object} The data for the node.</li></ul>
32346  * 
32347  * @constructor
32348  * @param {String/HTMLElement/Element} el The container element
32349  * @param {Object} config
32350  */
32351 Roo.tree.TreePanel = function(el, config){
32352     var root = false;
32353     var loader = false;
32354     if (config.root) {
32355         root = config.root;
32356         delete config.root;
32357     }
32358     if (config.loader) {
32359         loader = config.loader;
32360         delete config.loader;
32361     }
32362     
32363     Roo.apply(this, config);
32364     Roo.tree.TreePanel.superclass.constructor.call(this);
32365     this.el = Roo.get(el);
32366     this.el.addClass('x-tree');
32367     //console.log(root);
32368     if (root) {
32369         this.setRootNode( Roo.factory(root, Roo.tree));
32370     }
32371     if (loader) {
32372         this.loader = Roo.factory(loader, Roo.tree);
32373     }
32374    /**
32375     * Read-only. The id of the container element becomes this TreePanel's id.
32376     */
32377     this.id = this.el.id;
32378     this.addEvents({
32379         /**
32380         * @event beforeload
32381         * Fires before a node is loaded, return false to cancel
32382         * @param {Node} node The node being loaded
32383         */
32384         "beforeload" : true,
32385         /**
32386         * @event load
32387         * Fires when a node is loaded
32388         * @param {Node} node The node that was loaded
32389         */
32390         "load" : true,
32391         /**
32392         * @event textchange
32393         * Fires when the text for a node is changed
32394         * @param {Node} node The node
32395         * @param {String} text The new text
32396         * @param {String} oldText The old text
32397         */
32398         "textchange" : true,
32399         /**
32400         * @event beforeexpand
32401         * Fires before a node is expanded, return false to cancel.
32402         * @param {Node} node The node
32403         * @param {Boolean} deep
32404         * @param {Boolean} anim
32405         */
32406         "beforeexpand" : true,
32407         /**
32408         * @event beforecollapse
32409         * Fires before a node is collapsed, return false to cancel.
32410         * @param {Node} node The node
32411         * @param {Boolean} deep
32412         * @param {Boolean} anim
32413         */
32414         "beforecollapse" : true,
32415         /**
32416         * @event expand
32417         * Fires when a node is expanded
32418         * @param {Node} node The node
32419         */
32420         "expand" : true,
32421         /**
32422         * @event disabledchange
32423         * Fires when the disabled status of a node changes
32424         * @param {Node} node The node
32425         * @param {Boolean} disabled
32426         */
32427         "disabledchange" : true,
32428         /**
32429         * @event collapse
32430         * Fires when a node is collapsed
32431         * @param {Node} node The node
32432         */
32433         "collapse" : true,
32434         /**
32435         * @event beforeclick
32436         * Fires before click processing on a node. Return false to cancel the default action.
32437         * @param {Node} node The node
32438         * @param {Roo.EventObject} e The event object
32439         */
32440         "beforeclick":true,
32441         /**
32442         * @event checkchange
32443         * Fires when a node with a checkbox's checked property changes
32444         * @param {Node} this This node
32445         * @param {Boolean} checked
32446         */
32447         "checkchange":true,
32448         /**
32449         * @event click
32450         * Fires when a node is clicked
32451         * @param {Node} node The node
32452         * @param {Roo.EventObject} e The event object
32453         */
32454         "click":true,
32455         /**
32456         * @event dblclick
32457         * Fires when a node is double clicked
32458         * @param {Node} node The node
32459         * @param {Roo.EventObject} e The event object
32460         */
32461         "dblclick":true,
32462         /**
32463         * @event contextmenu
32464         * Fires when a node is right clicked
32465         * @param {Node} node The node
32466         * @param {Roo.EventObject} e The event object
32467         */
32468         "contextmenu":true,
32469         /**
32470         * @event beforechildrenrendered
32471         * Fires right before the child nodes for a node are rendered
32472         * @param {Node} node The node
32473         */
32474         "beforechildrenrendered":true,
32475         /**
32476         * @event startdrag
32477         * Fires when a node starts being dragged
32478         * @param {Roo.tree.TreePanel} this
32479         * @param {Roo.tree.TreeNode} node
32480         * @param {event} e The raw browser event
32481         */ 
32482        "startdrag" : true,
32483        /**
32484         * @event enddrag
32485         * Fires when a drag operation is complete
32486         * @param {Roo.tree.TreePanel} this
32487         * @param {Roo.tree.TreeNode} node
32488         * @param {event} e The raw browser event
32489         */
32490        "enddrag" : true,
32491        /**
32492         * @event dragdrop
32493         * Fires when a dragged node is dropped on a valid DD target
32494         * @param {Roo.tree.TreePanel} this
32495         * @param {Roo.tree.TreeNode} node
32496         * @param {DD} dd The dd it was dropped on
32497         * @param {event} e The raw browser event
32498         */
32499        "dragdrop" : true,
32500        /**
32501         * @event beforenodedrop
32502         * Fires when a DD object is dropped on a node in this tree for preprocessing. Return false to cancel the drop. The dropEvent
32503         * passed to handlers has the following properties:<br />
32504         * <ul style="padding:5px;padding-left:16px;">
32505         * <li>tree - The TreePanel</li>
32506         * <li>target - The node being targeted for the drop</li>
32507         * <li>data - The drag data from the drag source</li>
32508         * <li>point - The point of the drop - append, above or below</li>
32509         * <li>source - The drag source</li>
32510         * <li>rawEvent - Raw mouse event</li>
32511         * <li>dropNode - Drop node(s) provided by the source <b>OR</b> you can supply node(s)
32512         * to be inserted by setting them on this object.</li>
32513         * <li>cancel - Set this to true to cancel the drop.</li>
32514         * </ul>
32515         * @param {Object} dropEvent
32516         */
32517        "beforenodedrop" : true,
32518        /**
32519         * @event nodedrop
32520         * Fires after a DD object is dropped on a node in this tree. The dropEvent
32521         * passed to handlers has the following properties:<br />
32522         * <ul style="padding:5px;padding-left:16px;">
32523         * <li>tree - The TreePanel</li>
32524         * <li>target - The node being targeted for the drop</li>
32525         * <li>data - The drag data from the drag source</li>
32526         * <li>point - The point of the drop - append, above or below</li>
32527         * <li>source - The drag source</li>
32528         * <li>rawEvent - Raw mouse event</li>
32529         * <li>dropNode - Dropped node(s).</li>
32530         * </ul>
32531         * @param {Object} dropEvent
32532         */
32533        "nodedrop" : true,
32534         /**
32535         * @event nodedragover
32536         * Fires when a tree node is being targeted for a drag drop, return false to signal drop not allowed. The dragOverEvent
32537         * passed to handlers has the following properties:<br />
32538         * <ul style="padding:5px;padding-left:16px;">
32539         * <li>tree - The TreePanel</li>
32540         * <li>target - The node being targeted for the drop</li>
32541         * <li>data - The drag data from the drag source</li>
32542         * <li>point - The point of the drop - append, above or below</li>
32543         * <li>source - The drag source</li>
32544         * <li>rawEvent - Raw mouse event</li>
32545         * <li>dropNode - Drop node(s) provided by the source.</li>
32546         * <li>cancel - Set this to true to signal drop not allowed.</li>
32547         * </ul>
32548         * @param {Object} dragOverEvent
32549         */
32550        "nodedragover" : true
32551         
32552     });
32553     if(this.singleExpand){
32554        this.on("beforeexpand", this.restrictExpand, this);
32555     }
32556     if (this.editor) {
32557         this.editor.tree = this;
32558         this.editor = Roo.factory(this.editor, Roo.tree);
32559     }
32560     
32561     if (this.selModel) {
32562         this.selModel = Roo.factory(this.selModel, Roo.tree);
32563     }
32564    
32565 };
32566 Roo.extend(Roo.tree.TreePanel, Roo.data.Tree, {
32567     rootVisible : true,
32568     animate: Roo.enableFx,
32569     lines : true,
32570     enableDD : false,
32571     hlDrop : Roo.enableFx,
32572   
32573     renderer: false,
32574     
32575     rendererTip: false,
32576     // private
32577     restrictExpand : function(node){
32578         var p = node.parentNode;
32579         if(p){
32580             if(p.expandedChild && p.expandedChild.parentNode == p){
32581                 p.expandedChild.collapse();
32582             }
32583             p.expandedChild = node;
32584         }
32585     },
32586
32587     // private override
32588     setRootNode : function(node){
32589         Roo.tree.TreePanel.superclass.setRootNode.call(this, node);
32590         if(!this.rootVisible){
32591             node.ui = new Roo.tree.RootTreeNodeUI(node);
32592         }
32593         return node;
32594     },
32595
32596     /**
32597      * Returns the container element for this TreePanel
32598      */
32599     getEl : function(){
32600         return this.el;
32601     },
32602
32603     /**
32604      * Returns the default TreeLoader for this TreePanel
32605      */
32606     getLoader : function(){
32607         return this.loader;
32608     },
32609
32610     /**
32611      * Expand all nodes
32612      */
32613     expandAll : function(){
32614         this.root.expand(true);
32615     },
32616
32617     /**
32618      * Collapse all nodes
32619      */
32620     collapseAll : function(){
32621         this.root.collapse(true);
32622     },
32623
32624     /**
32625      * Returns the selection model used by this TreePanel
32626      */
32627     getSelectionModel : function(){
32628         if(!this.selModel){
32629             this.selModel = new Roo.tree.DefaultSelectionModel();
32630         }
32631         return this.selModel;
32632     },
32633
32634     /**
32635      * Retrieve an array of checked nodes, or an array of a specific attribute of checked nodes (e.g. "id")
32636      * @param {String} attribute (optional) Defaults to null (return the actual nodes)
32637      * @param {TreeNode} startNode (optional) The node to start from, defaults to the root
32638      * @return {Array}
32639      */
32640     getChecked : function(a, startNode){
32641         startNode = startNode || this.root;
32642         var r = [];
32643         var f = function(){
32644             if(this.attributes.checked){
32645                 r.push(!a ? this : (a == 'id' ? this.id : this.attributes[a]));
32646             }
32647         }
32648         startNode.cascade(f);
32649         return r;
32650     },
32651
32652     /**
32653      * Expands a specified path in this TreePanel. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
32654      * @param {String} path
32655      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
32656      * @param {Function} callback (optional) The callback to call when the expand is complete. The callback will be called with
32657      * (bSuccess, oLastNode) where bSuccess is if the expand was successful and oLastNode is the last node that was expanded.
32658      */
32659     expandPath : function(path, attr, callback){
32660         attr = attr || "id";
32661         var keys = path.split(this.pathSeparator);
32662         var curNode = this.root;
32663         if(curNode.attributes[attr] != keys[1]){ // invalid root
32664             if(callback){
32665                 callback(false, null);
32666             }
32667             return;
32668         }
32669         var index = 1;
32670         var f = function(){
32671             if(++index == keys.length){
32672                 if(callback){
32673                     callback(true, curNode);
32674                 }
32675                 return;
32676             }
32677             var c = curNode.findChild(attr, keys[index]);
32678             if(!c){
32679                 if(callback){
32680                     callback(false, curNode);
32681                 }
32682                 return;
32683             }
32684             curNode = c;
32685             c.expand(false, false, f);
32686         };
32687         curNode.expand(false, false, f);
32688     },
32689
32690     /**
32691      * Selects the node in this tree at the specified path. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
32692      * @param {String} path
32693      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
32694      * @param {Function} callback (optional) The callback to call when the selection is complete. The callback will be called with
32695      * (bSuccess, oSelNode) where bSuccess is if the selection was successful and oSelNode is the selected node.
32696      */
32697     selectPath : function(path, attr, callback){
32698         attr = attr || "id";
32699         var keys = path.split(this.pathSeparator);
32700         var v = keys.pop();
32701         if(keys.length > 0){
32702             var f = function(success, node){
32703                 if(success && node){
32704                     var n = node.findChild(attr, v);
32705                     if(n){
32706                         n.select();
32707                         if(callback){
32708                             callback(true, n);
32709                         }
32710                     }else if(callback){
32711                         callback(false, n);
32712                     }
32713                 }else{
32714                     if(callback){
32715                         callback(false, n);
32716                     }
32717                 }
32718             };
32719             this.expandPath(keys.join(this.pathSeparator), attr, f);
32720         }else{
32721             this.root.select();
32722             if(callback){
32723                 callback(true, this.root);
32724             }
32725         }
32726     },
32727
32728     getTreeEl : function(){
32729         return this.el;
32730     },
32731
32732     /**
32733      * Trigger rendering of this TreePanel
32734      */
32735     render : function(){
32736         if (this.innerCt) {
32737             return this; // stop it rendering more than once!!
32738         }
32739         
32740         this.innerCt = this.el.createChild({tag:"ul",
32741                cls:"x-tree-root-ct " +
32742                (this.lines ? "x-tree-lines" : "x-tree-no-lines")});
32743
32744         if(this.containerScroll){
32745             Roo.dd.ScrollManager.register(this.el);
32746         }
32747         if((this.enableDD || this.enableDrop) && !this.dropZone){
32748            /**
32749             * The dropZone used by this tree if drop is enabled
32750             * @type Roo.tree.TreeDropZone
32751             */
32752              this.dropZone = new Roo.tree.TreeDropZone(this, this.dropConfig || {
32753                ddGroup: this.ddGroup || "TreeDD", appendOnly: this.ddAppendOnly === true
32754            });
32755         }
32756         if((this.enableDD || this.enableDrag) && !this.dragZone){
32757            /**
32758             * The dragZone used by this tree if drag is enabled
32759             * @type Roo.tree.TreeDragZone
32760             */
32761             this.dragZone = new Roo.tree.TreeDragZone(this, this.dragConfig || {
32762                ddGroup: this.ddGroup || "TreeDD",
32763                scroll: this.ddScroll
32764            });
32765         }
32766         this.getSelectionModel().init(this);
32767         if (!this.root) {
32768             Roo.log("ROOT not set in tree");
32769             return this;
32770         }
32771         this.root.render();
32772         if(!this.rootVisible){
32773             this.root.renderChildren();
32774         }
32775         return this;
32776     }
32777 });/*
32778  * Based on:
32779  * Ext JS Library 1.1.1
32780  * Copyright(c) 2006-2007, Ext JS, LLC.
32781  *
32782  * Originally Released Under LGPL - original licence link has changed is not relivant.
32783  *
32784  * Fork - LGPL
32785  * <script type="text/javascript">
32786  */
32787  
32788
32789 /**
32790  * @class Roo.tree.DefaultSelectionModel
32791  * @extends Roo.util.Observable
32792  * The default single selection for a TreePanel.
32793  * @param {Object} cfg Configuration
32794  */
32795 Roo.tree.DefaultSelectionModel = function(cfg){
32796    this.selNode = null;
32797    
32798    
32799    
32800    this.addEvents({
32801        /**
32802         * @event selectionchange
32803         * Fires when the selected node changes
32804         * @param {DefaultSelectionModel} this
32805         * @param {TreeNode} node the new selection
32806         */
32807        "selectionchange" : true,
32808
32809        /**
32810         * @event beforeselect
32811         * Fires before the selected node changes, return false to cancel the change
32812         * @param {DefaultSelectionModel} this
32813         * @param {TreeNode} node the new selection
32814         * @param {TreeNode} node the old selection
32815         */
32816        "beforeselect" : true
32817    });
32818    
32819     Roo.tree.DefaultSelectionModel.superclass.constructor.call(this,cfg);
32820 };
32821
32822 Roo.extend(Roo.tree.DefaultSelectionModel, Roo.util.Observable, {
32823     init : function(tree){
32824         this.tree = tree;
32825         tree.getTreeEl().on("keydown", this.onKeyDown, this);
32826         tree.on("click", this.onNodeClick, this);
32827     },
32828     
32829     onNodeClick : function(node, e){
32830         if (e.ctrlKey && this.selNode == node)  {
32831             this.unselect(node);
32832             return;
32833         }
32834         this.select(node);
32835     },
32836     
32837     /**
32838      * Select a node.
32839      * @param {TreeNode} node The node to select
32840      * @return {TreeNode} The selected node
32841      */
32842     select : function(node){
32843         var last = this.selNode;
32844         if(last != node && this.fireEvent('beforeselect', this, node, last) !== false){
32845             if(last){
32846                 last.ui.onSelectedChange(false);
32847             }
32848             this.selNode = node;
32849             node.ui.onSelectedChange(true);
32850             this.fireEvent("selectionchange", this, node, last);
32851         }
32852         return node;
32853     },
32854     
32855     /**
32856      * Deselect a node.
32857      * @param {TreeNode} node The node to unselect
32858      */
32859     unselect : function(node){
32860         if(this.selNode == node){
32861             this.clearSelections();
32862         }    
32863     },
32864     
32865     /**
32866      * Clear all selections
32867      */
32868     clearSelections : function(){
32869         var n = this.selNode;
32870         if(n){
32871             n.ui.onSelectedChange(false);
32872             this.selNode = null;
32873             this.fireEvent("selectionchange", this, null);
32874         }
32875         return n;
32876     },
32877     
32878     /**
32879      * Get the selected node
32880      * @return {TreeNode} The selected node
32881      */
32882     getSelectedNode : function(){
32883         return this.selNode;    
32884     },
32885     
32886     /**
32887      * Returns true if the node is selected
32888      * @param {TreeNode} node The node to check
32889      * @return {Boolean}
32890      */
32891     isSelected : function(node){
32892         return this.selNode == node;  
32893     },
32894
32895     /**
32896      * Selects the node above the selected node in the tree, intelligently walking the nodes
32897      * @return TreeNode The new selection
32898      */
32899     selectPrevious : function(){
32900         var s = this.selNode || this.lastSelNode;
32901         if(!s){
32902             return null;
32903         }
32904         var ps = s.previousSibling;
32905         if(ps){
32906             if(!ps.isExpanded() || ps.childNodes.length < 1){
32907                 return this.select(ps);
32908             } else{
32909                 var lc = ps.lastChild;
32910                 while(lc && lc.isExpanded() && lc.childNodes.length > 0){
32911                     lc = lc.lastChild;
32912                 }
32913                 return this.select(lc);
32914             }
32915         } else if(s.parentNode && (this.tree.rootVisible || !s.parentNode.isRoot)){
32916             return this.select(s.parentNode);
32917         }
32918         return null;
32919     },
32920
32921     /**
32922      * Selects the node above the selected node in the tree, intelligently walking the nodes
32923      * @return TreeNode The new selection
32924      */
32925     selectNext : function(){
32926         var s = this.selNode || this.lastSelNode;
32927         if(!s){
32928             return null;
32929         }
32930         if(s.firstChild && s.isExpanded()){
32931              return this.select(s.firstChild);
32932          }else if(s.nextSibling){
32933              return this.select(s.nextSibling);
32934          }else if(s.parentNode){
32935             var newS = null;
32936             s.parentNode.bubble(function(){
32937                 if(this.nextSibling){
32938                     newS = this.getOwnerTree().selModel.select(this.nextSibling);
32939                     return false;
32940                 }
32941             });
32942             return newS;
32943          }
32944         return null;
32945     },
32946
32947     onKeyDown : function(e){
32948         var s = this.selNode || this.lastSelNode;
32949         // undesirable, but required
32950         var sm = this;
32951         if(!s){
32952             return;
32953         }
32954         var k = e.getKey();
32955         switch(k){
32956              case e.DOWN:
32957                  e.stopEvent();
32958                  this.selectNext();
32959              break;
32960              case e.UP:
32961                  e.stopEvent();
32962                  this.selectPrevious();
32963              break;
32964              case e.RIGHT:
32965                  e.preventDefault();
32966                  if(s.hasChildNodes()){
32967                      if(!s.isExpanded()){
32968                          s.expand();
32969                      }else if(s.firstChild){
32970                          this.select(s.firstChild, e);
32971                      }
32972                  }
32973              break;
32974              case e.LEFT:
32975                  e.preventDefault();
32976                  if(s.hasChildNodes() && s.isExpanded()){
32977                      s.collapse();
32978                  }else if(s.parentNode && (this.tree.rootVisible || s.parentNode != this.tree.getRootNode())){
32979                      this.select(s.parentNode, e);
32980                  }
32981              break;
32982         };
32983     }
32984 });
32985
32986 /**
32987  * @class Roo.tree.MultiSelectionModel
32988  * @extends Roo.util.Observable
32989  * Multi selection for a TreePanel.
32990  * @param {Object} cfg Configuration
32991  */
32992 Roo.tree.MultiSelectionModel = function(){
32993    this.selNodes = [];
32994    this.selMap = {};
32995    this.addEvents({
32996        /**
32997         * @event selectionchange
32998         * Fires when the selected nodes change
32999         * @param {MultiSelectionModel} this
33000         * @param {Array} nodes Array of the selected nodes
33001         */
33002        "selectionchange" : true
33003    });
33004    Roo.tree.MultiSelectionModel.superclass.constructor.call(this,cfg);
33005    
33006 };
33007
33008 Roo.extend(Roo.tree.MultiSelectionModel, Roo.util.Observable, {
33009     init : function(tree){
33010         this.tree = tree;
33011         tree.getTreeEl().on("keydown", this.onKeyDown, this);
33012         tree.on("click", this.onNodeClick, this);
33013     },
33014     
33015     onNodeClick : function(node, e){
33016         this.select(node, e, e.ctrlKey);
33017     },
33018     
33019     /**
33020      * Select a node.
33021      * @param {TreeNode} node The node to select
33022      * @param {EventObject} e (optional) An event associated with the selection
33023      * @param {Boolean} keepExisting True to retain existing selections
33024      * @return {TreeNode} The selected node
33025      */
33026     select : function(node, e, keepExisting){
33027         if(keepExisting !== true){
33028             this.clearSelections(true);
33029         }
33030         if(this.isSelected(node)){
33031             this.lastSelNode = node;
33032             return node;
33033         }
33034         this.selNodes.push(node);
33035         this.selMap[node.id] = node;
33036         this.lastSelNode = node;
33037         node.ui.onSelectedChange(true);
33038         this.fireEvent("selectionchange", this, this.selNodes);
33039         return node;
33040     },
33041     
33042     /**
33043      * Deselect a node.
33044      * @param {TreeNode} node The node to unselect
33045      */
33046     unselect : function(node){
33047         if(this.selMap[node.id]){
33048             node.ui.onSelectedChange(false);
33049             var sn = this.selNodes;
33050             var index = -1;
33051             if(sn.indexOf){
33052                 index = sn.indexOf(node);
33053             }else{
33054                 for(var i = 0, len = sn.length; i < len; i++){
33055                     if(sn[i] == node){
33056                         index = i;
33057                         break;
33058                     }
33059                 }
33060             }
33061             if(index != -1){
33062                 this.selNodes.splice(index, 1);
33063             }
33064             delete this.selMap[node.id];
33065             this.fireEvent("selectionchange", this, this.selNodes);
33066         }
33067     },
33068     
33069     /**
33070      * Clear all selections
33071      */
33072     clearSelections : function(suppressEvent){
33073         var sn = this.selNodes;
33074         if(sn.length > 0){
33075             for(var i = 0, len = sn.length; i < len; i++){
33076                 sn[i].ui.onSelectedChange(false);
33077             }
33078             this.selNodes = [];
33079             this.selMap = {};
33080             if(suppressEvent !== true){
33081                 this.fireEvent("selectionchange", this, this.selNodes);
33082             }
33083         }
33084     },
33085     
33086     /**
33087      * Returns true if the node is selected
33088      * @param {TreeNode} node The node to check
33089      * @return {Boolean}
33090      */
33091     isSelected : function(node){
33092         return this.selMap[node.id] ? true : false;  
33093     },
33094     
33095     /**
33096      * Returns an array of the selected nodes
33097      * @return {Array}
33098      */
33099     getSelectedNodes : function(){
33100         return this.selNodes;    
33101     },
33102
33103     onKeyDown : Roo.tree.DefaultSelectionModel.prototype.onKeyDown,
33104
33105     selectNext : Roo.tree.DefaultSelectionModel.prototype.selectNext,
33106
33107     selectPrevious : Roo.tree.DefaultSelectionModel.prototype.selectPrevious
33108 });/*
33109  * Based on:
33110  * Ext JS Library 1.1.1
33111  * Copyright(c) 2006-2007, Ext JS, LLC.
33112  *
33113  * Originally Released Under LGPL - original licence link has changed is not relivant.
33114  *
33115  * Fork - LGPL
33116  * <script type="text/javascript">
33117  */
33118  
33119 /**
33120  * @class Roo.tree.TreeNode
33121  * @extends Roo.data.Node
33122  * @cfg {String} text The text for this node
33123  * @cfg {Boolean} expanded true to start the node expanded
33124  * @cfg {Boolean} allowDrag false to make this node undraggable if DD is on (defaults to true)
33125  * @cfg {Boolean} allowDrop false if this node cannot be drop on
33126  * @cfg {Boolean} disabled true to start the node disabled
33127  * @cfg {String} icon The path to an icon for the node. The preferred way to do this
33128  * is to use the cls or iconCls attributes and add the icon via a CSS background image.
33129  * @cfg {String} cls A css class to be added to the node
33130  * @cfg {String} iconCls A css class to be added to the nodes icon element for applying css background images
33131  * @cfg {String} href URL of the link used for the node (defaults to #)
33132  * @cfg {String} hrefTarget target frame for the link
33133  * @cfg {String} qtip An Ext QuickTip for the node
33134  * @cfg {String} qtipCfg An Ext QuickTip config for the node (used instead of qtip)
33135  * @cfg {Boolean} singleClickExpand True for single click expand on this node
33136  * @cfg {Function} uiProvider A UI <b>class</b> to use for this node (defaults to Roo.tree.TreeNodeUI)
33137  * @cfg {Boolean} checked True to render a checked checkbox for this node, false to render an unchecked checkbox
33138  * (defaults to undefined with no checkbox rendered)
33139  * @constructor
33140  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node
33141  */
33142 Roo.tree.TreeNode = function(attributes){
33143     attributes = attributes || {};
33144     if(typeof attributes == "string"){
33145         attributes = {text: attributes};
33146     }
33147     this.childrenRendered = false;
33148     this.rendered = false;
33149     Roo.tree.TreeNode.superclass.constructor.call(this, attributes);
33150     this.expanded = attributes.expanded === true;
33151     this.isTarget = attributes.isTarget !== false;
33152     this.draggable = attributes.draggable !== false && attributes.allowDrag !== false;
33153     this.allowChildren = attributes.allowChildren !== false && attributes.allowDrop !== false;
33154
33155     /**
33156      * Read-only. The text for this node. To change it use setText().
33157      * @type String
33158      */
33159     this.text = attributes.text;
33160     /**
33161      * True if this node is disabled.
33162      * @type Boolean
33163      */
33164     this.disabled = attributes.disabled === true;
33165
33166     this.addEvents({
33167         /**
33168         * @event textchange
33169         * Fires when the text for this node is changed
33170         * @param {Node} this This node
33171         * @param {String} text The new text
33172         * @param {String} oldText The old text
33173         */
33174         "textchange" : true,
33175         /**
33176         * @event beforeexpand
33177         * Fires before this node is expanded, return false to cancel.
33178         * @param {Node} this This node
33179         * @param {Boolean} deep
33180         * @param {Boolean} anim
33181         */
33182         "beforeexpand" : true,
33183         /**
33184         * @event beforecollapse
33185         * Fires before this node is collapsed, return false to cancel.
33186         * @param {Node} this This node
33187         * @param {Boolean} deep
33188         * @param {Boolean} anim
33189         */
33190         "beforecollapse" : true,
33191         /**
33192         * @event expand
33193         * Fires when this node is expanded
33194         * @param {Node} this This node
33195         */
33196         "expand" : true,
33197         /**
33198         * @event disabledchange
33199         * Fires when the disabled status of this node changes
33200         * @param {Node} this This node
33201         * @param {Boolean} disabled
33202         */
33203         "disabledchange" : true,
33204         /**
33205         * @event collapse
33206         * Fires when this node is collapsed
33207         * @param {Node} this This node
33208         */
33209         "collapse" : true,
33210         /**
33211         * @event beforeclick
33212         * Fires before click processing. Return false to cancel the default action.
33213         * @param {Node} this This node
33214         * @param {Roo.EventObject} e The event object
33215         */
33216         "beforeclick":true,
33217         /**
33218         * @event checkchange
33219         * Fires when a node with a checkbox's checked property changes
33220         * @param {Node} this This node
33221         * @param {Boolean} checked
33222         */
33223         "checkchange":true,
33224         /**
33225         * @event click
33226         * Fires when this node is clicked
33227         * @param {Node} this This node
33228         * @param {Roo.EventObject} e The event object
33229         */
33230         "click":true,
33231         /**
33232         * @event dblclick
33233         * Fires when this node is double clicked
33234         * @param {Node} this This node
33235         * @param {Roo.EventObject} e The event object
33236         */
33237         "dblclick":true,
33238         /**
33239         * @event contextmenu
33240         * Fires when this node is right clicked
33241         * @param {Node} this This node
33242         * @param {Roo.EventObject} e The event object
33243         */
33244         "contextmenu":true,
33245         /**
33246         * @event beforechildrenrendered
33247         * Fires right before the child nodes for this node are rendered
33248         * @param {Node} this This node
33249         */
33250         "beforechildrenrendered":true
33251     });
33252
33253     var uiClass = this.attributes.uiProvider || Roo.tree.TreeNodeUI;
33254
33255     /**
33256      * Read-only. The UI for this node
33257      * @type TreeNodeUI
33258      */
33259     this.ui = new uiClass(this);
33260     
33261     // finally support items[]
33262     if (typeof(this.attributes.items) == 'undefined' || !this.attributes.items) {
33263         return;
33264     }
33265     
33266     
33267     Roo.each(this.attributes.items, function(c) {
33268         this.appendChild(Roo.factory(c,Roo.Tree));
33269     }, this);
33270     delete this.attributes.items;
33271     
33272     
33273     
33274 };
33275 Roo.extend(Roo.tree.TreeNode, Roo.data.Node, {
33276     preventHScroll: true,
33277     /**
33278      * Returns true if this node is expanded
33279      * @return {Boolean}
33280      */
33281     isExpanded : function(){
33282         return this.expanded;
33283     },
33284
33285     /**
33286      * Returns the UI object for this node
33287      * @return {TreeNodeUI}
33288      */
33289     getUI : function(){
33290         return this.ui;
33291     },
33292
33293     // private override
33294     setFirstChild : function(node){
33295         var of = this.firstChild;
33296         Roo.tree.TreeNode.superclass.setFirstChild.call(this, node);
33297         if(this.childrenRendered && of && node != of){
33298             of.renderIndent(true, true);
33299         }
33300         if(this.rendered){
33301             this.renderIndent(true, true);
33302         }
33303     },
33304
33305     // private override
33306     setLastChild : function(node){
33307         var ol = this.lastChild;
33308         Roo.tree.TreeNode.superclass.setLastChild.call(this, node);
33309         if(this.childrenRendered && ol && node != ol){
33310             ol.renderIndent(true, true);
33311         }
33312         if(this.rendered){
33313             this.renderIndent(true, true);
33314         }
33315     },
33316
33317     // these methods are overridden to provide lazy rendering support
33318     // private override
33319     appendChild : function()
33320     {
33321         var node = Roo.tree.TreeNode.superclass.appendChild.apply(this, arguments);
33322         if(node && this.childrenRendered){
33323             node.render();
33324         }
33325         this.ui.updateExpandIcon();
33326         return node;
33327     },
33328
33329     // private override
33330     removeChild : function(node){
33331         this.ownerTree.getSelectionModel().unselect(node);
33332         Roo.tree.TreeNode.superclass.removeChild.apply(this, arguments);
33333         // if it's been rendered remove dom node
33334         if(this.childrenRendered){
33335             node.ui.remove();
33336         }
33337         if(this.childNodes.length < 1){
33338             this.collapse(false, false);
33339         }else{
33340             this.ui.updateExpandIcon();
33341         }
33342         if(!this.firstChild) {
33343             this.childrenRendered = false;
33344         }
33345         return node;
33346     },
33347
33348     // private override
33349     insertBefore : function(node, refNode){
33350         var newNode = Roo.tree.TreeNode.superclass.insertBefore.apply(this, arguments);
33351         if(newNode && refNode && this.childrenRendered){
33352             node.render();
33353         }
33354         this.ui.updateExpandIcon();
33355         return newNode;
33356     },
33357
33358     /**
33359      * Sets the text for this node
33360      * @param {String} text
33361      */
33362     setText : function(text){
33363         var oldText = this.text;
33364         this.text = text;
33365         this.attributes.text = text;
33366         if(this.rendered){ // event without subscribing
33367             this.ui.onTextChange(this, text, oldText);
33368         }
33369         this.fireEvent("textchange", this, text, oldText);
33370     },
33371
33372     /**
33373      * Triggers selection of this node
33374      */
33375     select : function(){
33376         this.getOwnerTree().getSelectionModel().select(this);
33377     },
33378
33379     /**
33380      * Triggers deselection of this node
33381      */
33382     unselect : function(){
33383         this.getOwnerTree().getSelectionModel().unselect(this);
33384     },
33385
33386     /**
33387      * Returns true if this node is selected
33388      * @return {Boolean}
33389      */
33390     isSelected : function(){
33391         return this.getOwnerTree().getSelectionModel().isSelected(this);
33392     },
33393
33394     /**
33395      * Expand this node.
33396      * @param {Boolean} deep (optional) True to expand all children as well
33397      * @param {Boolean} anim (optional) false to cancel the default animation
33398      * @param {Function} callback (optional) A callback to be called when
33399      * expanding this node completes (does not wait for deep expand to complete).
33400      * Called with 1 parameter, this node.
33401      */
33402     expand : function(deep, anim, callback){
33403         if(!this.expanded){
33404             if(this.fireEvent("beforeexpand", this, deep, anim) === false){
33405                 return;
33406             }
33407             if(!this.childrenRendered){
33408                 this.renderChildren();
33409             }
33410             this.expanded = true;
33411             if(!this.isHiddenRoot() && (this.getOwnerTree().animate && anim !== false) || anim){
33412                 this.ui.animExpand(function(){
33413                     this.fireEvent("expand", this);
33414                     if(typeof callback == "function"){
33415                         callback(this);
33416                     }
33417                     if(deep === true){
33418                         this.expandChildNodes(true);
33419                     }
33420                 }.createDelegate(this));
33421                 return;
33422             }else{
33423                 this.ui.expand();
33424                 this.fireEvent("expand", this);
33425                 if(typeof callback == "function"){
33426                     callback(this);
33427                 }
33428             }
33429         }else{
33430            if(typeof callback == "function"){
33431                callback(this);
33432            }
33433         }
33434         if(deep === true){
33435             this.expandChildNodes(true);
33436         }
33437     },
33438
33439     isHiddenRoot : function(){
33440         return this.isRoot && !this.getOwnerTree().rootVisible;
33441     },
33442
33443     /**
33444      * Collapse this node.
33445      * @param {Boolean} deep (optional) True to collapse all children as well
33446      * @param {Boolean} anim (optional) false to cancel the default animation
33447      */
33448     collapse : function(deep, anim){
33449         if(this.expanded && !this.isHiddenRoot()){
33450             if(this.fireEvent("beforecollapse", this, deep, anim) === false){
33451                 return;
33452             }
33453             this.expanded = false;
33454             if((this.getOwnerTree().animate && anim !== false) || anim){
33455                 this.ui.animCollapse(function(){
33456                     this.fireEvent("collapse", this);
33457                     if(deep === true){
33458                         this.collapseChildNodes(true);
33459                     }
33460                 }.createDelegate(this));
33461                 return;
33462             }else{
33463                 this.ui.collapse();
33464                 this.fireEvent("collapse", this);
33465             }
33466         }
33467         if(deep === true){
33468             var cs = this.childNodes;
33469             for(var i = 0, len = cs.length; i < len; i++) {
33470                 cs[i].collapse(true, false);
33471             }
33472         }
33473     },
33474
33475     // private
33476     delayedExpand : function(delay){
33477         if(!this.expandProcId){
33478             this.expandProcId = this.expand.defer(delay, this);
33479         }
33480     },
33481
33482     // private
33483     cancelExpand : function(){
33484         if(this.expandProcId){
33485             clearTimeout(this.expandProcId);
33486         }
33487         this.expandProcId = false;
33488     },
33489
33490     /**
33491      * Toggles expanded/collapsed state of the node
33492      */
33493     toggle : function(){
33494         if(this.expanded){
33495             this.collapse();
33496         }else{
33497             this.expand();
33498         }
33499     },
33500
33501     /**
33502      * Ensures all parent nodes are expanded
33503      */
33504     ensureVisible : function(callback){
33505         var tree = this.getOwnerTree();
33506         tree.expandPath(this.parentNode.getPath(), false, function(){
33507             tree.getTreeEl().scrollChildIntoView(this.ui.anchor);
33508             Roo.callback(callback);
33509         }.createDelegate(this));
33510     },
33511
33512     /**
33513      * Expand all child nodes
33514      * @param {Boolean} deep (optional) true if the child nodes should also expand their child nodes
33515      */
33516     expandChildNodes : function(deep){
33517         var cs = this.childNodes;
33518         for(var i = 0, len = cs.length; i < len; i++) {
33519                 cs[i].expand(deep);
33520         }
33521     },
33522
33523     /**
33524      * Collapse all child nodes
33525      * @param {Boolean} deep (optional) true if the child nodes should also collapse their child nodes
33526      */
33527     collapseChildNodes : function(deep){
33528         var cs = this.childNodes;
33529         for(var i = 0, len = cs.length; i < len; i++) {
33530                 cs[i].collapse(deep);
33531         }
33532     },
33533
33534     /**
33535      * Disables this node
33536      */
33537     disable : function(){
33538         this.disabled = true;
33539         this.unselect();
33540         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
33541             this.ui.onDisableChange(this, true);
33542         }
33543         this.fireEvent("disabledchange", this, true);
33544     },
33545
33546     /**
33547      * Enables this node
33548      */
33549     enable : function(){
33550         this.disabled = false;
33551         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
33552             this.ui.onDisableChange(this, false);
33553         }
33554         this.fireEvent("disabledchange", this, false);
33555     },
33556
33557     // private
33558     renderChildren : function(suppressEvent){
33559         if(suppressEvent !== false){
33560             this.fireEvent("beforechildrenrendered", this);
33561         }
33562         var cs = this.childNodes;
33563         for(var i = 0, len = cs.length; i < len; i++){
33564             cs[i].render(true);
33565         }
33566         this.childrenRendered = true;
33567     },
33568
33569     // private
33570     sort : function(fn, scope){
33571         Roo.tree.TreeNode.superclass.sort.apply(this, arguments);
33572         if(this.childrenRendered){
33573             var cs = this.childNodes;
33574             for(var i = 0, len = cs.length; i < len; i++){
33575                 cs[i].render(true);
33576             }
33577         }
33578     },
33579
33580     // private
33581     render : function(bulkRender){
33582         this.ui.render(bulkRender);
33583         if(!this.rendered){
33584             this.rendered = true;
33585             if(this.expanded){
33586                 this.expanded = false;
33587                 this.expand(false, false);
33588             }
33589         }
33590     },
33591
33592     // private
33593     renderIndent : function(deep, refresh){
33594         if(refresh){
33595             this.ui.childIndent = null;
33596         }
33597         this.ui.renderIndent();
33598         if(deep === true && this.childrenRendered){
33599             var cs = this.childNodes;
33600             for(var i = 0, len = cs.length; i < len; i++){
33601                 cs[i].renderIndent(true, refresh);
33602             }
33603         }
33604     }
33605 });/*
33606  * Based on:
33607  * Ext JS Library 1.1.1
33608  * Copyright(c) 2006-2007, Ext JS, LLC.
33609  *
33610  * Originally Released Under LGPL - original licence link has changed is not relivant.
33611  *
33612  * Fork - LGPL
33613  * <script type="text/javascript">
33614  */
33615  
33616 /**
33617  * @class Roo.tree.AsyncTreeNode
33618  * @extends Roo.tree.TreeNode
33619  * @cfg {TreeLoader} loader A TreeLoader to be used by this node (defaults to the loader defined on the tree)
33620  * @constructor
33621  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node 
33622  */
33623  Roo.tree.AsyncTreeNode = function(config){
33624     this.loaded = false;
33625     this.loading = false;
33626     Roo.tree.AsyncTreeNode.superclass.constructor.apply(this, arguments);
33627     /**
33628     * @event beforeload
33629     * Fires before this node is loaded, return false to cancel
33630     * @param {Node} this This node
33631     */
33632     this.addEvents({'beforeload':true, 'load': true});
33633     /**
33634     * @event load
33635     * Fires when this node is loaded
33636     * @param {Node} this This node
33637     */
33638     /**
33639      * The loader used by this node (defaults to using the tree's defined loader)
33640      * @type TreeLoader
33641      * @property loader
33642      */
33643 };
33644 Roo.extend(Roo.tree.AsyncTreeNode, Roo.tree.TreeNode, {
33645     expand : function(deep, anim, callback){
33646         if(this.loading){ // if an async load is already running, waiting til it's done
33647             var timer;
33648             var f = function(){
33649                 if(!this.loading){ // done loading
33650                     clearInterval(timer);
33651                     this.expand(deep, anim, callback);
33652                 }
33653             }.createDelegate(this);
33654             timer = setInterval(f, 200);
33655             return;
33656         }
33657         if(!this.loaded){
33658             if(this.fireEvent("beforeload", this) === false){
33659                 return;
33660             }
33661             this.loading = true;
33662             this.ui.beforeLoad(this);
33663             var loader = this.loader || this.attributes.loader || this.getOwnerTree().getLoader();
33664             if(loader){
33665                 loader.load(this, this.loadComplete.createDelegate(this, [deep, anim, callback]));
33666                 return;
33667             }
33668         }
33669         Roo.tree.AsyncTreeNode.superclass.expand.call(this, deep, anim, callback);
33670     },
33671     
33672     /**
33673      * Returns true if this node is currently loading
33674      * @return {Boolean}
33675      */
33676     isLoading : function(){
33677         return this.loading;  
33678     },
33679     
33680     loadComplete : function(deep, anim, callback){
33681         this.loading = false;
33682         this.loaded = true;
33683         this.ui.afterLoad(this);
33684         this.fireEvent("load", this);
33685         this.expand(deep, anim, callback);
33686     },
33687     
33688     /**
33689      * Returns true if this node has been loaded
33690      * @return {Boolean}
33691      */
33692     isLoaded : function(){
33693         return this.loaded;
33694     },
33695     
33696     hasChildNodes : function(){
33697         if(!this.isLeaf() && !this.loaded){
33698             return true;
33699         }else{
33700             return Roo.tree.AsyncTreeNode.superclass.hasChildNodes.call(this);
33701         }
33702     },
33703
33704     /**
33705      * Trigger a reload for this node
33706      * @param {Function} callback
33707      */
33708     reload : function(callback){
33709         this.collapse(false, false);
33710         while(this.firstChild){
33711             this.removeChild(this.firstChild);
33712         }
33713         this.childrenRendered = false;
33714         this.loaded = false;
33715         if(this.isHiddenRoot()){
33716             this.expanded = false;
33717         }
33718         this.expand(false, false, callback);
33719     }
33720 });/*
33721  * Based on:
33722  * Ext JS Library 1.1.1
33723  * Copyright(c) 2006-2007, Ext JS, LLC.
33724  *
33725  * Originally Released Under LGPL - original licence link has changed is not relivant.
33726  *
33727  * Fork - LGPL
33728  * <script type="text/javascript">
33729  */
33730  
33731 /**
33732  * @class Roo.tree.TreeNodeUI
33733  * @constructor
33734  * @param {Object} node The node to render
33735  * The TreeNode UI implementation is separate from the
33736  * tree implementation. Unless you are customizing the tree UI,
33737  * you should never have to use this directly.
33738  */
33739 Roo.tree.TreeNodeUI = function(node){
33740     this.node = node;
33741     this.rendered = false;
33742     this.animating = false;
33743     this.emptyIcon = Roo.BLANK_IMAGE_URL;
33744 };
33745
33746 Roo.tree.TreeNodeUI.prototype = {
33747     removeChild : function(node){
33748         if(this.rendered){
33749             this.ctNode.removeChild(node.ui.getEl());
33750         }
33751     },
33752
33753     beforeLoad : function(){
33754          this.addClass("x-tree-node-loading");
33755     },
33756
33757     afterLoad : function(){
33758          this.removeClass("x-tree-node-loading");
33759     },
33760
33761     onTextChange : function(node, text, oldText){
33762         if(this.rendered){
33763             this.textNode.innerHTML = text;
33764         }
33765     },
33766
33767     onDisableChange : function(node, state){
33768         this.disabled = state;
33769         if(state){
33770             this.addClass("x-tree-node-disabled");
33771         }else{
33772             this.removeClass("x-tree-node-disabled");
33773         }
33774     },
33775
33776     onSelectedChange : function(state){
33777         if(state){
33778             this.focus();
33779             this.addClass("x-tree-selected");
33780         }else{
33781             //this.blur();
33782             this.removeClass("x-tree-selected");
33783         }
33784     },
33785
33786     onMove : function(tree, node, oldParent, newParent, index, refNode){
33787         this.childIndent = null;
33788         if(this.rendered){
33789             var targetNode = newParent.ui.getContainer();
33790             if(!targetNode){//target not rendered
33791                 this.holder = document.createElement("div");
33792                 this.holder.appendChild(this.wrap);
33793                 return;
33794             }
33795             var insertBefore = refNode ? refNode.ui.getEl() : null;
33796             if(insertBefore){
33797                 targetNode.insertBefore(this.wrap, insertBefore);
33798             }else{
33799                 targetNode.appendChild(this.wrap);
33800             }
33801             this.node.renderIndent(true);
33802         }
33803     },
33804
33805     addClass : function(cls){
33806         if(this.elNode){
33807             Roo.fly(this.elNode).addClass(cls);
33808         }
33809     },
33810
33811     removeClass : function(cls){
33812         if(this.elNode){
33813             Roo.fly(this.elNode).removeClass(cls);
33814         }
33815     },
33816
33817     remove : function(){
33818         if(this.rendered){
33819             this.holder = document.createElement("div");
33820             this.holder.appendChild(this.wrap);
33821         }
33822     },
33823
33824     fireEvent : function(){
33825         return this.node.fireEvent.apply(this.node, arguments);
33826     },
33827
33828     initEvents : function(){
33829         this.node.on("move", this.onMove, this);
33830         var E = Roo.EventManager;
33831         var a = this.anchor;
33832
33833         var el = Roo.fly(a, '_treeui');
33834
33835         if(Roo.isOpera){ // opera render bug ignores the CSS
33836             el.setStyle("text-decoration", "none");
33837         }
33838
33839         el.on("click", this.onClick, this);
33840         el.on("dblclick", this.onDblClick, this);
33841
33842         if(this.checkbox){
33843             Roo.EventManager.on(this.checkbox,
33844                     Roo.isIE ? 'click' : 'change', this.onCheckChange, this);
33845         }
33846
33847         el.on("contextmenu", this.onContextMenu, this);
33848
33849         var icon = Roo.fly(this.iconNode);
33850         icon.on("click", this.onClick, this);
33851         icon.on("dblclick", this.onDblClick, this);
33852         icon.on("contextmenu", this.onContextMenu, this);
33853         E.on(this.ecNode, "click", this.ecClick, this, true);
33854
33855         if(this.node.disabled){
33856             this.addClass("x-tree-node-disabled");
33857         }
33858         if(this.node.hidden){
33859             this.addClass("x-tree-node-disabled");
33860         }
33861         var ot = this.node.getOwnerTree();
33862         var dd = ot.enableDD || ot.enableDrag || ot.enableDrop;
33863         if(dd && (!this.node.isRoot || ot.rootVisible)){
33864             Roo.dd.Registry.register(this.elNode, {
33865                 node: this.node,
33866                 handles: this.getDDHandles(),
33867                 isHandle: false
33868             });
33869         }
33870     },
33871
33872     getDDHandles : function(){
33873         return [this.iconNode, this.textNode];
33874     },
33875
33876     hide : function(){
33877         if(this.rendered){
33878             this.wrap.style.display = "none";
33879         }
33880     },
33881
33882     show : function(){
33883         if(this.rendered){
33884             this.wrap.style.display = "";
33885         }
33886     },
33887
33888     onContextMenu : function(e){
33889         if (this.node.hasListener("contextmenu") || this.node.getOwnerTree().hasListener("contextmenu")) {
33890             e.preventDefault();
33891             this.focus();
33892             this.fireEvent("contextmenu", this.node, e);
33893         }
33894     },
33895
33896     onClick : function(e){
33897         if(this.dropping){
33898             e.stopEvent();
33899             return;
33900         }
33901         if(this.fireEvent("beforeclick", this.node, e) !== false){
33902             if(!this.disabled && this.node.attributes.href){
33903                 this.fireEvent("click", this.node, e);
33904                 return;
33905             }
33906             e.preventDefault();
33907             if(this.disabled){
33908                 return;
33909             }
33910
33911             if(this.node.attributes.singleClickExpand && !this.animating && this.node.hasChildNodes()){
33912                 this.node.toggle();
33913             }
33914
33915             this.fireEvent("click", this.node, e);
33916         }else{
33917             e.stopEvent();
33918         }
33919     },
33920
33921     onDblClick : function(e){
33922         e.preventDefault();
33923         if(this.disabled){
33924             return;
33925         }
33926         if(this.checkbox){
33927             this.toggleCheck();
33928         }
33929         if(!this.animating && this.node.hasChildNodes()){
33930             this.node.toggle();
33931         }
33932         this.fireEvent("dblclick", this.node, e);
33933     },
33934
33935     onCheckChange : function(){
33936         var checked = this.checkbox.checked;
33937         this.node.attributes.checked = checked;
33938         this.fireEvent('checkchange', this.node, checked);
33939     },
33940
33941     ecClick : function(e){
33942         if(!this.animating && this.node.hasChildNodes()){
33943             this.node.toggle();
33944         }
33945     },
33946
33947     startDrop : function(){
33948         this.dropping = true;
33949     },
33950
33951     // delayed drop so the click event doesn't get fired on a drop
33952     endDrop : function(){
33953        setTimeout(function(){
33954            this.dropping = false;
33955        }.createDelegate(this), 50);
33956     },
33957
33958     expand : function(){
33959         this.updateExpandIcon();
33960         this.ctNode.style.display = "";
33961     },
33962
33963     focus : function(){
33964         if(!this.node.preventHScroll){
33965             try{this.anchor.focus();
33966             }catch(e){}
33967         }else if(!Roo.isIE){
33968             try{
33969                 var noscroll = this.node.getOwnerTree().getTreeEl().dom;
33970                 var l = noscroll.scrollLeft;
33971                 this.anchor.focus();
33972                 noscroll.scrollLeft = l;
33973             }catch(e){}
33974         }
33975     },
33976
33977     toggleCheck : function(value){
33978         var cb = this.checkbox;
33979         if(cb){
33980             cb.checked = (value === undefined ? !cb.checked : value);
33981         }
33982     },
33983
33984     blur : function(){
33985         try{
33986             this.anchor.blur();
33987         }catch(e){}
33988     },
33989
33990     animExpand : function(callback){
33991         var ct = Roo.get(this.ctNode);
33992         ct.stopFx();
33993         if(!this.node.hasChildNodes()){
33994             this.updateExpandIcon();
33995             this.ctNode.style.display = "";
33996             Roo.callback(callback);
33997             return;
33998         }
33999         this.animating = true;
34000         this.updateExpandIcon();
34001
34002         ct.slideIn('t', {
34003            callback : function(){
34004                this.animating = false;
34005                Roo.callback(callback);
34006             },
34007             scope: this,
34008             duration: this.node.ownerTree.duration || .25
34009         });
34010     },
34011
34012     highlight : function(){
34013         var tree = this.node.getOwnerTree();
34014         Roo.fly(this.wrap).highlight(
34015             tree.hlColor || "C3DAF9",
34016             {endColor: tree.hlBaseColor}
34017         );
34018     },
34019
34020     collapse : function(){
34021         this.updateExpandIcon();
34022         this.ctNode.style.display = "none";
34023     },
34024
34025     animCollapse : function(callback){
34026         var ct = Roo.get(this.ctNode);
34027         ct.enableDisplayMode('block');
34028         ct.stopFx();
34029
34030         this.animating = true;
34031         this.updateExpandIcon();
34032
34033         ct.slideOut('t', {
34034             callback : function(){
34035                this.animating = false;
34036                Roo.callback(callback);
34037             },
34038             scope: this,
34039             duration: this.node.ownerTree.duration || .25
34040         });
34041     },
34042
34043     getContainer : function(){
34044         return this.ctNode;
34045     },
34046
34047     getEl : function(){
34048         return this.wrap;
34049     },
34050
34051     appendDDGhost : function(ghostNode){
34052         ghostNode.appendChild(this.elNode.cloneNode(true));
34053     },
34054
34055     getDDRepairXY : function(){
34056         return Roo.lib.Dom.getXY(this.iconNode);
34057     },
34058
34059     onRender : function(){
34060         this.render();
34061     },
34062
34063     render : function(bulkRender){
34064         var n = this.node, a = n.attributes;
34065         var targetNode = n.parentNode ?
34066               n.parentNode.ui.getContainer() : n.ownerTree.innerCt.dom;
34067
34068         if(!this.rendered){
34069             this.rendered = true;
34070
34071             this.renderElements(n, a, targetNode, bulkRender);
34072
34073             if(a.qtip){
34074                if(this.textNode.setAttributeNS){
34075                    this.textNode.setAttributeNS("ext", "qtip", a.qtip);
34076                    if(a.qtipTitle){
34077                        this.textNode.setAttributeNS("ext", "qtitle", a.qtipTitle);
34078                    }
34079                }else{
34080                    this.textNode.setAttribute("ext:qtip", a.qtip);
34081                    if(a.qtipTitle){
34082                        this.textNode.setAttribute("ext:qtitle", a.qtipTitle);
34083                    }
34084                }
34085             }else if(a.qtipCfg){
34086                 a.qtipCfg.target = Roo.id(this.textNode);
34087                 Roo.QuickTips.register(a.qtipCfg);
34088             }
34089             this.initEvents();
34090             if(!this.node.expanded){
34091                 this.updateExpandIcon();
34092             }
34093         }else{
34094             if(bulkRender === true) {
34095                 targetNode.appendChild(this.wrap);
34096             }
34097         }
34098     },
34099
34100     renderElements : function(n, a, targetNode, bulkRender)
34101     {
34102         // add some indent caching, this helps performance when rendering a large tree
34103         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
34104         var t = n.getOwnerTree();
34105         var txt = t.renderer ? t.renderer(n.attributes) : Roo.util.Format.htmlEncode(n.text);
34106         if (typeof(n.attributes.html) != 'undefined') {
34107             txt = n.attributes.html;
34108         }
34109         var tip = t.rendererTip ? t.rendererTip(n.attributes) : txt;
34110         var cb = typeof a.checked == 'boolean';
34111         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
34112         var buf = ['<li class="x-tree-node"><div class="x-tree-node-el ', a.cls,'">',
34113             '<span class="x-tree-node-indent">',this.indentMarkup,"</span>",
34114             '<img src="', this.emptyIcon, '" class="x-tree-ec-icon" />',
34115             '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',(a.icon ? " x-tree-node-inline-icon" : ""),(a.iconCls ? " "+a.iconCls : ""),'" unselectable="on" />',
34116             cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + (a.checked ? 'checked="checked" />' : ' />')) : '',
34117             '<a hidefocus="on" href="',href,'" tabIndex="1" ',
34118              a.hrefTarget ? ' target="'+a.hrefTarget+'"' : "", 
34119                 '><span unselectable="on" qtip="' , tip ,'">',txt,"</span></a></div>",
34120             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
34121             "</li>"];
34122
34123         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
34124             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
34125                                 n.nextSibling.ui.getEl(), buf.join(""));
34126         }else{
34127             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
34128         }
34129
34130         this.elNode = this.wrap.childNodes[0];
34131         this.ctNode = this.wrap.childNodes[1];
34132         var cs = this.elNode.childNodes;
34133         this.indentNode = cs[0];
34134         this.ecNode = cs[1];
34135         this.iconNode = cs[2];
34136         var index = 3;
34137         if(cb){
34138             this.checkbox = cs[3];
34139             index++;
34140         }
34141         this.anchor = cs[index];
34142         this.textNode = cs[index].firstChild;
34143     },
34144
34145     getAnchor : function(){
34146         return this.anchor;
34147     },
34148
34149     getTextEl : function(){
34150         return this.textNode;
34151     },
34152
34153     getIconEl : function(){
34154         return this.iconNode;
34155     },
34156
34157     isChecked : function(){
34158         return this.checkbox ? this.checkbox.checked : false;
34159     },
34160
34161     updateExpandIcon : function(){
34162         if(this.rendered){
34163             var n = this.node, c1, c2;
34164             var cls = n.isLast() ? "x-tree-elbow-end" : "x-tree-elbow";
34165             var hasChild = n.hasChildNodes();
34166             if(hasChild){
34167                 if(n.expanded){
34168                     cls += "-minus";
34169                     c1 = "x-tree-node-collapsed";
34170                     c2 = "x-tree-node-expanded";
34171                 }else{
34172                     cls += "-plus";
34173                     c1 = "x-tree-node-expanded";
34174                     c2 = "x-tree-node-collapsed";
34175                 }
34176                 if(this.wasLeaf){
34177                     this.removeClass("x-tree-node-leaf");
34178                     this.wasLeaf = false;
34179                 }
34180                 if(this.c1 != c1 || this.c2 != c2){
34181                     Roo.fly(this.elNode).replaceClass(c1, c2);
34182                     this.c1 = c1; this.c2 = c2;
34183                 }
34184             }else{
34185                 // this changes non-leafs into leafs if they have no children.
34186                 // it's not very rational behaviour..
34187                 
34188                 if(!this.wasLeaf && this.node.leaf){
34189                     Roo.fly(this.elNode).replaceClass("x-tree-node-expanded", "x-tree-node-leaf");
34190                     delete this.c1;
34191                     delete this.c2;
34192                     this.wasLeaf = true;
34193                 }
34194             }
34195             var ecc = "x-tree-ec-icon "+cls;
34196             if(this.ecc != ecc){
34197                 this.ecNode.className = ecc;
34198                 this.ecc = ecc;
34199             }
34200         }
34201     },
34202
34203     getChildIndent : function(){
34204         if(!this.childIndent){
34205             var buf = [];
34206             var p = this.node;
34207             while(p){
34208                 if(!p.isRoot || (p.isRoot && p.ownerTree.rootVisible)){
34209                     if(!p.isLast()) {
34210                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-elbow-line" />');
34211                     } else {
34212                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-icon" />');
34213                     }
34214                 }
34215                 p = p.parentNode;
34216             }
34217             this.childIndent = buf.join("");
34218         }
34219         return this.childIndent;
34220     },
34221
34222     renderIndent : function(){
34223         if(this.rendered){
34224             var indent = "";
34225             var p = this.node.parentNode;
34226             if(p){
34227                 indent = p.ui.getChildIndent();
34228             }
34229             if(this.indentMarkup != indent){ // don't rerender if not required
34230                 this.indentNode.innerHTML = indent;
34231                 this.indentMarkup = indent;
34232             }
34233             this.updateExpandIcon();
34234         }
34235     }
34236 };
34237
34238 Roo.tree.RootTreeNodeUI = function(){
34239     Roo.tree.RootTreeNodeUI.superclass.constructor.apply(this, arguments);
34240 };
34241 Roo.extend(Roo.tree.RootTreeNodeUI, Roo.tree.TreeNodeUI, {
34242     render : function(){
34243         if(!this.rendered){
34244             var targetNode = this.node.ownerTree.innerCt.dom;
34245             this.node.expanded = true;
34246             targetNode.innerHTML = '<div class="x-tree-root-node"></div>';
34247             this.wrap = this.ctNode = targetNode.firstChild;
34248         }
34249     },
34250     collapse : function(){
34251     },
34252     expand : function(){
34253     }
34254 });/*
34255  * Based on:
34256  * Ext JS Library 1.1.1
34257  * Copyright(c) 2006-2007, Ext JS, LLC.
34258  *
34259  * Originally Released Under LGPL - original licence link has changed is not relivant.
34260  *
34261  * Fork - LGPL
34262  * <script type="text/javascript">
34263  */
34264 /**
34265  * @class Roo.tree.TreeLoader
34266  * @extends Roo.util.Observable
34267  * A TreeLoader provides for lazy loading of an {@link Roo.tree.TreeNode}'s child
34268  * nodes from a specified URL. The response must be a javascript Array definition
34269  * who's elements are node definition objects. eg:
34270  * <pre><code>
34271 {  success : true,
34272    data :      [
34273    
34274     { 'id': 1, 'text': 'A folder Node', 'leaf': false },
34275     { 'id': 2, 'text': 'A leaf Node', 'leaf': true }
34276     ]
34277 }
34278
34279
34280 </code></pre>
34281  * <br><br>
34282  * The old style respose with just an array is still supported, but not recommended.
34283  * <br><br>
34284  *
34285  * A server request is sent, and child nodes are loaded only when a node is expanded.
34286  * The loading node's id is passed to the server under the parameter name "node" to
34287  * enable the server to produce the correct child nodes.
34288  * <br><br>
34289  * To pass extra parameters, an event handler may be attached to the "beforeload"
34290  * event, and the parameters specified in the TreeLoader's baseParams property:
34291  * <pre><code>
34292     myTreeLoader.on("beforeload", function(treeLoader, node) {
34293         this.baseParams.category = node.attributes.category;
34294     }, this);
34295 </code></pre><
34296  * This would pass an HTTP parameter called "category" to the server containing
34297  * the value of the Node's "category" attribute.
34298  * @constructor
34299  * Creates a new Treeloader.
34300  * @param {Object} config A config object containing config properties.
34301  */
34302 Roo.tree.TreeLoader = function(config){
34303     this.baseParams = {};
34304     this.requestMethod = "POST";
34305     Roo.apply(this, config);
34306
34307     this.addEvents({
34308     
34309         /**
34310          * @event beforeload
34311          * Fires before a network request is made to retrieve the Json text which specifies a node's children.
34312          * @param {Object} This TreeLoader object.
34313          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
34314          * @param {Object} callback The callback function specified in the {@link #load} call.
34315          */
34316         beforeload : true,
34317         /**
34318          * @event load
34319          * Fires when the node has been successfuly loaded.
34320          * @param {Object} This TreeLoader object.
34321          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
34322          * @param {Object} response The response object containing the data from the server.
34323          */
34324         load : true,
34325         /**
34326          * @event loadexception
34327          * Fires if the network request failed.
34328          * @param {Object} This TreeLoader object.
34329          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
34330          * @param {Object} response The response object containing the data from the server.
34331          */
34332         loadexception : true,
34333         /**
34334          * @event create
34335          * Fires before a node is created, enabling you to return custom Node types 
34336          * @param {Object} This TreeLoader object.
34337          * @param {Object} attr - the data returned from the AJAX call (modify it to suit)
34338          */
34339         create : true
34340     });
34341
34342     Roo.tree.TreeLoader.superclass.constructor.call(this);
34343 };
34344
34345 Roo.extend(Roo.tree.TreeLoader, Roo.util.Observable, {
34346     /**
34347     * @cfg {String} dataUrl The URL from which to request a Json string which
34348     * specifies an array of node definition object representing the child nodes
34349     * to be loaded.
34350     */
34351     /**
34352     * @cfg {String} requestMethod either GET or POST
34353     * defaults to POST (due to BC)
34354     * to be loaded.
34355     */
34356     /**
34357     * @cfg {Object} baseParams (optional) An object containing properties which
34358     * specify HTTP parameters to be passed to each request for child nodes.
34359     */
34360     /**
34361     * @cfg {Object} baseAttrs (optional) An object containing attributes to be added to all nodes
34362     * created by this loader. If the attributes sent by the server have an attribute in this object,
34363     * they take priority.
34364     */
34365     /**
34366     * @cfg {Object} uiProviders (optional) An object containing properties which
34367     * 
34368     * DEPRECATED - use 'create' event handler to modify attributes - which affect creation.
34369     * specify custom {@link Roo.tree.TreeNodeUI} implementations. If the optional
34370     * <i>uiProvider</i> attribute of a returned child node is a string rather
34371     * than a reference to a TreeNodeUI implementation, this that string value
34372     * is used as a property name in the uiProviders object. You can define the provider named
34373     * 'default' , and this will be used for all nodes (if no uiProvider is delivered by the node data)
34374     */
34375     uiProviders : {},
34376
34377     /**
34378     * @cfg {Boolean} clearOnLoad (optional) Default to true. Remove previously existing
34379     * child nodes before loading.
34380     */
34381     clearOnLoad : true,
34382
34383     /**
34384     * @cfg {String} root (optional) Default to false. Use this to read data from an object 
34385     * property on loading, rather than expecting an array. (eg. more compatible to a standard
34386     * Grid query { data : [ .....] }
34387     */
34388     
34389     root : false,
34390      /**
34391     * @cfg {String} queryParam (optional) 
34392     * Name of the query as it will be passed on the querystring (defaults to 'node')
34393     * eg. the request will be ?node=[id]
34394     */
34395     
34396     
34397     queryParam: false,
34398     
34399     /**
34400      * Load an {@link Roo.tree.TreeNode} from the URL specified in the constructor.
34401      * This is called automatically when a node is expanded, but may be used to reload
34402      * a node (or append new children if the {@link #clearOnLoad} option is false.)
34403      * @param {Roo.tree.TreeNode} node
34404      * @param {Function} callback
34405      */
34406     load : function(node, callback){
34407         if(this.clearOnLoad){
34408             while(node.firstChild){
34409                 node.removeChild(node.firstChild);
34410             }
34411         }
34412         if(node.attributes.children){ // preloaded json children
34413             var cs = node.attributes.children;
34414             for(var i = 0, len = cs.length; i < len; i++){
34415                 node.appendChild(this.createNode(cs[i]));
34416             }
34417             if(typeof callback == "function"){
34418                 callback();
34419             }
34420         }else if(this.dataUrl){
34421             this.requestData(node, callback);
34422         }
34423     },
34424
34425     getParams: function(node){
34426         var buf = [], bp = this.baseParams;
34427         for(var key in bp){
34428             if(typeof bp[key] != "function"){
34429                 buf.push(encodeURIComponent(key), "=", encodeURIComponent(bp[key]), "&");
34430             }
34431         }
34432         var n = this.queryParam === false ? 'node' : this.queryParam;
34433         buf.push(n + "=", encodeURIComponent(node.id));
34434         return buf.join("");
34435     },
34436
34437     requestData : function(node, callback){
34438         if(this.fireEvent("beforeload", this, node, callback) !== false){
34439             this.transId = Roo.Ajax.request({
34440                 method:this.requestMethod,
34441                 url: this.dataUrl||this.url,
34442                 success: this.handleResponse,
34443                 failure: this.handleFailure,
34444                 scope: this,
34445                 argument: {callback: callback, node: node},
34446                 params: this.getParams(node)
34447             });
34448         }else{
34449             // if the load is cancelled, make sure we notify
34450             // the node that we are done
34451             if(typeof callback == "function"){
34452                 callback();
34453             }
34454         }
34455     },
34456
34457     isLoading : function(){
34458         return this.transId ? true : false;
34459     },
34460
34461     abort : function(){
34462         if(this.isLoading()){
34463             Roo.Ajax.abort(this.transId);
34464         }
34465     },
34466
34467     // private
34468     createNode : function(attr)
34469     {
34470         // apply baseAttrs, nice idea Corey!
34471         if(this.baseAttrs){
34472             Roo.applyIf(attr, this.baseAttrs);
34473         }
34474         if(this.applyLoader !== false){
34475             attr.loader = this;
34476         }
34477         // uiProvider = depreciated..
34478         
34479         if(typeof(attr.uiProvider) == 'string'){
34480            attr.uiProvider = this.uiProviders[attr.uiProvider] || 
34481                 /**  eval:var:attr */ eval(attr.uiProvider);
34482         }
34483         if(typeof(this.uiProviders['default']) != 'undefined') {
34484             attr.uiProvider = this.uiProviders['default'];
34485         }
34486         
34487         this.fireEvent('create', this, attr);
34488         
34489         attr.leaf  = typeof(attr.leaf) == 'string' ? attr.leaf * 1 : attr.leaf;
34490         return(attr.leaf ?
34491                         new Roo.tree.TreeNode(attr) :
34492                         new Roo.tree.AsyncTreeNode(attr));
34493     },
34494
34495     processResponse : function(response, node, callback)
34496     {
34497         var json = response.responseText;
34498         try {
34499             
34500             var o = Roo.decode(json);
34501             
34502             if (this.root === false && typeof(o.success) != undefined) {
34503                 this.root = 'data'; // the default behaviour for list like data..
34504                 }
34505                 
34506             if (this.root !== false &&  !o.success) {
34507                 // it's a failure condition.
34508                 var a = response.argument;
34509                 this.fireEvent("loadexception", this, a.node, response);
34510                 Roo.log("Load failed - should have a handler really");
34511                 return;
34512             }
34513             
34514             
34515             
34516             if (this.root !== false) {
34517                  o = o[this.root];
34518             }
34519             
34520             for(var i = 0, len = o.length; i < len; i++){
34521                 var n = this.createNode(o[i]);
34522                 if(n){
34523                     node.appendChild(n);
34524                 }
34525             }
34526             if(typeof callback == "function"){
34527                 callback(this, node);
34528             }
34529         }catch(e){
34530             this.handleFailure(response);
34531         }
34532     },
34533
34534     handleResponse : function(response){
34535         this.transId = false;
34536         var a = response.argument;
34537         this.processResponse(response, a.node, a.callback);
34538         this.fireEvent("load", this, a.node, response);
34539     },
34540
34541     handleFailure : function(response)
34542     {
34543         // should handle failure better..
34544         this.transId = false;
34545         var a = response.argument;
34546         this.fireEvent("loadexception", this, a.node, response);
34547         if(typeof a.callback == "function"){
34548             a.callback(this, a.node);
34549         }
34550     }
34551 });/*
34552  * Based on:
34553  * Ext JS Library 1.1.1
34554  * Copyright(c) 2006-2007, Ext JS, LLC.
34555  *
34556  * Originally Released Under LGPL - original licence link has changed is not relivant.
34557  *
34558  * Fork - LGPL
34559  * <script type="text/javascript">
34560  */
34561
34562 /**
34563 * @class Roo.tree.TreeFilter
34564 * Note this class is experimental and doesn't update the indent (lines) or expand collapse icons of the nodes
34565 * @param {TreePanel} tree
34566 * @param {Object} config (optional)
34567  */
34568 Roo.tree.TreeFilter = function(tree, config){
34569     this.tree = tree;
34570     this.filtered = {};
34571     Roo.apply(this, config);
34572 };
34573
34574 Roo.tree.TreeFilter.prototype = {
34575     clearBlank:false,
34576     reverse:false,
34577     autoClear:false,
34578     remove:false,
34579
34580      /**
34581      * Filter the data by a specific attribute.
34582      * @param {String/RegExp} value Either string that the attribute value
34583      * should start with or a RegExp to test against the attribute
34584      * @param {String} attr (optional) The attribute passed in your node's attributes collection. Defaults to "text".
34585      * @param {TreeNode} startNode (optional) The node to start the filter at.
34586      */
34587     filter : function(value, attr, startNode){
34588         attr = attr || "text";
34589         var f;
34590         if(typeof value == "string"){
34591             var vlen = value.length;
34592             // auto clear empty filter
34593             if(vlen == 0 && this.clearBlank){
34594                 this.clear();
34595                 return;
34596             }
34597             value = value.toLowerCase();
34598             f = function(n){
34599                 return n.attributes[attr].substr(0, vlen).toLowerCase() == value;
34600             };
34601         }else if(value.exec){ // regex?
34602             f = function(n){
34603                 return value.test(n.attributes[attr]);
34604             };
34605         }else{
34606             throw 'Illegal filter type, must be string or regex';
34607         }
34608         this.filterBy(f, null, startNode);
34609         },
34610
34611     /**
34612      * Filter by a function. The passed function will be called with each
34613      * node in the tree (or from the startNode). If the function returns true, the node is kept
34614      * otherwise it is filtered. If a node is filtered, its children are also filtered.
34615      * @param {Function} fn The filter function
34616      * @param {Object} scope (optional) The scope of the function (defaults to the current node)
34617      */
34618     filterBy : function(fn, scope, startNode){
34619         startNode = startNode || this.tree.root;
34620         if(this.autoClear){
34621             this.clear();
34622         }
34623         var af = this.filtered, rv = this.reverse;
34624         var f = function(n){
34625             if(n == startNode){
34626                 return true;
34627             }
34628             if(af[n.id]){
34629                 return false;
34630             }
34631             var m = fn.call(scope || n, n);
34632             if(!m || rv){
34633                 af[n.id] = n;
34634                 n.ui.hide();
34635                 return false;
34636             }
34637             return true;
34638         };
34639         startNode.cascade(f);
34640         if(this.remove){
34641            for(var id in af){
34642                if(typeof id != "function"){
34643                    var n = af[id];
34644                    if(n && n.parentNode){
34645                        n.parentNode.removeChild(n);
34646                    }
34647                }
34648            }
34649         }
34650     },
34651
34652     /**
34653      * Clears the current filter. Note: with the "remove" option
34654      * set a filter cannot be cleared.
34655      */
34656     clear : function(){
34657         var t = this.tree;
34658         var af = this.filtered;
34659         for(var id in af){
34660             if(typeof id != "function"){
34661                 var n = af[id];
34662                 if(n){
34663                     n.ui.show();
34664                 }
34665             }
34666         }
34667         this.filtered = {};
34668     }
34669 };
34670 /*
34671  * Based on:
34672  * Ext JS Library 1.1.1
34673  * Copyright(c) 2006-2007, Ext JS, LLC.
34674  *
34675  * Originally Released Under LGPL - original licence link has changed is not relivant.
34676  *
34677  * Fork - LGPL
34678  * <script type="text/javascript">
34679  */
34680  
34681
34682 /**
34683  * @class Roo.tree.TreeSorter
34684  * Provides sorting of nodes in a TreePanel
34685  * 
34686  * @cfg {Boolean} folderSort True to sort leaf nodes under non leaf nodes
34687  * @cfg {String} property The named attribute on the node to sort by (defaults to text)
34688  * @cfg {String} dir The direction to sort (asc or desc) (defaults to asc)
34689  * @cfg {String} leafAttr The attribute used to determine leaf nodes in folder sort (defaults to "leaf")
34690  * @cfg {Boolean} caseSensitive true for case sensitive sort (defaults to false)
34691  * @cfg {Function} sortType A custom "casting" function used to convert node values before sorting
34692  * @constructor
34693  * @param {TreePanel} tree
34694  * @param {Object} config
34695  */
34696 Roo.tree.TreeSorter = function(tree, config){
34697     Roo.apply(this, config);
34698     tree.on("beforechildrenrendered", this.doSort, this);
34699     tree.on("append", this.updateSort, this);
34700     tree.on("insert", this.updateSort, this);
34701     
34702     var dsc = this.dir && this.dir.toLowerCase() == "desc";
34703     var p = this.property || "text";
34704     var sortType = this.sortType;
34705     var fs = this.folderSort;
34706     var cs = this.caseSensitive === true;
34707     var leafAttr = this.leafAttr || 'leaf';
34708
34709     this.sortFn = function(n1, n2){
34710         if(fs){
34711             if(n1.attributes[leafAttr] && !n2.attributes[leafAttr]){
34712                 return 1;
34713             }
34714             if(!n1.attributes[leafAttr] && n2.attributes[leafAttr]){
34715                 return -1;
34716             }
34717         }
34718         var v1 = sortType ? sortType(n1) : (cs ? n1.attributes[p] : n1.attributes[p].toUpperCase());
34719         var v2 = sortType ? sortType(n2) : (cs ? n2.attributes[p] : n2.attributes[p].toUpperCase());
34720         if(v1 < v2){
34721                         return dsc ? +1 : -1;
34722                 }else if(v1 > v2){
34723                         return dsc ? -1 : +1;
34724         }else{
34725                 return 0;
34726         }
34727     };
34728 };
34729
34730 Roo.tree.TreeSorter.prototype = {
34731     doSort : function(node){
34732         node.sort(this.sortFn);
34733     },
34734     
34735     compareNodes : function(n1, n2){
34736         return (n1.text.toUpperCase() > n2.text.toUpperCase() ? 1 : -1);
34737     },
34738     
34739     updateSort : function(tree, node){
34740         if(node.childrenRendered){
34741             this.doSort.defer(1, this, [node]);
34742         }
34743     }
34744 };/*
34745  * Based on:
34746  * Ext JS Library 1.1.1
34747  * Copyright(c) 2006-2007, Ext JS, LLC.
34748  *
34749  * Originally Released Under LGPL - original licence link has changed is not relivant.
34750  *
34751  * Fork - LGPL
34752  * <script type="text/javascript">
34753  */
34754
34755 if(Roo.dd.DropZone){
34756     
34757 Roo.tree.TreeDropZone = function(tree, config){
34758     this.allowParentInsert = false;
34759     this.allowContainerDrop = false;
34760     this.appendOnly = false;
34761     Roo.tree.TreeDropZone.superclass.constructor.call(this, tree.innerCt, config);
34762     this.tree = tree;
34763     this.lastInsertClass = "x-tree-no-status";
34764     this.dragOverData = {};
34765 };
34766
34767 Roo.extend(Roo.tree.TreeDropZone, Roo.dd.DropZone, {
34768     ddGroup : "TreeDD",
34769     scroll:  true,
34770     
34771     expandDelay : 1000,
34772     
34773     expandNode : function(node){
34774         if(node.hasChildNodes() && !node.isExpanded()){
34775             node.expand(false, null, this.triggerCacheRefresh.createDelegate(this));
34776         }
34777     },
34778     
34779     queueExpand : function(node){
34780         this.expandProcId = this.expandNode.defer(this.expandDelay, this, [node]);
34781     },
34782     
34783     cancelExpand : function(){
34784         if(this.expandProcId){
34785             clearTimeout(this.expandProcId);
34786             this.expandProcId = false;
34787         }
34788     },
34789     
34790     isValidDropPoint : function(n, pt, dd, e, data){
34791         if(!n || !data){ return false; }
34792         var targetNode = n.node;
34793         var dropNode = data.node;
34794         // default drop rules
34795         if(!(targetNode && targetNode.isTarget && pt)){
34796             return false;
34797         }
34798         if(pt == "append" && targetNode.allowChildren === false){
34799             return false;
34800         }
34801         if((pt == "above" || pt == "below") && (targetNode.parentNode && targetNode.parentNode.allowChildren === false)){
34802             return false;
34803         }
34804         if(dropNode && (targetNode == dropNode || dropNode.contains(targetNode))){
34805             return false;
34806         }
34807         // reuse the object
34808         var overEvent = this.dragOverData;
34809         overEvent.tree = this.tree;
34810         overEvent.target = targetNode;
34811         overEvent.data = data;
34812         overEvent.point = pt;
34813         overEvent.source = dd;
34814         overEvent.rawEvent = e;
34815         overEvent.dropNode = dropNode;
34816         overEvent.cancel = false;  
34817         var result = this.tree.fireEvent("nodedragover", overEvent);
34818         return overEvent.cancel === false && result !== false;
34819     },
34820     
34821     getDropPoint : function(e, n, dd)
34822     {
34823         var tn = n.node;
34824         if(tn.isRoot){
34825             return tn.allowChildren !== false ? "append" : false; // always append for root
34826         }
34827         var dragEl = n.ddel;
34828         var t = Roo.lib.Dom.getY(dragEl), b = t + dragEl.offsetHeight;
34829         var y = Roo.lib.Event.getPageY(e);
34830         //var noAppend = tn.allowChildren === false || tn.isLeaf();
34831         
34832         // we may drop nodes anywhere, as long as allowChildren has not been set to false..
34833         var noAppend = tn.allowChildren === false;
34834         if(this.appendOnly || tn.parentNode.allowChildren === false){
34835             return noAppend ? false : "append";
34836         }
34837         var noBelow = false;
34838         if(!this.allowParentInsert){
34839             noBelow = tn.hasChildNodes() && tn.isExpanded();
34840         }
34841         var q = (b - t) / (noAppend ? 2 : 3);
34842         if(y >= t && y < (t + q)){
34843             return "above";
34844         }else if(!noBelow && (noAppend || y >= b-q && y <= b)){
34845             return "below";
34846         }else{
34847             return "append";
34848         }
34849     },
34850     
34851     onNodeEnter : function(n, dd, e, data)
34852     {
34853         this.cancelExpand();
34854     },
34855     
34856     onNodeOver : function(n, dd, e, data)
34857     {
34858        
34859         var pt = this.getDropPoint(e, n, dd);
34860         var node = n.node;
34861         
34862         // auto node expand check
34863         if(!this.expandProcId && pt == "append" && node.hasChildNodes() && !n.node.isExpanded()){
34864             this.queueExpand(node);
34865         }else if(pt != "append"){
34866             this.cancelExpand();
34867         }
34868         
34869         // set the insert point style on the target node
34870         var returnCls = this.dropNotAllowed;
34871         if(this.isValidDropPoint(n, pt, dd, e, data)){
34872            if(pt){
34873                var el = n.ddel;
34874                var cls;
34875                if(pt == "above"){
34876                    returnCls = n.node.isFirst() ? "x-tree-drop-ok-above" : "x-tree-drop-ok-between";
34877                    cls = "x-tree-drag-insert-above";
34878                }else if(pt == "below"){
34879                    returnCls = n.node.isLast() ? "x-tree-drop-ok-below" : "x-tree-drop-ok-between";
34880                    cls = "x-tree-drag-insert-below";
34881                }else{
34882                    returnCls = "x-tree-drop-ok-append";
34883                    cls = "x-tree-drag-append";
34884                }
34885                if(this.lastInsertClass != cls){
34886                    Roo.fly(el).replaceClass(this.lastInsertClass, cls);
34887                    this.lastInsertClass = cls;
34888                }
34889            }
34890        }
34891        return returnCls;
34892     },
34893     
34894     onNodeOut : function(n, dd, e, data){
34895         
34896         this.cancelExpand();
34897         this.removeDropIndicators(n);
34898     },
34899     
34900     onNodeDrop : function(n, dd, e, data){
34901         var point = this.getDropPoint(e, n, dd);
34902         var targetNode = n.node;
34903         targetNode.ui.startDrop();
34904         if(!this.isValidDropPoint(n, point, dd, e, data)){
34905             targetNode.ui.endDrop();
34906             return false;
34907         }
34908         // first try to find the drop node
34909         var dropNode = data.node || (dd.getTreeNode ? dd.getTreeNode(data, targetNode, point, e) : null);
34910         var dropEvent = {
34911             tree : this.tree,
34912             target: targetNode,
34913             data: data,
34914             point: point,
34915             source: dd,
34916             rawEvent: e,
34917             dropNode: dropNode,
34918             cancel: !dropNode   
34919         };
34920         var retval = this.tree.fireEvent("beforenodedrop", dropEvent);
34921         if(retval === false || dropEvent.cancel === true || !dropEvent.dropNode){
34922             targetNode.ui.endDrop();
34923             return false;
34924         }
34925         // allow target changing
34926         targetNode = dropEvent.target;
34927         if(point == "append" && !targetNode.isExpanded()){
34928             targetNode.expand(false, null, function(){
34929                 this.completeDrop(dropEvent);
34930             }.createDelegate(this));
34931         }else{
34932             this.completeDrop(dropEvent);
34933         }
34934         return true;
34935     },
34936     
34937     completeDrop : function(de){
34938         var ns = de.dropNode, p = de.point, t = de.target;
34939         if(!(ns instanceof Array)){
34940             ns = [ns];
34941         }
34942         var n;
34943         for(var i = 0, len = ns.length; i < len; i++){
34944             n = ns[i];
34945             if(p == "above"){
34946                 t.parentNode.insertBefore(n, t);
34947             }else if(p == "below"){
34948                 t.parentNode.insertBefore(n, t.nextSibling);
34949             }else{
34950                 t.appendChild(n);
34951             }
34952         }
34953         n.ui.focus();
34954         if(this.tree.hlDrop){
34955             n.ui.highlight();
34956         }
34957         t.ui.endDrop();
34958         this.tree.fireEvent("nodedrop", de);
34959     },
34960     
34961     afterNodeMoved : function(dd, data, e, targetNode, dropNode){
34962         if(this.tree.hlDrop){
34963             dropNode.ui.focus();
34964             dropNode.ui.highlight();
34965         }
34966         this.tree.fireEvent("nodedrop", this.tree, targetNode, data, dd, e);
34967     },
34968     
34969     getTree : function(){
34970         return this.tree;
34971     },
34972     
34973     removeDropIndicators : function(n){
34974         if(n && n.ddel){
34975             var el = n.ddel;
34976             Roo.fly(el).removeClass([
34977                     "x-tree-drag-insert-above",
34978                     "x-tree-drag-insert-below",
34979                     "x-tree-drag-append"]);
34980             this.lastInsertClass = "_noclass";
34981         }
34982     },
34983     
34984     beforeDragDrop : function(target, e, id){
34985         this.cancelExpand();
34986         return true;
34987     },
34988     
34989     afterRepair : function(data){
34990         if(data && Roo.enableFx){
34991             data.node.ui.highlight();
34992         }
34993         this.hideProxy();
34994     } 
34995     
34996 });
34997
34998 }
34999 /*
35000  * Based on:
35001  * Ext JS Library 1.1.1
35002  * Copyright(c) 2006-2007, Ext JS, LLC.
35003  *
35004  * Originally Released Under LGPL - original licence link has changed is not relivant.
35005  *
35006  * Fork - LGPL
35007  * <script type="text/javascript">
35008  */
35009  
35010
35011 if(Roo.dd.DragZone){
35012 Roo.tree.TreeDragZone = function(tree, config){
35013     Roo.tree.TreeDragZone.superclass.constructor.call(this, tree.getTreeEl(), config);
35014     this.tree = tree;
35015 };
35016
35017 Roo.extend(Roo.tree.TreeDragZone, Roo.dd.DragZone, {
35018     ddGroup : "TreeDD",
35019    
35020     onBeforeDrag : function(data, e){
35021         var n = data.node;
35022         return n && n.draggable && !n.disabled;
35023     },
35024      
35025     
35026     onInitDrag : function(e){
35027         var data = this.dragData;
35028         this.tree.getSelectionModel().select(data.node);
35029         this.proxy.update("");
35030         data.node.ui.appendDDGhost(this.proxy.ghost.dom);
35031         this.tree.fireEvent("startdrag", this.tree, data.node, e);
35032     },
35033     
35034     getRepairXY : function(e, data){
35035         return data.node.ui.getDDRepairXY();
35036     },
35037     
35038     onEndDrag : function(data, e){
35039         this.tree.fireEvent("enddrag", this.tree, data.node, e);
35040         
35041         
35042     },
35043     
35044     onValidDrop : function(dd, e, id){
35045         this.tree.fireEvent("dragdrop", this.tree, this.dragData.node, dd, e);
35046         this.hideProxy();
35047     },
35048     
35049     beforeInvalidDrop : function(e, id){
35050         // this scrolls the original position back into view
35051         var sm = this.tree.getSelectionModel();
35052         sm.clearSelections();
35053         sm.select(this.dragData.node);
35054     }
35055 });
35056 }/*
35057  * Based on:
35058  * Ext JS Library 1.1.1
35059  * Copyright(c) 2006-2007, Ext JS, LLC.
35060  *
35061  * Originally Released Under LGPL - original licence link has changed is not relivant.
35062  *
35063  * Fork - LGPL
35064  * <script type="text/javascript">
35065  */
35066 /**
35067  * @class Roo.tree.TreeEditor
35068  * @extends Roo.Editor
35069  * Provides editor functionality for inline tree node editing.  Any valid {@link Roo.form.Field} can be used
35070  * as the editor field.
35071  * @constructor
35072  * @param {Object} config (used to be the tree panel.)
35073  * @param {Object} oldconfig DEPRECIATED Either a prebuilt {@link Roo.form.Field} instance or a Field config object
35074  * 
35075  * @cfg {Roo.tree.TreePanel} tree The tree to bind to.
35076  * @cfg {Roo.form.TextField|Object} field The field configuration
35077  *
35078  * 
35079  */
35080 Roo.tree.TreeEditor = function(config, oldconfig) { // was -- (tree, config){
35081     var tree = config;
35082     var field;
35083     if (oldconfig) { // old style..
35084         field = oldconfig.events ? oldconfig : new Roo.form.TextField(oldconfig);
35085     } else {
35086         // new style..
35087         tree = config.tree;
35088         config.field = config.field  || {};
35089         config.field.xtype = 'TextField';
35090         field = Roo.factory(config.field, Roo.form);
35091     }
35092     config = config || {};
35093     
35094     
35095     this.addEvents({
35096         /**
35097          * @event beforenodeedit
35098          * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
35099          * false from the handler of this event.
35100          * @param {Editor} this
35101          * @param {Roo.tree.Node} node 
35102          */
35103         "beforenodeedit" : true
35104     });
35105     
35106     //Roo.log(config);
35107     Roo.tree.TreeEditor.superclass.constructor.call(this, field, config);
35108
35109     this.tree = tree;
35110
35111     tree.on('beforeclick', this.beforeNodeClick, this);
35112     tree.getTreeEl().on('mousedown', this.hide, this);
35113     this.on('complete', this.updateNode, this);
35114     this.on('beforestartedit', this.fitToTree, this);
35115     this.on('startedit', this.bindScroll, this, {delay:10});
35116     this.on('specialkey', this.onSpecialKey, this);
35117 };
35118
35119 Roo.extend(Roo.tree.TreeEditor, Roo.Editor, {
35120     /**
35121      * @cfg {String} alignment
35122      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "l-l").
35123      */
35124     alignment: "l-l",
35125     // inherit
35126     autoSize: false,
35127     /**
35128      * @cfg {Boolean} hideEl
35129      * True to hide the bound element while the editor is displayed (defaults to false)
35130      */
35131     hideEl : false,
35132     /**
35133      * @cfg {String} cls
35134      * CSS class to apply to the editor (defaults to "x-small-editor x-tree-editor")
35135      */
35136     cls: "x-small-editor x-tree-editor",
35137     /**
35138      * @cfg {Boolean} shim
35139      * True to shim the editor if selects/iframes could be displayed beneath it (defaults to false)
35140      */
35141     shim:false,
35142     // inherit
35143     shadow:"frame",
35144     /**
35145      * @cfg {Number} maxWidth
35146      * The maximum width in pixels of the editor field (defaults to 250).  Note that if the maxWidth would exceed
35147      * the containing tree element's size, it will be automatically limited for you to the container width, taking
35148      * scroll and client offsets into account prior to each edit.
35149      */
35150     maxWidth: 250,
35151
35152     editDelay : 350,
35153
35154     // private
35155     fitToTree : function(ed, el){
35156         var td = this.tree.getTreeEl().dom, nd = el.dom;
35157         if(td.scrollLeft >  nd.offsetLeft){ // ensure the node left point is visible
35158             td.scrollLeft = nd.offsetLeft;
35159         }
35160         var w = Math.min(
35161                 this.maxWidth,
35162                 (td.clientWidth > 20 ? td.clientWidth : td.offsetWidth) - Math.max(0, nd.offsetLeft-td.scrollLeft) - /*cushion*/5);
35163         this.setSize(w, '');
35164         
35165         return this.fireEvent('beforenodeedit', this, this.editNode);
35166         
35167     },
35168
35169     // private
35170     triggerEdit : function(node){
35171         this.completeEdit();
35172         this.editNode = node;
35173         this.startEdit(node.ui.textNode, node.text);
35174     },
35175
35176     // private
35177     bindScroll : function(){
35178         this.tree.getTreeEl().on('scroll', this.cancelEdit, this);
35179     },
35180
35181     // private
35182     beforeNodeClick : function(node, e){
35183         var sinceLast = (this.lastClick ? this.lastClick.getElapsed() : 0);
35184         this.lastClick = new Date();
35185         if(sinceLast > this.editDelay && this.tree.getSelectionModel().isSelected(node)){
35186             e.stopEvent();
35187             this.triggerEdit(node);
35188             return false;
35189         }
35190         return true;
35191     },
35192
35193     // private
35194     updateNode : function(ed, value){
35195         this.tree.getTreeEl().un('scroll', this.cancelEdit, this);
35196         this.editNode.setText(value);
35197     },
35198
35199     // private
35200     onHide : function(){
35201         Roo.tree.TreeEditor.superclass.onHide.call(this);
35202         if(this.editNode){
35203             this.editNode.ui.focus();
35204         }
35205     },
35206
35207     // private
35208     onSpecialKey : function(field, e){
35209         var k = e.getKey();
35210         if(k == e.ESC){
35211             e.stopEvent();
35212             this.cancelEdit();
35213         }else if(k == e.ENTER && !e.hasModifier()){
35214             e.stopEvent();
35215             this.completeEdit();
35216         }
35217     }
35218 });//<Script type="text/javascript">
35219 /*
35220  * Based on:
35221  * Ext JS Library 1.1.1
35222  * Copyright(c) 2006-2007, Ext JS, LLC.
35223  *
35224  * Originally Released Under LGPL - original licence link has changed is not relivant.
35225  *
35226  * Fork - LGPL
35227  * <script type="text/javascript">
35228  */
35229  
35230 /**
35231  * Not documented??? - probably should be...
35232  */
35233
35234 Roo.tree.ColumnNodeUI = Roo.extend(Roo.tree.TreeNodeUI, {
35235     //focus: Roo.emptyFn, // prevent odd scrolling behavior
35236     
35237     renderElements : function(n, a, targetNode, bulkRender){
35238         //consel.log("renderElements?");
35239         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
35240
35241         var t = n.getOwnerTree();
35242         var tid = Pman.Tab.Document_TypesTree.tree.el.id;
35243         
35244         var cols = t.columns;
35245         var bw = t.borderWidth;
35246         var c = cols[0];
35247         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
35248          var cb = typeof a.checked == "boolean";
35249         var tx = String.format('{0}',n.text || (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
35250         var colcls = 'x-t-' + tid + '-c0';
35251         var buf = [
35252             '<li class="x-tree-node">',
35253             
35254                 
35255                 '<div class="x-tree-node-el ', a.cls,'">',
35256                     // extran...
35257                     '<div class="x-tree-col ', colcls, '" style="width:', c.width-bw, 'px;">',
35258                 
35259                 
35260                         '<span class="x-tree-node-indent">',this.indentMarkup,'</span>',
35261                         '<img src="', this.emptyIcon, '" class="x-tree-ec-icon  " />',
35262                         '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',
35263                            (a.icon ? ' x-tree-node-inline-icon' : ''),
35264                            (a.iconCls ? ' '+a.iconCls : ''),
35265                            '" unselectable="on" />',
35266                         (cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + 
35267                              (a.checked ? 'checked="checked" />' : ' />')) : ''),
35268                              
35269                         '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
35270                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>',
35271                             '<span unselectable="on" qtip="' + tx + '">',
35272                              tx,
35273                              '</span></a>' ,
35274                     '</div>',
35275                      '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
35276                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>'
35277                  ];
35278         for(var i = 1, len = cols.length; i < len; i++){
35279             c = cols[i];
35280             colcls = 'x-t-' + tid + '-c' +i;
35281             tx = String.format('{0}', (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
35282             buf.push('<div class="x-tree-col ', colcls, ' ' ,(c.cls?c.cls:''),'" style="width:',c.width-bw,'px;">',
35283                         '<div class="x-tree-col-text" qtip="' + tx +'">',tx,"</div>",
35284                       "</div>");
35285          }
35286          
35287          buf.push(
35288             '</a>',
35289             '<div class="x-clear"></div></div>',
35290             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
35291             "</li>");
35292         
35293         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
35294             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
35295                                 n.nextSibling.ui.getEl(), buf.join(""));
35296         }else{
35297             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
35298         }
35299         var el = this.wrap.firstChild;
35300         this.elRow = el;
35301         this.elNode = el.firstChild;
35302         this.ranchor = el.childNodes[1];
35303         this.ctNode = this.wrap.childNodes[1];
35304         var cs = el.firstChild.childNodes;
35305         this.indentNode = cs[0];
35306         this.ecNode = cs[1];
35307         this.iconNode = cs[2];
35308         var index = 3;
35309         if(cb){
35310             this.checkbox = cs[3];
35311             index++;
35312         }
35313         this.anchor = cs[index];
35314         
35315         this.textNode = cs[index].firstChild;
35316         
35317         //el.on("click", this.onClick, this);
35318         //el.on("dblclick", this.onDblClick, this);
35319         
35320         
35321        // console.log(this);
35322     },
35323     initEvents : function(){
35324         Roo.tree.ColumnNodeUI.superclass.initEvents.call(this);
35325         
35326             
35327         var a = this.ranchor;
35328
35329         var el = Roo.get(a);
35330
35331         if(Roo.isOpera){ // opera render bug ignores the CSS
35332             el.setStyle("text-decoration", "none");
35333         }
35334
35335         el.on("click", this.onClick, this);
35336         el.on("dblclick", this.onDblClick, this);
35337         el.on("contextmenu", this.onContextMenu, this);
35338         
35339     },
35340     
35341     /*onSelectedChange : function(state){
35342         if(state){
35343             this.focus();
35344             this.addClass("x-tree-selected");
35345         }else{
35346             //this.blur();
35347             this.removeClass("x-tree-selected");
35348         }
35349     },*/
35350     addClass : function(cls){
35351         if(this.elRow){
35352             Roo.fly(this.elRow).addClass(cls);
35353         }
35354         
35355     },
35356     
35357     
35358     removeClass : function(cls){
35359         if(this.elRow){
35360             Roo.fly(this.elRow).removeClass(cls);
35361         }
35362     }
35363
35364     
35365     
35366 });//<Script type="text/javascript">
35367
35368 /*
35369  * Based on:
35370  * Ext JS Library 1.1.1
35371  * Copyright(c) 2006-2007, Ext JS, LLC.
35372  *
35373  * Originally Released Under LGPL - original licence link has changed is not relivant.
35374  *
35375  * Fork - LGPL
35376  * <script type="text/javascript">
35377  */
35378  
35379
35380 /**
35381  * @class Roo.tree.ColumnTree
35382  * @extends Roo.data.TreePanel
35383  * @cfg {Object} columns  Including width, header, renderer, cls, dataIndex 
35384  * @cfg {int} borderWidth  compined right/left border allowance
35385  * @constructor
35386  * @param {String/HTMLElement/Element} el The container element
35387  * @param {Object} config
35388  */
35389 Roo.tree.ColumnTree =  function(el, config)
35390 {
35391    Roo.tree.ColumnTree.superclass.constructor.call(this, el , config);
35392    this.addEvents({
35393         /**
35394         * @event resize
35395         * Fire this event on a container when it resizes
35396         * @param {int} w Width
35397         * @param {int} h Height
35398         */
35399        "resize" : true
35400     });
35401     this.on('resize', this.onResize, this);
35402 };
35403
35404 Roo.extend(Roo.tree.ColumnTree, Roo.tree.TreePanel, {
35405     //lines:false,
35406     
35407     
35408     borderWidth: Roo.isBorderBox ? 0 : 2, 
35409     headEls : false,
35410     
35411     render : function(){
35412         // add the header.....
35413        
35414         Roo.tree.ColumnTree.superclass.render.apply(this);
35415         
35416         this.el.addClass('x-column-tree');
35417         
35418         this.headers = this.el.createChild(
35419             {cls:'x-tree-headers'},this.innerCt.dom);
35420    
35421         var cols = this.columns, c;
35422         var totalWidth = 0;
35423         this.headEls = [];
35424         var  len = cols.length;
35425         for(var i = 0; i < len; i++){
35426              c = cols[i];
35427              totalWidth += c.width;
35428             this.headEls.push(this.headers.createChild({
35429                  cls:'x-tree-hd ' + (c.cls?c.cls+'-hd':''),
35430                  cn: {
35431                      cls:'x-tree-hd-text',
35432                      html: c.header
35433                  },
35434                  style:'width:'+(c.width-this.borderWidth)+'px;'
35435              }));
35436         }
35437         this.headers.createChild({cls:'x-clear'});
35438         // prevent floats from wrapping when clipped
35439         this.headers.setWidth(totalWidth);
35440         //this.innerCt.setWidth(totalWidth);
35441         this.innerCt.setStyle({ overflow: 'auto' });
35442         this.onResize(this.width, this.height);
35443              
35444         
35445     },
35446     onResize : function(w,h)
35447     {
35448         this.height = h;
35449         this.width = w;
35450         // resize cols..
35451         this.innerCt.setWidth(this.width);
35452         this.innerCt.setHeight(this.height-20);
35453         
35454         // headers...
35455         var cols = this.columns, c;
35456         var totalWidth = 0;
35457         var expEl = false;
35458         var len = cols.length;
35459         for(var i = 0; i < len; i++){
35460             c = cols[i];
35461             if (this.autoExpandColumn !== false && c.dataIndex == this.autoExpandColumn) {
35462                 // it's the expander..
35463                 expEl  = this.headEls[i];
35464                 continue;
35465             }
35466             totalWidth += c.width;
35467             
35468         }
35469         if (expEl) {
35470             expEl.setWidth(  ((w - totalWidth)-this.borderWidth - 20));
35471         }
35472         this.headers.setWidth(w-20);
35473
35474         
35475         
35476         
35477     }
35478 });
35479 /*
35480  * Based on:
35481  * Ext JS Library 1.1.1
35482  * Copyright(c) 2006-2007, Ext JS, LLC.
35483  *
35484  * Originally Released Under LGPL - original licence link has changed is not relivant.
35485  *
35486  * Fork - LGPL
35487  * <script type="text/javascript">
35488  */
35489  
35490 /**
35491  * @class Roo.menu.Menu
35492  * @extends Roo.util.Observable
35493  * A menu object.  This is the container to which you add all other menu items.  Menu can also serve a as a base class
35494  * when you want a specialzed menu based off of another component (like {@link Roo.menu.DateMenu} for example).
35495  * @constructor
35496  * Creates a new Menu
35497  * @param {Object} config Configuration options
35498  */
35499 Roo.menu.Menu = function(config){
35500     Roo.apply(this, config);
35501     this.id = this.id || Roo.id();
35502     this.addEvents({
35503         /**
35504          * @event beforeshow
35505          * Fires before this menu is displayed
35506          * @param {Roo.menu.Menu} this
35507          */
35508         beforeshow : true,
35509         /**
35510          * @event beforehide
35511          * Fires before this menu is hidden
35512          * @param {Roo.menu.Menu} this
35513          */
35514         beforehide : true,
35515         /**
35516          * @event show
35517          * Fires after this menu is displayed
35518          * @param {Roo.menu.Menu} this
35519          */
35520         show : true,
35521         /**
35522          * @event hide
35523          * Fires after this menu is hidden
35524          * @param {Roo.menu.Menu} this
35525          */
35526         hide : true,
35527         /**
35528          * @event click
35529          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
35530          * @param {Roo.menu.Menu} this
35531          * @param {Roo.menu.Item} menuItem The menu item that was clicked
35532          * @param {Roo.EventObject} e
35533          */
35534         click : true,
35535         /**
35536          * @event mouseover
35537          * Fires when the mouse is hovering over this menu
35538          * @param {Roo.menu.Menu} this
35539          * @param {Roo.EventObject} e
35540          * @param {Roo.menu.Item} menuItem The menu item that was clicked
35541          */
35542         mouseover : true,
35543         /**
35544          * @event mouseout
35545          * Fires when the mouse exits this menu
35546          * @param {Roo.menu.Menu} this
35547          * @param {Roo.EventObject} e
35548          * @param {Roo.menu.Item} menuItem The menu item that was clicked
35549          */
35550         mouseout : true,
35551         /**
35552          * @event itemclick
35553          * Fires when a menu item contained in this menu is clicked
35554          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
35555          * @param {Roo.EventObject} e
35556          */
35557         itemclick: true
35558     });
35559     if (this.registerMenu) {
35560         Roo.menu.MenuMgr.register(this);
35561     }
35562     
35563     var mis = this.items;
35564     this.items = new Roo.util.MixedCollection();
35565     if(mis){
35566         this.add.apply(this, mis);
35567     }
35568 };
35569
35570 Roo.extend(Roo.menu.Menu, Roo.util.Observable, {
35571     /**
35572      * @cfg {Number} minWidth The minimum width of the menu in pixels (defaults to 120)
35573      */
35574     minWidth : 120,
35575     /**
35576      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop"
35577      * for bottom-right shadow (defaults to "sides")
35578      */
35579     shadow : "sides",
35580     /**
35581      * @cfg {String} subMenuAlign The {@link Roo.Element#alignTo} anchor position value to use for submenus of
35582      * this menu (defaults to "tl-tr?")
35583      */
35584     subMenuAlign : "tl-tr?",
35585     /**
35586      * @cfg {String} defaultAlign The default {@link Roo.Element#alignTo) anchor position value for this menu
35587      * relative to its element of origin (defaults to "tl-bl?")
35588      */
35589     defaultAlign : "tl-bl?",
35590     /**
35591      * @cfg {Boolean} allowOtherMenus True to allow multiple menus to be displayed at the same time (defaults to false)
35592      */
35593     allowOtherMenus : false,
35594     /**
35595      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
35596      */
35597     registerMenu : true,
35598
35599     hidden:true,
35600
35601     // private
35602     render : function(){
35603         if(this.el){
35604             return;
35605         }
35606         var el = this.el = new Roo.Layer({
35607             cls: "x-menu",
35608             shadow:this.shadow,
35609             constrain: false,
35610             parentEl: this.parentEl || document.body,
35611             zindex:15000
35612         });
35613
35614         this.keyNav = new Roo.menu.MenuNav(this);
35615
35616         if(this.plain){
35617             el.addClass("x-menu-plain");
35618         }
35619         if(this.cls){
35620             el.addClass(this.cls);
35621         }
35622         // generic focus element
35623         this.focusEl = el.createChild({
35624             tag: "a", cls: "x-menu-focus", href: "#", onclick: "return false;", tabIndex:"-1"
35625         });
35626         var ul = el.createChild({tag: "ul", cls: "x-menu-list"});
35627         ul.on(Roo.isTouch ? 'touchstart' : 'click'   , this.onClick, this);
35628         
35629         ul.on("mouseover", this.onMouseOver, this);
35630         ul.on("mouseout", this.onMouseOut, this);
35631         this.items.each(function(item){
35632             if (item.hidden) {
35633                 return;
35634             }
35635             
35636             var li = document.createElement("li");
35637             li.className = "x-menu-list-item";
35638             ul.dom.appendChild(li);
35639             item.render(li, this);
35640         }, this);
35641         this.ul = ul;
35642         this.autoWidth();
35643     },
35644
35645     // private
35646     autoWidth : function(){
35647         var el = this.el, ul = this.ul;
35648         if(!el){
35649             return;
35650         }
35651         var w = this.width;
35652         if(w){
35653             el.setWidth(w);
35654         }else if(Roo.isIE){
35655             el.setWidth(this.minWidth);
35656             var t = el.dom.offsetWidth; // force recalc
35657             el.setWidth(ul.getWidth()+el.getFrameWidth("lr"));
35658         }
35659     },
35660
35661     // private
35662     delayAutoWidth : function(){
35663         if(this.rendered){
35664             if(!this.awTask){
35665                 this.awTask = new Roo.util.DelayedTask(this.autoWidth, this);
35666             }
35667             this.awTask.delay(20);
35668         }
35669     },
35670
35671     // private
35672     findTargetItem : function(e){
35673         var t = e.getTarget(".x-menu-list-item", this.ul,  true);
35674         if(t && t.menuItemId){
35675             return this.items.get(t.menuItemId);
35676         }
35677     },
35678
35679     // private
35680     onClick : function(e){
35681         Roo.log("menu.onClick");
35682         var t = this.findTargetItem(e);
35683         if(!t){
35684             return;
35685         }
35686         Roo.log(e);
35687         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
35688             if(t == this.activeItem && t.shouldDeactivate(e)){
35689                 this.activeItem.deactivate();
35690                 delete this.activeItem;
35691                 return;
35692             }
35693             if(t.canActivate){
35694                 this.setActiveItem(t, true);
35695             }
35696             return;
35697             
35698             
35699         }
35700         
35701         t.onClick(e);
35702         this.fireEvent("click", this, t, e);
35703     },
35704
35705     // private
35706     setActiveItem : function(item, autoExpand){
35707         if(item != this.activeItem){
35708             if(this.activeItem){
35709                 this.activeItem.deactivate();
35710             }
35711             this.activeItem = item;
35712             item.activate(autoExpand);
35713         }else if(autoExpand){
35714             item.expandMenu();
35715         }
35716     },
35717
35718     // private
35719     tryActivate : function(start, step){
35720         var items = this.items;
35721         for(var i = start, len = items.length; i >= 0 && i < len; i+= step){
35722             var item = items.get(i);
35723             if(!item.disabled && item.canActivate){
35724                 this.setActiveItem(item, false);
35725                 return item;
35726             }
35727         }
35728         return false;
35729     },
35730
35731     // private
35732     onMouseOver : function(e){
35733         var t;
35734         if(t = this.findTargetItem(e)){
35735             if(t.canActivate && !t.disabled){
35736                 this.setActiveItem(t, true);
35737             }
35738         }
35739         this.fireEvent("mouseover", this, e, t);
35740     },
35741
35742     // private
35743     onMouseOut : function(e){
35744         var t;
35745         if(t = this.findTargetItem(e)){
35746             if(t == this.activeItem && t.shouldDeactivate(e)){
35747                 this.activeItem.deactivate();
35748                 delete this.activeItem;
35749             }
35750         }
35751         this.fireEvent("mouseout", this, e, t);
35752     },
35753
35754     /**
35755      * Read-only.  Returns true if the menu is currently displayed, else false.
35756      * @type Boolean
35757      */
35758     isVisible : function(){
35759         return this.el && !this.hidden;
35760     },
35761
35762     /**
35763      * Displays this menu relative to another element
35764      * @param {String/HTMLElement/Roo.Element} element The element to align to
35765      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
35766      * the element (defaults to this.defaultAlign)
35767      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
35768      */
35769     show : function(el, pos, parentMenu){
35770         this.parentMenu = parentMenu;
35771         if(!this.el){
35772             this.render();
35773         }
35774         this.fireEvent("beforeshow", this);
35775         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
35776     },
35777
35778     /**
35779      * Displays this menu at a specific xy position
35780      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
35781      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
35782      */
35783     showAt : function(xy, parentMenu, /* private: */_e){
35784         this.parentMenu = parentMenu;
35785         if(!this.el){
35786             this.render();
35787         }
35788         if(_e !== false){
35789             this.fireEvent("beforeshow", this);
35790             xy = this.el.adjustForConstraints(xy);
35791         }
35792         this.el.setXY(xy);
35793         this.el.show();
35794         this.hidden = false;
35795         this.focus();
35796         this.fireEvent("show", this);
35797     },
35798
35799     focus : function(){
35800         if(!this.hidden){
35801             this.doFocus.defer(50, this);
35802         }
35803     },
35804
35805     doFocus : function(){
35806         if(!this.hidden){
35807             this.focusEl.focus();
35808         }
35809     },
35810
35811     /**
35812      * Hides this menu and optionally all parent menus
35813      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
35814      */
35815     hide : function(deep){
35816         if(this.el && this.isVisible()){
35817             this.fireEvent("beforehide", this);
35818             if(this.activeItem){
35819                 this.activeItem.deactivate();
35820                 this.activeItem = null;
35821             }
35822             this.el.hide();
35823             this.hidden = true;
35824             this.fireEvent("hide", this);
35825         }
35826         if(deep === true && this.parentMenu){
35827             this.parentMenu.hide(true);
35828         }
35829     },
35830
35831     /**
35832      * Addds one or more items of any type supported by the Menu class, or that can be converted into menu items.
35833      * Any of the following are valid:
35834      * <ul>
35835      * <li>Any menu item object based on {@link Roo.menu.Item}</li>
35836      * <li>An HTMLElement object which will be converted to a menu item</li>
35837      * <li>A menu item config object that will be created as a new menu item</li>
35838      * <li>A string, which can either be '-' or 'separator' to add a menu separator, otherwise
35839      * it will be converted into a {@link Roo.menu.TextItem} and added</li>
35840      * </ul>
35841      * Usage:
35842      * <pre><code>
35843 // Create the menu
35844 var menu = new Roo.menu.Menu();
35845
35846 // Create a menu item to add by reference
35847 var menuItem = new Roo.menu.Item({ text: 'New Item!' });
35848
35849 // Add a bunch of items at once using different methods.
35850 // Only the last item added will be returned.
35851 var item = menu.add(
35852     menuItem,                // add existing item by ref
35853     'Dynamic Item',          // new TextItem
35854     '-',                     // new separator
35855     { text: 'Config Item' }  // new item by config
35856 );
35857 </code></pre>
35858      * @param {Mixed} args One or more menu items, menu item configs or other objects that can be converted to menu items
35859      * @return {Roo.menu.Item} The menu item that was added, or the last one if multiple items were added
35860      */
35861     add : function(){
35862         var a = arguments, l = a.length, item;
35863         for(var i = 0; i < l; i++){
35864             var el = a[i];
35865             if ((typeof(el) == "object") && el.xtype && el.xns) {
35866                 el = Roo.factory(el, Roo.menu);
35867             }
35868             
35869             if(el.render){ // some kind of Item
35870                 item = this.addItem(el);
35871             }else if(typeof el == "string"){ // string
35872                 if(el == "separator" || el == "-"){
35873                     item = this.addSeparator();
35874                 }else{
35875                     item = this.addText(el);
35876                 }
35877             }else if(el.tagName || el.el){ // element
35878                 item = this.addElement(el);
35879             }else if(typeof el == "object"){ // must be menu item config?
35880                 item = this.addMenuItem(el);
35881             }
35882         }
35883         return item;
35884     },
35885
35886     /**
35887      * Returns this menu's underlying {@link Roo.Element} object
35888      * @return {Roo.Element} The element
35889      */
35890     getEl : function(){
35891         if(!this.el){
35892             this.render();
35893         }
35894         return this.el;
35895     },
35896
35897     /**
35898      * Adds a separator bar to the menu
35899      * @return {Roo.menu.Item} The menu item that was added
35900      */
35901     addSeparator : function(){
35902         return this.addItem(new Roo.menu.Separator());
35903     },
35904
35905     /**
35906      * Adds an {@link Roo.Element} object to the menu
35907      * @param {String/HTMLElement/Roo.Element} el The element or DOM node to add, or its id
35908      * @return {Roo.menu.Item} The menu item that was added
35909      */
35910     addElement : function(el){
35911         return this.addItem(new Roo.menu.BaseItem(el));
35912     },
35913
35914     /**
35915      * Adds an existing object based on {@link Roo.menu.Item} to the menu
35916      * @param {Roo.menu.Item} item The menu item to add
35917      * @return {Roo.menu.Item} The menu item that was added
35918      */
35919     addItem : function(item){
35920         this.items.add(item);
35921         if(this.ul){
35922             var li = document.createElement("li");
35923             li.className = "x-menu-list-item";
35924             this.ul.dom.appendChild(li);
35925             item.render(li, this);
35926             this.delayAutoWidth();
35927         }
35928         return item;
35929     },
35930
35931     /**
35932      * Creates a new {@link Roo.menu.Item} based an the supplied config object and adds it to the menu
35933      * @param {Object} config A MenuItem config object
35934      * @return {Roo.menu.Item} The menu item that was added
35935      */
35936     addMenuItem : function(config){
35937         if(!(config instanceof Roo.menu.Item)){
35938             if(typeof config.checked == "boolean"){ // must be check menu item config?
35939                 config = new Roo.menu.CheckItem(config);
35940             }else{
35941                 config = new Roo.menu.Item(config);
35942             }
35943         }
35944         return this.addItem(config);
35945     },
35946
35947     /**
35948      * Creates a new {@link Roo.menu.TextItem} with the supplied text and adds it to the menu
35949      * @param {String} text The text to display in the menu item
35950      * @return {Roo.menu.Item} The menu item that was added
35951      */
35952     addText : function(text){
35953         return this.addItem(new Roo.menu.TextItem({ text : text }));
35954     },
35955
35956     /**
35957      * Inserts an existing object based on {@link Roo.menu.Item} to the menu at a specified index
35958      * @param {Number} index The index in the menu's list of current items where the new item should be inserted
35959      * @param {Roo.menu.Item} item The menu item to add
35960      * @return {Roo.menu.Item} The menu item that was added
35961      */
35962     insert : function(index, item){
35963         this.items.insert(index, item);
35964         if(this.ul){
35965             var li = document.createElement("li");
35966             li.className = "x-menu-list-item";
35967             this.ul.dom.insertBefore(li, this.ul.dom.childNodes[index]);
35968             item.render(li, this);
35969             this.delayAutoWidth();
35970         }
35971         return item;
35972     },
35973
35974     /**
35975      * Removes an {@link Roo.menu.Item} from the menu and destroys the object
35976      * @param {Roo.menu.Item} item The menu item to remove
35977      */
35978     remove : function(item){
35979         this.items.removeKey(item.id);
35980         item.destroy();
35981     },
35982
35983     /**
35984      * Removes and destroys all items in the menu
35985      */
35986     removeAll : function(){
35987         var f;
35988         while(f = this.items.first()){
35989             this.remove(f);
35990         }
35991     }
35992 });
35993
35994 // MenuNav is a private utility class used internally by the Menu
35995 Roo.menu.MenuNav = function(menu){
35996     Roo.menu.MenuNav.superclass.constructor.call(this, menu.el);
35997     this.scope = this.menu = menu;
35998 };
35999
36000 Roo.extend(Roo.menu.MenuNav, Roo.KeyNav, {
36001     doRelay : function(e, h){
36002         var k = e.getKey();
36003         if(!this.menu.activeItem && e.isNavKeyPress() && k != e.SPACE && k != e.RETURN){
36004             this.menu.tryActivate(0, 1);
36005             return false;
36006         }
36007         return h.call(this.scope || this, e, this.menu);
36008     },
36009
36010     up : function(e, m){
36011         if(!m.tryActivate(m.items.indexOf(m.activeItem)-1, -1)){
36012             m.tryActivate(m.items.length-1, -1);
36013         }
36014     },
36015
36016     down : function(e, m){
36017         if(!m.tryActivate(m.items.indexOf(m.activeItem)+1, 1)){
36018             m.tryActivate(0, 1);
36019         }
36020     },
36021
36022     right : function(e, m){
36023         if(m.activeItem){
36024             m.activeItem.expandMenu(true);
36025         }
36026     },
36027
36028     left : function(e, m){
36029         m.hide();
36030         if(m.parentMenu && m.parentMenu.activeItem){
36031             m.parentMenu.activeItem.activate();
36032         }
36033     },
36034
36035     enter : function(e, m){
36036         if(m.activeItem){
36037             e.stopPropagation();
36038             m.activeItem.onClick(e);
36039             m.fireEvent("click", this, m.activeItem);
36040             return true;
36041         }
36042     }
36043 });/*
36044  * Based on:
36045  * Ext JS Library 1.1.1
36046  * Copyright(c) 2006-2007, Ext JS, LLC.
36047  *
36048  * Originally Released Under LGPL - original licence link has changed is not relivant.
36049  *
36050  * Fork - LGPL
36051  * <script type="text/javascript">
36052  */
36053  
36054 /**
36055  * @class Roo.menu.MenuMgr
36056  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
36057  * @singleton
36058  */
36059 Roo.menu.MenuMgr = function(){
36060    var menus, active, groups = {}, attached = false, lastShow = new Date();
36061
36062    // private - called when first menu is created
36063    function init(){
36064        menus = {};
36065        active = new Roo.util.MixedCollection();
36066        Roo.get(document).addKeyListener(27, function(){
36067            if(active.length > 0){
36068                hideAll();
36069            }
36070        });
36071    }
36072
36073    // private
36074    function hideAll(){
36075        if(active && active.length > 0){
36076            var c = active.clone();
36077            c.each(function(m){
36078                m.hide();
36079            });
36080        }
36081    }
36082
36083    // private
36084    function onHide(m){
36085        active.remove(m);
36086        if(active.length < 1){
36087            Roo.get(document).un("mousedown", onMouseDown);
36088            attached = false;
36089        }
36090    }
36091
36092    // private
36093    function onShow(m){
36094        var last = active.last();
36095        lastShow = new Date();
36096        active.add(m);
36097        if(!attached){
36098            Roo.get(document).on("mousedown", onMouseDown);
36099            attached = true;
36100        }
36101        if(m.parentMenu){
36102           m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
36103           m.parentMenu.activeChild = m;
36104        }else if(last && last.isVisible()){
36105           m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
36106        }
36107    }
36108
36109    // private
36110    function onBeforeHide(m){
36111        if(m.activeChild){
36112            m.activeChild.hide();
36113        }
36114        if(m.autoHideTimer){
36115            clearTimeout(m.autoHideTimer);
36116            delete m.autoHideTimer;
36117        }
36118    }
36119
36120    // private
36121    function onBeforeShow(m){
36122        var pm = m.parentMenu;
36123        if(!pm && !m.allowOtherMenus){
36124            hideAll();
36125        }else if(pm && pm.activeChild && active != m){
36126            pm.activeChild.hide();
36127        }
36128    }
36129
36130    // private
36131    function onMouseDown(e){
36132        if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".x-menu")){
36133            hideAll();
36134        }
36135    }
36136
36137    // private
36138    function onBeforeCheck(mi, state){
36139        if(state){
36140            var g = groups[mi.group];
36141            for(var i = 0, l = g.length; i < l; i++){
36142                if(g[i] != mi){
36143                    g[i].setChecked(false);
36144                }
36145            }
36146        }
36147    }
36148
36149    return {
36150
36151        /**
36152         * Hides all menus that are currently visible
36153         */
36154        hideAll : function(){
36155             hideAll();  
36156        },
36157
36158        // private
36159        register : function(menu){
36160            if(!menus){
36161                init();
36162            }
36163            menus[menu.id] = menu;
36164            menu.on("beforehide", onBeforeHide);
36165            menu.on("hide", onHide);
36166            menu.on("beforeshow", onBeforeShow);
36167            menu.on("show", onShow);
36168            var g = menu.group;
36169            if(g && menu.events["checkchange"]){
36170                if(!groups[g]){
36171                    groups[g] = [];
36172                }
36173                groups[g].push(menu);
36174                menu.on("checkchange", onCheck);
36175            }
36176        },
36177
36178         /**
36179          * Returns a {@link Roo.menu.Menu} object
36180          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
36181          * be used to generate and return a new Menu instance.
36182          */
36183        get : function(menu){
36184            if(typeof menu == "string"){ // menu id
36185                return menus[menu];
36186            }else if(menu.events){  // menu instance
36187                return menu;
36188            }else if(typeof menu.length == 'number'){ // array of menu items?
36189                return new Roo.menu.Menu({items:menu});
36190            }else{ // otherwise, must be a config
36191                return new Roo.menu.Menu(menu);
36192            }
36193        },
36194
36195        // private
36196        unregister : function(menu){
36197            delete menus[menu.id];
36198            menu.un("beforehide", onBeforeHide);
36199            menu.un("hide", onHide);
36200            menu.un("beforeshow", onBeforeShow);
36201            menu.un("show", onShow);
36202            var g = menu.group;
36203            if(g && menu.events["checkchange"]){
36204                groups[g].remove(menu);
36205                menu.un("checkchange", onCheck);
36206            }
36207        },
36208
36209        // private
36210        registerCheckable : function(menuItem){
36211            var g = menuItem.group;
36212            if(g){
36213                if(!groups[g]){
36214                    groups[g] = [];
36215                }
36216                groups[g].push(menuItem);
36217                menuItem.on("beforecheckchange", onBeforeCheck);
36218            }
36219        },
36220
36221        // private
36222        unregisterCheckable : function(menuItem){
36223            var g = menuItem.group;
36224            if(g){
36225                groups[g].remove(menuItem);
36226                menuItem.un("beforecheckchange", onBeforeCheck);
36227            }
36228        }
36229    };
36230 }();/*
36231  * Based on:
36232  * Ext JS Library 1.1.1
36233  * Copyright(c) 2006-2007, Ext JS, LLC.
36234  *
36235  * Originally Released Under LGPL - original licence link has changed is not relivant.
36236  *
36237  * Fork - LGPL
36238  * <script type="text/javascript">
36239  */
36240  
36241
36242 /**
36243  * @class Roo.menu.BaseItem
36244  * @extends Roo.Component
36245  * The base class for all items that render into menus.  BaseItem provides default rendering, activated state
36246  * management and base configuration options shared by all menu components.
36247  * @constructor
36248  * Creates a new BaseItem
36249  * @param {Object} config Configuration options
36250  */
36251 Roo.menu.BaseItem = function(config){
36252     Roo.menu.BaseItem.superclass.constructor.call(this, config);
36253
36254     this.addEvents({
36255         /**
36256          * @event click
36257          * Fires when this item is clicked
36258          * @param {Roo.menu.BaseItem} this
36259          * @param {Roo.EventObject} e
36260          */
36261         click: true,
36262         /**
36263          * @event activate
36264          * Fires when this item is activated
36265          * @param {Roo.menu.BaseItem} this
36266          */
36267         activate : true,
36268         /**
36269          * @event deactivate
36270          * Fires when this item is deactivated
36271          * @param {Roo.menu.BaseItem} this
36272          */
36273         deactivate : true
36274     });
36275
36276     if(this.handler){
36277         this.on("click", this.handler, this.scope, true);
36278     }
36279 };
36280
36281 Roo.extend(Roo.menu.BaseItem, Roo.Component, {
36282     /**
36283      * @cfg {Function} handler
36284      * A function that will handle the click event of this menu item (defaults to undefined)
36285      */
36286     /**
36287      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to false)
36288      */
36289     canActivate : false,
36290     
36291      /**
36292      * @cfg {Boolean} hidden True to prevent creation of this menu item (defaults to false)
36293      */
36294     hidden: false,
36295     
36296     /**
36297      * @cfg {String} activeClass The CSS class to use when the item becomes activated (defaults to "x-menu-item-active")
36298      */
36299     activeClass : "x-menu-item-active",
36300     /**
36301      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to true)
36302      */
36303     hideOnClick : true,
36304     /**
36305      * @cfg {Number} hideDelay Length of time in milliseconds to wait before hiding after a click (defaults to 100)
36306      */
36307     hideDelay : 100,
36308
36309     // private
36310     ctype: "Roo.menu.BaseItem",
36311
36312     // private
36313     actionMode : "container",
36314
36315     // private
36316     render : function(container, parentMenu){
36317         this.parentMenu = parentMenu;
36318         Roo.menu.BaseItem.superclass.render.call(this, container);
36319         this.container.menuItemId = this.id;
36320     },
36321
36322     // private
36323     onRender : function(container, position){
36324         this.el = Roo.get(this.el);
36325         container.dom.appendChild(this.el.dom);
36326     },
36327
36328     // private
36329     onClick : function(e){
36330         if(!this.disabled && this.fireEvent("click", this, e) !== false
36331                 && this.parentMenu.fireEvent("itemclick", this, e) !== false){
36332             this.handleClick(e);
36333         }else{
36334             e.stopEvent();
36335         }
36336     },
36337
36338     // private
36339     activate : function(){
36340         if(this.disabled){
36341             return false;
36342         }
36343         var li = this.container;
36344         li.addClass(this.activeClass);
36345         this.region = li.getRegion().adjust(2, 2, -2, -2);
36346         this.fireEvent("activate", this);
36347         return true;
36348     },
36349
36350     // private
36351     deactivate : function(){
36352         this.container.removeClass(this.activeClass);
36353         this.fireEvent("deactivate", this);
36354     },
36355
36356     // private
36357     shouldDeactivate : function(e){
36358         return !this.region || !this.region.contains(e.getPoint());
36359     },
36360
36361     // private
36362     handleClick : function(e){
36363         if(this.hideOnClick){
36364             this.parentMenu.hide.defer(this.hideDelay, this.parentMenu, [true]);
36365         }
36366     },
36367
36368     // private
36369     expandMenu : function(autoActivate){
36370         // do nothing
36371     },
36372
36373     // private
36374     hideMenu : function(){
36375         // do nothing
36376     }
36377 });/*
36378  * Based on:
36379  * Ext JS Library 1.1.1
36380  * Copyright(c) 2006-2007, Ext JS, LLC.
36381  *
36382  * Originally Released Under LGPL - original licence link has changed is not relivant.
36383  *
36384  * Fork - LGPL
36385  * <script type="text/javascript">
36386  */
36387  
36388 /**
36389  * @class Roo.menu.Adapter
36390  * @extends Roo.menu.BaseItem
36391  * A base utility class that adapts a non-menu component so that it can be wrapped by a menu item and added to a menu.
36392  * It provides basic rendering, activation management and enable/disable logic required to work in menus.
36393  * @constructor
36394  * Creates a new Adapter
36395  * @param {Object} config Configuration options
36396  */
36397 Roo.menu.Adapter = function(component, config){
36398     Roo.menu.Adapter.superclass.constructor.call(this, config);
36399     this.component = component;
36400 };
36401 Roo.extend(Roo.menu.Adapter, Roo.menu.BaseItem, {
36402     // private
36403     canActivate : true,
36404
36405     // private
36406     onRender : function(container, position){
36407         this.component.render(container);
36408         this.el = this.component.getEl();
36409     },
36410
36411     // private
36412     activate : function(){
36413         if(this.disabled){
36414             return false;
36415         }
36416         this.component.focus();
36417         this.fireEvent("activate", this);
36418         return true;
36419     },
36420
36421     // private
36422     deactivate : function(){
36423         this.fireEvent("deactivate", this);
36424     },
36425
36426     // private
36427     disable : function(){
36428         this.component.disable();
36429         Roo.menu.Adapter.superclass.disable.call(this);
36430     },
36431
36432     // private
36433     enable : function(){
36434         this.component.enable();
36435         Roo.menu.Adapter.superclass.enable.call(this);
36436     }
36437 });/*
36438  * Based on:
36439  * Ext JS Library 1.1.1
36440  * Copyright(c) 2006-2007, Ext JS, LLC.
36441  *
36442  * Originally Released Under LGPL - original licence link has changed is not relivant.
36443  *
36444  * Fork - LGPL
36445  * <script type="text/javascript">
36446  */
36447
36448 /**
36449  * @class Roo.menu.TextItem
36450  * @extends Roo.menu.BaseItem
36451  * Adds a static text string to a menu, usually used as either a heading or group separator.
36452  * Note: old style constructor with text is still supported.
36453  * 
36454  * @constructor
36455  * Creates a new TextItem
36456  * @param {Object} cfg Configuration
36457  */
36458 Roo.menu.TextItem = function(cfg){
36459     if (typeof(cfg) == 'string') {
36460         this.text = cfg;
36461     } else {
36462         Roo.apply(this,cfg);
36463     }
36464     
36465     Roo.menu.TextItem.superclass.constructor.call(this);
36466 };
36467
36468 Roo.extend(Roo.menu.TextItem, Roo.menu.BaseItem, {
36469     /**
36470      * @cfg {Boolean} text Text to show on item.
36471      */
36472     text : '',
36473     
36474     /**
36475      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
36476      */
36477     hideOnClick : false,
36478     /**
36479      * @cfg {String} itemCls The default CSS class to use for text items (defaults to "x-menu-text")
36480      */
36481     itemCls : "x-menu-text",
36482
36483     // private
36484     onRender : function(){
36485         var s = document.createElement("span");
36486         s.className = this.itemCls;
36487         s.innerHTML = this.text;
36488         this.el = s;
36489         Roo.menu.TextItem.superclass.onRender.apply(this, arguments);
36490     }
36491 });/*
36492  * Based on:
36493  * Ext JS Library 1.1.1
36494  * Copyright(c) 2006-2007, Ext JS, LLC.
36495  *
36496  * Originally Released Under LGPL - original licence link has changed is not relivant.
36497  *
36498  * Fork - LGPL
36499  * <script type="text/javascript">
36500  */
36501
36502 /**
36503  * @class Roo.menu.Separator
36504  * @extends Roo.menu.BaseItem
36505  * Adds a separator bar to a menu, used to divide logical groups of menu items. Generally you will
36506  * add one of these by using "-" in you call to add() or in your items config rather than creating one directly.
36507  * @constructor
36508  * @param {Object} config Configuration options
36509  */
36510 Roo.menu.Separator = function(config){
36511     Roo.menu.Separator.superclass.constructor.call(this, config);
36512 };
36513
36514 Roo.extend(Roo.menu.Separator, Roo.menu.BaseItem, {
36515     /**
36516      * @cfg {String} itemCls The default CSS class to use for separators (defaults to "x-menu-sep")
36517      */
36518     itemCls : "x-menu-sep",
36519     /**
36520      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
36521      */
36522     hideOnClick : false,
36523
36524     // private
36525     onRender : function(li){
36526         var s = document.createElement("span");
36527         s.className = this.itemCls;
36528         s.innerHTML = "&#160;";
36529         this.el = s;
36530         li.addClass("x-menu-sep-li");
36531         Roo.menu.Separator.superclass.onRender.apply(this, arguments);
36532     }
36533 });/*
36534  * Based on:
36535  * Ext JS Library 1.1.1
36536  * Copyright(c) 2006-2007, Ext JS, LLC.
36537  *
36538  * Originally Released Under LGPL - original licence link has changed is not relivant.
36539  *
36540  * Fork - LGPL
36541  * <script type="text/javascript">
36542  */
36543 /**
36544  * @class Roo.menu.Item
36545  * @extends Roo.menu.BaseItem
36546  * A base class for all menu items that require menu-related functionality (like sub-menus) and are not static
36547  * display items.  Item extends the base functionality of {@link Roo.menu.BaseItem} by adding menu-specific
36548  * activation and click handling.
36549  * @constructor
36550  * Creates a new Item
36551  * @param {Object} config Configuration options
36552  */
36553 Roo.menu.Item = function(config){
36554     Roo.menu.Item.superclass.constructor.call(this, config);
36555     if(this.menu){
36556         this.menu = Roo.menu.MenuMgr.get(this.menu);
36557     }
36558 };
36559 Roo.extend(Roo.menu.Item, Roo.menu.BaseItem, {
36560     
36561     /**
36562      * @cfg {String} text
36563      * The text to show on the menu item.
36564      */
36565     text: '',
36566      /**
36567      * @cfg {String} HTML to render in menu
36568      * The text to show on the menu item (HTML version).
36569      */
36570     html: '',
36571     /**
36572      * @cfg {String} icon
36573      * The path to an icon to display in this menu item (defaults to Roo.BLANK_IMAGE_URL)
36574      */
36575     icon: undefined,
36576     /**
36577      * @cfg {String} itemCls The default CSS class to use for menu items (defaults to "x-menu-item")
36578      */
36579     itemCls : "x-menu-item",
36580     /**
36581      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to true)
36582      */
36583     canActivate : true,
36584     /**
36585      * @cfg {Number} showDelay Length of time in milliseconds to wait before showing this item (defaults to 200)
36586      */
36587     showDelay: 200,
36588     // doc'd in BaseItem
36589     hideDelay: 200,
36590
36591     // private
36592     ctype: "Roo.menu.Item",
36593     
36594     // private
36595     onRender : function(container, position){
36596         var el = document.createElement("a");
36597         el.hideFocus = true;
36598         el.unselectable = "on";
36599         el.href = this.href || "#";
36600         if(this.hrefTarget){
36601             el.target = this.hrefTarget;
36602         }
36603         el.className = this.itemCls + (this.menu ?  " x-menu-item-arrow" : "") + (this.cls ?  " " + this.cls : "");
36604         
36605         var html = this.html.length ? this.html  : String.format('{0}',this.text);
36606         
36607         el.innerHTML = String.format(
36608                 '<img src="{0}" class="x-menu-item-icon {1}" />' + html,
36609                 this.icon || Roo.BLANK_IMAGE_URL, this.iconCls || '');
36610         this.el = el;
36611         Roo.menu.Item.superclass.onRender.call(this, container, position);
36612     },
36613
36614     /**
36615      * Sets the text to display in this menu item
36616      * @param {String} text The text to display
36617      * @param {Boolean} isHTML true to indicate text is pure html.
36618      */
36619     setText : function(text, isHTML){
36620         if (isHTML) {
36621             this.html = text;
36622         } else {
36623             this.text = text;
36624             this.html = '';
36625         }
36626         if(this.rendered){
36627             var html = this.html.length ? this.html  : String.format('{0}',this.text);
36628      
36629             this.el.update(String.format(
36630                 '<img src="{0}" class="x-menu-item-icon {2}">' + html,
36631                 this.icon || Roo.BLANK_IMAGE_URL, this.text, this.iconCls || ''));
36632             this.parentMenu.autoWidth();
36633         }
36634     },
36635
36636     // private
36637     handleClick : function(e){
36638         if(!this.href){ // if no link defined, stop the event automatically
36639             e.stopEvent();
36640         }
36641         Roo.menu.Item.superclass.handleClick.apply(this, arguments);
36642     },
36643
36644     // private
36645     activate : function(autoExpand){
36646         if(Roo.menu.Item.superclass.activate.apply(this, arguments)){
36647             this.focus();
36648             if(autoExpand){
36649                 this.expandMenu();
36650             }
36651         }
36652         return true;
36653     },
36654
36655     // private
36656     shouldDeactivate : function(e){
36657         if(Roo.menu.Item.superclass.shouldDeactivate.call(this, e)){
36658             if(this.menu && this.menu.isVisible()){
36659                 return !this.menu.getEl().getRegion().contains(e.getPoint());
36660             }
36661             return true;
36662         }
36663         return false;
36664     },
36665
36666     // private
36667     deactivate : function(){
36668         Roo.menu.Item.superclass.deactivate.apply(this, arguments);
36669         this.hideMenu();
36670     },
36671
36672     // private
36673     expandMenu : function(autoActivate){
36674         if(!this.disabled && this.menu){
36675             clearTimeout(this.hideTimer);
36676             delete this.hideTimer;
36677             if(!this.menu.isVisible() && !this.showTimer){
36678                 this.showTimer = this.deferExpand.defer(this.showDelay, this, [autoActivate]);
36679             }else if (this.menu.isVisible() && autoActivate){
36680                 this.menu.tryActivate(0, 1);
36681             }
36682         }
36683     },
36684
36685     // private
36686     deferExpand : function(autoActivate){
36687         delete this.showTimer;
36688         this.menu.show(this.container, this.parentMenu.subMenuAlign || "tl-tr?", this.parentMenu);
36689         if(autoActivate){
36690             this.menu.tryActivate(0, 1);
36691         }
36692     },
36693
36694     // private
36695     hideMenu : function(){
36696         clearTimeout(this.showTimer);
36697         delete this.showTimer;
36698         if(!this.hideTimer && this.menu && this.menu.isVisible()){
36699             this.hideTimer = this.deferHide.defer(this.hideDelay, this);
36700         }
36701     },
36702
36703     // private
36704     deferHide : function(){
36705         delete this.hideTimer;
36706         this.menu.hide();
36707     }
36708 });/*
36709  * Based on:
36710  * Ext JS Library 1.1.1
36711  * Copyright(c) 2006-2007, Ext JS, LLC.
36712  *
36713  * Originally Released Under LGPL - original licence link has changed is not relivant.
36714  *
36715  * Fork - LGPL
36716  * <script type="text/javascript">
36717  */
36718  
36719 /**
36720  * @class Roo.menu.CheckItem
36721  * @extends Roo.menu.Item
36722  * Adds a menu item that contains a checkbox by default, but can also be part of a radio group.
36723  * @constructor
36724  * Creates a new CheckItem
36725  * @param {Object} config Configuration options
36726  */
36727 Roo.menu.CheckItem = function(config){
36728     Roo.menu.CheckItem.superclass.constructor.call(this, config);
36729     this.addEvents({
36730         /**
36731          * @event beforecheckchange
36732          * Fires before the checked value is set, providing an opportunity to cancel if needed
36733          * @param {Roo.menu.CheckItem} this
36734          * @param {Boolean} checked The new checked value that will be set
36735          */
36736         "beforecheckchange" : true,
36737         /**
36738          * @event checkchange
36739          * Fires after the checked value has been set
36740          * @param {Roo.menu.CheckItem} this
36741          * @param {Boolean} checked The checked value that was set
36742          */
36743         "checkchange" : true
36744     });
36745     if(this.checkHandler){
36746         this.on('checkchange', this.checkHandler, this.scope);
36747     }
36748 };
36749 Roo.extend(Roo.menu.CheckItem, Roo.menu.Item, {
36750     /**
36751      * @cfg {String} group
36752      * All check items with the same group name will automatically be grouped into a single-select
36753      * radio button group (defaults to '')
36754      */
36755     /**
36756      * @cfg {String} itemCls The default CSS class to use for check items (defaults to "x-menu-item x-menu-check-item")
36757      */
36758     itemCls : "x-menu-item x-menu-check-item",
36759     /**
36760      * @cfg {String} groupClass The default CSS class to use for radio group check items (defaults to "x-menu-group-item")
36761      */
36762     groupClass : "x-menu-group-item",
36763
36764     /**
36765      * @cfg {Boolean} checked True to initialize this checkbox as checked (defaults to false).  Note that
36766      * if this checkbox is part of a radio group (group = true) only the last item in the group that is
36767      * initialized with checked = true will be rendered as checked.
36768      */
36769     checked: false,
36770
36771     // private
36772     ctype: "Roo.menu.CheckItem",
36773
36774     // private
36775     onRender : function(c){
36776         Roo.menu.CheckItem.superclass.onRender.apply(this, arguments);
36777         if(this.group){
36778             this.el.addClass(this.groupClass);
36779         }
36780         Roo.menu.MenuMgr.registerCheckable(this);
36781         if(this.checked){
36782             this.checked = false;
36783             this.setChecked(true, true);
36784         }
36785     },
36786
36787     // private
36788     destroy : function(){
36789         if(this.rendered){
36790             Roo.menu.MenuMgr.unregisterCheckable(this);
36791         }
36792         Roo.menu.CheckItem.superclass.destroy.apply(this, arguments);
36793     },
36794
36795     /**
36796      * Set the checked state of this item
36797      * @param {Boolean} checked The new checked value
36798      * @param {Boolean} suppressEvent (optional) True to prevent the checkchange event from firing (defaults to false)
36799      */
36800     setChecked : function(state, suppressEvent){
36801         if(this.checked != state && this.fireEvent("beforecheckchange", this, state) !== false){
36802             if(this.container){
36803                 this.container[state ? "addClass" : "removeClass"]("x-menu-item-checked");
36804             }
36805             this.checked = state;
36806             if(suppressEvent !== true){
36807                 this.fireEvent("checkchange", this, state);
36808             }
36809         }
36810     },
36811
36812     // private
36813     handleClick : function(e){
36814        if(!this.disabled && !(this.checked && this.group)){// disable unselect on radio item
36815            this.setChecked(!this.checked);
36816        }
36817        Roo.menu.CheckItem.superclass.handleClick.apply(this, arguments);
36818     }
36819 });/*
36820  * Based on:
36821  * Ext JS Library 1.1.1
36822  * Copyright(c) 2006-2007, Ext JS, LLC.
36823  *
36824  * Originally Released Under LGPL - original licence link has changed is not relivant.
36825  *
36826  * Fork - LGPL
36827  * <script type="text/javascript">
36828  */
36829  
36830 /**
36831  * @class Roo.menu.DateItem
36832  * @extends Roo.menu.Adapter
36833  * A menu item that wraps the {@link Roo.DatPicker} component.
36834  * @constructor
36835  * Creates a new DateItem
36836  * @param {Object} config Configuration options
36837  */
36838 Roo.menu.DateItem = function(config){
36839     Roo.menu.DateItem.superclass.constructor.call(this, new Roo.DatePicker(config), config);
36840     /** The Roo.DatePicker object @type Roo.DatePicker */
36841     this.picker = this.component;
36842     this.addEvents({select: true});
36843     
36844     this.picker.on("render", function(picker){
36845         picker.getEl().swallowEvent("click");
36846         picker.container.addClass("x-menu-date-item");
36847     });
36848
36849     this.picker.on("select", this.onSelect, this);
36850 };
36851
36852 Roo.extend(Roo.menu.DateItem, Roo.menu.Adapter, {
36853     // private
36854     onSelect : function(picker, date){
36855         this.fireEvent("select", this, date, picker);
36856         Roo.menu.DateItem.superclass.handleClick.call(this);
36857     }
36858 });/*
36859  * Based on:
36860  * Ext JS Library 1.1.1
36861  * Copyright(c) 2006-2007, Ext JS, LLC.
36862  *
36863  * Originally Released Under LGPL - original licence link has changed is not relivant.
36864  *
36865  * Fork - LGPL
36866  * <script type="text/javascript">
36867  */
36868  
36869 /**
36870  * @class Roo.menu.ColorItem
36871  * @extends Roo.menu.Adapter
36872  * A menu item that wraps the {@link Roo.ColorPalette} component.
36873  * @constructor
36874  * Creates a new ColorItem
36875  * @param {Object} config Configuration options
36876  */
36877 Roo.menu.ColorItem = function(config){
36878     Roo.menu.ColorItem.superclass.constructor.call(this, new Roo.ColorPalette(config), config);
36879     /** The Roo.ColorPalette object @type Roo.ColorPalette */
36880     this.palette = this.component;
36881     this.relayEvents(this.palette, ["select"]);
36882     if(this.selectHandler){
36883         this.on('select', this.selectHandler, this.scope);
36884     }
36885 };
36886 Roo.extend(Roo.menu.ColorItem, Roo.menu.Adapter);/*
36887  * Based on:
36888  * Ext JS Library 1.1.1
36889  * Copyright(c) 2006-2007, Ext JS, LLC.
36890  *
36891  * Originally Released Under LGPL - original licence link has changed is not relivant.
36892  *
36893  * Fork - LGPL
36894  * <script type="text/javascript">
36895  */
36896  
36897
36898 /**
36899  * @class Roo.menu.DateMenu
36900  * @extends Roo.menu.Menu
36901  * A menu containing a {@link Roo.menu.DateItem} component (which provides a date picker).
36902  * @constructor
36903  * Creates a new DateMenu
36904  * @param {Object} config Configuration options
36905  */
36906 Roo.menu.DateMenu = function(config){
36907     Roo.menu.DateMenu.superclass.constructor.call(this, config);
36908     this.plain = true;
36909     var di = new Roo.menu.DateItem(config);
36910     this.add(di);
36911     /**
36912      * The {@link Roo.DatePicker} instance for this DateMenu
36913      * @type DatePicker
36914      */
36915     this.picker = di.picker;
36916     /**
36917      * @event select
36918      * @param {DatePicker} picker
36919      * @param {Date} date
36920      */
36921     this.relayEvents(di, ["select"]);
36922     this.on('beforeshow', function(){
36923         if(this.picker){
36924             this.picker.hideMonthPicker(false);
36925         }
36926     }, this);
36927 };
36928 Roo.extend(Roo.menu.DateMenu, Roo.menu.Menu, {
36929     cls:'x-date-menu'
36930 });/*
36931  * Based on:
36932  * Ext JS Library 1.1.1
36933  * Copyright(c) 2006-2007, Ext JS, LLC.
36934  *
36935  * Originally Released Under LGPL - original licence link has changed is not relivant.
36936  *
36937  * Fork - LGPL
36938  * <script type="text/javascript">
36939  */
36940  
36941
36942 /**
36943  * @class Roo.menu.ColorMenu
36944  * @extends Roo.menu.Menu
36945  * A menu containing a {@link Roo.menu.ColorItem} component (which provides a basic color picker).
36946  * @constructor
36947  * Creates a new ColorMenu
36948  * @param {Object} config Configuration options
36949  */
36950 Roo.menu.ColorMenu = function(config){
36951     Roo.menu.ColorMenu.superclass.constructor.call(this, config);
36952     this.plain = true;
36953     var ci = new Roo.menu.ColorItem(config);
36954     this.add(ci);
36955     /**
36956      * The {@link Roo.ColorPalette} instance for this ColorMenu
36957      * @type ColorPalette
36958      */
36959     this.palette = ci.palette;
36960     /**
36961      * @event select
36962      * @param {ColorPalette} palette
36963      * @param {String} color
36964      */
36965     this.relayEvents(ci, ["select"]);
36966 };
36967 Roo.extend(Roo.menu.ColorMenu, Roo.menu.Menu);/*
36968  * Based on:
36969  * Ext JS Library 1.1.1
36970  * Copyright(c) 2006-2007, Ext JS, LLC.
36971  *
36972  * Originally Released Under LGPL - original licence link has changed is not relivant.
36973  *
36974  * Fork - LGPL
36975  * <script type="text/javascript">
36976  */
36977  
36978 /**
36979  * @class Roo.form.Field
36980  * @extends Roo.BoxComponent
36981  * Base class for form fields that provides default event handling, sizing, value handling and other functionality.
36982  * @constructor
36983  * Creates a new Field
36984  * @param {Object} config Configuration options
36985  */
36986 Roo.form.Field = function(config){
36987     Roo.form.Field.superclass.constructor.call(this, config);
36988 };
36989
36990 Roo.extend(Roo.form.Field, Roo.BoxComponent,  {
36991     /**
36992      * @cfg {String} fieldLabel Label to use when rendering a form.
36993      */
36994        /**
36995      * @cfg {String} qtip Mouse over tip
36996      */
36997      
36998     /**
36999      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
37000      */
37001     invalidClass : "x-form-invalid",
37002     /**
37003      * @cfg {String} invalidText The error text to use when marking a field invalid and no message is provided (defaults to "The value in this field is invalid")
37004      */
37005     invalidText : "The value in this field is invalid",
37006     /**
37007      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
37008      */
37009     focusClass : "x-form-focus",
37010     /**
37011      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
37012       automatic validation (defaults to "keyup").
37013      */
37014     validationEvent : "keyup",
37015     /**
37016      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
37017      */
37018     validateOnBlur : true,
37019     /**
37020      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
37021      */
37022     validationDelay : 250,
37023     /**
37024      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
37025      * {tag: "input", type: "text", size: "20", autocomplete: "off"})
37026      */
37027     defaultAutoCreate : {tag: "input", type: "text", size: "20", autocomplete: "off"},
37028     /**
37029      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field")
37030      */
37031     fieldClass : "x-form-field",
37032     /**
37033      * @cfg {String} msgTarget The location where error text should display.  Should be one of the following values (defaults to 'qtip'):
37034      *<pre>
37035 Value         Description
37036 -----------   ----------------------------------------------------------------------
37037 qtip          Display a quick tip when the user hovers over the field
37038 title         Display a default browser title attribute popup
37039 under         Add a block div beneath the field containing the error text
37040 side          Add an error icon to the right of the field with a popup on hover
37041 [element id]  Add the error text directly to the innerHTML of the specified element
37042 </pre>
37043      */
37044     msgTarget : 'qtip',
37045     /**
37046      * @cfg {String} msgFx <b>Experimental</b> The effect used when displaying a validation message under the field (defaults to 'normal').
37047      */
37048     msgFx : 'normal',
37049
37050     /**
37051      * @cfg {Boolean} readOnly True to mark the field as readOnly in HTML (defaults to false) -- Note: this only sets the element's readOnly DOM attribute.
37052      */
37053     readOnly : false,
37054
37055     /**
37056      * @cfg {Boolean} disabled True to disable the field (defaults to false).
37057      */
37058     disabled : false,
37059
37060     /**
37061      * @cfg {String} inputType The type attribute for input fields -- e.g. radio, text, password (defaults to "text").
37062      */
37063     inputType : undefined,
37064     
37065     /**
37066      * @cfg {Number} tabIndex The tabIndex for this field. Note this only applies to fields that are rendered, not those which are built via applyTo (defaults to undefined).
37067          */
37068         tabIndex : undefined,
37069         
37070     // private
37071     isFormField : true,
37072
37073     // private
37074     hasFocus : false,
37075     /**
37076      * @property {Roo.Element} fieldEl
37077      * Element Containing the rendered Field (with label etc.)
37078      */
37079     /**
37080      * @cfg {Mixed} value A value to initialize this field with.
37081      */
37082     value : undefined,
37083
37084     /**
37085      * @cfg {String} name The field's HTML name attribute.
37086      */
37087     /**
37088      * @cfg {String} cls A CSS class to apply to the field's underlying element.
37089      */
37090
37091         // private ??
37092         initComponent : function(){
37093         Roo.form.Field.superclass.initComponent.call(this);
37094         this.addEvents({
37095             /**
37096              * @event focus
37097              * Fires when this field receives input focus.
37098              * @param {Roo.form.Field} this
37099              */
37100             focus : true,
37101             /**
37102              * @event blur
37103              * Fires when this field loses input focus.
37104              * @param {Roo.form.Field} this
37105              */
37106             blur : true,
37107             /**
37108              * @event specialkey
37109              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
37110              * {@link Roo.EventObject#getKey} to determine which key was pressed.
37111              * @param {Roo.form.Field} this
37112              * @param {Roo.EventObject} e The event object
37113              */
37114             specialkey : true,
37115             /**
37116              * @event change
37117              * Fires just before the field blurs if the field value has changed.
37118              * @param {Roo.form.Field} this
37119              * @param {Mixed} newValue The new value
37120              * @param {Mixed} oldValue The original value
37121              */
37122             change : true,
37123             /**
37124              * @event invalid
37125              * Fires after the field has been marked as invalid.
37126              * @param {Roo.form.Field} this
37127              * @param {String} msg The validation message
37128              */
37129             invalid : true,
37130             /**
37131              * @event valid
37132              * Fires after the field has been validated with no errors.
37133              * @param {Roo.form.Field} this
37134              */
37135             valid : true,
37136              /**
37137              * @event keyup
37138              * Fires after the key up
37139              * @param {Roo.form.Field} this
37140              * @param {Roo.EventObject}  e The event Object
37141              */
37142             keyup : true
37143         });
37144     },
37145
37146     /**
37147      * Returns the name attribute of the field if available
37148      * @return {String} name The field name
37149      */
37150     getName: function(){
37151          return this.rendered && this.el.dom.name ? this.el.dom.name : (this.hiddenName || '');
37152     },
37153
37154     // private
37155     onRender : function(ct, position){
37156         Roo.form.Field.superclass.onRender.call(this, ct, position);
37157         if(!this.el){
37158             var cfg = this.getAutoCreate();
37159             if(!cfg.name){
37160                 cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
37161             }
37162             if (!cfg.name.length) {
37163                 delete cfg.name;
37164             }
37165             if(this.inputType){
37166                 cfg.type = this.inputType;
37167             }
37168             this.el = ct.createChild(cfg, position);
37169         }
37170         var type = this.el.dom.type;
37171         if(type){
37172             if(type == 'password'){
37173                 type = 'text';
37174             }
37175             this.el.addClass('x-form-'+type);
37176         }
37177         if(this.readOnly){
37178             this.el.dom.readOnly = true;
37179         }
37180         if(this.tabIndex !== undefined){
37181             this.el.dom.setAttribute('tabIndex', this.tabIndex);
37182         }
37183
37184         this.el.addClass([this.fieldClass, this.cls]);
37185         this.initValue();
37186     },
37187
37188     /**
37189      * Apply the behaviors of this component to an existing element. <b>This is used instead of render().</b>
37190      * @param {String/HTMLElement/Element} el The id of the node, a DOM node or an existing Element
37191      * @return {Roo.form.Field} this
37192      */
37193     applyTo : function(target){
37194         this.allowDomMove = false;
37195         this.el = Roo.get(target);
37196         this.render(this.el.dom.parentNode);
37197         return this;
37198     },
37199
37200     // private
37201     initValue : function(){
37202         if(this.value !== undefined){
37203             this.setValue(this.value);
37204         }else if(this.el.dom.value.length > 0){
37205             this.setValue(this.el.dom.value);
37206         }
37207     },
37208
37209     /**
37210      * Returns true if this field has been changed since it was originally loaded and is not disabled.
37211      */
37212     isDirty : function() {
37213         if(this.disabled) {
37214             return false;
37215         }
37216         return String(this.getValue()) !== String(this.originalValue);
37217     },
37218
37219     // private
37220     afterRender : function(){
37221         Roo.form.Field.superclass.afterRender.call(this);
37222         this.initEvents();
37223     },
37224
37225     // private
37226     fireKey : function(e){
37227         //Roo.log('field ' + e.getKey());
37228         if(e.isNavKeyPress()){
37229             this.fireEvent("specialkey", this, e);
37230         }
37231     },
37232
37233     /**
37234      * Resets the current field value to the originally loaded value and clears any validation messages
37235      */
37236     reset : function(){
37237         this.setValue(this.resetValue);
37238         this.clearInvalid();
37239     },
37240
37241     // private
37242     initEvents : function(){
37243         // safari killled keypress - so keydown is now used..
37244         this.el.on("keydown" , this.fireKey,  this);
37245         this.el.on("focus", this.onFocus,  this);
37246         this.el.on("blur", this.onBlur,  this);
37247         this.el.relayEvent('keyup', this);
37248
37249         // reference to original value for reset
37250         this.originalValue = this.getValue();
37251         this.resetValue =  this.getValue();
37252     },
37253
37254     // private
37255     onFocus : function(){
37256         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
37257             this.el.addClass(this.focusClass);
37258         }
37259         if(!this.hasFocus){
37260             this.hasFocus = true;
37261             this.startValue = this.getValue();
37262             this.fireEvent("focus", this);
37263         }
37264     },
37265
37266     beforeBlur : Roo.emptyFn,
37267
37268     // private
37269     onBlur : function(){
37270         this.beforeBlur();
37271         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
37272             this.el.removeClass(this.focusClass);
37273         }
37274         this.hasFocus = false;
37275         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
37276             this.validate();
37277         }
37278         var v = this.getValue();
37279         if(String(v) !== String(this.startValue)){
37280             this.fireEvent('change', this, v, this.startValue);
37281         }
37282         this.fireEvent("blur", this);
37283     },
37284
37285     /**
37286      * Returns whether or not the field value is currently valid
37287      * @param {Boolean} preventMark True to disable marking the field invalid
37288      * @return {Boolean} True if the value is valid, else false
37289      */
37290     isValid : function(preventMark){
37291         if(this.disabled){
37292             return true;
37293         }
37294         var restore = this.preventMark;
37295         this.preventMark = preventMark === true;
37296         var v = this.validateValue(this.processValue(this.getRawValue()));
37297         this.preventMark = restore;
37298         return v;
37299     },
37300
37301     /**
37302      * Validates the field value
37303      * @return {Boolean} True if the value is valid, else false
37304      */
37305     validate : function(){
37306         if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
37307             this.clearInvalid();
37308             return true;
37309         }
37310         return false;
37311     },
37312
37313     processValue : function(value){
37314         return value;
37315     },
37316
37317     // private
37318     // Subclasses should provide the validation implementation by overriding this
37319     validateValue : function(value){
37320         return true;
37321     },
37322
37323     /**
37324      * Mark this field as invalid
37325      * @param {String} msg The validation message
37326      */
37327     markInvalid : function(msg){
37328         if(!this.rendered || this.preventMark){ // not rendered
37329             return;
37330         }
37331         
37332         var obj = (typeof(this.combo) != 'undefined') ? this.combo : this; // fix the combox array!!
37333         
37334         obj.el.addClass(this.invalidClass);
37335         msg = msg || this.invalidText;
37336         switch(this.msgTarget){
37337             case 'qtip':
37338                 obj.el.dom.qtip = msg;
37339                 obj.el.dom.qclass = 'x-form-invalid-tip';
37340                 if(Roo.QuickTips){ // fix for floating editors interacting with DND
37341                     Roo.QuickTips.enable();
37342                 }
37343                 break;
37344             case 'title':
37345                 this.el.dom.title = msg;
37346                 break;
37347             case 'under':
37348                 if(!this.errorEl){
37349                     var elp = this.el.findParent('.x-form-element', 5, true);
37350                     this.errorEl = elp.createChild({cls:'x-form-invalid-msg'});
37351                     this.errorEl.setWidth(elp.getWidth(true)-20);
37352                 }
37353                 this.errorEl.update(msg);
37354                 Roo.form.Field.msgFx[this.msgFx].show(this.errorEl, this);
37355                 break;
37356             case 'side':
37357                 if(!this.errorIcon){
37358                     var elp = this.el.findParent('.x-form-element', 5, true);
37359                     this.errorIcon = elp.createChild({cls:'x-form-invalid-icon'});
37360                 }
37361                 this.alignErrorIcon();
37362                 this.errorIcon.dom.qtip = msg;
37363                 this.errorIcon.dom.qclass = 'x-form-invalid-tip';
37364                 this.errorIcon.show();
37365                 this.on('resize', this.alignErrorIcon, this);
37366                 break;
37367             default:
37368                 var t = Roo.getDom(this.msgTarget);
37369                 t.innerHTML = msg;
37370                 t.style.display = this.msgDisplay;
37371                 break;
37372         }
37373         this.fireEvent('invalid', this, msg);
37374     },
37375
37376     // private
37377     alignErrorIcon : function(){
37378         this.errorIcon.alignTo(this.el, 'tl-tr', [2, 0]);
37379     },
37380
37381     /**
37382      * Clear any invalid styles/messages for this field
37383      */
37384     clearInvalid : function(){
37385         if(!this.rendered || this.preventMark){ // not rendered
37386             return;
37387         }
37388         var obj = (typeof(this.combo) != 'undefined') ? this.combo : this; // fix the combox array!!
37389         
37390         obj.el.removeClass(this.invalidClass);
37391         switch(this.msgTarget){
37392             case 'qtip':
37393                 obj.el.dom.qtip = '';
37394                 break;
37395             case 'title':
37396                 this.el.dom.title = '';
37397                 break;
37398             case 'under':
37399                 if(this.errorEl){
37400                     Roo.form.Field.msgFx[this.msgFx].hide(this.errorEl, this);
37401                 }
37402                 break;
37403             case 'side':
37404                 if(this.errorIcon){
37405                     this.errorIcon.dom.qtip = '';
37406                     this.errorIcon.hide();
37407                     this.un('resize', this.alignErrorIcon, this);
37408                 }
37409                 break;
37410             default:
37411                 var t = Roo.getDom(this.msgTarget);
37412                 t.innerHTML = '';
37413                 t.style.display = 'none';
37414                 break;
37415         }
37416         this.fireEvent('valid', this);
37417     },
37418
37419     /**
37420      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
37421      * @return {Mixed} value The field value
37422      */
37423     getRawValue : function(){
37424         var v = this.el.getValue();
37425         
37426         return v;
37427     },
37428
37429     /**
37430      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
37431      * @return {Mixed} value The field value
37432      */
37433     getValue : function(){
37434         var v = this.el.getValue();
37435          
37436         return v;
37437     },
37438
37439     /**
37440      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
37441      * @param {Mixed} value The value to set
37442      */
37443     setRawValue : function(v){
37444         return this.el.dom.value = (v === null || v === undefined ? '' : v);
37445     },
37446
37447     /**
37448      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
37449      * @param {Mixed} value The value to set
37450      */
37451     setValue : function(v){
37452         this.value = v;
37453         if(this.rendered){
37454             this.el.dom.value = (v === null || v === undefined ? '' : v);
37455              this.validate();
37456         }
37457     },
37458
37459     adjustSize : function(w, h){
37460         var s = Roo.form.Field.superclass.adjustSize.call(this, w, h);
37461         s.width = this.adjustWidth(this.el.dom.tagName, s.width);
37462         return s;
37463     },
37464
37465     adjustWidth : function(tag, w){
37466         tag = tag.toLowerCase();
37467         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
37468             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
37469                 if(tag == 'input'){
37470                     return w + 2;
37471                 }
37472                 if(tag == 'textarea'){
37473                     return w-2;
37474                 }
37475             }else if(Roo.isOpera){
37476                 if(tag == 'input'){
37477                     return w + 2;
37478                 }
37479                 if(tag == 'textarea'){
37480                     return w-2;
37481                 }
37482             }
37483         }
37484         return w;
37485     }
37486 });
37487
37488
37489 // anything other than normal should be considered experimental
37490 Roo.form.Field.msgFx = {
37491     normal : {
37492         show: function(msgEl, f){
37493             msgEl.setDisplayed('block');
37494         },
37495
37496         hide : function(msgEl, f){
37497             msgEl.setDisplayed(false).update('');
37498         }
37499     },
37500
37501     slide : {
37502         show: function(msgEl, f){
37503             msgEl.slideIn('t', {stopFx:true});
37504         },
37505
37506         hide : function(msgEl, f){
37507             msgEl.slideOut('t', {stopFx:true,useDisplay:true});
37508         }
37509     },
37510
37511     slideRight : {
37512         show: function(msgEl, f){
37513             msgEl.fixDisplay();
37514             msgEl.alignTo(f.el, 'tl-tr');
37515             msgEl.slideIn('l', {stopFx:true});
37516         },
37517
37518         hide : function(msgEl, f){
37519             msgEl.slideOut('l', {stopFx:true,useDisplay:true});
37520         }
37521     }
37522 };/*
37523  * Based on:
37524  * Ext JS Library 1.1.1
37525  * Copyright(c) 2006-2007, Ext JS, LLC.
37526  *
37527  * Originally Released Under LGPL - original licence link has changed is not relivant.
37528  *
37529  * Fork - LGPL
37530  * <script type="text/javascript">
37531  */
37532  
37533
37534 /**
37535  * @class Roo.form.TextField
37536  * @extends Roo.form.Field
37537  * Basic text field.  Can be used as a direct replacement for traditional text inputs, or as the base
37538  * class for more sophisticated input controls (like {@link Roo.form.TextArea} and {@link Roo.form.ComboBox}).
37539  * @constructor
37540  * Creates a new TextField
37541  * @param {Object} config Configuration options
37542  */
37543 Roo.form.TextField = function(config){
37544     Roo.form.TextField.superclass.constructor.call(this, config);
37545     this.addEvents({
37546         /**
37547          * @event autosize
37548          * Fires when the autosize function is triggered.  The field may or may not have actually changed size
37549          * according to the default logic, but this event provides a hook for the developer to apply additional
37550          * logic at runtime to resize the field if needed.
37551              * @param {Roo.form.Field} this This text field
37552              * @param {Number} width The new field width
37553              */
37554         autosize : true
37555     });
37556 };
37557
37558 Roo.extend(Roo.form.TextField, Roo.form.Field,  {
37559     /**
37560      * @cfg {Boolean} grow True if this field should automatically grow and shrink to its content
37561      */
37562     grow : false,
37563     /**
37564      * @cfg {Number} growMin The minimum width to allow when grow = true (defaults to 30)
37565      */
37566     growMin : 30,
37567     /**
37568      * @cfg {Number} growMax The maximum width to allow when grow = true (defaults to 800)
37569      */
37570     growMax : 800,
37571     /**
37572      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
37573      */
37574     vtype : null,
37575     /**
37576      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
37577      */
37578     maskRe : null,
37579     /**
37580      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
37581      */
37582     disableKeyFilter : false,
37583     /**
37584      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
37585      */
37586     allowBlank : true,
37587     /**
37588      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
37589      */
37590     minLength : 0,
37591     /**
37592      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
37593      */
37594     maxLength : Number.MAX_VALUE,
37595     /**
37596      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
37597      */
37598     minLengthText : "The minimum length for this field is {0}",
37599     /**
37600      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
37601      */
37602     maxLengthText : "The maximum length for this field is {0}",
37603     /**
37604      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
37605      */
37606     selectOnFocus : false,
37607     /**
37608      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
37609      */
37610     blankText : "This field is required",
37611     /**
37612      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
37613      * If available, this function will be called only after the basic validators all return true, and will be passed the
37614      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
37615      */
37616     validator : null,
37617     /**
37618      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
37619      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
37620      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
37621      */
37622     regex : null,
37623     /**
37624      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
37625      */
37626     regexText : "",
37627     /**
37628      * @cfg {String} emptyText The default text to display in an empty field - placeholder... (defaults to null).
37629      */
37630     emptyText : null,
37631    
37632
37633     // private
37634     initEvents : function()
37635     {
37636         if (this.emptyText) {
37637             this.el.attr('placeholder', this.emptyText);
37638         }
37639         
37640         Roo.form.TextField.superclass.initEvents.call(this);
37641         if(this.validationEvent == 'keyup'){
37642             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
37643             this.el.on('keyup', this.filterValidation, this);
37644         }
37645         else if(this.validationEvent !== false){
37646             this.el.on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
37647         }
37648         
37649         if(this.selectOnFocus){
37650             this.on("focus", this.preFocus, this);
37651             
37652         }
37653         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
37654             this.el.on("keypress", this.filterKeys, this);
37655         }
37656         if(this.grow){
37657             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
37658             this.el.on("click", this.autoSize,  this);
37659         }
37660         if(this.el.is('input[type=password]') && Roo.isSafari){
37661             this.el.on('keydown', this.SafariOnKeyDown, this);
37662         }
37663     },
37664
37665     processValue : function(value){
37666         if(this.stripCharsRe){
37667             var newValue = value.replace(this.stripCharsRe, '');
37668             if(newValue !== value){
37669                 this.setRawValue(newValue);
37670                 return newValue;
37671             }
37672         }
37673         return value;
37674     },
37675
37676     filterValidation : function(e){
37677         if(!e.isNavKeyPress()){
37678             this.validationTask.delay(this.validationDelay);
37679         }
37680     },
37681
37682     // private
37683     onKeyUp : function(e){
37684         if(!e.isNavKeyPress()){
37685             this.autoSize();
37686         }
37687     },
37688
37689     /**
37690      * Resets the current field value to the originally-loaded value and clears any validation messages.
37691      *  
37692      */
37693     reset : function(){
37694         Roo.form.TextField.superclass.reset.call(this);
37695        
37696     },
37697
37698     
37699     // private
37700     preFocus : function(){
37701         
37702         if(this.selectOnFocus){
37703             this.el.dom.select();
37704         }
37705     },
37706
37707     
37708     // private
37709     filterKeys : function(e){
37710         var k = e.getKey();
37711         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
37712             return;
37713         }
37714         var c = e.getCharCode(), cc = String.fromCharCode(c);
37715         if(Roo.isIE && (e.isSpecialKey() || !cc)){
37716             return;
37717         }
37718         if(!this.maskRe.test(cc)){
37719             e.stopEvent();
37720         }
37721     },
37722
37723     setValue : function(v){
37724         
37725         Roo.form.TextField.superclass.setValue.apply(this, arguments);
37726         
37727         this.autoSize();
37728     },
37729
37730     /**
37731      * Validates a value according to the field's validation rules and marks the field as invalid
37732      * if the validation fails
37733      * @param {Mixed} value The value to validate
37734      * @return {Boolean} True if the value is valid, else false
37735      */
37736     validateValue : function(value){
37737         if(value.length < 1)  { // if it's blank
37738              if(this.allowBlank){
37739                 this.clearInvalid();
37740                 return true;
37741              }else{
37742                 this.markInvalid(this.blankText);
37743                 return false;
37744              }
37745         }
37746         if(value.length < this.minLength){
37747             this.markInvalid(String.format(this.minLengthText, this.minLength));
37748             return false;
37749         }
37750         if(value.length > this.maxLength){
37751             this.markInvalid(String.format(this.maxLengthText, this.maxLength));
37752             return false;
37753         }
37754         if(this.vtype){
37755             var vt = Roo.form.VTypes;
37756             if(!vt[this.vtype](value, this)){
37757                 this.markInvalid(this.vtypeText || vt[this.vtype +'Text']);
37758                 return false;
37759             }
37760         }
37761         if(typeof this.validator == "function"){
37762             var msg = this.validator(value);
37763             if(msg !== true){
37764                 this.markInvalid(msg);
37765                 return false;
37766             }
37767         }
37768         if(this.regex && !this.regex.test(value)){
37769             this.markInvalid(this.regexText);
37770             return false;
37771         }
37772         return true;
37773     },
37774
37775     /**
37776      * Selects text in this field
37777      * @param {Number} start (optional) The index where the selection should start (defaults to 0)
37778      * @param {Number} end (optional) The index where the selection should end (defaults to the text length)
37779      */
37780     selectText : function(start, end){
37781         var v = this.getRawValue();
37782         if(v.length > 0){
37783             start = start === undefined ? 0 : start;
37784             end = end === undefined ? v.length : end;
37785             var d = this.el.dom;
37786             if(d.setSelectionRange){
37787                 d.setSelectionRange(start, end);
37788             }else if(d.createTextRange){
37789                 var range = d.createTextRange();
37790                 range.moveStart("character", start);
37791                 range.moveEnd("character", v.length-end);
37792                 range.select();
37793             }
37794         }
37795     },
37796
37797     /**
37798      * Automatically grows the field to accomodate the width of the text up to the maximum field width allowed.
37799      * This only takes effect if grow = true, and fires the autosize event.
37800      */
37801     autoSize : function(){
37802         if(!this.grow || !this.rendered){
37803             return;
37804         }
37805         if(!this.metrics){
37806             this.metrics = Roo.util.TextMetrics.createInstance(this.el);
37807         }
37808         var el = this.el;
37809         var v = el.dom.value;
37810         var d = document.createElement('div');
37811         d.appendChild(document.createTextNode(v));
37812         v = d.innerHTML;
37813         d = null;
37814         v += "&#160;";
37815         var w = Math.min(this.growMax, Math.max(this.metrics.getWidth(v) + /* add extra padding */ 10, this.growMin));
37816         this.el.setWidth(w);
37817         this.fireEvent("autosize", this, w);
37818     },
37819     
37820     // private
37821     SafariOnKeyDown : function(event)
37822     {
37823         // this is a workaround for a password hang bug on chrome/ webkit.
37824         
37825         var isSelectAll = false;
37826         
37827         if(this.el.dom.selectionEnd > 0){
37828             isSelectAll = (this.el.dom.selectionEnd - this.el.dom.selectionStart - this.getValue().length == 0) ? true : false;
37829         }
37830         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
37831             event.preventDefault();
37832             this.setValue('');
37833             return;
37834         }
37835         
37836         if(isSelectAll){ // backspace and delete key
37837             
37838             event.preventDefault();
37839             // this is very hacky as keydown always get's upper case.
37840             //
37841             var cc = String.fromCharCode(event.getCharCode());
37842             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
37843             
37844         }
37845         
37846         
37847     }
37848 });/*
37849  * Based on:
37850  * Ext JS Library 1.1.1
37851  * Copyright(c) 2006-2007, Ext JS, LLC.
37852  *
37853  * Originally Released Under LGPL - original licence link has changed is not relivant.
37854  *
37855  * Fork - LGPL
37856  * <script type="text/javascript">
37857  */
37858  
37859 /**
37860  * @class Roo.form.Hidden
37861  * @extends Roo.form.TextField
37862  * Simple Hidden element used on forms 
37863  * 
37864  * usage: form.add(new Roo.form.HiddenField({ 'name' : 'test1' }));
37865  * 
37866  * @constructor
37867  * Creates a new Hidden form element.
37868  * @param {Object} config Configuration options
37869  */
37870
37871
37872
37873 // easy hidden field...
37874 Roo.form.Hidden = function(config){
37875     Roo.form.Hidden.superclass.constructor.call(this, config);
37876 };
37877   
37878 Roo.extend(Roo.form.Hidden, Roo.form.TextField, {
37879     fieldLabel:      '',
37880     inputType:      'hidden',
37881     width:          50,
37882     allowBlank:     true,
37883     labelSeparator: '',
37884     hidden:         true,
37885     itemCls :       'x-form-item-display-none'
37886
37887
37888 });
37889
37890
37891 /*
37892  * Based on:
37893  * Ext JS Library 1.1.1
37894  * Copyright(c) 2006-2007, Ext JS, LLC.
37895  *
37896  * Originally Released Under LGPL - original licence link has changed is not relivant.
37897  *
37898  * Fork - LGPL
37899  * <script type="text/javascript">
37900  */
37901  
37902 /**
37903  * @class Roo.form.TriggerField
37904  * @extends Roo.form.TextField
37905  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
37906  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
37907  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
37908  * for which you can provide a custom implementation.  For example:
37909  * <pre><code>
37910 var trigger = new Roo.form.TriggerField();
37911 trigger.onTriggerClick = myTriggerFn;
37912 trigger.applyTo('my-field');
37913 </code></pre>
37914  *
37915  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
37916  * {@link Roo.form.DateField} and {@link Roo.form.ComboBox} are perfect examples of this.
37917  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
37918  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
37919  * @constructor
37920  * Create a new TriggerField.
37921  * @param {Object} config Configuration options (valid {@Roo.form.TextField} config options will also be applied
37922  * to the base TextField)
37923  */
37924 Roo.form.TriggerField = function(config){
37925     this.mimicing = false;
37926     Roo.form.TriggerField.superclass.constructor.call(this, config);
37927 };
37928
37929 Roo.extend(Roo.form.TriggerField, Roo.form.TextField,  {
37930     /**
37931      * @cfg {String} triggerClass A CSS class to apply to the trigger
37932      */
37933     /**
37934      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
37935      * {tag: "input", type: "text", size: "16", autocomplete: "off"})
37936      */
37937     defaultAutoCreate : {tag: "input", type: "text", size: "16", autocomplete: "off"},
37938     /**
37939      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
37940      */
37941     hideTrigger:false,
37942
37943     /** @cfg {Boolean} grow @hide */
37944     /** @cfg {Number} growMin @hide */
37945     /** @cfg {Number} growMax @hide */
37946
37947     /**
37948      * @hide 
37949      * @method
37950      */
37951     autoSize: Roo.emptyFn,
37952     // private
37953     monitorTab : true,
37954     // private
37955     deferHeight : true,
37956
37957     
37958     actionMode : 'wrap',
37959     // private
37960     onResize : function(w, h){
37961         Roo.form.TriggerField.superclass.onResize.apply(this, arguments);
37962         if(typeof w == 'number'){
37963             var x = w - this.trigger.getWidth();
37964             this.el.setWidth(this.adjustWidth('input', x));
37965             this.trigger.setStyle('left', x+'px');
37966         }
37967     },
37968
37969     // private
37970     adjustSize : Roo.BoxComponent.prototype.adjustSize,
37971
37972     // private
37973     getResizeEl : function(){
37974         return this.wrap;
37975     },
37976
37977     // private
37978     getPositionEl : function(){
37979         return this.wrap;
37980     },
37981
37982     // private
37983     alignErrorIcon : function(){
37984         this.errorIcon.alignTo(this.wrap, 'tl-tr', [2, 0]);
37985     },
37986
37987     // private
37988     onRender : function(ct, position){
37989         Roo.form.TriggerField.superclass.onRender.call(this, ct, position);
37990         this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
37991         this.trigger = this.wrap.createChild(this.triggerConfig ||
37992                 {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.triggerClass});
37993         if(this.hideTrigger){
37994             this.trigger.setDisplayed(false);
37995         }
37996         this.initTrigger();
37997         if(!this.width){
37998             this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
37999         }
38000     },
38001
38002     // private
38003     initTrigger : function(){
38004         this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
38005         this.trigger.addClassOnOver('x-form-trigger-over');
38006         this.trigger.addClassOnClick('x-form-trigger-click');
38007     },
38008
38009     // private
38010     onDestroy : function(){
38011         if(this.trigger){
38012             this.trigger.removeAllListeners();
38013             this.trigger.remove();
38014         }
38015         if(this.wrap){
38016             this.wrap.remove();
38017         }
38018         Roo.form.TriggerField.superclass.onDestroy.call(this);
38019     },
38020
38021     // private
38022     onFocus : function(){
38023         Roo.form.TriggerField.superclass.onFocus.call(this);
38024         if(!this.mimicing){
38025             this.wrap.addClass('x-trigger-wrap-focus');
38026             this.mimicing = true;
38027             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
38028             if(this.monitorTab){
38029                 this.el.on("keydown", this.checkTab, this);
38030             }
38031         }
38032     },
38033
38034     // private
38035     checkTab : function(e){
38036         if(e.getKey() == e.TAB){
38037             this.triggerBlur();
38038         }
38039     },
38040
38041     // private
38042     onBlur : function(){
38043         // do nothing
38044     },
38045
38046     // private
38047     mimicBlur : function(e, t){
38048         if(!this.wrap.contains(t) && this.validateBlur()){
38049             this.triggerBlur();
38050         }
38051     },
38052
38053     // private
38054     triggerBlur : function(){
38055         this.mimicing = false;
38056         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
38057         if(this.monitorTab){
38058             this.el.un("keydown", this.checkTab, this);
38059         }
38060         this.wrap.removeClass('x-trigger-wrap-focus');
38061         Roo.form.TriggerField.superclass.onBlur.call(this);
38062     },
38063
38064     // private
38065     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
38066     validateBlur : function(e, t){
38067         return true;
38068     },
38069
38070     // private
38071     onDisable : function(){
38072         Roo.form.TriggerField.superclass.onDisable.call(this);
38073         if(this.wrap){
38074             this.wrap.addClass('x-item-disabled');
38075         }
38076     },
38077
38078     // private
38079     onEnable : function(){
38080         Roo.form.TriggerField.superclass.onEnable.call(this);
38081         if(this.wrap){
38082             this.wrap.removeClass('x-item-disabled');
38083         }
38084     },
38085
38086     // private
38087     onShow : function(){
38088         var ae = this.getActionEl();
38089         
38090         if(ae){
38091             ae.dom.style.display = '';
38092             ae.dom.style.visibility = 'visible';
38093         }
38094     },
38095
38096     // private
38097     
38098     onHide : function(){
38099         var ae = this.getActionEl();
38100         ae.dom.style.display = 'none';
38101     },
38102
38103     /**
38104      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
38105      * by an implementing function.
38106      * @method
38107      * @param {EventObject} e
38108      */
38109     onTriggerClick : Roo.emptyFn
38110 });
38111
38112 // TwinTriggerField is not a public class to be used directly.  It is meant as an abstract base class
38113 // to be extended by an implementing class.  For an example of implementing this class, see the custom
38114 // SearchField implementation here: http://extjs.com/deploy/ext/examples/form/custom.html
38115 Roo.form.TwinTriggerField = Roo.extend(Roo.form.TriggerField, {
38116     initComponent : function(){
38117         Roo.form.TwinTriggerField.superclass.initComponent.call(this);
38118
38119         this.triggerConfig = {
38120             tag:'span', cls:'x-form-twin-triggers', cn:[
38121             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger1Class},
38122             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger2Class}
38123         ]};
38124     },
38125
38126     getTrigger : function(index){
38127         return this.triggers[index];
38128     },
38129
38130     initTrigger : function(){
38131         var ts = this.trigger.select('.x-form-trigger', true);
38132         this.wrap.setStyle('overflow', 'hidden');
38133         var triggerField = this;
38134         ts.each(function(t, all, index){
38135             t.hide = function(){
38136                 var w = triggerField.wrap.getWidth();
38137                 this.dom.style.display = 'none';
38138                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
38139             };
38140             t.show = function(){
38141                 var w = triggerField.wrap.getWidth();
38142                 this.dom.style.display = '';
38143                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
38144             };
38145             var triggerIndex = 'Trigger'+(index+1);
38146
38147             if(this['hide'+triggerIndex]){
38148                 t.dom.style.display = 'none';
38149             }
38150             t.on("click", this['on'+triggerIndex+'Click'], this, {preventDefault:true});
38151             t.addClassOnOver('x-form-trigger-over');
38152             t.addClassOnClick('x-form-trigger-click');
38153         }, this);
38154         this.triggers = ts.elements;
38155     },
38156
38157     onTrigger1Click : Roo.emptyFn,
38158     onTrigger2Click : Roo.emptyFn
38159 });/*
38160  * Based on:
38161  * Ext JS Library 1.1.1
38162  * Copyright(c) 2006-2007, Ext JS, LLC.
38163  *
38164  * Originally Released Under LGPL - original licence link has changed is not relivant.
38165  *
38166  * Fork - LGPL
38167  * <script type="text/javascript">
38168  */
38169  
38170 /**
38171  * @class Roo.form.TextArea
38172  * @extends Roo.form.TextField
38173  * Multiline text field.  Can be used as a direct replacement for traditional textarea fields, plus adds
38174  * support for auto-sizing.
38175  * @constructor
38176  * Creates a new TextArea
38177  * @param {Object} config Configuration options
38178  */
38179 Roo.form.TextArea = function(config){
38180     Roo.form.TextArea.superclass.constructor.call(this, config);
38181     // these are provided exchanges for backwards compat
38182     // minHeight/maxHeight were replaced by growMin/growMax to be
38183     // compatible with TextField growing config values
38184     if(this.minHeight !== undefined){
38185         this.growMin = this.minHeight;
38186     }
38187     if(this.maxHeight !== undefined){
38188         this.growMax = this.maxHeight;
38189     }
38190 };
38191
38192 Roo.extend(Roo.form.TextArea, Roo.form.TextField,  {
38193     /**
38194      * @cfg {Number} growMin The minimum height to allow when grow = true (defaults to 60)
38195      */
38196     growMin : 60,
38197     /**
38198      * @cfg {Number} growMax The maximum height to allow when grow = true (defaults to 1000)
38199      */
38200     growMax: 1000,
38201     /**
38202      * @cfg {Boolean} preventScrollbars True to prevent scrollbars from appearing regardless of how much text is
38203      * in the field (equivalent to setting overflow: hidden, defaults to false)
38204      */
38205     preventScrollbars: false,
38206     /**
38207      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
38208      * {tag: "textarea", style: "width:300px;height:60px;", autocomplete: "off"})
38209      */
38210
38211     // private
38212     onRender : function(ct, position){
38213         if(!this.el){
38214             this.defaultAutoCreate = {
38215                 tag: "textarea",
38216                 style:"width:300px;height:60px;",
38217                 autocomplete: "off"
38218             };
38219         }
38220         Roo.form.TextArea.superclass.onRender.call(this, ct, position);
38221         if(this.grow){
38222             this.textSizeEl = Roo.DomHelper.append(document.body, {
38223                 tag: "pre", cls: "x-form-grow-sizer"
38224             });
38225             if(this.preventScrollbars){
38226                 this.el.setStyle("overflow", "hidden");
38227             }
38228             this.el.setHeight(this.growMin);
38229         }
38230     },
38231
38232     onDestroy : function(){
38233         if(this.textSizeEl){
38234             this.textSizeEl.parentNode.removeChild(this.textSizeEl);
38235         }
38236         Roo.form.TextArea.superclass.onDestroy.call(this);
38237     },
38238
38239     // private
38240     onKeyUp : function(e){
38241         if(!e.isNavKeyPress() || e.getKey() == e.ENTER){
38242             this.autoSize();
38243         }
38244     },
38245
38246     /**
38247      * Automatically grows the field to accomodate the height of the text up to the maximum field height allowed.
38248      * This only takes effect if grow = true, and fires the autosize event if the height changes.
38249      */
38250     autoSize : function(){
38251         if(!this.grow || !this.textSizeEl){
38252             return;
38253         }
38254         var el = this.el;
38255         var v = el.dom.value;
38256         var ts = this.textSizeEl;
38257
38258         ts.innerHTML = '';
38259         ts.appendChild(document.createTextNode(v));
38260         v = ts.innerHTML;
38261
38262         Roo.fly(ts).setWidth(this.el.getWidth());
38263         if(v.length < 1){
38264             v = "&#160;&#160;";
38265         }else{
38266             if(Roo.isIE){
38267                 v = v.replace(/\n/g, '<p>&#160;</p>');
38268             }
38269             v += "&#160;\n&#160;";
38270         }
38271         ts.innerHTML = v;
38272         var h = Math.min(this.growMax, Math.max(ts.offsetHeight, this.growMin));
38273         if(h != this.lastHeight){
38274             this.lastHeight = h;
38275             this.el.setHeight(h);
38276             this.fireEvent("autosize", this, h);
38277         }
38278     }
38279 });/*
38280  * Based on:
38281  * Ext JS Library 1.1.1
38282  * Copyright(c) 2006-2007, Ext JS, LLC.
38283  *
38284  * Originally Released Under LGPL - original licence link has changed is not relivant.
38285  *
38286  * Fork - LGPL
38287  * <script type="text/javascript">
38288  */
38289  
38290
38291 /**
38292  * @class Roo.form.NumberField
38293  * @extends Roo.form.TextField
38294  * Numeric text field that provides automatic keystroke filtering and numeric validation.
38295  * @constructor
38296  * Creates a new NumberField
38297  * @param {Object} config Configuration options
38298  */
38299 Roo.form.NumberField = function(config){
38300     Roo.form.NumberField.superclass.constructor.call(this, config);
38301 };
38302
38303 Roo.extend(Roo.form.NumberField, Roo.form.TextField,  {
38304     /**
38305      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field x-form-num-field")
38306      */
38307     fieldClass: "x-form-field x-form-num-field",
38308     /**
38309      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
38310      */
38311     allowDecimals : true,
38312     /**
38313      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
38314      */
38315     decimalSeparator : ".",
38316     /**
38317      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
38318      */
38319     decimalPrecision : 2,
38320     /**
38321      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
38322      */
38323     allowNegative : true,
38324     /**
38325      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
38326      */
38327     minValue : Number.NEGATIVE_INFINITY,
38328     /**
38329      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
38330      */
38331     maxValue : Number.MAX_VALUE,
38332     /**
38333      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
38334      */
38335     minText : "The minimum value for this field is {0}",
38336     /**
38337      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
38338      */
38339     maxText : "The maximum value for this field is {0}",
38340     /**
38341      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
38342      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
38343      */
38344     nanText : "{0} is not a valid number",
38345
38346     // private
38347     initEvents : function(){
38348         Roo.form.NumberField.superclass.initEvents.call(this);
38349         var allowed = "0123456789";
38350         if(this.allowDecimals){
38351             allowed += this.decimalSeparator;
38352         }
38353         if(this.allowNegative){
38354             allowed += "-";
38355         }
38356         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
38357         var keyPress = function(e){
38358             var k = e.getKey();
38359             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
38360                 return;
38361             }
38362             var c = e.getCharCode();
38363             if(allowed.indexOf(String.fromCharCode(c)) === -1){
38364                 e.stopEvent();
38365             }
38366         };
38367         this.el.on("keypress", keyPress, this);
38368     },
38369
38370     // private
38371     validateValue : function(value){
38372         if(!Roo.form.NumberField.superclass.validateValue.call(this, value)){
38373             return false;
38374         }
38375         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
38376              return true;
38377         }
38378         var num = this.parseValue(value);
38379         if(isNaN(num)){
38380             this.markInvalid(String.format(this.nanText, value));
38381             return false;
38382         }
38383         if(num < this.minValue){
38384             this.markInvalid(String.format(this.minText, this.minValue));
38385             return false;
38386         }
38387         if(num > this.maxValue){
38388             this.markInvalid(String.format(this.maxText, this.maxValue));
38389             return false;
38390         }
38391         return true;
38392     },
38393
38394     getValue : function(){
38395         return this.fixPrecision(this.parseValue(Roo.form.NumberField.superclass.getValue.call(this)));
38396     },
38397
38398     // private
38399     parseValue : function(value){
38400         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
38401         return isNaN(value) ? '' : value;
38402     },
38403
38404     // private
38405     fixPrecision : function(value){
38406         var nan = isNaN(value);
38407         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
38408             return nan ? '' : value;
38409         }
38410         return parseFloat(value).toFixed(this.decimalPrecision);
38411     },
38412
38413     setValue : function(v){
38414         v = this.fixPrecision(v);
38415         Roo.form.NumberField.superclass.setValue.call(this, String(v).replace(".", this.decimalSeparator));
38416     },
38417
38418     // private
38419     decimalPrecisionFcn : function(v){
38420         return Math.floor(v);
38421     },
38422
38423     beforeBlur : function(){
38424         var v = this.parseValue(this.getRawValue());
38425         if(v){
38426             this.setValue(v);
38427         }
38428     }
38429 });/*
38430  * Based on:
38431  * Ext JS Library 1.1.1
38432  * Copyright(c) 2006-2007, Ext JS, LLC.
38433  *
38434  * Originally Released Under LGPL - original licence link has changed is not relivant.
38435  *
38436  * Fork - LGPL
38437  * <script type="text/javascript">
38438  */
38439  
38440 /**
38441  * @class Roo.form.DateField
38442  * @extends Roo.form.TriggerField
38443  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
38444 * @constructor
38445 * Create a new DateField
38446 * @param {Object} config
38447  */
38448 Roo.form.DateField = function(config){
38449     Roo.form.DateField.superclass.constructor.call(this, config);
38450     
38451       this.addEvents({
38452          
38453         /**
38454          * @event select
38455          * Fires when a date is selected
38456              * @param {Roo.form.DateField} combo This combo box
38457              * @param {Date} date The date selected
38458              */
38459         'select' : true
38460          
38461     });
38462     
38463     
38464     if(typeof this.minValue == "string") this.minValue = this.parseDate(this.minValue);
38465     if(typeof this.maxValue == "string") this.maxValue = this.parseDate(this.maxValue);
38466     this.ddMatch = null;
38467     if(this.disabledDates){
38468         var dd = this.disabledDates;
38469         var re = "(?:";
38470         for(var i = 0; i < dd.length; i++){
38471             re += dd[i];
38472             if(i != dd.length-1) re += "|";
38473         }
38474         this.ddMatch = new RegExp(re + ")");
38475     }
38476 };
38477
38478 Roo.extend(Roo.form.DateField, Roo.form.TriggerField,  {
38479     /**
38480      * @cfg {String} format
38481      * The default date format string which can be overriden for localization support.  The format must be
38482      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
38483      */
38484     format : "m/d/y",
38485     /**
38486      * @cfg {String} altFormats
38487      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
38488      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
38489      */
38490     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
38491     /**
38492      * @cfg {Array} disabledDays
38493      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
38494      */
38495     disabledDays : null,
38496     /**
38497      * @cfg {String} disabledDaysText
38498      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
38499      */
38500     disabledDaysText : "Disabled",
38501     /**
38502      * @cfg {Array} disabledDates
38503      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
38504      * expression so they are very powerful. Some examples:
38505      * <ul>
38506      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
38507      * <li>["03/08", "09/16"] would disable those days for every year</li>
38508      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
38509      * <li>["03/../2006"] would disable every day in March 2006</li>
38510      * <li>["^03"] would disable every day in every March</li>
38511      * </ul>
38512      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
38513      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
38514      */
38515     disabledDates : null,
38516     /**
38517      * @cfg {String} disabledDatesText
38518      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
38519      */
38520     disabledDatesText : "Disabled",
38521     /**
38522      * @cfg {Date/String} minValue
38523      * The minimum allowed date. Can be either a Javascript date object or a string date in a
38524      * valid format (defaults to null).
38525      */
38526     minValue : null,
38527     /**
38528      * @cfg {Date/String} maxValue
38529      * The maximum allowed date. Can be either a Javascript date object or a string date in a
38530      * valid format (defaults to null).
38531      */
38532     maxValue : null,
38533     /**
38534      * @cfg {String} minText
38535      * The error text to display when the date in the cell is before minValue (defaults to
38536      * 'The date in this field must be after {minValue}').
38537      */
38538     minText : "The date in this field must be equal to or after {0}",
38539     /**
38540      * @cfg {String} maxText
38541      * The error text to display when the date in the cell is after maxValue (defaults to
38542      * 'The date in this field must be before {maxValue}').
38543      */
38544     maxText : "The date in this field must be equal to or before {0}",
38545     /**
38546      * @cfg {String} invalidText
38547      * The error text to display when the date in the field is invalid (defaults to
38548      * '{value} is not a valid date - it must be in the format {format}').
38549      */
38550     invalidText : "{0} is not a valid date - it must be in the format {1}",
38551     /**
38552      * @cfg {String} triggerClass
38553      * An additional CSS class used to style the trigger button.  The trigger will always get the
38554      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
38555      * which displays a calendar icon).
38556      */
38557     triggerClass : 'x-form-date-trigger',
38558     
38559
38560     /**
38561      * @cfg {Boolean} useIso
38562      * if enabled, then the date field will use a hidden field to store the 
38563      * real value as iso formated date. default (false)
38564      */ 
38565     useIso : false,
38566     /**
38567      * @cfg {String/Object} autoCreate
38568      * A DomHelper element spec, or true for a default element spec (defaults to
38569      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
38570      */ 
38571     // private
38572     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "off"},
38573     
38574     // private
38575     hiddenField: false,
38576     
38577     onRender : function(ct, position)
38578     {
38579         Roo.form.DateField.superclass.onRender.call(this, ct, position);
38580         if (this.useIso) {
38581             //this.el.dom.removeAttribute('name'); 
38582             Roo.log("Changing name?");
38583             this.el.dom.setAttribute('name', this.name + '____hidden___' ); 
38584             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
38585                     'before', true);
38586             this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
38587             // prevent input submission
38588             this.hiddenName = this.name;
38589         }
38590             
38591             
38592     },
38593     
38594     // private
38595     validateValue : function(value)
38596     {
38597         value = this.formatDate(value);
38598         if(!Roo.form.DateField.superclass.validateValue.call(this, value)){
38599             Roo.log('super failed');
38600             return false;
38601         }
38602         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
38603              return true;
38604         }
38605         var svalue = value;
38606         value = this.parseDate(value);
38607         if(!value){
38608             Roo.log('parse date failed' + svalue);
38609             this.markInvalid(String.format(this.invalidText, svalue, this.format));
38610             return false;
38611         }
38612         var time = value.getTime();
38613         if(this.minValue && time < this.minValue.getTime()){
38614             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
38615             return false;
38616         }
38617         if(this.maxValue && time > this.maxValue.getTime()){
38618             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
38619             return false;
38620         }
38621         if(this.disabledDays){
38622             var day = value.getDay();
38623             for(var i = 0; i < this.disabledDays.length; i++) {
38624                 if(day === this.disabledDays[i]){
38625                     this.markInvalid(this.disabledDaysText);
38626                     return false;
38627                 }
38628             }
38629         }
38630         var fvalue = this.formatDate(value);
38631         if(this.ddMatch && this.ddMatch.test(fvalue)){
38632             this.markInvalid(String.format(this.disabledDatesText, fvalue));
38633             return false;
38634         }
38635         return true;
38636     },
38637
38638     // private
38639     // Provides logic to override the default TriggerField.validateBlur which just returns true
38640     validateBlur : function(){
38641         return !this.menu || !this.menu.isVisible();
38642     },
38643     
38644     getName: function()
38645     {
38646         // returns hidden if it's set..
38647         if (!this.rendered) {return ''};
38648         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
38649         
38650     },
38651
38652     /**
38653      * Returns the current date value of the date field.
38654      * @return {Date} The date value
38655      */
38656     getValue : function(){
38657         
38658         return  this.hiddenField ?
38659                 this.hiddenField.value :
38660                 this.parseDate(Roo.form.DateField.superclass.getValue.call(this)) || "";
38661     },
38662
38663     /**
38664      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
38665      * date, using DateField.format as the date format, according to the same rules as {@link Date#parseDate}
38666      * (the default format used is "m/d/y").
38667      * <br />Usage:
38668      * <pre><code>
38669 //All of these calls set the same date value (May 4, 2006)
38670
38671 //Pass a date object:
38672 var dt = new Date('5/4/06');
38673 dateField.setValue(dt);
38674
38675 //Pass a date string (default format):
38676 dateField.setValue('5/4/06');
38677
38678 //Pass a date string (custom format):
38679 dateField.format = 'Y-m-d';
38680 dateField.setValue('2006-5-4');
38681 </code></pre>
38682      * @param {String/Date} date The date or valid date string
38683      */
38684     setValue : function(date){
38685         if (this.hiddenField) {
38686             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
38687         }
38688         Roo.form.DateField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
38689         // make sure the value field is always stored as a date..
38690         this.value = this.parseDate(date);
38691         
38692         
38693     },
38694
38695     // private
38696     parseDate : function(value){
38697         if(!value || value instanceof Date){
38698             return value;
38699         }
38700         var v = Date.parseDate(value, this.format);
38701          if (!v && this.useIso) {
38702             v = Date.parseDate(value, 'Y-m-d');
38703         }
38704         if(!v && this.altFormats){
38705             if(!this.altFormatsArray){
38706                 this.altFormatsArray = this.altFormats.split("|");
38707             }
38708             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
38709                 v = Date.parseDate(value, this.altFormatsArray[i]);
38710             }
38711         }
38712         return v;
38713     },
38714
38715     // private
38716     formatDate : function(date, fmt){
38717         return (!date || !(date instanceof Date)) ?
38718                date : date.dateFormat(fmt || this.format);
38719     },
38720
38721     // private
38722     menuListeners : {
38723         select: function(m, d){
38724             
38725             this.setValue(d);
38726             this.fireEvent('select', this, d);
38727         },
38728         show : function(){ // retain focus styling
38729             this.onFocus();
38730         },
38731         hide : function(){
38732             this.focus.defer(10, this);
38733             var ml = this.menuListeners;
38734             this.menu.un("select", ml.select,  this);
38735             this.menu.un("show", ml.show,  this);
38736             this.menu.un("hide", ml.hide,  this);
38737         }
38738     },
38739
38740     // private
38741     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
38742     onTriggerClick : function(){
38743         if(this.disabled){
38744             return;
38745         }
38746         if(this.menu == null){
38747             this.menu = new Roo.menu.DateMenu();
38748         }
38749         Roo.apply(this.menu.picker,  {
38750             showClear: this.allowBlank,
38751             minDate : this.minValue,
38752             maxDate : this.maxValue,
38753             disabledDatesRE : this.ddMatch,
38754             disabledDatesText : this.disabledDatesText,
38755             disabledDays : this.disabledDays,
38756             disabledDaysText : this.disabledDaysText,
38757             format : this.useIso ? 'Y-m-d' : this.format,
38758             minText : String.format(this.minText, this.formatDate(this.minValue)),
38759             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
38760         });
38761         this.menu.on(Roo.apply({}, this.menuListeners, {
38762             scope:this
38763         }));
38764         this.menu.picker.setValue(this.getValue() || new Date());
38765         this.menu.show(this.el, "tl-bl?");
38766     },
38767
38768     beforeBlur : function(){
38769         var v = this.parseDate(this.getRawValue());
38770         if(v){
38771             this.setValue(v);
38772         }
38773     },
38774
38775     /*@
38776      * overide
38777      * 
38778      */
38779     isDirty : function() {
38780         if(this.disabled) {
38781             return false;
38782         }
38783         
38784         if(typeof(this.startValue) === 'undefined'){
38785             return false;
38786         }
38787         
38788         return String(this.getValue()) !== String(this.startValue);
38789         
38790     }
38791 });/*
38792  * Based on:
38793  * Ext JS Library 1.1.1
38794  * Copyright(c) 2006-2007, Ext JS, LLC.
38795  *
38796  * Originally Released Under LGPL - original licence link has changed is not relivant.
38797  *
38798  * Fork - LGPL
38799  * <script type="text/javascript">
38800  */
38801  
38802 /**
38803  * @class Roo.form.MonthField
38804  * @extends Roo.form.TriggerField
38805  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
38806 * @constructor
38807 * Create a new MonthField
38808 * @param {Object} config
38809  */
38810 Roo.form.MonthField = function(config){
38811     
38812     Roo.form.MonthField.superclass.constructor.call(this, config);
38813     
38814       this.addEvents({
38815          
38816         /**
38817          * @event select
38818          * Fires when a date is selected
38819              * @param {Roo.form.MonthFieeld} combo This combo box
38820              * @param {Date} date The date selected
38821              */
38822         'select' : true
38823          
38824     });
38825     
38826     
38827     if(typeof this.minValue == "string") this.minValue = this.parseDate(this.minValue);
38828     if(typeof this.maxValue == "string") this.maxValue = this.parseDate(this.maxValue);
38829     this.ddMatch = null;
38830     if(this.disabledDates){
38831         var dd = this.disabledDates;
38832         var re = "(?:";
38833         for(var i = 0; i < dd.length; i++){
38834             re += dd[i];
38835             if(i != dd.length-1) re += "|";
38836         }
38837         this.ddMatch = new RegExp(re + ")");
38838     }
38839 };
38840
38841 Roo.extend(Roo.form.MonthField, Roo.form.TriggerField,  {
38842     /**
38843      * @cfg {String} format
38844      * The default date format string which can be overriden for localization support.  The format must be
38845      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
38846      */
38847     format : "M Y",
38848     /**
38849      * @cfg {String} altFormats
38850      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
38851      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
38852      */
38853     altFormats : "M Y|m/Y|m-y|m-Y|my|mY",
38854     /**
38855      * @cfg {Array} disabledDays
38856      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
38857      */
38858     disabledDays : [0,1,2,3,4,5,6],
38859     /**
38860      * @cfg {String} disabledDaysText
38861      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
38862      */
38863     disabledDaysText : "Disabled",
38864     /**
38865      * @cfg {Array} disabledDates
38866      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
38867      * expression so they are very powerful. Some examples:
38868      * <ul>
38869      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
38870      * <li>["03/08", "09/16"] would disable those days for every year</li>
38871      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
38872      * <li>["03/../2006"] would disable every day in March 2006</li>
38873      * <li>["^03"] would disable every day in every March</li>
38874      * </ul>
38875      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
38876      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
38877      */
38878     disabledDates : null,
38879     /**
38880      * @cfg {String} disabledDatesText
38881      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
38882      */
38883     disabledDatesText : "Disabled",
38884     /**
38885      * @cfg {Date/String} minValue
38886      * The minimum allowed date. Can be either a Javascript date object or a string date in a
38887      * valid format (defaults to null).
38888      */
38889     minValue : null,
38890     /**
38891      * @cfg {Date/String} maxValue
38892      * The maximum allowed date. Can be either a Javascript date object or a string date in a
38893      * valid format (defaults to null).
38894      */
38895     maxValue : null,
38896     /**
38897      * @cfg {String} minText
38898      * The error text to display when the date in the cell is before minValue (defaults to
38899      * 'The date in this field must be after {minValue}').
38900      */
38901     minText : "The date in this field must be equal to or after {0}",
38902     /**
38903      * @cfg {String} maxTextf
38904      * The error text to display when the date in the cell is after maxValue (defaults to
38905      * 'The date in this field must be before {maxValue}').
38906      */
38907     maxText : "The date in this field must be equal to or before {0}",
38908     /**
38909      * @cfg {String} invalidText
38910      * The error text to display when the date in the field is invalid (defaults to
38911      * '{value} is not a valid date - it must be in the format {format}').
38912      */
38913     invalidText : "{0} is not a valid date - it must be in the format {1}",
38914     /**
38915      * @cfg {String} triggerClass
38916      * An additional CSS class used to style the trigger button.  The trigger will always get the
38917      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
38918      * which displays a calendar icon).
38919      */
38920     triggerClass : 'x-form-date-trigger',
38921     
38922
38923     /**
38924      * @cfg {Boolean} useIso
38925      * if enabled, then the date field will use a hidden field to store the 
38926      * real value as iso formated date. default (true)
38927      */ 
38928     useIso : true,
38929     /**
38930      * @cfg {String/Object} autoCreate
38931      * A DomHelper element spec, or true for a default element spec (defaults to
38932      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
38933      */ 
38934     // private
38935     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "off"},
38936     
38937     // private
38938     hiddenField: false,
38939     
38940     hideMonthPicker : false,
38941     
38942     onRender : function(ct, position)
38943     {
38944         Roo.form.MonthField.superclass.onRender.call(this, ct, position);
38945         if (this.useIso) {
38946             this.el.dom.removeAttribute('name'); 
38947             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
38948                     'before', true);
38949             this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
38950             // prevent input submission
38951             this.hiddenName = this.name;
38952         }
38953             
38954             
38955     },
38956     
38957     // private
38958     validateValue : function(value)
38959     {
38960         value = this.formatDate(value);
38961         if(!Roo.form.MonthField.superclass.validateValue.call(this, value)){
38962             return false;
38963         }
38964         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
38965              return true;
38966         }
38967         var svalue = value;
38968         value = this.parseDate(value);
38969         if(!value){
38970             this.markInvalid(String.format(this.invalidText, svalue, this.format));
38971             return false;
38972         }
38973         var time = value.getTime();
38974         if(this.minValue && time < this.minValue.getTime()){
38975             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
38976             return false;
38977         }
38978         if(this.maxValue && time > this.maxValue.getTime()){
38979             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
38980             return false;
38981         }
38982         /*if(this.disabledDays){
38983             var day = value.getDay();
38984             for(var i = 0; i < this.disabledDays.length; i++) {
38985                 if(day === this.disabledDays[i]){
38986                     this.markInvalid(this.disabledDaysText);
38987                     return false;
38988                 }
38989             }
38990         }
38991         */
38992         var fvalue = this.formatDate(value);
38993         /*if(this.ddMatch && this.ddMatch.test(fvalue)){
38994             this.markInvalid(String.format(this.disabledDatesText, fvalue));
38995             return false;
38996         }
38997         */
38998         return true;
38999     },
39000
39001     // private
39002     // Provides logic to override the default TriggerField.validateBlur which just returns true
39003     validateBlur : function(){
39004         return !this.menu || !this.menu.isVisible();
39005     },
39006
39007     /**
39008      * Returns the current date value of the date field.
39009      * @return {Date} The date value
39010      */
39011     getValue : function(){
39012         
39013         
39014         
39015         return  this.hiddenField ?
39016                 this.hiddenField.value :
39017                 this.parseDate(Roo.form.MonthField.superclass.getValue.call(this)) || "";
39018     },
39019
39020     /**
39021      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
39022      * date, using MonthField.format as the date format, according to the same rules as {@link Date#parseDate}
39023      * (the default format used is "m/d/y").
39024      * <br />Usage:
39025      * <pre><code>
39026 //All of these calls set the same date value (May 4, 2006)
39027
39028 //Pass a date object:
39029 var dt = new Date('5/4/06');
39030 monthField.setValue(dt);
39031
39032 //Pass a date string (default format):
39033 monthField.setValue('5/4/06');
39034
39035 //Pass a date string (custom format):
39036 monthField.format = 'Y-m-d';
39037 monthField.setValue('2006-5-4');
39038 </code></pre>
39039      * @param {String/Date} date The date or valid date string
39040      */
39041     setValue : function(date){
39042         Roo.log('month setValue' + date);
39043         // can only be first of month..
39044         
39045         var val = this.parseDate(date);
39046         
39047         if (this.hiddenField) {
39048             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
39049         }
39050         Roo.form.MonthField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
39051         this.value = this.parseDate(date);
39052     },
39053
39054     // private
39055     parseDate : function(value){
39056         if(!value || value instanceof Date){
39057             value = value ? Date.parseDate(value.format('Y-m') + '-01', 'Y-m-d') : null;
39058             return value;
39059         }
39060         var v = Date.parseDate(value, this.format);
39061         if (!v && this.useIso) {
39062             v = Date.parseDate(value, 'Y-m-d');
39063         }
39064         if (v) {
39065             // 
39066             v = Date.parseDate(v.format('Y-m') +'-01', 'Y-m-d');
39067         }
39068         
39069         
39070         if(!v && this.altFormats){
39071             if(!this.altFormatsArray){
39072                 this.altFormatsArray = this.altFormats.split("|");
39073             }
39074             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
39075                 v = Date.parseDate(value, this.altFormatsArray[i]);
39076             }
39077         }
39078         return v;
39079     },
39080
39081     // private
39082     formatDate : function(date, fmt){
39083         return (!date || !(date instanceof Date)) ?
39084                date : date.dateFormat(fmt || this.format);
39085     },
39086
39087     // private
39088     menuListeners : {
39089         select: function(m, d){
39090             this.setValue(d);
39091             this.fireEvent('select', this, d);
39092         },
39093         show : function(){ // retain focus styling
39094             this.onFocus();
39095         },
39096         hide : function(){
39097             this.focus.defer(10, this);
39098             var ml = this.menuListeners;
39099             this.menu.un("select", ml.select,  this);
39100             this.menu.un("show", ml.show,  this);
39101             this.menu.un("hide", ml.hide,  this);
39102         }
39103     },
39104     // private
39105     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
39106     onTriggerClick : function(){
39107         if(this.disabled){
39108             return;
39109         }
39110         if(this.menu == null){
39111             this.menu = new Roo.menu.DateMenu();
39112            
39113         }
39114         
39115         Roo.apply(this.menu.picker,  {
39116             
39117             showClear: this.allowBlank,
39118             minDate : this.minValue,
39119             maxDate : this.maxValue,
39120             disabledDatesRE : this.ddMatch,
39121             disabledDatesText : this.disabledDatesText,
39122             
39123             format : this.useIso ? 'Y-m-d' : this.format,
39124             minText : String.format(this.minText, this.formatDate(this.minValue)),
39125             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
39126             
39127         });
39128          this.menu.on(Roo.apply({}, this.menuListeners, {
39129             scope:this
39130         }));
39131        
39132         
39133         var m = this.menu;
39134         var p = m.picker;
39135         
39136         // hide month picker get's called when we called by 'before hide';
39137         
39138         var ignorehide = true;
39139         p.hideMonthPicker  = function(disableAnim){
39140             if (ignorehide) {
39141                 return;
39142             }
39143              if(this.monthPicker){
39144                 Roo.log("hideMonthPicker called");
39145                 if(disableAnim === true){
39146                     this.monthPicker.hide();
39147                 }else{
39148                     this.monthPicker.slideOut('t', {duration:.2});
39149                     p.setValue(new Date(m.picker.mpSelYear, m.picker.mpSelMonth, 1));
39150                     p.fireEvent("select", this, this.value);
39151                     m.hide();
39152                 }
39153             }
39154         }
39155         
39156         Roo.log('picker set value');
39157         Roo.log(this.getValue());
39158         p.setValue(this.getValue() ? this.parseDate(this.getValue()) : new Date());
39159         m.show(this.el, 'tl-bl?');
39160         ignorehide  = false;
39161         // this will trigger hideMonthPicker..
39162         
39163         
39164         // hidden the day picker
39165         Roo.select('.x-date-picker table', true).first().dom.style.visibility = "hidden";
39166         
39167         
39168         
39169       
39170         
39171         p.showMonthPicker.defer(100, p);
39172     
39173         
39174        
39175     },
39176
39177     beforeBlur : function(){
39178         var v = this.parseDate(this.getRawValue());
39179         if(v){
39180             this.setValue(v);
39181         }
39182     }
39183
39184     /** @cfg {Boolean} grow @hide */
39185     /** @cfg {Number} growMin @hide */
39186     /** @cfg {Number} growMax @hide */
39187     /**
39188      * @hide
39189      * @method autoSize
39190      */
39191 });/*
39192  * Based on:
39193  * Ext JS Library 1.1.1
39194  * Copyright(c) 2006-2007, Ext JS, LLC.
39195  *
39196  * Originally Released Under LGPL - original licence link has changed is not relivant.
39197  *
39198  * Fork - LGPL
39199  * <script type="text/javascript">
39200  */
39201  
39202
39203 /**
39204  * @class Roo.form.ComboBox
39205  * @extends Roo.form.TriggerField
39206  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
39207  * @constructor
39208  * Create a new ComboBox.
39209  * @param {Object} config Configuration options
39210  */
39211 Roo.form.ComboBox = function(config){
39212     Roo.form.ComboBox.superclass.constructor.call(this, config);
39213     this.addEvents({
39214         /**
39215          * @event expand
39216          * Fires when the dropdown list is expanded
39217              * @param {Roo.form.ComboBox} combo This combo box
39218              */
39219         'expand' : true,
39220         /**
39221          * @event collapse
39222          * Fires when the dropdown list is collapsed
39223              * @param {Roo.form.ComboBox} combo This combo box
39224              */
39225         'collapse' : true,
39226         /**
39227          * @event beforeselect
39228          * Fires before a list item is selected. Return false to cancel the selection.
39229              * @param {Roo.form.ComboBox} combo This combo box
39230              * @param {Roo.data.Record} record The data record returned from the underlying store
39231              * @param {Number} index The index of the selected item in the dropdown list
39232              */
39233         'beforeselect' : true,
39234         /**
39235          * @event select
39236          * Fires when a list item is selected
39237              * @param {Roo.form.ComboBox} combo This combo box
39238              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
39239              * @param {Number} index The index of the selected item in the dropdown list
39240              */
39241         'select' : true,
39242         /**
39243          * @event beforequery
39244          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
39245          * The event object passed has these properties:
39246              * @param {Roo.form.ComboBox} combo This combo box
39247              * @param {String} query The query
39248              * @param {Boolean} forceAll true to force "all" query
39249              * @param {Boolean} cancel true to cancel the query
39250              * @param {Object} e The query event object
39251              */
39252         'beforequery': true,
39253          /**
39254          * @event add
39255          * Fires when the 'add' icon is pressed (add a listener to enable add button)
39256              * @param {Roo.form.ComboBox} combo This combo box
39257              */
39258         'add' : true,
39259         /**
39260          * @event edit
39261          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
39262              * @param {Roo.form.ComboBox} combo This combo box
39263              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
39264              */
39265         'edit' : true
39266         
39267         
39268     });
39269     if(this.transform){
39270         this.allowDomMove = false;
39271         var s = Roo.getDom(this.transform);
39272         if(!this.hiddenName){
39273             this.hiddenName = s.name;
39274         }
39275         if(!this.store){
39276             this.mode = 'local';
39277             var d = [], opts = s.options;
39278             for(var i = 0, len = opts.length;i < len; i++){
39279                 var o = opts[i];
39280                 var value = (Roo.isIE ? o.getAttributeNode('value').specified : o.hasAttribute('value')) ? o.value : o.text;
39281                 if(o.selected) {
39282                     this.value = value;
39283                 }
39284                 d.push([value, o.text]);
39285             }
39286             this.store = new Roo.data.SimpleStore({
39287                 'id': 0,
39288                 fields: ['value', 'text'],
39289                 data : d
39290             });
39291             this.valueField = 'value';
39292             this.displayField = 'text';
39293         }
39294         s.name = Roo.id(); // wipe out the name in case somewhere else they have a reference
39295         if(!this.lazyRender){
39296             this.target = true;
39297             this.el = Roo.DomHelper.insertBefore(s, this.autoCreate || this.defaultAutoCreate);
39298             s.parentNode.removeChild(s); // remove it
39299             this.render(this.el.parentNode);
39300         }else{
39301             s.parentNode.removeChild(s); // remove it
39302         }
39303
39304     }
39305     if (this.store) {
39306         this.store = Roo.factory(this.store, Roo.data);
39307     }
39308     
39309     this.selectedIndex = -1;
39310     if(this.mode == 'local'){
39311         if(config.queryDelay === undefined){
39312             this.queryDelay = 10;
39313         }
39314         if(config.minChars === undefined){
39315             this.minChars = 0;
39316         }
39317     }
39318 };
39319
39320 Roo.extend(Roo.form.ComboBox, Roo.form.TriggerField, {
39321     /**
39322      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
39323      */
39324     /**
39325      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
39326      * rendering into an Roo.Editor, defaults to false)
39327      */
39328     /**
39329      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
39330      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
39331      */
39332     /**
39333      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
39334      */
39335     /**
39336      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
39337      * the dropdown list (defaults to undefined, with no header element)
39338      */
39339
39340      /**
39341      * @cfg {String/Roo.Template} tpl The template to use to render the output
39342      */
39343      
39344     // private
39345     defaultAutoCreate : {tag: "input", type: "text", size: "24", autocomplete: "off"},
39346     /**
39347      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
39348      */
39349     listWidth: undefined,
39350     /**
39351      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
39352      * mode = 'remote' or 'text' if mode = 'local')
39353      */
39354     displayField: undefined,
39355     /**
39356      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
39357      * mode = 'remote' or 'value' if mode = 'local'). 
39358      * Note: use of a valueField requires the user make a selection
39359      * in order for a value to be mapped.
39360      */
39361     valueField: undefined,
39362     
39363     
39364     /**
39365      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
39366      * field's data value (defaults to the underlying DOM element's name)
39367      */
39368     hiddenName: undefined,
39369     /**
39370      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
39371      */
39372     listClass: '',
39373     /**
39374      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
39375      */
39376     selectedClass: 'x-combo-selected',
39377     /**
39378      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
39379      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
39380      * which displays a downward arrow icon).
39381      */
39382     triggerClass : 'x-form-arrow-trigger',
39383     /**
39384      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
39385      */
39386     shadow:'sides',
39387     /**
39388      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
39389      * anchor positions (defaults to 'tl-bl')
39390      */
39391     listAlign: 'tl-bl?',
39392     /**
39393      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
39394      */
39395     maxHeight: 300,
39396     /**
39397      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
39398      * query specified by the allQuery config option (defaults to 'query')
39399      */
39400     triggerAction: 'query',
39401     /**
39402      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
39403      * (defaults to 4, does not apply if editable = false)
39404      */
39405     minChars : 4,
39406     /**
39407      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
39408      * delay (typeAheadDelay) if it matches a known value (defaults to false)
39409      */
39410     typeAhead: false,
39411     /**
39412      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
39413      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
39414      */
39415     queryDelay: 500,
39416     /**
39417      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
39418      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
39419      */
39420     pageSize: 0,
39421     /**
39422      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
39423      * when editable = true (defaults to false)
39424      */
39425     selectOnFocus:false,
39426     /**
39427      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
39428      */
39429     queryParam: 'query',
39430     /**
39431      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
39432      * when mode = 'remote' (defaults to 'Loading...')
39433      */
39434     loadingText: 'Loading...',
39435     /**
39436      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
39437      */
39438     resizable: false,
39439     /**
39440      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
39441      */
39442     handleHeight : 8,
39443     /**
39444      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
39445      * traditional select (defaults to true)
39446      */
39447     editable: true,
39448     /**
39449      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
39450      */
39451     allQuery: '',
39452     /**
39453      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
39454      */
39455     mode: 'remote',
39456     /**
39457      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
39458      * listWidth has a higher value)
39459      */
39460     minListWidth : 70,
39461     /**
39462      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
39463      * allow the user to set arbitrary text into the field (defaults to false)
39464      */
39465     forceSelection:false,
39466     /**
39467      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
39468      * if typeAhead = true (defaults to 250)
39469      */
39470     typeAheadDelay : 250,
39471     /**
39472      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
39473      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
39474      */
39475     valueNotFoundText : undefined,
39476     /**
39477      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
39478      */
39479     blockFocus : false,
39480     
39481     /**
39482      * @cfg {Boolean} disableClear Disable showing of clear button.
39483      */
39484     disableClear : false,
39485     /**
39486      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
39487      */
39488     alwaysQuery : false,
39489     
39490     //private
39491     addicon : false,
39492     editicon: false,
39493     
39494     // element that contains real text value.. (when hidden is used..)
39495      
39496     // private
39497     onRender : function(ct, position){
39498         Roo.form.ComboBox.superclass.onRender.call(this, ct, position);
39499         if(this.hiddenName){
39500             this.hiddenField = this.el.insertSibling({tag:'input', type:'hidden', name: this.hiddenName, id:  (this.hiddenId||this.hiddenName)},
39501                     'before', true);
39502             this.hiddenField.value =
39503                 this.hiddenValue !== undefined ? this.hiddenValue :
39504                 this.value !== undefined ? this.value : '';
39505
39506             // prevent input submission
39507             this.el.dom.removeAttribute('name');
39508              
39509              
39510         }
39511         if(Roo.isGecko){
39512             this.el.dom.setAttribute('autocomplete', 'off');
39513         }
39514
39515         var cls = 'x-combo-list';
39516
39517         this.list = new Roo.Layer({
39518             shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
39519         });
39520
39521         var lw = this.listWidth || Math.max(this.wrap.getWidth(), this.minListWidth);
39522         this.list.setWidth(lw);
39523         this.list.swallowEvent('mousewheel');
39524         this.assetHeight = 0;
39525
39526         if(this.title){
39527             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
39528             this.assetHeight += this.header.getHeight();
39529         }
39530
39531         this.innerList = this.list.createChild({cls:cls+'-inner'});
39532         this.innerList.on('mouseover', this.onViewOver, this);
39533         this.innerList.on('mousemove', this.onViewMove, this);
39534         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
39535         
39536         if(this.allowBlank && !this.pageSize && !this.disableClear){
39537             this.footer = this.list.createChild({cls:cls+'-ft'});
39538             this.pageTb = new Roo.Toolbar(this.footer);
39539            
39540         }
39541         if(this.pageSize){
39542             this.footer = this.list.createChild({cls:cls+'-ft'});
39543             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
39544                     {pageSize: this.pageSize});
39545             
39546         }
39547         
39548         if (this.pageTb && this.allowBlank && !this.disableClear) {
39549             var _this = this;
39550             this.pageTb.add(new Roo.Toolbar.Fill(), {
39551                 cls: 'x-btn-icon x-btn-clear',
39552                 text: '&#160;',
39553                 handler: function()
39554                 {
39555                     _this.collapse();
39556                     _this.clearValue();
39557                     _this.onSelect(false, -1);
39558                 }
39559             });
39560         }
39561         if (this.footer) {
39562             this.assetHeight += this.footer.getHeight();
39563         }
39564         
39565
39566         if(!this.tpl){
39567             this.tpl = '<div class="'+cls+'-item">{' + this.displayField + '}</div>';
39568         }
39569
39570         this.view = new Roo.View(this.innerList, this.tpl, {
39571             singleSelect:true, store: this.store, selectedClass: this.selectedClass
39572         });
39573
39574         this.view.on('click', this.onViewClick, this);
39575
39576         this.store.on('beforeload', this.onBeforeLoad, this);
39577         this.store.on('load', this.onLoad, this);
39578         this.store.on('loadexception', this.onLoadException, this);
39579
39580         if(this.resizable){
39581             this.resizer = new Roo.Resizable(this.list,  {
39582                pinned:true, handles:'se'
39583             });
39584             this.resizer.on('resize', function(r, w, h){
39585                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
39586                 this.listWidth = w;
39587                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
39588                 this.restrictHeight();
39589             }, this);
39590             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
39591         }
39592         if(!this.editable){
39593             this.editable = true;
39594             this.setEditable(false);
39595         }  
39596         
39597         
39598         if (typeof(this.events.add.listeners) != 'undefined') {
39599             
39600             this.addicon = this.wrap.createChild(
39601                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
39602        
39603             this.addicon.on('click', function(e) {
39604                 this.fireEvent('add', this);
39605             }, this);
39606         }
39607         if (typeof(this.events.edit.listeners) != 'undefined') {
39608             
39609             this.editicon = this.wrap.createChild(
39610                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
39611             if (this.addicon) {
39612                 this.editicon.setStyle('margin-left', '40px');
39613             }
39614             this.editicon.on('click', function(e) {
39615                 
39616                 // we fire even  if inothing is selected..
39617                 this.fireEvent('edit', this, this.lastData );
39618                 
39619             }, this);
39620         }
39621         
39622         
39623         
39624     },
39625
39626     // private
39627     initEvents : function(){
39628         Roo.form.ComboBox.superclass.initEvents.call(this);
39629
39630         this.keyNav = new Roo.KeyNav(this.el, {
39631             "up" : function(e){
39632                 this.inKeyMode = true;
39633                 this.selectPrev();
39634             },
39635
39636             "down" : function(e){
39637                 if(!this.isExpanded()){
39638                     this.onTriggerClick();
39639                 }else{
39640                     this.inKeyMode = true;
39641                     this.selectNext();
39642                 }
39643             },
39644
39645             "enter" : function(e){
39646                 this.onViewClick();
39647                 //return true;
39648             },
39649
39650             "esc" : function(e){
39651                 this.collapse();
39652             },
39653
39654             "tab" : function(e){
39655                 this.onViewClick(false);
39656                 this.fireEvent("specialkey", this, e);
39657                 return true;
39658             },
39659
39660             scope : this,
39661
39662             doRelay : function(foo, bar, hname){
39663                 if(hname == 'down' || this.scope.isExpanded()){
39664                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
39665                 }
39666                 return true;
39667             },
39668
39669             forceKeyDown: true
39670         });
39671         this.queryDelay = Math.max(this.queryDelay || 10,
39672                 this.mode == 'local' ? 10 : 250);
39673         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
39674         if(this.typeAhead){
39675             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
39676         }
39677         if(this.editable !== false){
39678             this.el.on("keyup", this.onKeyUp, this);
39679         }
39680         if(this.forceSelection){
39681             this.on('blur', this.doForce, this);
39682         }
39683     },
39684
39685     onDestroy : function(){
39686         if(this.view){
39687             this.view.setStore(null);
39688             this.view.el.removeAllListeners();
39689             this.view.el.remove();
39690             this.view.purgeListeners();
39691         }
39692         if(this.list){
39693             this.list.destroy();
39694         }
39695         if(this.store){
39696             this.store.un('beforeload', this.onBeforeLoad, this);
39697             this.store.un('load', this.onLoad, this);
39698             this.store.un('loadexception', this.onLoadException, this);
39699         }
39700         Roo.form.ComboBox.superclass.onDestroy.call(this);
39701     },
39702
39703     // private
39704     fireKey : function(e){
39705         if(e.isNavKeyPress() && !this.list.isVisible()){
39706             this.fireEvent("specialkey", this, e);
39707         }
39708     },
39709
39710     // private
39711     onResize: function(w, h){
39712         Roo.form.ComboBox.superclass.onResize.apply(this, arguments);
39713         
39714         if(typeof w != 'number'){
39715             // we do not handle it!?!?
39716             return;
39717         }
39718         var tw = this.trigger.getWidth();
39719         tw += this.addicon ? this.addicon.getWidth() : 0;
39720         tw += this.editicon ? this.editicon.getWidth() : 0;
39721         var x = w - tw;
39722         this.el.setWidth( this.adjustWidth('input', x));
39723             
39724         this.trigger.setStyle('left', x+'px');
39725         
39726         if(this.list && this.listWidth === undefined){
39727             var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
39728             this.list.setWidth(lw);
39729             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
39730         }
39731         
39732     
39733         
39734     },
39735
39736     /**
39737      * Allow or prevent the user from directly editing the field text.  If false is passed,
39738      * the user will only be able to select from the items defined in the dropdown list.  This method
39739      * is the runtime equivalent of setting the 'editable' config option at config time.
39740      * @param {Boolean} value True to allow the user to directly edit the field text
39741      */
39742     setEditable : function(value){
39743         if(value == this.editable){
39744             return;
39745         }
39746         this.editable = value;
39747         if(!value){
39748             this.el.dom.setAttribute('readOnly', true);
39749             this.el.on('mousedown', this.onTriggerClick,  this);
39750             this.el.addClass('x-combo-noedit');
39751         }else{
39752             this.el.dom.setAttribute('readOnly', false);
39753             this.el.un('mousedown', this.onTriggerClick,  this);
39754             this.el.removeClass('x-combo-noedit');
39755         }
39756     },
39757
39758     // private
39759     onBeforeLoad : function(){
39760         if(!this.hasFocus){
39761             return;
39762         }
39763         this.innerList.update(this.loadingText ?
39764                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
39765         this.restrictHeight();
39766         this.selectedIndex = -1;
39767     },
39768
39769     // private
39770     onLoad : function(){
39771         if(!this.hasFocus){
39772             return;
39773         }
39774         if(this.store.getCount() > 0){
39775             this.expand();
39776             this.restrictHeight();
39777             if(this.lastQuery == this.allQuery){
39778                 if(this.editable){
39779                     this.el.dom.select();
39780                 }
39781                 if(!this.selectByValue(this.value, true)){
39782                     this.select(0, true);
39783                 }
39784             }else{
39785                 this.selectNext();
39786                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
39787                     this.taTask.delay(this.typeAheadDelay);
39788                 }
39789             }
39790         }else{
39791             this.onEmptyResults();
39792         }
39793         //this.el.focus();
39794     },
39795     // private
39796     onLoadException : function()
39797     {
39798         this.collapse();
39799         Roo.log(this.store.reader.jsonData);
39800         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
39801             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
39802         }
39803         
39804         
39805     },
39806     // private
39807     onTypeAhead : function(){
39808         if(this.store.getCount() > 0){
39809             var r = this.store.getAt(0);
39810             var newValue = r.data[this.displayField];
39811             var len = newValue.length;
39812             var selStart = this.getRawValue().length;
39813             if(selStart != len){
39814                 this.setRawValue(newValue);
39815                 this.selectText(selStart, newValue.length);
39816             }
39817         }
39818     },
39819
39820     // private
39821     onSelect : function(record, index){
39822         if(this.fireEvent('beforeselect', this, record, index) !== false){
39823             this.setFromData(index > -1 ? record.data : false);
39824             this.collapse();
39825             this.fireEvent('select', this, record, index);
39826         }
39827     },
39828
39829     /**
39830      * Returns the currently selected field value or empty string if no value is set.
39831      * @return {String} value The selected value
39832      */
39833     getValue : function(){
39834         if(this.valueField){
39835             return typeof this.value != 'undefined' ? this.value : '';
39836         }else{
39837             return Roo.form.ComboBox.superclass.getValue.call(this);
39838         }
39839     },
39840
39841     /**
39842      * Clears any text/value currently set in the field
39843      */
39844     clearValue : function(){
39845         if(this.hiddenField){
39846             this.hiddenField.value = '';
39847         }
39848         this.value = '';
39849         this.setRawValue('');
39850         this.lastSelectionText = '';
39851         
39852     },
39853
39854     /**
39855      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
39856      * will be displayed in the field.  If the value does not match the data value of an existing item,
39857      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
39858      * Otherwise the field will be blank (although the value will still be set).
39859      * @param {String} value The value to match
39860      */
39861     setValue : function(v){
39862         var text = v;
39863         if(this.valueField){
39864             var r = this.findRecord(this.valueField, v);
39865             if(r){
39866                 text = r.data[this.displayField];
39867             }else if(this.valueNotFoundText !== undefined){
39868                 text = this.valueNotFoundText;
39869             }
39870         }
39871         this.lastSelectionText = text;
39872         if(this.hiddenField){
39873             this.hiddenField.value = v;
39874         }
39875         Roo.form.ComboBox.superclass.setValue.call(this, text);
39876         this.value = v;
39877     },
39878     /**
39879      * @property {Object} the last set data for the element
39880      */
39881     
39882     lastData : false,
39883     /**
39884      * Sets the value of the field based on a object which is related to the record format for the store.
39885      * @param {Object} value the value to set as. or false on reset?
39886      */
39887     setFromData : function(o){
39888         var dv = ''; // display value
39889         var vv = ''; // value value..
39890         this.lastData = o;
39891         if (this.displayField) {
39892             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
39893         } else {
39894             // this is an error condition!!!
39895             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
39896         }
39897         
39898         if(this.valueField){
39899             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
39900         }
39901         if(this.hiddenField){
39902             this.hiddenField.value = vv;
39903             
39904             this.lastSelectionText = dv;
39905             Roo.form.ComboBox.superclass.setValue.call(this, dv);
39906             this.value = vv;
39907             return;
39908         }
39909         // no hidden field.. - we store the value in 'value', but still display
39910         // display field!!!!
39911         this.lastSelectionText = dv;
39912         Roo.form.ComboBox.superclass.setValue.call(this, dv);
39913         this.value = vv;
39914         
39915         
39916     },
39917     // private
39918     reset : function(){
39919         // overridden so that last data is reset..
39920         this.setValue(this.resetValue);
39921         this.clearInvalid();
39922         this.lastData = false;
39923         if (this.view) {
39924             this.view.clearSelections();
39925         }
39926     },
39927     // private
39928     findRecord : function(prop, value){
39929         var record;
39930         if(this.store.getCount() > 0){
39931             this.store.each(function(r){
39932                 if(r.data[prop] == value){
39933                     record = r;
39934                     return false;
39935                 }
39936                 return true;
39937             });
39938         }
39939         return record;
39940     },
39941     
39942     getName: function()
39943     {
39944         // returns hidden if it's set..
39945         if (!this.rendered) {return ''};
39946         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
39947         
39948     },
39949     // private
39950     onViewMove : function(e, t){
39951         this.inKeyMode = false;
39952     },
39953
39954     // private
39955     onViewOver : function(e, t){
39956         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
39957             return;
39958         }
39959         var item = this.view.findItemFromChild(t);
39960         if(item){
39961             var index = this.view.indexOf(item);
39962             this.select(index, false);
39963         }
39964     },
39965
39966     // private
39967     onViewClick : function(doFocus)
39968     {
39969         var index = this.view.getSelectedIndexes()[0];
39970         var r = this.store.getAt(index);
39971         if(r){
39972             this.onSelect(r, index);
39973         }
39974         if(doFocus !== false && !this.blockFocus){
39975             this.el.focus();
39976         }
39977     },
39978
39979     // private
39980     restrictHeight : function(){
39981         this.innerList.dom.style.height = '';
39982         var inner = this.innerList.dom;
39983         var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
39984         this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
39985         this.list.beginUpdate();
39986         this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
39987         this.list.alignTo(this.el, this.listAlign);
39988         this.list.endUpdate();
39989     },
39990
39991     // private
39992     onEmptyResults : function(){
39993         this.collapse();
39994     },
39995
39996     /**
39997      * Returns true if the dropdown list is expanded, else false.
39998      */
39999     isExpanded : function(){
40000         return this.list.isVisible();
40001     },
40002
40003     /**
40004      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
40005      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
40006      * @param {String} value The data value of the item to select
40007      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
40008      * selected item if it is not currently in view (defaults to true)
40009      * @return {Boolean} True if the value matched an item in the list, else false
40010      */
40011     selectByValue : function(v, scrollIntoView){
40012         if(v !== undefined && v !== null){
40013             var r = this.findRecord(this.valueField || this.displayField, v);
40014             if(r){
40015                 this.select(this.store.indexOf(r), scrollIntoView);
40016                 return true;
40017             }
40018         }
40019         return false;
40020     },
40021
40022     /**
40023      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
40024      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
40025      * @param {Number} index The zero-based index of the list item to select
40026      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
40027      * selected item if it is not currently in view (defaults to true)
40028      */
40029     select : function(index, scrollIntoView){
40030         this.selectedIndex = index;
40031         this.view.select(index);
40032         if(scrollIntoView !== false){
40033             var el = this.view.getNode(index);
40034             if(el){
40035                 this.innerList.scrollChildIntoView(el, false);
40036             }
40037         }
40038     },
40039
40040     // private
40041     selectNext : function(){
40042         var ct = this.store.getCount();
40043         if(ct > 0){
40044             if(this.selectedIndex == -1){
40045                 this.select(0);
40046             }else if(this.selectedIndex < ct-1){
40047                 this.select(this.selectedIndex+1);
40048             }
40049         }
40050     },
40051
40052     // private
40053     selectPrev : function(){
40054         var ct = this.store.getCount();
40055         if(ct > 0){
40056             if(this.selectedIndex == -1){
40057                 this.select(0);
40058             }else if(this.selectedIndex != 0){
40059                 this.select(this.selectedIndex-1);
40060             }
40061         }
40062     },
40063
40064     // private
40065     onKeyUp : function(e){
40066         if(this.editable !== false && !e.isSpecialKey()){
40067             this.lastKey = e.getKey();
40068             this.dqTask.delay(this.queryDelay);
40069         }
40070     },
40071
40072     // private
40073     validateBlur : function(){
40074         return !this.list || !this.list.isVisible();   
40075     },
40076
40077     // private
40078     initQuery : function(){
40079         this.doQuery(this.getRawValue());
40080     },
40081
40082     // private
40083     doForce : function(){
40084         if(this.el.dom.value.length > 0){
40085             this.el.dom.value =
40086                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
40087              
40088         }
40089     },
40090
40091     /**
40092      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
40093      * query allowing the query action to be canceled if needed.
40094      * @param {String} query The SQL query to execute
40095      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
40096      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
40097      * saved in the current store (defaults to false)
40098      */
40099     doQuery : function(q, forceAll){
40100         if(q === undefined || q === null){
40101             q = '';
40102         }
40103         var qe = {
40104             query: q,
40105             forceAll: forceAll,
40106             combo: this,
40107             cancel:false
40108         };
40109         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
40110             return false;
40111         }
40112         q = qe.query;
40113         forceAll = qe.forceAll;
40114         if(forceAll === true || (q.length >= this.minChars)){
40115             if(this.lastQuery != q || this.alwaysQuery){
40116                 this.lastQuery = q;
40117                 if(this.mode == 'local'){
40118                     this.selectedIndex = -1;
40119                     if(forceAll){
40120                         this.store.clearFilter();
40121                     }else{
40122                         this.store.filter(this.displayField, q);
40123                     }
40124                     this.onLoad();
40125                 }else{
40126                     this.store.baseParams[this.queryParam] = q;
40127                     this.store.load({
40128                         params: this.getParams(q)
40129                     });
40130                     this.expand();
40131                 }
40132             }else{
40133                 this.selectedIndex = -1;
40134                 this.onLoad();   
40135             }
40136         }
40137     },
40138
40139     // private
40140     getParams : function(q){
40141         var p = {};
40142         //p[this.queryParam] = q;
40143         if(this.pageSize){
40144             p.start = 0;
40145             p.limit = this.pageSize;
40146         }
40147         return p;
40148     },
40149
40150     /**
40151      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
40152      */
40153     collapse : function(){
40154         if(!this.isExpanded()){
40155             return;
40156         }
40157         this.list.hide();
40158         Roo.get(document).un('mousedown', this.collapseIf, this);
40159         Roo.get(document).un('mousewheel', this.collapseIf, this);
40160         if (!this.editable) {
40161             Roo.get(document).un('keydown', this.listKeyPress, this);
40162         }
40163         this.fireEvent('collapse', this);
40164     },
40165
40166     // private
40167     collapseIf : function(e){
40168         if(!e.within(this.wrap) && !e.within(this.list)){
40169             this.collapse();
40170         }
40171     },
40172
40173     /**
40174      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
40175      */
40176     expand : function(){
40177         if(this.isExpanded() || !this.hasFocus){
40178             return;
40179         }
40180         this.list.alignTo(this.el, this.listAlign);
40181         this.list.show();
40182         Roo.get(document).on('mousedown', this.collapseIf, this);
40183         Roo.get(document).on('mousewheel', this.collapseIf, this);
40184         if (!this.editable) {
40185             Roo.get(document).on('keydown', this.listKeyPress, this);
40186         }
40187         
40188         this.fireEvent('expand', this);
40189     },
40190
40191     // private
40192     // Implements the default empty TriggerField.onTriggerClick function
40193     onTriggerClick : function(){
40194         if(this.disabled){
40195             return;
40196         }
40197         if(this.isExpanded()){
40198             this.collapse();
40199             if (!this.blockFocus) {
40200                 this.el.focus();
40201             }
40202             
40203         }else {
40204             this.hasFocus = true;
40205             if(this.triggerAction == 'all') {
40206                 this.doQuery(this.allQuery, true);
40207             } else {
40208                 this.doQuery(this.getRawValue());
40209             }
40210             if (!this.blockFocus) {
40211                 this.el.focus();
40212             }
40213         }
40214     },
40215     listKeyPress : function(e)
40216     {
40217         //Roo.log('listkeypress');
40218         // scroll to first matching element based on key pres..
40219         if (e.isSpecialKey()) {
40220             return false;
40221         }
40222         var k = String.fromCharCode(e.getKey()).toUpperCase();
40223         //Roo.log(k);
40224         var match  = false;
40225         var csel = this.view.getSelectedNodes();
40226         var cselitem = false;
40227         if (csel.length) {
40228             var ix = this.view.indexOf(csel[0]);
40229             cselitem  = this.store.getAt(ix);
40230             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
40231                 cselitem = false;
40232             }
40233             
40234         }
40235         
40236         this.store.each(function(v) { 
40237             if (cselitem) {
40238                 // start at existing selection.
40239                 if (cselitem.id == v.id) {
40240                     cselitem = false;
40241                 }
40242                 return;
40243             }
40244                 
40245             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
40246                 match = this.store.indexOf(v);
40247                 return false;
40248             }
40249         }, this);
40250         
40251         if (match === false) {
40252             return true; // no more action?
40253         }
40254         // scroll to?
40255         this.view.select(match);
40256         var sn = Roo.get(this.view.getSelectedNodes()[0])
40257         sn.scrollIntoView(sn.dom.parentNode, false);
40258     }
40259
40260     /** 
40261     * @cfg {Boolean} grow 
40262     * @hide 
40263     */
40264     /** 
40265     * @cfg {Number} growMin 
40266     * @hide 
40267     */
40268     /** 
40269     * @cfg {Number} growMax 
40270     * @hide 
40271     */
40272     /**
40273      * @hide
40274      * @method autoSize
40275      */
40276 });/*
40277  * Copyright(c) 2010-2012, Roo J Solutions Limited
40278  *
40279  * Licence LGPL
40280  *
40281  */
40282
40283 /**
40284  * @class Roo.form.ComboBoxArray
40285  * @extends Roo.form.TextField
40286  * A facebook style adder... for lists of email / people / countries  etc...
40287  * pick multiple items from a combo box, and shows each one.
40288  *
40289  *  Fred [x]  Brian [x]  [Pick another |v]
40290  *
40291  *
40292  *  For this to work: it needs various extra information
40293  *    - normal combo problay has
40294  *      name, hiddenName
40295  *    + displayField, valueField
40296  *
40297  *    For our purpose...
40298  *
40299  *
40300  *   If we change from 'extends' to wrapping...
40301  *   
40302  *  
40303  *
40304  
40305  
40306  * @constructor
40307  * Create a new ComboBoxArray.
40308  * @param {Object} config Configuration options
40309  */
40310  
40311
40312 Roo.form.ComboBoxArray = function(config)
40313 {
40314     this.addEvents({
40315         /**
40316          * @event remove
40317          * Fires when remove the value from the list
40318              * @param {Roo.form.ComboBoxArray} _self This combo box array
40319              * @param {Roo.form.ComboBoxArray.Item} item removed item
40320              */
40321         'remove' : true
40322         
40323         
40324     });
40325     
40326     Roo.form.ComboBoxArray.superclass.constructor.call(this, config);
40327     
40328     this.items = new Roo.util.MixedCollection(false);
40329     
40330     // construct the child combo...
40331     
40332     
40333     
40334     
40335    
40336     
40337 }
40338
40339  
40340 Roo.extend(Roo.form.ComboBoxArray, Roo.form.TextField,
40341
40342     /**
40343      * @cfg {Roo.form.Combo} combo The combo box that is wrapped
40344      */
40345     
40346     lastData : false,
40347     
40348     // behavies liek a hiddne field
40349     inputType:      'hidden',
40350     /**
40351      * @cfg {Number} width The width of the box that displays the selected element
40352      */ 
40353     width:          300,
40354
40355     
40356     
40357     /**
40358      * @cfg {String} name    The name of the visable items on this form (eg. titles not ids)
40359      */
40360     name : false,
40361     /**
40362      * @cfg {String} hiddenName    The hidden name of the field, often contains an comma seperated list of names
40363      */
40364     hiddenName : false,
40365     
40366     
40367     // private the array of items that are displayed..
40368     items  : false,
40369     // private - the hidden field el.
40370     hiddenEl : false,
40371     // private - the filed el..
40372     el : false,
40373     
40374     //validateValue : function() { return true; }, // all values are ok!
40375     //onAddClick: function() { },
40376     
40377     onRender : function(ct, position) 
40378     {
40379         
40380         // create the standard hidden element
40381         //Roo.form.ComboBoxArray.superclass.onRender.call(this, ct, position);
40382         
40383         
40384         // give fake names to child combo;
40385         this.combo.hiddenName = this.hiddenName ? (this.hiddenName+'-subcombo') : this.hiddenName;
40386         this.combo.name = this.name? (this.name+'-subcombo') : this.name;
40387         
40388         this.combo = Roo.factory(this.combo, Roo.form);
40389         this.combo.onRender(ct, position);
40390         if (typeof(this.combo.width) != 'undefined') {
40391             this.combo.onResize(this.combo.width,0);
40392         }
40393         
40394         this.combo.initEvents();
40395         
40396         // assigned so form know we need to do this..
40397         this.store          = this.combo.store;
40398         this.valueField     = this.combo.valueField;
40399         this.displayField   = this.combo.displayField ;
40400         
40401         
40402         this.combo.wrap.addClass('x-cbarray-grp');
40403         
40404         var cbwrap = this.combo.wrap.createChild(
40405             {tag: 'div', cls: 'x-cbarray-cb'},
40406             this.combo.el.dom
40407         );
40408         
40409              
40410         this.hiddenEl = this.combo.wrap.createChild({
40411             tag: 'input',  type:'hidden' , name: this.hiddenName, value : ''
40412         });
40413         this.el = this.combo.wrap.createChild({
40414             tag: 'input',  type:'hidden' , name: this.name, value : ''
40415         });
40416          //   this.el.dom.removeAttribute("name");
40417         
40418         
40419         this.outerWrap = this.combo.wrap;
40420         this.wrap = cbwrap;
40421         
40422         this.outerWrap.setWidth(this.width);
40423         this.outerWrap.dom.removeChild(this.el.dom);
40424         
40425         this.wrap.dom.appendChild(this.el.dom);
40426         this.outerWrap.dom.removeChild(this.combo.trigger.dom);
40427         this.combo.wrap.dom.appendChild(this.combo.trigger.dom);
40428         
40429         this.combo.trigger.setStyle('position','relative');
40430         this.combo.trigger.setStyle('left', '0px');
40431         this.combo.trigger.setStyle('top', '2px');
40432         
40433         this.combo.el.setStyle('vertical-align', 'text-bottom');
40434         
40435         //this.trigger.setStyle('vertical-align', 'top');
40436         
40437         // this should use the code from combo really... on('add' ....)
40438         if (this.adder) {
40439             
40440         
40441             this.adder = this.outerWrap.createChild(
40442                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-adder', style: 'margin-left:2px'});  
40443             var _t = this;
40444             this.adder.on('click', function(e) {
40445                 _t.fireEvent('adderclick', this, e);
40446             }, _t);
40447         }
40448         //var _t = this;
40449         //this.adder.on('click', this.onAddClick, _t);
40450         
40451         
40452         this.combo.on('select', function(cb, rec, ix) {
40453             this.addItem(rec.data);
40454             
40455             cb.setValue('');
40456             cb.el.dom.value = '';
40457             //cb.lastData = rec.data;
40458             // add to list
40459             
40460         }, this);
40461         
40462         
40463     },
40464     
40465     
40466     getName: function()
40467     {
40468         // returns hidden if it's set..
40469         if (!this.rendered) {return ''};
40470         return  this.hiddenName ? this.hiddenName : this.name;
40471         
40472     },
40473     
40474     
40475     onResize: function(w, h){
40476         
40477         return;
40478         // not sure if this is needed..
40479         //this.combo.onResize(w,h);
40480         
40481         if(typeof w != 'number'){
40482             // we do not handle it!?!?
40483             return;
40484         }
40485         var tw = this.combo.trigger.getWidth();
40486         tw += this.addicon ? this.addicon.getWidth() : 0;
40487         tw += this.editicon ? this.editicon.getWidth() : 0;
40488         var x = w - tw;
40489         this.combo.el.setWidth( this.combo.adjustWidth('input', x));
40490             
40491         this.combo.trigger.setStyle('left', '0px');
40492         
40493         if(this.list && this.listWidth === undefined){
40494             var lw = Math.max(x + this.combo.trigger.getWidth(), this.combo.minListWidth);
40495             this.list.setWidth(lw);
40496             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
40497         }
40498         
40499     
40500         
40501     },
40502     
40503     addItem: function(rec)
40504     {
40505         var valueField = this.combo.valueField;
40506         var displayField = this.combo.displayField;
40507         if (this.items.indexOfKey(rec[valueField]) > -1) {
40508             //console.log("GOT " + rec.data.id);
40509             return;
40510         }
40511         
40512         var x = new Roo.form.ComboBoxArray.Item({
40513             //id : rec[this.idField],
40514             data : rec,
40515             displayField : displayField ,
40516             tipField : displayField ,
40517             cb : this
40518         });
40519         // use the 
40520         this.items.add(rec[valueField],x);
40521         // add it before the element..
40522         this.updateHiddenEl();
40523         x.render(this.outerWrap, this.wrap.dom);
40524         // add the image handler..
40525     },
40526     
40527     updateHiddenEl : function()
40528     {
40529         this.validate();
40530         if (!this.hiddenEl) {
40531             return;
40532         }
40533         var ar = [];
40534         var idField = this.combo.valueField;
40535         
40536         this.items.each(function(f) {
40537             ar.push(f.data[idField]);
40538            
40539         });
40540         this.hiddenEl.dom.value = ar.join(',');
40541         this.validate();
40542     },
40543     
40544     reset : function()
40545     {
40546         //Roo.form.ComboBoxArray.superclass.reset.call(this); 
40547         this.items.each(function(f) {
40548            f.remove(); 
40549         });
40550         this.el.dom.value = '';
40551         if (this.hiddenEl) {
40552             this.hiddenEl.dom.value = '';
40553         }
40554         
40555     },
40556     getValue: function()
40557     {
40558         return this.hiddenEl ? this.hiddenEl.dom.value : '';
40559     },
40560     setValue: function(v) // not a valid action - must use addItems..
40561     {
40562          
40563         this.reset();
40564         
40565         
40566         
40567         if (this.store.isLocal && (typeof(v) == 'string')) {
40568             // then we can use the store to find the values..
40569             // comma seperated at present.. this needs to allow JSON based encoding..
40570             this.hiddenEl.value  = v;
40571             var v_ar = [];
40572             Roo.each(v.split(','), function(k) {
40573                 Roo.log("CHECK " + this.valueField + ',' + k);
40574                 var li = this.store.query(this.valueField, k);
40575                 if (!li.length) {
40576                     return;
40577                 }
40578                 var add = {};
40579                 add[this.valueField] = k;
40580                 add[this.displayField] = li.item(0).data[this.displayField];
40581                 
40582                 this.addItem(add);
40583             }, this) 
40584              
40585         }
40586         if (typeof(v) == 'object') {
40587             // then let's assume it's an array of objects..
40588             Roo.each(v, function(l) {
40589                 this.addItem(l);
40590             }, this);
40591              
40592         }
40593         
40594         
40595     },
40596     setFromData: function(v)
40597     {
40598         // this recieves an object, if setValues is called.
40599         this.reset();
40600         this.el.dom.value = v[this.displayField];
40601         this.hiddenEl.dom.value = v[this.valueField];
40602         if (typeof(v[this.valueField]) != 'string' || !v[this.valueField].length) {
40603             return;
40604         }
40605         var kv = v[this.valueField];
40606         var dv = v[this.displayField];
40607         kv = typeof(kv) != 'string' ? '' : kv;
40608         dv = typeof(dv) != 'string' ? '' : dv;
40609         
40610         
40611         var keys = kv.split(',');
40612         var display = dv.split(',');
40613         for (var i = 0 ; i < keys.length; i++) {
40614             
40615             add = {};
40616             add[this.valueField] = keys[i];
40617             add[this.displayField] = display[i];
40618             this.addItem(add);
40619         }
40620       
40621         
40622     },
40623     
40624     /**
40625      * Validates the combox array value
40626      * @return {Boolean} True if the value is valid, else false
40627      */
40628     validate : function(){
40629         if(this.disabled || this.validateValue(this.processValue(this.getValue()))){
40630             this.clearInvalid();
40631             return true;
40632         }
40633         return false;
40634     },
40635     
40636     validateValue : function(value){
40637         return Roo.form.ComboBoxArray.superclass.validateValue.call(this, this.getValue());
40638         
40639     },
40640     
40641     /*@
40642      * overide
40643      * 
40644      */
40645     isDirty : function() {
40646         if(this.disabled) {
40647             return false;
40648         }
40649         
40650         try {
40651             var d = Roo.decode(String(this.originalValue));
40652         } catch (e) {
40653             return String(this.getValue()) !== String(this.originalValue);
40654         }
40655         
40656         var originalValue = [];
40657         
40658         for (var i = 0; i < d.length; i++){
40659             originalValue.push(d[i][this.valueField]);
40660         }
40661         
40662         return String(this.getValue()) !== String(originalValue.join(','));
40663         
40664     }
40665     
40666 });
40667
40668
40669
40670 /**
40671  * @class Roo.form.ComboBoxArray.Item
40672  * @extends Roo.BoxComponent
40673  * A selected item in the list
40674  *  Fred [x]  Brian [x]  [Pick another |v]
40675  * 
40676  * @constructor
40677  * Create a new item.
40678  * @param {Object} config Configuration options
40679  */
40680  
40681 Roo.form.ComboBoxArray.Item = function(config) {
40682     config.id = Roo.id();
40683     Roo.form.ComboBoxArray.Item.superclass.constructor.call(this, config);
40684 }
40685
40686 Roo.extend(Roo.form.ComboBoxArray.Item, Roo.BoxComponent, {
40687     data : {},
40688     cb: false,
40689     displayField : false,
40690     tipField : false,
40691     
40692     
40693     defaultAutoCreate : {
40694         tag: 'div',
40695         cls: 'x-cbarray-item',
40696         cn : [ 
40697             { tag: 'div' },
40698             {
40699                 tag: 'img',
40700                 width:16,
40701                 height : 16,
40702                 src : Roo.BLANK_IMAGE_URL ,
40703                 align: 'center'
40704             }
40705         ]
40706         
40707     },
40708     
40709  
40710     onRender : function(ct, position)
40711     {
40712         Roo.form.Field.superclass.onRender.call(this, ct, position);
40713         
40714         if(!this.el){
40715             var cfg = this.getAutoCreate();
40716             this.el = ct.createChild(cfg, position);
40717         }
40718         
40719         this.el.child('img').dom.setAttribute('src', Roo.BLANK_IMAGE_URL);
40720         
40721         this.el.child('div').dom.innerHTML = this.cb.renderer ? 
40722             this.cb.renderer(this.data) :
40723             String.format('{0}',this.data[this.displayField]);
40724         
40725             
40726         this.el.child('div').dom.setAttribute('qtip',
40727                         String.format('{0}',this.data[this.tipField])
40728         );
40729         
40730         this.el.child('img').on('click', this.remove, this);
40731         
40732     },
40733    
40734     remove : function()
40735     {
40736         this.cb.items.remove(this);
40737         this.el.child('img').un('click', this.remove, this);
40738         this.el.remove();
40739         this.cb.updateHiddenEl();
40740         
40741         this.cb.fireEvent('remove', this.cb, this);
40742     }
40743 });/*
40744  * Based on:
40745  * Ext JS Library 1.1.1
40746  * Copyright(c) 2006-2007, Ext JS, LLC.
40747  *
40748  * Originally Released Under LGPL - original licence link has changed is not relivant.
40749  *
40750  * Fork - LGPL
40751  * <script type="text/javascript">
40752  */
40753 /**
40754  * @class Roo.form.Checkbox
40755  * @extends Roo.form.Field
40756  * Single checkbox field.  Can be used as a direct replacement for traditional checkbox fields.
40757  * @constructor
40758  * Creates a new Checkbox
40759  * @param {Object} config Configuration options
40760  */
40761 Roo.form.Checkbox = function(config){
40762     Roo.form.Checkbox.superclass.constructor.call(this, config);
40763     this.addEvents({
40764         /**
40765          * @event check
40766          * Fires when the checkbox is checked or unchecked.
40767              * @param {Roo.form.Checkbox} this This checkbox
40768              * @param {Boolean} checked The new checked value
40769              */
40770         check : true
40771     });
40772 };
40773
40774 Roo.extend(Roo.form.Checkbox, Roo.form.Field,  {
40775     /**
40776      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
40777      */
40778     focusClass : undefined,
40779     /**
40780      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
40781      */
40782     fieldClass: "x-form-field",
40783     /**
40784      * @cfg {Boolean} checked True if the the checkbox should render already checked (defaults to false)
40785      */
40786     checked: false,
40787     /**
40788      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
40789      * {tag: "input", type: "checkbox", autocomplete: "off"})
40790      */
40791     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "off"},
40792     /**
40793      * @cfg {String} boxLabel The text that appears beside the checkbox
40794      */
40795     boxLabel : "",
40796     /**
40797      * @cfg {String} inputValue The value that should go into the generated input element's value attribute
40798      */  
40799     inputValue : '1',
40800     /**
40801      * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
40802      */
40803      valueOff: '0', // value when not checked..
40804
40805     actionMode : 'viewEl', 
40806     //
40807     // private
40808     itemCls : 'x-menu-check-item x-form-item',
40809     groupClass : 'x-menu-group-item',
40810     inputType : 'hidden',
40811     
40812     
40813     inSetChecked: false, // check that we are not calling self...
40814     
40815     inputElement: false, // real input element?
40816     basedOn: false, // ????
40817     
40818     isFormField: true, // not sure where this is needed!!!!
40819
40820     onResize : function(){
40821         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
40822         if(!this.boxLabel){
40823             this.el.alignTo(this.wrap, 'c-c');
40824         }
40825     },
40826
40827     initEvents : function(){
40828         Roo.form.Checkbox.superclass.initEvents.call(this);
40829         this.el.on("click", this.onClick,  this);
40830         this.el.on("change", this.onClick,  this);
40831     },
40832
40833
40834     getResizeEl : function(){
40835         return this.wrap;
40836     },
40837
40838     getPositionEl : function(){
40839         return this.wrap;
40840     },
40841
40842     // private
40843     onRender : function(ct, position){
40844         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
40845         /*
40846         if(this.inputValue !== undefined){
40847             this.el.dom.value = this.inputValue;
40848         }
40849         */
40850         //this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
40851         this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
40852         var viewEl = this.wrap.createChild({ 
40853             tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
40854         this.viewEl = viewEl;   
40855         this.wrap.on('click', this.onClick,  this); 
40856         
40857         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
40858         this.el.on('propertychange', this.setFromHidden,  this);  //ie
40859         
40860         
40861         
40862         if(this.boxLabel){
40863             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
40864         //    viewEl.on('click', this.onClick,  this); 
40865         }
40866         //if(this.checked){
40867             this.setChecked(this.checked);
40868         //}else{
40869             //this.checked = this.el.dom;
40870         //}
40871
40872     },
40873
40874     // private
40875     initValue : Roo.emptyFn,
40876
40877     /**
40878      * Returns the checked state of the checkbox.
40879      * @return {Boolean} True if checked, else false
40880      */
40881     getValue : function(){
40882         if(this.el){
40883             return String(this.el.dom.value) == String(this.inputValue ) ? this.inputValue : this.valueOff;
40884         }
40885         return this.valueOff;
40886         
40887     },
40888
40889         // private
40890     onClick : function(){ 
40891         this.setChecked(!this.checked);
40892
40893         //if(this.el.dom.checked != this.checked){
40894         //    this.setValue(this.el.dom.checked);
40895        // }
40896     },
40897
40898     /**
40899      * Sets the checked state of the checkbox.
40900      * On is always based on a string comparison between inputValue and the param.
40901      * @param {Boolean/String} value - the value to set 
40902      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
40903      */
40904     setValue : function(v,suppressEvent){
40905         
40906         
40907         //this.checked = (v === true || v === 'true' || v == '1' || String(v).toLowerCase() == 'on');
40908         //if(this.el && this.el.dom){
40909         //    this.el.dom.checked = this.checked;
40910         //    this.el.dom.defaultChecked = this.checked;
40911         //}
40912         this.setChecked(String(v) === String(this.inputValue), suppressEvent);
40913         //this.fireEvent("check", this, this.checked);
40914     },
40915     // private..
40916     setChecked : function(state,suppressEvent)
40917     {
40918         if (this.inSetChecked) {
40919             this.checked = state;
40920             return;
40921         }
40922         
40923     
40924         if(this.wrap){
40925             this.wrap[state ? 'addClass' : 'removeClass']('x-menu-item-checked');
40926         }
40927         this.checked = state;
40928         if(suppressEvent !== true){
40929             this.fireEvent('check', this, state);
40930         }
40931         this.inSetChecked = true;
40932         this.el.dom.value = state ? this.inputValue : this.valueOff;
40933         this.inSetChecked = false;
40934         
40935     },
40936     // handle setting of hidden value by some other method!!?!?
40937     setFromHidden: function()
40938     {
40939         if(!this.el){
40940             return;
40941         }
40942         //console.log("SET FROM HIDDEN");
40943         //alert('setFrom hidden');
40944         this.setValue(this.el.dom.value);
40945     },
40946     
40947     onDestroy : function()
40948     {
40949         if(this.viewEl){
40950             Roo.get(this.viewEl).remove();
40951         }
40952          
40953         Roo.form.Checkbox.superclass.onDestroy.call(this);
40954     }
40955
40956 });/*
40957  * Based on:
40958  * Ext JS Library 1.1.1
40959  * Copyright(c) 2006-2007, Ext JS, LLC.
40960  *
40961  * Originally Released Under LGPL - original licence link has changed is not relivant.
40962  *
40963  * Fork - LGPL
40964  * <script type="text/javascript">
40965  */
40966  
40967 /**
40968  * @class Roo.form.Radio
40969  * @extends Roo.form.Checkbox
40970  * Single radio field.  Same as Checkbox, but provided as a convenience for automatically setting the input type.
40971  * Radio grouping is handled automatically by the browser if you give each radio in a group the same name.
40972  * @constructor
40973  * Creates a new Radio
40974  * @param {Object} config Configuration options
40975  */
40976 Roo.form.Radio = function(){
40977     Roo.form.Radio.superclass.constructor.apply(this, arguments);
40978 };
40979 Roo.extend(Roo.form.Radio, Roo.form.Checkbox, {
40980     inputType: 'radio',
40981
40982     /**
40983      * If this radio is part of a group, it will return the selected value
40984      * @return {String}
40985      */
40986     getGroupValue : function(){
40987         return this.el.up('form').child('input[name='+this.el.dom.name+']:checked', true).value;
40988     },
40989     
40990     
40991     onRender : function(ct, position){
40992         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
40993         
40994         if(this.inputValue !== undefined){
40995             this.el.dom.value = this.inputValue;
40996         }
40997          
40998         this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
40999         //this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
41000         //var viewEl = this.wrap.createChild({ 
41001         //    tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
41002         //this.viewEl = viewEl;   
41003         //this.wrap.on('click', this.onClick,  this); 
41004         
41005         //this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
41006         //this.el.on('propertychange', this.setFromHidden,  this);  //ie
41007         
41008         
41009         
41010         if(this.boxLabel){
41011             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
41012         //    viewEl.on('click', this.onClick,  this); 
41013         }
41014          if(this.checked){
41015             this.el.dom.checked =   'checked' ;
41016         }
41017          
41018     } 
41019     
41020     
41021 });//<script type="text/javascript">
41022
41023 /*
41024  * Based  Ext JS Library 1.1.1
41025  * Copyright(c) 2006-2007, Ext JS, LLC.
41026  * LGPL
41027  *
41028  */
41029  
41030 /**
41031  * @class Roo.HtmlEditorCore
41032  * @extends Roo.Component
41033  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
41034  *
41035  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
41036  */
41037
41038 Roo.HtmlEditorCore = function(config){
41039     
41040     
41041     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
41042     this.addEvents({
41043         /**
41044          * @event initialize
41045          * Fires when the editor is fully initialized (including the iframe)
41046          * @param {Roo.HtmlEditorCore} this
41047          */
41048         initialize: true,
41049         /**
41050          * @event activate
41051          * Fires when the editor is first receives the focus. Any insertion must wait
41052          * until after this event.
41053          * @param {Roo.HtmlEditorCore} this
41054          */
41055         activate: true,
41056          /**
41057          * @event beforesync
41058          * Fires before the textarea is updated with content from the editor iframe. Return false
41059          * to cancel the sync.
41060          * @param {Roo.HtmlEditorCore} this
41061          * @param {String} html
41062          */
41063         beforesync: true,
41064          /**
41065          * @event beforepush
41066          * Fires before the iframe editor is updated with content from the textarea. Return false
41067          * to cancel the push.
41068          * @param {Roo.HtmlEditorCore} this
41069          * @param {String} html
41070          */
41071         beforepush: true,
41072          /**
41073          * @event sync
41074          * Fires when the textarea is updated with content from the editor iframe.
41075          * @param {Roo.HtmlEditorCore} this
41076          * @param {String} html
41077          */
41078         sync: true,
41079          /**
41080          * @event push
41081          * Fires when the iframe editor is updated with content from the textarea.
41082          * @param {Roo.HtmlEditorCore} this
41083          * @param {String} html
41084          */
41085         push: true,
41086         
41087         /**
41088          * @event editorevent
41089          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
41090          * @param {Roo.HtmlEditorCore} this
41091          */
41092         editorevent: true
41093     });
41094      
41095 };
41096
41097
41098 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
41099
41100
41101      /**
41102      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
41103      */
41104     
41105     owner : false,
41106     
41107      /**
41108      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
41109      *                        Roo.resizable.
41110      */
41111     resizable : false,
41112      /**
41113      * @cfg {Number} height (in pixels)
41114      */   
41115     height: 300,
41116    /**
41117      * @cfg {Number} width (in pixels)
41118      */   
41119     width: 500,
41120     
41121     /**
41122      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
41123      * 
41124      */
41125     stylesheets: false,
41126     
41127     // id of frame..
41128     frameId: false,
41129     
41130     // private properties
41131     validationEvent : false,
41132     deferHeight: true,
41133     initialized : false,
41134     activated : false,
41135     sourceEditMode : false,
41136     onFocus : Roo.emptyFn,
41137     iframePad:3,
41138     hideMode:'offsets',
41139     
41140     clearUp: true,
41141     
41142      
41143     
41144
41145     /**
41146      * Protected method that will not generally be called directly. It
41147      * is called when the editor initializes the iframe with HTML contents. Override this method if you
41148      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
41149      */
41150     getDocMarkup : function(){
41151         // body styles..
41152         var st = '';
41153         Roo.log(this.stylesheets);
41154         
41155         // inherit styels from page...?? 
41156         if (this.stylesheets === false) {
41157             
41158             Roo.get(document.head).select('style').each(function(node) {
41159                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
41160             });
41161             
41162             Roo.get(document.head).select('link').each(function(node) { 
41163                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
41164             });
41165             
41166         } else if (!this.stylesheets.length) {
41167                 // simple..
41168                 st = '<style type="text/css">' +
41169                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
41170                    '</style>';
41171         } else {
41172             Roo.each(this.stylesheets, function(s) {
41173                 st += '<link rel="stylesheet" type="text/css" href="' + s +'" />'
41174             });
41175             
41176         }
41177         
41178         st +=  '<style type="text/css">' +
41179             'IMG { cursor: pointer } ' +
41180         '</style>';
41181
41182         
41183         return '<html><head>' + st  +
41184             //<style type="text/css">' +
41185             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
41186             //'</style>' +
41187             ' </head><body class="roo-htmleditor-body"></body></html>';
41188     },
41189
41190     // private
41191     onRender : function(ct, position)
41192     {
41193         var _t = this;
41194         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
41195         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
41196         
41197         
41198         this.el.dom.style.border = '0 none';
41199         this.el.dom.setAttribute('tabIndex', -1);
41200         this.el.addClass('x-hidden hide');
41201         
41202         
41203         
41204         if(Roo.isIE){ // fix IE 1px bogus margin
41205             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
41206         }
41207        
41208         
41209         this.frameId = Roo.id();
41210         
41211          
41212         
41213         var iframe = this.owner.wrap.createChild({
41214             tag: 'iframe',
41215             cls: 'form-control', // bootstrap..
41216             id: this.frameId,
41217             name: this.frameId,
41218             frameBorder : 'no',
41219             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
41220         }, this.el
41221         );
41222         
41223         
41224         this.iframe = iframe.dom;
41225
41226          this.assignDocWin();
41227         
41228         this.doc.designMode = 'on';
41229        
41230         this.doc.open();
41231         this.doc.write(this.getDocMarkup());
41232         this.doc.close();
41233
41234         
41235         var task = { // must defer to wait for browser to be ready
41236             run : function(){
41237                 //console.log("run task?" + this.doc.readyState);
41238                 this.assignDocWin();
41239                 if(this.doc.body || this.doc.readyState == 'complete'){
41240                     try {
41241                         this.doc.designMode="on";
41242                     } catch (e) {
41243                         return;
41244                     }
41245                     Roo.TaskMgr.stop(task);
41246                     this.initEditor.defer(10, this);
41247                 }
41248             },
41249             interval : 10,
41250             duration: 10000,
41251             scope: this
41252         };
41253         Roo.TaskMgr.start(task);
41254
41255         
41256          
41257     },
41258
41259     // private
41260     onResize : function(w, h)
41261     {
41262          Roo.log('resize: ' +w + ',' + h );
41263         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
41264         if(!this.iframe){
41265             return;
41266         }
41267         if(typeof w == 'number'){
41268             
41269             this.iframe.style.width = w + 'px';
41270         }
41271         if(typeof h == 'number'){
41272             
41273             this.iframe.style.height = h + 'px';
41274             if(this.doc){
41275                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
41276             }
41277         }
41278         
41279     },
41280
41281     /**
41282      * Toggles the editor between standard and source edit mode.
41283      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
41284      */
41285     toggleSourceEdit : function(sourceEditMode){
41286         
41287         this.sourceEditMode = sourceEditMode === true;
41288         
41289         if(this.sourceEditMode){
41290  
41291             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
41292             
41293         }else{
41294             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
41295             //this.iframe.className = '';
41296             this.deferFocus();
41297         }
41298         //this.setSize(this.owner.wrap.getSize());
41299         //this.fireEvent('editmodechange', this, this.sourceEditMode);
41300     },
41301
41302     
41303   
41304
41305     /**
41306      * Protected method that will not generally be called directly. If you need/want
41307      * custom HTML cleanup, this is the method you should override.
41308      * @param {String} html The HTML to be cleaned
41309      * return {String} The cleaned HTML
41310      */
41311     cleanHtml : function(html){
41312         html = String(html);
41313         if(html.length > 5){
41314             if(Roo.isSafari){ // strip safari nonsense
41315                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
41316             }
41317         }
41318         if(html == '&nbsp;'){
41319             html = '';
41320         }
41321         return html;
41322     },
41323
41324     /**
41325      * HTML Editor -> Textarea
41326      * Protected method that will not generally be called directly. Syncs the contents
41327      * of the editor iframe with the textarea.
41328      */
41329     syncValue : function(){
41330         if(this.initialized){
41331             var bd = (this.doc.body || this.doc.documentElement);
41332             //this.cleanUpPaste(); -- this is done else where and causes havoc..
41333             var html = bd.innerHTML;
41334             if(Roo.isSafari){
41335                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
41336                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
41337                 if(m && m[1]){
41338                     html = '<div style="'+m[0]+'">' + html + '</div>';
41339                 }
41340             }
41341             html = this.cleanHtml(html);
41342             // fix up the special chars.. normaly like back quotes in word...
41343             // however we do not want to do this with chinese..
41344             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
41345                 var cc = b.charCodeAt();
41346                 if (
41347                     (cc >= 0x4E00 && cc < 0xA000 ) ||
41348                     (cc >= 0x3400 && cc < 0x4E00 ) ||
41349                     (cc >= 0xf900 && cc < 0xfb00 )
41350                 ) {
41351                         return b;
41352                 }
41353                 return "&#"+cc+";" 
41354             });
41355             if(this.owner.fireEvent('beforesync', this, html) !== false){
41356                 this.el.dom.value = html;
41357                 this.owner.fireEvent('sync', this, html);
41358             }
41359         }
41360     },
41361
41362     /**
41363      * Protected method that will not generally be called directly. Pushes the value of the textarea
41364      * into the iframe editor.
41365      */
41366     pushValue : function(){
41367         if(this.initialized){
41368             var v = this.el.dom.value.trim();
41369             
41370 //            if(v.length < 1){
41371 //                v = '&#160;';
41372 //            }
41373             
41374             if(this.owner.fireEvent('beforepush', this, v) !== false){
41375                 var d = (this.doc.body || this.doc.documentElement);
41376                 d.innerHTML = v;
41377                 this.cleanUpPaste();
41378                 this.el.dom.value = d.innerHTML;
41379                 this.owner.fireEvent('push', this, v);
41380             }
41381         }
41382     },
41383
41384     // private
41385     deferFocus : function(){
41386         this.focus.defer(10, this);
41387     },
41388
41389     // doc'ed in Field
41390     focus : function(){
41391         if(this.win && !this.sourceEditMode){
41392             this.win.focus();
41393         }else{
41394             this.el.focus();
41395         }
41396     },
41397     
41398     assignDocWin: function()
41399     {
41400         var iframe = this.iframe;
41401         
41402          if(Roo.isIE){
41403             this.doc = iframe.contentWindow.document;
41404             this.win = iframe.contentWindow;
41405         } else {
41406 //            if (!Roo.get(this.frameId)) {
41407 //                return;
41408 //            }
41409 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
41410 //            this.win = Roo.get(this.frameId).dom.contentWindow;
41411             
41412             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
41413                 return;
41414             }
41415             
41416             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
41417             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
41418         }
41419     },
41420     
41421     // private
41422     initEditor : function(){
41423         //console.log("INIT EDITOR");
41424         this.assignDocWin();
41425         
41426         
41427         
41428         this.doc.designMode="on";
41429         this.doc.open();
41430         this.doc.write(this.getDocMarkup());
41431         this.doc.close();
41432         
41433         var dbody = (this.doc.body || this.doc.documentElement);
41434         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
41435         // this copies styles from the containing element into thsi one..
41436         // not sure why we need all of this..
41437         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
41438         
41439         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
41440         //ss['background-attachment'] = 'fixed'; // w3c
41441         dbody.bgProperties = 'fixed'; // ie
41442         //Roo.DomHelper.applyStyles(dbody, ss);
41443         Roo.EventManager.on(this.doc, {
41444             //'mousedown': this.onEditorEvent,
41445             'mouseup': this.onEditorEvent,
41446             'dblclick': this.onEditorEvent,
41447             'click': this.onEditorEvent,
41448             'keyup': this.onEditorEvent,
41449             buffer:100,
41450             scope: this
41451         });
41452         if(Roo.isGecko){
41453             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
41454         }
41455         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
41456             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
41457         }
41458         this.initialized = true;
41459
41460         this.owner.fireEvent('initialize', this);
41461         this.pushValue();
41462     },
41463
41464     // private
41465     onDestroy : function(){
41466         
41467         
41468         
41469         if(this.rendered){
41470             
41471             //for (var i =0; i < this.toolbars.length;i++) {
41472             //    // fixme - ask toolbars for heights?
41473             //    this.toolbars[i].onDestroy();
41474            // }
41475             
41476             //this.wrap.dom.innerHTML = '';
41477             //this.wrap.remove();
41478         }
41479     },
41480
41481     // private
41482     onFirstFocus : function(){
41483         
41484         this.assignDocWin();
41485         
41486         
41487         this.activated = true;
41488          
41489     
41490         if(Roo.isGecko){ // prevent silly gecko errors
41491             this.win.focus();
41492             var s = this.win.getSelection();
41493             if(!s.focusNode || s.focusNode.nodeType != 3){
41494                 var r = s.getRangeAt(0);
41495                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
41496                 r.collapse(true);
41497                 this.deferFocus();
41498             }
41499             try{
41500                 this.execCmd('useCSS', true);
41501                 this.execCmd('styleWithCSS', false);
41502             }catch(e){}
41503         }
41504         this.owner.fireEvent('activate', this);
41505     },
41506
41507     // private
41508     adjustFont: function(btn){
41509         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
41510         //if(Roo.isSafari){ // safari
41511         //    adjust *= 2;
41512        // }
41513         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
41514         if(Roo.isSafari){ // safari
41515             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
41516             v =  (v < 10) ? 10 : v;
41517             v =  (v > 48) ? 48 : v;
41518             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
41519             
41520         }
41521         
41522         
41523         v = Math.max(1, v+adjust);
41524         
41525         this.execCmd('FontSize', v  );
41526     },
41527
41528     onEditorEvent : function(e){
41529         this.owner.fireEvent('editorevent', this, e);
41530       //  this.updateToolbar();
41531         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
41532     },
41533
41534     insertTag : function(tg)
41535     {
41536         // could be a bit smarter... -> wrap the current selected tRoo..
41537         if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
41538             
41539             range = this.createRange(this.getSelection());
41540             var wrappingNode = this.doc.createElement(tg.toLowerCase());
41541             wrappingNode.appendChild(range.extractContents());
41542             range.insertNode(wrappingNode);
41543
41544             return;
41545             
41546             
41547             
41548         }
41549         this.execCmd("formatblock",   tg);
41550         
41551     },
41552     
41553     insertText : function(txt)
41554     {
41555         
41556         
41557         var range = this.createRange();
41558         range.deleteContents();
41559                //alert(Sender.getAttribute('label'));
41560                
41561         range.insertNode(this.doc.createTextNode(txt));
41562     } ,
41563     
41564      
41565
41566     /**
41567      * Executes a Midas editor command on the editor document and performs necessary focus and
41568      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
41569      * @param {String} cmd The Midas command
41570      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
41571      */
41572     relayCmd : function(cmd, value){
41573         this.win.focus();
41574         this.execCmd(cmd, value);
41575         this.owner.fireEvent('editorevent', this);
41576         //this.updateToolbar();
41577         this.owner.deferFocus();
41578     },
41579
41580     /**
41581      * Executes a Midas editor command directly on the editor document.
41582      * For visual commands, you should use {@link #relayCmd} instead.
41583      * <b>This should only be called after the editor is initialized.</b>
41584      * @param {String} cmd The Midas command
41585      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
41586      */
41587     execCmd : function(cmd, value){
41588         this.doc.execCommand(cmd, false, value === undefined ? null : value);
41589         this.syncValue();
41590     },
41591  
41592  
41593    
41594     /**
41595      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
41596      * to insert tRoo.
41597      * @param {String} text | dom node.. 
41598      */
41599     insertAtCursor : function(text)
41600     {
41601         
41602         
41603         
41604         if(!this.activated){
41605             return;
41606         }
41607         /*
41608         if(Roo.isIE){
41609             this.win.focus();
41610             var r = this.doc.selection.createRange();
41611             if(r){
41612                 r.collapse(true);
41613                 r.pasteHTML(text);
41614                 this.syncValue();
41615                 this.deferFocus();
41616             
41617             }
41618             return;
41619         }
41620         */
41621         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
41622             this.win.focus();
41623             
41624             
41625             // from jquery ui (MIT licenced)
41626             var range, node;
41627             var win = this.win;
41628             
41629             if (win.getSelection && win.getSelection().getRangeAt) {
41630                 range = win.getSelection().getRangeAt(0);
41631                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
41632                 range.insertNode(node);
41633             } else if (win.document.selection && win.document.selection.createRange) {
41634                 // no firefox support
41635                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
41636                 win.document.selection.createRange().pasteHTML(txt);
41637             } else {
41638                 // no firefox support
41639                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
41640                 this.execCmd('InsertHTML', txt);
41641             } 
41642             
41643             this.syncValue();
41644             
41645             this.deferFocus();
41646         }
41647     },
41648  // private
41649     mozKeyPress : function(e){
41650         if(e.ctrlKey){
41651             var c = e.getCharCode(), cmd;
41652           
41653             if(c > 0){
41654                 c = String.fromCharCode(c).toLowerCase();
41655                 switch(c){
41656                     case 'b':
41657                         cmd = 'bold';
41658                         break;
41659                     case 'i':
41660                         cmd = 'italic';
41661                         break;
41662                     
41663                     case 'u':
41664                         cmd = 'underline';
41665                         break;
41666                     
41667                     case 'v':
41668                         this.cleanUpPaste.defer(100, this);
41669                         return;
41670                         
41671                 }
41672                 if(cmd){
41673                     this.win.focus();
41674                     this.execCmd(cmd);
41675                     this.deferFocus();
41676                     e.preventDefault();
41677                 }
41678                 
41679             }
41680         }
41681     },
41682
41683     // private
41684     fixKeys : function(){ // load time branching for fastest keydown performance
41685         if(Roo.isIE){
41686             return function(e){
41687                 var k = e.getKey(), r;
41688                 if(k == e.TAB){
41689                     e.stopEvent();
41690                     r = this.doc.selection.createRange();
41691                     if(r){
41692                         r.collapse(true);
41693                         r.pasteHTML('&#160;&#160;&#160;&#160;');
41694                         this.deferFocus();
41695                     }
41696                     return;
41697                 }
41698                 
41699                 if(k == e.ENTER){
41700                     r = this.doc.selection.createRange();
41701                     if(r){
41702                         var target = r.parentElement();
41703                         if(!target || target.tagName.toLowerCase() != 'li'){
41704                             e.stopEvent();
41705                             r.pasteHTML('<br />');
41706                             r.collapse(false);
41707                             r.select();
41708                         }
41709                     }
41710                 }
41711                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
41712                     this.cleanUpPaste.defer(100, this);
41713                     return;
41714                 }
41715                 
41716                 
41717             };
41718         }else if(Roo.isOpera){
41719             return function(e){
41720                 var k = e.getKey();
41721                 if(k == e.TAB){
41722                     e.stopEvent();
41723                     this.win.focus();
41724                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
41725                     this.deferFocus();
41726                 }
41727                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
41728                     this.cleanUpPaste.defer(100, this);
41729                     return;
41730                 }
41731                 
41732             };
41733         }else if(Roo.isSafari){
41734             return function(e){
41735                 var k = e.getKey();
41736                 
41737                 if(k == e.TAB){
41738                     e.stopEvent();
41739                     this.execCmd('InsertText','\t');
41740                     this.deferFocus();
41741                     return;
41742                 }
41743                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
41744                     this.cleanUpPaste.defer(100, this);
41745                     return;
41746                 }
41747                 
41748              };
41749         }
41750     }(),
41751     
41752     getAllAncestors: function()
41753     {
41754         var p = this.getSelectedNode();
41755         var a = [];
41756         if (!p) {
41757             a.push(p); // push blank onto stack..
41758             p = this.getParentElement();
41759         }
41760         
41761         
41762         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
41763             a.push(p);
41764             p = p.parentNode;
41765         }
41766         a.push(this.doc.body);
41767         return a;
41768     },
41769     lastSel : false,
41770     lastSelNode : false,
41771     
41772     
41773     getSelection : function() 
41774     {
41775         this.assignDocWin();
41776         return Roo.isIE ? this.doc.selection : this.win.getSelection();
41777     },
41778     
41779     getSelectedNode: function() 
41780     {
41781         // this may only work on Gecko!!!
41782         
41783         // should we cache this!!!!
41784         
41785         
41786         
41787          
41788         var range = this.createRange(this.getSelection()).cloneRange();
41789         
41790         if (Roo.isIE) {
41791             var parent = range.parentElement();
41792             while (true) {
41793                 var testRange = range.duplicate();
41794                 testRange.moveToElementText(parent);
41795                 if (testRange.inRange(range)) {
41796                     break;
41797                 }
41798                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
41799                     break;
41800                 }
41801                 parent = parent.parentElement;
41802             }
41803             return parent;
41804         }
41805         
41806         // is ancestor a text element.
41807         var ac =  range.commonAncestorContainer;
41808         if (ac.nodeType == 3) {
41809             ac = ac.parentNode;
41810         }
41811         
41812         var ar = ac.childNodes;
41813          
41814         var nodes = [];
41815         var other_nodes = [];
41816         var has_other_nodes = false;
41817         for (var i=0;i<ar.length;i++) {
41818             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
41819                 continue;
41820             }
41821             // fullly contained node.
41822             
41823             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
41824                 nodes.push(ar[i]);
41825                 continue;
41826             }
41827             
41828             // probably selected..
41829             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
41830                 other_nodes.push(ar[i]);
41831                 continue;
41832             }
41833             // outer..
41834             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
41835                 continue;
41836             }
41837             
41838             
41839             has_other_nodes = true;
41840         }
41841         if (!nodes.length && other_nodes.length) {
41842             nodes= other_nodes;
41843         }
41844         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
41845             return false;
41846         }
41847         
41848         return nodes[0];
41849     },
41850     createRange: function(sel)
41851     {
41852         // this has strange effects when using with 
41853         // top toolbar - not sure if it's a great idea.
41854         //this.editor.contentWindow.focus();
41855         if (typeof sel != "undefined") {
41856             try {
41857                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
41858             } catch(e) {
41859                 return this.doc.createRange();
41860             }
41861         } else {
41862             return this.doc.createRange();
41863         }
41864     },
41865     getParentElement: function()
41866     {
41867         
41868         this.assignDocWin();
41869         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
41870         
41871         var range = this.createRange(sel);
41872          
41873         try {
41874             var p = range.commonAncestorContainer;
41875             while (p.nodeType == 3) { // text node
41876                 p = p.parentNode;
41877             }
41878             return p;
41879         } catch (e) {
41880             return null;
41881         }
41882     
41883     },
41884     /***
41885      *
41886      * Range intersection.. the hard stuff...
41887      *  '-1' = before
41888      *  '0' = hits..
41889      *  '1' = after.
41890      *         [ -- selected range --- ]
41891      *   [fail]                        [fail]
41892      *
41893      *    basically..
41894      *      if end is before start or  hits it. fail.
41895      *      if start is after end or hits it fail.
41896      *
41897      *   if either hits (but other is outside. - then it's not 
41898      *   
41899      *    
41900      **/
41901     
41902     
41903     // @see http://www.thismuchiknow.co.uk/?p=64.
41904     rangeIntersectsNode : function(range, node)
41905     {
41906         var nodeRange = node.ownerDocument.createRange();
41907         try {
41908             nodeRange.selectNode(node);
41909         } catch (e) {
41910             nodeRange.selectNodeContents(node);
41911         }
41912     
41913         var rangeStartRange = range.cloneRange();
41914         rangeStartRange.collapse(true);
41915     
41916         var rangeEndRange = range.cloneRange();
41917         rangeEndRange.collapse(false);
41918     
41919         var nodeStartRange = nodeRange.cloneRange();
41920         nodeStartRange.collapse(true);
41921     
41922         var nodeEndRange = nodeRange.cloneRange();
41923         nodeEndRange.collapse(false);
41924     
41925         return rangeStartRange.compareBoundaryPoints(
41926                  Range.START_TO_START, nodeEndRange) == -1 &&
41927                rangeEndRange.compareBoundaryPoints(
41928                  Range.START_TO_START, nodeStartRange) == 1;
41929         
41930          
41931     },
41932     rangeCompareNode : function(range, node)
41933     {
41934         var nodeRange = node.ownerDocument.createRange();
41935         try {
41936             nodeRange.selectNode(node);
41937         } catch (e) {
41938             nodeRange.selectNodeContents(node);
41939         }
41940         
41941         
41942         range.collapse(true);
41943     
41944         nodeRange.collapse(true);
41945      
41946         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
41947         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
41948          
41949         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
41950         
41951         var nodeIsBefore   =  ss == 1;
41952         var nodeIsAfter    = ee == -1;
41953         
41954         if (nodeIsBefore && nodeIsAfter)
41955             return 0; // outer
41956         if (!nodeIsBefore && nodeIsAfter)
41957             return 1; //right trailed.
41958         
41959         if (nodeIsBefore && !nodeIsAfter)
41960             return 2;  // left trailed.
41961         // fully contined.
41962         return 3;
41963     },
41964
41965     // private? - in a new class?
41966     cleanUpPaste :  function()
41967     {
41968         // cleans up the whole document..
41969         Roo.log('cleanuppaste');
41970         
41971         this.cleanUpChildren(this.doc.body);
41972         var clean = this.cleanWordChars(this.doc.body.innerHTML);
41973         if (clean != this.doc.body.innerHTML) {
41974             this.doc.body.innerHTML = clean;
41975         }
41976         
41977     },
41978     
41979     cleanWordChars : function(input) {// change the chars to hex code
41980         var he = Roo.HtmlEditorCore;
41981         
41982         var output = input;
41983         Roo.each(he.swapCodes, function(sw) { 
41984             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
41985             
41986             output = output.replace(swapper, sw[1]);
41987         });
41988         
41989         return output;
41990     },
41991     
41992     
41993     cleanUpChildren : function (n)
41994     {
41995         if (!n.childNodes.length) {
41996             return;
41997         }
41998         for (var i = n.childNodes.length-1; i > -1 ; i--) {
41999            this.cleanUpChild(n.childNodes[i]);
42000         }
42001     },
42002     
42003     
42004         
42005     
42006     cleanUpChild : function (node)
42007     {
42008         var ed = this;
42009         //console.log(node);
42010         if (node.nodeName == "#text") {
42011             // clean up silly Windows -- stuff?
42012             return; 
42013         }
42014         if (node.nodeName == "#comment") {
42015             node.parentNode.removeChild(node);
42016             // clean up silly Windows -- stuff?
42017             return; 
42018         }
42019         
42020         if (Roo.HtmlEditorCore.black.indexOf(node.tagName.toLowerCase()) > -1 && this.clearUp) {
42021             // remove node.
42022             node.parentNode.removeChild(node);
42023             return;
42024             
42025         }
42026         
42027         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
42028         
42029         // remove <a name=....> as rendering on yahoo mailer is borked with this.
42030         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
42031         
42032         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
42033         //    remove_keep_children = true;
42034         //}
42035         
42036         if (remove_keep_children) {
42037             this.cleanUpChildren(node);
42038             // inserts everything just before this node...
42039             while (node.childNodes.length) {
42040                 var cn = node.childNodes[0];
42041                 node.removeChild(cn);
42042                 node.parentNode.insertBefore(cn, node);
42043             }
42044             node.parentNode.removeChild(node);
42045             return;
42046         }
42047         
42048         if (!node.attributes || !node.attributes.length) {
42049             this.cleanUpChildren(node);
42050             return;
42051         }
42052         
42053         function cleanAttr(n,v)
42054         {
42055             
42056             if (v.match(/^\./) || v.match(/^\//)) {
42057                 return;
42058             }
42059             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
42060                 return;
42061             }
42062             if (v.match(/^#/)) {
42063                 return;
42064             }
42065 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
42066             node.removeAttribute(n);
42067             
42068         }
42069         
42070         function cleanStyle(n,v)
42071         {
42072             if (v.match(/expression/)) { //XSS?? should we even bother..
42073                 node.removeAttribute(n);
42074                 return;
42075             }
42076             var cwhite = typeof(ed.cwhite) == 'undefined' ? Roo.HtmlEditorCore.cwhite : ed.cwhite;
42077             var cblack = typeof(ed.cblack) == 'undefined' ? Roo.HtmlEditorCore.cblack : ed.cblack;
42078             
42079             
42080             var parts = v.split(/;/);
42081             var clean = [];
42082             
42083             Roo.each(parts, function(p) {
42084                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
42085                 if (!p.length) {
42086                     return true;
42087                 }
42088                 var l = p.split(':').shift().replace(/\s+/g,'');
42089                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
42090                 
42091                 if ( cblack.indexOf(l) > -1) {
42092 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
42093                     //node.removeAttribute(n);
42094                     return true;
42095                 }
42096                 //Roo.log()
42097                 // only allow 'c whitelisted system attributes'
42098                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
42099 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
42100                     //node.removeAttribute(n);
42101                     return true;
42102                 }
42103                 
42104                 
42105                  
42106                 
42107                 clean.push(p);
42108                 return true;
42109             });
42110             if (clean.length) { 
42111                 node.setAttribute(n, clean.join(';'));
42112             } else {
42113                 node.removeAttribute(n);
42114             }
42115             
42116         }
42117         
42118         
42119         for (var i = node.attributes.length-1; i > -1 ; i--) {
42120             var a = node.attributes[i];
42121             //console.log(a);
42122             
42123             if (a.name.toLowerCase().substr(0,2)=='on')  {
42124                 node.removeAttribute(a.name);
42125                 continue;
42126             }
42127             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
42128                 node.removeAttribute(a.name);
42129                 continue;
42130             }
42131             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
42132                 cleanAttr(a.name,a.value); // fixme..
42133                 continue;
42134             }
42135             if (a.name == 'style') {
42136                 cleanStyle(a.name,a.value);
42137                 continue;
42138             }
42139             /// clean up MS crap..
42140             // tecnically this should be a list of valid class'es..
42141             
42142             
42143             if (a.name == 'class') {
42144                 if (a.value.match(/^Mso/)) {
42145                     node.className = '';
42146                 }
42147                 
42148                 if (a.value.match(/body/)) {
42149                     node.className = '';
42150                 }
42151                 continue;
42152             }
42153             
42154             // style cleanup!?
42155             // class cleanup?
42156             
42157         }
42158         
42159         
42160         this.cleanUpChildren(node);
42161         
42162         
42163     },
42164     /**
42165      * Clean up MS wordisms...
42166      */
42167     cleanWord : function(node)
42168     {
42169         var _t = this;
42170         var cleanWordChildren = function()
42171         {
42172             if (!node.childNodes.length) {
42173                 return;
42174             }
42175             for (var i = node.childNodes.length-1; i > -1 ; i--) {
42176                _t.cleanWord(node.childNodes[i]);
42177             }
42178         }
42179         
42180         
42181         if (!node) {
42182             this.cleanWord(this.doc.body);
42183             return;
42184         }
42185         if (node.nodeName == "#text") {
42186             // clean up silly Windows -- stuff?
42187             return; 
42188         }
42189         if (node.nodeName == "#comment") {
42190             node.parentNode.removeChild(node);
42191             // clean up silly Windows -- stuff?
42192             return; 
42193         }
42194         
42195         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
42196             node.parentNode.removeChild(node);
42197             return;
42198         }
42199         
42200         // remove - but keep children..
42201         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
42202             while (node.childNodes.length) {
42203                 var cn = node.childNodes[0];
42204                 node.removeChild(cn);
42205                 node.parentNode.insertBefore(cn, node);
42206             }
42207             node.parentNode.removeChild(node);
42208             cleanWordChildren();
42209             return;
42210         }
42211         // clean styles
42212         if (node.className.length) {
42213             
42214             var cn = node.className.split(/\W+/);
42215             var cna = [];
42216             Roo.each(cn, function(cls) {
42217                 if (cls.match(/Mso[a-zA-Z]+/)) {
42218                     return;
42219                 }
42220                 cna.push(cls);
42221             });
42222             node.className = cna.length ? cna.join(' ') : '';
42223             if (!cna.length) {
42224                 node.removeAttribute("class");
42225             }
42226         }
42227         
42228         if (node.hasAttribute("lang")) {
42229             node.removeAttribute("lang");
42230         }
42231         
42232         if (node.hasAttribute("style")) {
42233             
42234             var styles = node.getAttribute("style").split(";");
42235             var nstyle = [];
42236             Roo.each(styles, function(s) {
42237                 if (!s.match(/:/)) {
42238                     return;
42239                 }
42240                 var kv = s.split(":");
42241                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
42242                     return;
42243                 }
42244                 // what ever is left... we allow.
42245                 nstyle.push(s);
42246             });
42247             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
42248             if (!nstyle.length) {
42249                 node.removeAttribute('style');
42250             }
42251         }
42252         
42253         cleanWordChildren();
42254         
42255         
42256     },
42257     domToHTML : function(currentElement, depth, nopadtext) {
42258         
42259             depth = depth || 0;
42260             nopadtext = nopadtext || false;
42261         
42262             if (!currentElement) {
42263                 return this.domToHTML(this.doc.body);
42264             }
42265             
42266             //Roo.log(currentElement);
42267             var j;
42268             var allText = false;
42269             var nodeName = currentElement.nodeName;
42270             var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
42271             
42272             if  (nodeName == '#text') {
42273                 return currentElement.nodeValue;
42274             }
42275             
42276             
42277             var ret = '';
42278             if (nodeName != 'BODY') {
42279                  
42280                 var i = 0;
42281                 // Prints the node tagName, such as <A>, <IMG>, etc
42282                 if (tagName) {
42283                     var attr = [];
42284                     for(i = 0; i < currentElement.attributes.length;i++) {
42285                         // quoting?
42286                         var aname = currentElement.attributes.item(i).name;
42287                         if (!currentElement.attributes.item(i).value.length) {
42288                             continue;
42289                         }
42290                         attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
42291                     }
42292                     
42293                     ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
42294                 } 
42295                 else {
42296                     
42297                     // eack
42298                 }
42299             } else {
42300                 tagName = false;
42301             }
42302             if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
42303                 return ret;
42304             }
42305             if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
42306                 nopadtext = true;
42307             }
42308             
42309             
42310             // Traverse the tree
42311             i = 0;
42312             var currentElementChild = currentElement.childNodes.item(i);
42313             var allText = true;
42314             var innerHTML  = '';
42315             lastnode = '';
42316             while (currentElementChild) {
42317                 // Formatting code (indent the tree so it looks nice on the screen)
42318                 var nopad = nopadtext;
42319                 if (lastnode == 'SPAN') {
42320                     nopad  = true;
42321                 }
42322                 // text
42323                 if  (currentElementChild.nodeName == '#text') {
42324                     var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
42325                     if (!nopad && toadd.length > 80) {
42326                         innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
42327                     }
42328                     innerHTML  += toadd;
42329                     
42330                     i++;
42331                     currentElementChild = currentElement.childNodes.item(i);
42332                     lastNode = '';
42333                     continue;
42334                 }
42335                 allText = false;
42336                 
42337                 innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
42338                     
42339                 // Recursively traverse the tree structure of the child node
42340                 innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
42341                 lastnode = currentElementChild.nodeName;
42342                 i++;
42343                 currentElementChild=currentElement.childNodes.item(i);
42344             }
42345             
42346             ret += innerHTML;
42347             
42348             if (!allText) {
42349                     // The remaining code is mostly for formatting the tree
42350                 ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
42351             }
42352             
42353             
42354             if (tagName) {
42355                 ret+= "</"+tagName+">";
42356             }
42357             return ret;
42358             
42359         }
42360     
42361     // hide stuff that is not compatible
42362     /**
42363      * @event blur
42364      * @hide
42365      */
42366     /**
42367      * @event change
42368      * @hide
42369      */
42370     /**
42371      * @event focus
42372      * @hide
42373      */
42374     /**
42375      * @event specialkey
42376      * @hide
42377      */
42378     /**
42379      * @cfg {String} fieldClass @hide
42380      */
42381     /**
42382      * @cfg {String} focusClass @hide
42383      */
42384     /**
42385      * @cfg {String} autoCreate @hide
42386      */
42387     /**
42388      * @cfg {String} inputType @hide
42389      */
42390     /**
42391      * @cfg {String} invalidClass @hide
42392      */
42393     /**
42394      * @cfg {String} invalidText @hide
42395      */
42396     /**
42397      * @cfg {String} msgFx @hide
42398      */
42399     /**
42400      * @cfg {String} validateOnBlur @hide
42401      */
42402 });
42403
42404 Roo.HtmlEditorCore.white = [
42405         'area', 'br', 'img', 'input', 'hr', 'wbr',
42406         
42407        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
42408        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
42409        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
42410        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
42411        'table',   'ul',         'xmp', 
42412        
42413        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
42414       'thead',   'tr', 
42415      
42416       'dir', 'menu', 'ol', 'ul', 'dl',
42417        
42418       'embed',  'object'
42419 ];
42420
42421
42422 Roo.HtmlEditorCore.black = [
42423     //    'embed',  'object', // enable - backend responsiblity to clean thiese
42424         'applet', // 
42425         'base',   'basefont', 'bgsound', 'blink',  'body', 
42426         'frame',  'frameset', 'head',    'html',   'ilayer', 
42427         'iframe', 'layer',  'link',     'meta',    'object',   
42428         'script', 'style' ,'title',  'xml' // clean later..
42429 ];
42430 Roo.HtmlEditorCore.clean = [
42431     'script', 'style', 'title', 'xml'
42432 ];
42433 Roo.HtmlEditorCore.remove = [
42434     'font'
42435 ];
42436 // attributes..
42437
42438 Roo.HtmlEditorCore.ablack = [
42439     'on'
42440 ];
42441     
42442 Roo.HtmlEditorCore.aclean = [ 
42443     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
42444 ];
42445
42446 // protocols..
42447 Roo.HtmlEditorCore.pwhite= [
42448         'http',  'https',  'mailto'
42449 ];
42450
42451 // white listed style attributes.
42452 Roo.HtmlEditorCore.cwhite= [
42453       //  'text-align', /// default is to allow most things..
42454       
42455          
42456 //        'font-size'//??
42457 ];
42458
42459 // black listed style attributes.
42460 Roo.HtmlEditorCore.cblack= [
42461       //  'font-size' -- this can be set by the project 
42462 ];
42463
42464
42465 Roo.HtmlEditorCore.swapCodes   =[ 
42466     [    8211, "--" ], 
42467     [    8212, "--" ], 
42468     [    8216,  "'" ],  
42469     [    8217, "'" ],  
42470     [    8220, '"' ],  
42471     [    8221, '"' ],  
42472     [    8226, "*" ],  
42473     [    8230, "..." ]
42474 ]; 
42475
42476     //<script type="text/javascript">
42477
42478 /*
42479  * Ext JS Library 1.1.1
42480  * Copyright(c) 2006-2007, Ext JS, LLC.
42481  * Licence LGPL
42482  * 
42483  */
42484  
42485  
42486 Roo.form.HtmlEditor = function(config){
42487     
42488     
42489     
42490     Roo.form.HtmlEditor.superclass.constructor.call(this, config);
42491     
42492     if (!this.toolbars) {
42493         this.toolbars = [];
42494     }
42495     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
42496     
42497     
42498 };
42499
42500 /**
42501  * @class Roo.form.HtmlEditor
42502  * @extends Roo.form.Field
42503  * Provides a lightweight HTML Editor component.
42504  *
42505  * This has been tested on Fireforx / Chrome.. IE may not be so great..
42506  * 
42507  * <br><br><b>Note: The focus/blur and validation marking functionality inherited from Ext.form.Field is NOT
42508  * supported by this editor.</b><br/><br/>
42509  * An Editor is a sensitive component that can't be used in all spots standard fields can be used. Putting an Editor within
42510  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
42511  */
42512 Roo.extend(Roo.form.HtmlEditor, Roo.form.Field, {
42513     /**
42514      * @cfg {Boolean} clearUp
42515      */
42516     clearUp : true,
42517       /**
42518      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
42519      */
42520     toolbars : false,
42521    
42522      /**
42523      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
42524      *                        Roo.resizable.
42525      */
42526     resizable : false,
42527      /**
42528      * @cfg {Number} height (in pixels)
42529      */   
42530     height: 300,
42531    /**
42532      * @cfg {Number} width (in pixels)
42533      */   
42534     width: 500,
42535     
42536     /**
42537      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
42538      * 
42539      */
42540     stylesheets: false,
42541     
42542     // id of frame..
42543     frameId: false,
42544     
42545     // private properties
42546     validationEvent : false,
42547     deferHeight: true,
42548     initialized : false,
42549     activated : false,
42550     
42551     onFocus : Roo.emptyFn,
42552     iframePad:3,
42553     hideMode:'offsets',
42554     
42555     defaultAutoCreate : { // modified by initCompnoent..
42556         tag: "textarea",
42557         style:"width:500px;height:300px;",
42558         autocomplete: "off"
42559     },
42560
42561     // private
42562     initComponent : function(){
42563         this.addEvents({
42564             /**
42565              * @event initialize
42566              * Fires when the editor is fully initialized (including the iframe)
42567              * @param {HtmlEditor} this
42568              */
42569             initialize: true,
42570             /**
42571              * @event activate
42572              * Fires when the editor is first receives the focus. Any insertion must wait
42573              * until after this event.
42574              * @param {HtmlEditor} this
42575              */
42576             activate: true,
42577              /**
42578              * @event beforesync
42579              * Fires before the textarea is updated with content from the editor iframe. Return false
42580              * to cancel the sync.
42581              * @param {HtmlEditor} this
42582              * @param {String} html
42583              */
42584             beforesync: true,
42585              /**
42586              * @event beforepush
42587              * Fires before the iframe editor is updated with content from the textarea. Return false
42588              * to cancel the push.
42589              * @param {HtmlEditor} this
42590              * @param {String} html
42591              */
42592             beforepush: true,
42593              /**
42594              * @event sync
42595              * Fires when the textarea is updated with content from the editor iframe.
42596              * @param {HtmlEditor} this
42597              * @param {String} html
42598              */
42599             sync: true,
42600              /**
42601              * @event push
42602              * Fires when the iframe editor is updated with content from the textarea.
42603              * @param {HtmlEditor} this
42604              * @param {String} html
42605              */
42606             push: true,
42607              /**
42608              * @event editmodechange
42609              * Fires when the editor switches edit modes
42610              * @param {HtmlEditor} this
42611              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
42612              */
42613             editmodechange: true,
42614             /**
42615              * @event editorevent
42616              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
42617              * @param {HtmlEditor} this
42618              */
42619             editorevent: true,
42620             /**
42621              * @event firstfocus
42622              * Fires when on first focus - needed by toolbars..
42623              * @param {HtmlEditor} this
42624              */
42625             firstfocus: true,
42626             /**
42627              * @event autosave
42628              * Auto save the htmlEditor value as a file into Events
42629              * @param {HtmlEditor} this
42630              */
42631             autosave: true,
42632             /**
42633              * @event savedpreview
42634              * preview the saved version of htmlEditor
42635              * @param {HtmlEditor} this
42636              */
42637             savedpreview: true
42638         });
42639         this.defaultAutoCreate =  {
42640             tag: "textarea",
42641             style:'width: ' + this.width + 'px;height: ' + this.height + 'px;',
42642             autocomplete: "off"
42643         };
42644     },
42645
42646     /**
42647      * Protected method that will not generally be called directly. It
42648      * is called when the editor creates its toolbar. Override this method if you need to
42649      * add custom toolbar buttons.
42650      * @param {HtmlEditor} editor
42651      */
42652     createToolbar : function(editor){
42653         Roo.log("create toolbars");
42654         if (!editor.toolbars || !editor.toolbars.length) {
42655             editor.toolbars = [ new Roo.form.HtmlEditor.ToolbarStandard() ]; // can be empty?
42656         }
42657         
42658         for (var i =0 ; i < editor.toolbars.length;i++) {
42659             editor.toolbars[i] = Roo.factory(
42660                     typeof(editor.toolbars[i]) == 'string' ?
42661                         { xtype: editor.toolbars[i]} : editor.toolbars[i],
42662                 Roo.form.HtmlEditor);
42663             editor.toolbars[i].init(editor);
42664         }
42665          
42666         
42667     },
42668
42669      
42670     // private
42671     onRender : function(ct, position)
42672     {
42673         var _t = this;
42674         Roo.form.HtmlEditor.superclass.onRender.call(this, ct, position);
42675         
42676         this.wrap = this.el.wrap({
42677             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
42678         });
42679         
42680         this.editorcore.onRender(ct, position);
42681          
42682         if (this.resizable) {
42683             this.resizeEl = new Roo.Resizable(this.wrap, {
42684                 pinned : true,
42685                 wrap: true,
42686                 dynamic : true,
42687                 minHeight : this.height,
42688                 height: this.height,
42689                 handles : this.resizable,
42690                 width: this.width,
42691                 listeners : {
42692                     resize : function(r, w, h) {
42693                         _t.onResize(w,h); // -something
42694                     }
42695                 }
42696             });
42697             
42698         }
42699         this.createToolbar(this);
42700        
42701         
42702         if(!this.width){
42703             this.setSize(this.wrap.getSize());
42704         }
42705         if (this.resizeEl) {
42706             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
42707             // should trigger onReize..
42708         }
42709         
42710 //        if(this.autosave && this.w){
42711 //            this.autoSaveFn = setInterval(this.autosave, 1000);
42712 //        }
42713     },
42714
42715     // private
42716     onResize : function(w, h)
42717     {
42718         //Roo.log('resize: ' +w + ',' + h );
42719         Roo.form.HtmlEditor.superclass.onResize.apply(this, arguments);
42720         var ew = false;
42721         var eh = false;
42722         
42723         if(this.el ){
42724             if(typeof w == 'number'){
42725                 var aw = w - this.wrap.getFrameWidth('lr');
42726                 this.el.setWidth(this.adjustWidth('textarea', aw));
42727                 ew = aw;
42728             }
42729             if(typeof h == 'number'){
42730                 var tbh = 0;
42731                 for (var i =0; i < this.toolbars.length;i++) {
42732                     // fixme - ask toolbars for heights?
42733                     tbh += this.toolbars[i].tb.el.getHeight();
42734                     if (this.toolbars[i].footer) {
42735                         tbh += this.toolbars[i].footer.el.getHeight();
42736                     }
42737                 }
42738                 
42739                 
42740                 
42741                 
42742                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
42743                 ah -= 5; // knock a few pixes off for look..
42744                 this.el.setHeight(this.adjustWidth('textarea', ah));
42745                 var eh = ah;
42746             }
42747         }
42748         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
42749         this.editorcore.onResize(ew,eh);
42750         
42751     },
42752
42753     /**
42754      * Toggles the editor between standard and source edit mode.
42755      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
42756      */
42757     toggleSourceEdit : function(sourceEditMode)
42758     {
42759         this.editorcore.toggleSourceEdit(sourceEditMode);
42760         
42761         if(this.editorcore.sourceEditMode){
42762             Roo.log('editor - showing textarea');
42763             
42764 //            Roo.log('in');
42765 //            Roo.log(this.syncValue());
42766             this.editorcore.syncValue();
42767             this.el.removeClass('x-hidden');
42768             this.el.dom.removeAttribute('tabIndex');
42769             this.el.focus();
42770         }else{
42771             Roo.log('editor - hiding textarea');
42772 //            Roo.log('out')
42773 //            Roo.log(this.pushValue()); 
42774             this.editorcore.pushValue();
42775             
42776             this.el.addClass('x-hidden');
42777             this.el.dom.setAttribute('tabIndex', -1);
42778             //this.deferFocus();
42779         }
42780          
42781         this.setSize(this.wrap.getSize());
42782         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
42783     },
42784  
42785     // private (for BoxComponent)
42786     adjustSize : Roo.BoxComponent.prototype.adjustSize,
42787
42788     // private (for BoxComponent)
42789     getResizeEl : function(){
42790         return this.wrap;
42791     },
42792
42793     // private (for BoxComponent)
42794     getPositionEl : function(){
42795         return this.wrap;
42796     },
42797
42798     // private
42799     initEvents : function(){
42800         this.originalValue = this.getValue();
42801     },
42802
42803     /**
42804      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
42805      * @method
42806      */
42807     markInvalid : Roo.emptyFn,
42808     /**
42809      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
42810      * @method
42811      */
42812     clearInvalid : Roo.emptyFn,
42813
42814     setValue : function(v){
42815         Roo.form.HtmlEditor.superclass.setValue.call(this, v);
42816         this.editorcore.pushValue();
42817     },
42818
42819      
42820     // private
42821     deferFocus : function(){
42822         this.focus.defer(10, this);
42823     },
42824
42825     // doc'ed in Field
42826     focus : function(){
42827         this.editorcore.focus();
42828         
42829     },
42830       
42831
42832     // private
42833     onDestroy : function(){
42834         
42835         
42836         
42837         if(this.rendered){
42838             
42839             for (var i =0; i < this.toolbars.length;i++) {
42840                 // fixme - ask toolbars for heights?
42841                 this.toolbars[i].onDestroy();
42842             }
42843             
42844             this.wrap.dom.innerHTML = '';
42845             this.wrap.remove();
42846         }
42847     },
42848
42849     // private
42850     onFirstFocus : function(){
42851         //Roo.log("onFirstFocus");
42852         this.editorcore.onFirstFocus();
42853          for (var i =0; i < this.toolbars.length;i++) {
42854             this.toolbars[i].onFirstFocus();
42855         }
42856         
42857     },
42858     
42859     // private
42860     syncValue : function()
42861     {
42862         this.editorcore.syncValue();
42863     },
42864     
42865     pushValue : function()
42866     {
42867         this.editorcore.pushValue();
42868     }
42869      
42870     
42871     // hide stuff that is not compatible
42872     /**
42873      * @event blur
42874      * @hide
42875      */
42876     /**
42877      * @event change
42878      * @hide
42879      */
42880     /**
42881      * @event focus
42882      * @hide
42883      */
42884     /**
42885      * @event specialkey
42886      * @hide
42887      */
42888     /**
42889      * @cfg {String} fieldClass @hide
42890      */
42891     /**
42892      * @cfg {String} focusClass @hide
42893      */
42894     /**
42895      * @cfg {String} autoCreate @hide
42896      */
42897     /**
42898      * @cfg {String} inputType @hide
42899      */
42900     /**
42901      * @cfg {String} invalidClass @hide
42902      */
42903     /**
42904      * @cfg {String} invalidText @hide
42905      */
42906     /**
42907      * @cfg {String} msgFx @hide
42908      */
42909     /**
42910      * @cfg {String} validateOnBlur @hide
42911      */
42912 });
42913  
42914     // <script type="text/javascript">
42915 /*
42916  * Based on
42917  * Ext JS Library 1.1.1
42918  * Copyright(c) 2006-2007, Ext JS, LLC.
42919  *  
42920  
42921  */
42922
42923 /**
42924  * @class Roo.form.HtmlEditorToolbar1
42925  * Basic Toolbar
42926  * 
42927  * Usage:
42928  *
42929  new Roo.form.HtmlEditor({
42930     ....
42931     toolbars : [
42932         new Roo.form.HtmlEditorToolbar1({
42933             disable : { fonts: 1 , format: 1, ..., ... , ...],
42934             btns : [ .... ]
42935         })
42936     }
42937      
42938  * 
42939  * @cfg {Object} disable List of elements to disable..
42940  * @cfg {Array} btns List of additional buttons.
42941  * 
42942  * 
42943  * NEEDS Extra CSS? 
42944  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
42945  */
42946  
42947 Roo.form.HtmlEditor.ToolbarStandard = function(config)
42948 {
42949     
42950     Roo.apply(this, config);
42951     
42952     // default disabled, based on 'good practice'..
42953     this.disable = this.disable || {};
42954     Roo.applyIf(this.disable, {
42955         fontSize : true,
42956         colors : true,
42957         specialElements : true
42958     });
42959     
42960     
42961     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
42962     // dont call parent... till later.
42963 }
42964
42965 Roo.apply(Roo.form.HtmlEditor.ToolbarStandard.prototype,  {
42966     
42967     tb: false,
42968     
42969     rendered: false,
42970     
42971     editor : false,
42972     editorcore : false,
42973     /**
42974      * @cfg {Object} disable  List of toolbar elements to disable
42975          
42976      */
42977     disable : false,
42978     
42979     
42980      /**
42981      * @cfg {String} createLinkText The default text for the create link prompt
42982      */
42983     createLinkText : 'Please enter the URL for the link:',
42984     /**
42985      * @cfg {String} defaultLinkValue The default value for the create link prompt (defaults to http:/ /)
42986      */
42987     defaultLinkValue : 'http:/'+'/',
42988    
42989     
42990       /**
42991      * @cfg {Array} fontFamilies An array of available font families
42992      */
42993     fontFamilies : [
42994         'Arial',
42995         'Courier New',
42996         'Tahoma',
42997         'Times New Roman',
42998         'Verdana'
42999     ],
43000     
43001     specialChars : [
43002            "&#169;",
43003           "&#174;",     
43004           "&#8482;",    
43005           "&#163;" ,    
43006          // "&#8212;",    
43007           "&#8230;",    
43008           "&#247;" ,    
43009         //  "&#225;" ,     ?? a acute?
43010            "&#8364;"    , //Euro
43011        //   "&#8220;"    ,
43012         //  "&#8221;"    ,
43013         //  "&#8226;"    ,
43014           "&#176;"  //   , // degrees
43015
43016          // "&#233;"     , // e ecute
43017          // "&#250;"     , // u ecute?
43018     ],
43019     
43020     specialElements : [
43021         {
43022             text: "Insert Table",
43023             xtype: 'MenuItem',
43024             xns : Roo.Menu,
43025             ihtml :  '<table><tr><td>Cell</td></tr></table>' 
43026                 
43027         },
43028         {    
43029             text: "Insert Image",
43030             xtype: 'MenuItem',
43031             xns : Roo.Menu,
43032             ihtml : '<img src="about:blank"/>'
43033             
43034         }
43035         
43036          
43037     ],
43038     
43039     
43040     inputElements : [ 
43041             "form", "input:text", "input:hidden", "input:checkbox", "input:radio", "input:password", 
43042             "input:submit", "input:button", "select", "textarea", "label" ],
43043     formats : [
43044         ["p"] ,  
43045         ["h1"],["h2"],["h3"],["h4"],["h5"],["h6"], 
43046         ["pre"],[ "code"], 
43047         ["abbr"],[ "acronym"],[ "address"],[ "cite"],[ "samp"],[ "var"],
43048         ['div'],['span']
43049     ],
43050     
43051     cleanStyles : [
43052         "font-size"
43053     ],
43054      /**
43055      * @cfg {String} defaultFont default font to use.
43056      */
43057     defaultFont: 'tahoma',
43058    
43059     fontSelect : false,
43060     
43061     
43062     formatCombo : false,
43063     
43064     init : function(editor)
43065     {
43066         this.editor = editor;
43067         this.editorcore = editor.editorcore ? editor.editorcore : editor;
43068         var editorcore = this.editorcore;
43069         
43070         var _t = this;
43071         
43072         var fid = editorcore.frameId;
43073         var etb = this;
43074         function btn(id, toggle, handler){
43075             var xid = fid + '-'+ id ;
43076             return {
43077                 id : xid,
43078                 cmd : id,
43079                 cls : 'x-btn-icon x-edit-'+id,
43080                 enableToggle:toggle !== false,
43081                 scope: _t, // was editor...
43082                 handler:handler||_t.relayBtnCmd,
43083                 clickEvent:'mousedown',
43084                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
43085                 tabIndex:-1
43086             };
43087         }
43088         
43089         
43090         
43091         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
43092         this.tb = tb;
43093          // stop form submits
43094         tb.el.on('click', function(e){
43095             e.preventDefault(); // what does this do?
43096         });
43097
43098         if(!this.disable.font) { // && !Roo.isSafari){
43099             /* why no safari for fonts 
43100             editor.fontSelect = tb.el.createChild({
43101                 tag:'select',
43102                 tabIndex: -1,
43103                 cls:'x-font-select',
43104                 html: this.createFontOptions()
43105             });
43106             
43107             editor.fontSelect.on('change', function(){
43108                 var font = editor.fontSelect.dom.value;
43109                 editor.relayCmd('fontname', font);
43110                 editor.deferFocus();
43111             }, editor);
43112             
43113             tb.add(
43114                 editor.fontSelect.dom,
43115                 '-'
43116             );
43117             */
43118             
43119         };
43120         if(!this.disable.formats){
43121             this.formatCombo = new Roo.form.ComboBox({
43122                 store: new Roo.data.SimpleStore({
43123                     id : 'tag',
43124                     fields: ['tag'],
43125                     data : this.formats // from states.js
43126                 }),
43127                 blockFocus : true,
43128                 name : '',
43129                 //autoCreate : {tag: "div",  size: "20"},
43130                 displayField:'tag',
43131                 typeAhead: false,
43132                 mode: 'local',
43133                 editable : false,
43134                 triggerAction: 'all',
43135                 emptyText:'Add tag',
43136                 selectOnFocus:true,
43137                 width:135,
43138                 listeners : {
43139                     'select': function(c, r, i) {
43140                         editorcore.insertTag(r.get('tag'));
43141                         editor.focus();
43142                     }
43143                 }
43144
43145             });
43146             tb.addField(this.formatCombo);
43147             
43148         }
43149         
43150         if(!this.disable.format){
43151             tb.add(
43152                 btn('bold'),
43153                 btn('italic'),
43154                 btn('underline')
43155             );
43156         };
43157         if(!this.disable.fontSize){
43158             tb.add(
43159                 '-',
43160                 
43161                 
43162                 btn('increasefontsize', false, editorcore.adjustFont),
43163                 btn('decreasefontsize', false, editorcore.adjustFont)
43164             );
43165         };
43166         
43167         
43168         if(!this.disable.colors){
43169             tb.add(
43170                 '-', {
43171                     id:editorcore.frameId +'-forecolor',
43172                     cls:'x-btn-icon x-edit-forecolor',
43173                     clickEvent:'mousedown',
43174                     tooltip: this.buttonTips['forecolor'] || undefined,
43175                     tabIndex:-1,
43176                     menu : new Roo.menu.ColorMenu({
43177                         allowReselect: true,
43178                         focus: Roo.emptyFn,
43179                         value:'000000',
43180                         plain:true,
43181                         selectHandler: function(cp, color){
43182                             editorcore.execCmd('forecolor', Roo.isSafari || Roo.isIE ? '#'+color : color);
43183                             editor.deferFocus();
43184                         },
43185                         scope: editorcore,
43186                         clickEvent:'mousedown'
43187                     })
43188                 }, {
43189                     id:editorcore.frameId +'backcolor',
43190                     cls:'x-btn-icon x-edit-backcolor',
43191                     clickEvent:'mousedown',
43192                     tooltip: this.buttonTips['backcolor'] || undefined,
43193                     tabIndex:-1,
43194                     menu : new Roo.menu.ColorMenu({
43195                         focus: Roo.emptyFn,
43196                         value:'FFFFFF',
43197                         plain:true,
43198                         allowReselect: true,
43199                         selectHandler: function(cp, color){
43200                             if(Roo.isGecko){
43201                                 editorcore.execCmd('useCSS', false);
43202                                 editorcore.execCmd('hilitecolor', color);
43203                                 editorcore.execCmd('useCSS', true);
43204                                 editor.deferFocus();
43205                             }else{
43206                                 editorcore.execCmd(Roo.isOpera ? 'hilitecolor' : 'backcolor', 
43207                                     Roo.isSafari || Roo.isIE ? '#'+color : color);
43208                                 editor.deferFocus();
43209                             }
43210                         },
43211                         scope:editorcore,
43212                         clickEvent:'mousedown'
43213                     })
43214                 }
43215             );
43216         };
43217         // now add all the items...
43218         
43219
43220         if(!this.disable.alignments){
43221             tb.add(
43222                 '-',
43223                 btn('justifyleft'),
43224                 btn('justifycenter'),
43225                 btn('justifyright')
43226             );
43227         };
43228
43229         //if(!Roo.isSafari){
43230             if(!this.disable.links){
43231                 tb.add(
43232                     '-',
43233                     btn('createlink', false, this.createLink)    /// MOVE TO HERE?!!?!?!?!
43234                 );
43235             };
43236
43237             if(!this.disable.lists){
43238                 tb.add(
43239                     '-',
43240                     btn('insertorderedlist'),
43241                     btn('insertunorderedlist')
43242                 );
43243             }
43244             if(!this.disable.sourceEdit){
43245                 tb.add(
43246                     '-',
43247                     btn('sourceedit', true, function(btn){
43248                         Roo.log(this);
43249                         this.toggleSourceEdit(btn.pressed);
43250                     })
43251                 );
43252             }
43253         //}
43254         
43255         var smenu = { };
43256         // special menu.. - needs to be tidied up..
43257         if (!this.disable.special) {
43258             smenu = {
43259                 text: "&#169;",
43260                 cls: 'x-edit-none',
43261                 
43262                 menu : {
43263                     items : []
43264                 }
43265             };
43266             for (var i =0; i < this.specialChars.length; i++) {
43267                 smenu.menu.items.push({
43268                     
43269                     html: this.specialChars[i],
43270                     handler: function(a,b) {
43271                         editorcore.insertAtCursor(String.fromCharCode(a.html.replace('&#','').replace(';', '')));
43272                         //editor.insertAtCursor(a.html);
43273                         
43274                     },
43275                     tabIndex:-1
43276                 });
43277             }
43278             
43279             
43280             tb.add(smenu);
43281             
43282             
43283         }
43284         
43285         var cmenu = { };
43286         if (!this.disable.cleanStyles) {
43287             cmenu = {
43288                 cls: 'x-btn-icon x-btn-clear',
43289                 
43290                 menu : {
43291                     items : []
43292                 }
43293             };
43294             for (var i =0; i < this.cleanStyles.length; i++) {
43295                 cmenu.menu.items.push({
43296                     actiontype : this.cleanStyles[i],
43297                     html: 'Remove ' + this.cleanStyles[i],
43298                     handler: function(a,b) {
43299                         Roo.log(a);
43300                         Roo.log(b);
43301                         var c = Roo.get(editorcore.doc.body);
43302                         c.select('[style]').each(function(s) {
43303                             s.dom.style.removeProperty(a.actiontype);
43304                         });
43305                         editorcore.syncValue();
43306                     },
43307                     tabIndex:-1
43308                 });
43309             }
43310             cmenu.menu.items.push({
43311                 actiontype : 'word',
43312                 html: 'Remove MS Word Formating',
43313                 handler: function(a,b) {
43314                     editorcore.cleanWord();
43315                     editorcore.syncValue();
43316                 },
43317                 tabIndex:-1
43318             });
43319             
43320             cmenu.menu.items.push({
43321                 actiontype : 'all',
43322                 html: 'Remove All Styles',
43323                 handler: function(a,b) {
43324                     
43325                     var c = Roo.get(editorcore.doc.body);
43326                     c.select('[style]').each(function(s) {
43327                         s.dom.removeAttribute('style');
43328                     });
43329                     editorcore.syncValue();
43330                 },
43331                 tabIndex:-1
43332             });
43333              cmenu.menu.items.push({
43334                 actiontype : 'word',
43335                 html: 'Tidy HTML Source',
43336                 handler: function(a,b) {
43337                     editorcore.doc.body.innerHTML = editorcore.domToHTML();
43338                     editorcore.syncValue();
43339                 },
43340                 tabIndex:-1
43341             });
43342             
43343             
43344             tb.add(cmenu);
43345         }
43346          
43347         if (!this.disable.specialElements) {
43348             var semenu = {
43349                 text: "Other;",
43350                 cls: 'x-edit-none',
43351                 menu : {
43352                     items : []
43353                 }
43354             };
43355             for (var i =0; i < this.specialElements.length; i++) {
43356                 semenu.menu.items.push(
43357                     Roo.apply({ 
43358                         handler: function(a,b) {
43359                             editor.insertAtCursor(this.ihtml);
43360                         }
43361                     }, this.specialElements[i])
43362                 );
43363                     
43364             }
43365             
43366             tb.add(semenu);
43367             
43368             
43369         }
43370          
43371         
43372         if (this.btns) {
43373             for(var i =0; i< this.btns.length;i++) {
43374                 var b = Roo.factory(this.btns[i],Roo.form);
43375                 b.cls =  'x-edit-none';
43376                 b.scope = editorcore;
43377                 tb.add(b);
43378             }
43379         
43380         }
43381         
43382         
43383         
43384         // disable everything...
43385         
43386         this.tb.items.each(function(item){
43387            if(item.id != editorcore.frameId+ '-sourceedit'){
43388                 item.disable();
43389             }
43390         });
43391         this.rendered = true;
43392         
43393         // the all the btns;
43394         editor.on('editorevent', this.updateToolbar, this);
43395         // other toolbars need to implement this..
43396         //editor.on('editmodechange', this.updateToolbar, this);
43397     },
43398     
43399     
43400     relayBtnCmd : function(btn) {
43401         this.editorcore.relayCmd(btn.cmd);
43402     },
43403     // private used internally
43404     createLink : function(){
43405         Roo.log("create link?");
43406         var url = prompt(this.createLinkText, this.defaultLinkValue);
43407         if(url && url != 'http:/'+'/'){
43408             this.editorcore.relayCmd('createlink', url);
43409         }
43410     },
43411
43412     
43413     /**
43414      * Protected method that will not generally be called directly. It triggers
43415      * a toolbar update by reading the markup state of the current selection in the editor.
43416      */
43417     updateToolbar: function(){
43418
43419         if(!this.editorcore.activated){
43420             this.editor.onFirstFocus();
43421             return;
43422         }
43423
43424         var btns = this.tb.items.map, 
43425             doc = this.editorcore.doc,
43426             frameId = this.editorcore.frameId;
43427
43428         if(!this.disable.font && !Roo.isSafari){
43429             /*
43430             var name = (doc.queryCommandValue('FontName')||this.editor.defaultFont).toLowerCase();
43431             if(name != this.fontSelect.dom.value){
43432                 this.fontSelect.dom.value = name;
43433             }
43434             */
43435         }
43436         if(!this.disable.format){
43437             btns[frameId + '-bold'].toggle(doc.queryCommandState('bold'));
43438             btns[frameId + '-italic'].toggle(doc.queryCommandState('italic'));
43439             btns[frameId + '-underline'].toggle(doc.queryCommandState('underline'));
43440         }
43441         if(!this.disable.alignments){
43442             btns[frameId + '-justifyleft'].toggle(doc.queryCommandState('justifyleft'));
43443             btns[frameId + '-justifycenter'].toggle(doc.queryCommandState('justifycenter'));
43444             btns[frameId + '-justifyright'].toggle(doc.queryCommandState('justifyright'));
43445         }
43446         if(!Roo.isSafari && !this.disable.lists){
43447             btns[frameId + '-insertorderedlist'].toggle(doc.queryCommandState('insertorderedlist'));
43448             btns[frameId + '-insertunorderedlist'].toggle(doc.queryCommandState('insertunorderedlist'));
43449         }
43450         
43451         var ans = this.editorcore.getAllAncestors();
43452         if (this.formatCombo) {
43453             
43454             
43455             var store = this.formatCombo.store;
43456             this.formatCombo.setValue("");
43457             for (var i =0; i < ans.length;i++) {
43458                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
43459                     // select it..
43460                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
43461                     break;
43462                 }
43463             }
43464         }
43465         
43466         
43467         
43468         // hides menus... - so this cant be on a menu...
43469         Roo.menu.MenuMgr.hideAll();
43470
43471         //this.editorsyncValue();
43472     },
43473    
43474     
43475     createFontOptions : function(){
43476         var buf = [], fs = this.fontFamilies, ff, lc;
43477         
43478         
43479         
43480         for(var i = 0, len = fs.length; i< len; i++){
43481             ff = fs[i];
43482             lc = ff.toLowerCase();
43483             buf.push(
43484                 '<option value="',lc,'" style="font-family:',ff,';"',
43485                     (this.defaultFont == lc ? ' selected="true">' : '>'),
43486                     ff,
43487                 '</option>'
43488             );
43489         }
43490         return buf.join('');
43491     },
43492     
43493     toggleSourceEdit : function(sourceEditMode){
43494         
43495         Roo.log("toolbar toogle");
43496         if(sourceEditMode === undefined){
43497             sourceEditMode = !this.sourceEditMode;
43498         }
43499         this.sourceEditMode = sourceEditMode === true;
43500         var btn = this.tb.items.get(this.editorcore.frameId +'-sourceedit');
43501         // just toggle the button?
43502         if(btn.pressed !== this.sourceEditMode){
43503             btn.toggle(this.sourceEditMode);
43504             return;
43505         }
43506         
43507         if(sourceEditMode){
43508             Roo.log("disabling buttons");
43509             this.tb.items.each(function(item){
43510                 if(item.cmd != 'sourceedit'){
43511                     item.disable();
43512                 }
43513             });
43514           
43515         }else{
43516             Roo.log("enabling buttons");
43517             if(this.editorcore.initialized){
43518                 this.tb.items.each(function(item){
43519                     item.enable();
43520                 });
43521             }
43522             
43523         }
43524         Roo.log("calling toggole on editor");
43525         // tell the editor that it's been pressed..
43526         this.editor.toggleSourceEdit(sourceEditMode);
43527        
43528     },
43529      /**
43530      * Object collection of toolbar tooltips for the buttons in the editor. The key
43531      * is the command id associated with that button and the value is a valid QuickTips object.
43532      * For example:
43533 <pre><code>
43534 {
43535     bold : {
43536         title: 'Bold (Ctrl+B)',
43537         text: 'Make the selected text bold.',
43538         cls: 'x-html-editor-tip'
43539     },
43540     italic : {
43541         title: 'Italic (Ctrl+I)',
43542         text: 'Make the selected text italic.',
43543         cls: 'x-html-editor-tip'
43544     },
43545     ...
43546 </code></pre>
43547     * @type Object
43548      */
43549     buttonTips : {
43550         bold : {
43551             title: 'Bold (Ctrl+B)',
43552             text: 'Make the selected text bold.',
43553             cls: 'x-html-editor-tip'
43554         },
43555         italic : {
43556             title: 'Italic (Ctrl+I)',
43557             text: 'Make the selected text italic.',
43558             cls: 'x-html-editor-tip'
43559         },
43560         underline : {
43561             title: 'Underline (Ctrl+U)',
43562             text: 'Underline the selected text.',
43563             cls: 'x-html-editor-tip'
43564         },
43565         increasefontsize : {
43566             title: 'Grow Text',
43567             text: 'Increase the font size.',
43568             cls: 'x-html-editor-tip'
43569         },
43570         decreasefontsize : {
43571             title: 'Shrink Text',
43572             text: 'Decrease the font size.',
43573             cls: 'x-html-editor-tip'
43574         },
43575         backcolor : {
43576             title: 'Text Highlight Color',
43577             text: 'Change the background color of the selected text.',
43578             cls: 'x-html-editor-tip'
43579         },
43580         forecolor : {
43581             title: 'Font Color',
43582             text: 'Change the color of the selected text.',
43583             cls: 'x-html-editor-tip'
43584         },
43585         justifyleft : {
43586             title: 'Align Text Left',
43587             text: 'Align text to the left.',
43588             cls: 'x-html-editor-tip'
43589         },
43590         justifycenter : {
43591             title: 'Center Text',
43592             text: 'Center text in the editor.',
43593             cls: 'x-html-editor-tip'
43594         },
43595         justifyright : {
43596             title: 'Align Text Right',
43597             text: 'Align text to the right.',
43598             cls: 'x-html-editor-tip'
43599         },
43600         insertunorderedlist : {
43601             title: 'Bullet List',
43602             text: 'Start a bulleted list.',
43603             cls: 'x-html-editor-tip'
43604         },
43605         insertorderedlist : {
43606             title: 'Numbered List',
43607             text: 'Start a numbered list.',
43608             cls: 'x-html-editor-tip'
43609         },
43610         createlink : {
43611             title: 'Hyperlink',
43612             text: 'Make the selected text a hyperlink.',
43613             cls: 'x-html-editor-tip'
43614         },
43615         sourceedit : {
43616             title: 'Source Edit',
43617             text: 'Switch to source editing mode.',
43618             cls: 'x-html-editor-tip'
43619         }
43620     },
43621     // private
43622     onDestroy : function(){
43623         if(this.rendered){
43624             
43625             this.tb.items.each(function(item){
43626                 if(item.menu){
43627                     item.menu.removeAll();
43628                     if(item.menu.el){
43629                         item.menu.el.destroy();
43630                     }
43631                 }
43632                 item.destroy();
43633             });
43634              
43635         }
43636     },
43637     onFirstFocus: function() {
43638         this.tb.items.each(function(item){
43639            item.enable();
43640         });
43641     }
43642 });
43643
43644
43645
43646
43647 // <script type="text/javascript">
43648 /*
43649  * Based on
43650  * Ext JS Library 1.1.1
43651  * Copyright(c) 2006-2007, Ext JS, LLC.
43652  *  
43653  
43654  */
43655
43656  
43657 /**
43658  * @class Roo.form.HtmlEditor.ToolbarContext
43659  * Context Toolbar
43660  * 
43661  * Usage:
43662  *
43663  new Roo.form.HtmlEditor({
43664     ....
43665     toolbars : [
43666         { xtype: 'ToolbarStandard', styles : {} }
43667         { xtype: 'ToolbarContext', disable : {} }
43668     ]
43669 })
43670
43671      
43672  * 
43673  * @config : {Object} disable List of elements to disable.. (not done yet.)
43674  * @config : {Object} styles  Map of styles available.
43675  * 
43676  */
43677
43678 Roo.form.HtmlEditor.ToolbarContext = function(config)
43679 {
43680     
43681     Roo.apply(this, config);
43682     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
43683     // dont call parent... till later.
43684     this.styles = this.styles || {};
43685 }
43686
43687  
43688
43689 Roo.form.HtmlEditor.ToolbarContext.types = {
43690     'IMG' : {
43691         width : {
43692             title: "Width",
43693             width: 40
43694         },
43695         height:  {
43696             title: "Height",
43697             width: 40
43698         },
43699         align: {
43700             title: "Align",
43701             opts : [ [""],[ "left"],[ "right"],[ "center"],[ "top"]],
43702             width : 80
43703             
43704         },
43705         border: {
43706             title: "Border",
43707             width: 40
43708         },
43709         alt: {
43710             title: "Alt",
43711             width: 120
43712         },
43713         src : {
43714             title: "Src",
43715             width: 220
43716         }
43717         
43718     },
43719     'A' : {
43720         name : {
43721             title: "Name",
43722             width: 50
43723         },
43724         target:  {
43725             title: "Target",
43726             width: 120
43727         },
43728         href:  {
43729             title: "Href",
43730             width: 220
43731         } // border?
43732         
43733     },
43734     'TABLE' : {
43735         rows : {
43736             title: "Rows",
43737             width: 20
43738         },
43739         cols : {
43740             title: "Cols",
43741             width: 20
43742         },
43743         width : {
43744             title: "Width",
43745             width: 40
43746         },
43747         height : {
43748             title: "Height",
43749             width: 40
43750         },
43751         border : {
43752             title: "Border",
43753             width: 20
43754         }
43755     },
43756     'TD' : {
43757         width : {
43758             title: "Width",
43759             width: 40
43760         },
43761         height : {
43762             title: "Height",
43763             width: 40
43764         },   
43765         align: {
43766             title: "Align",
43767             opts : [[""],[ "left"],[ "center"],[ "right"],[ "justify"],[ "char"]],
43768             width: 80
43769         },
43770         valign: {
43771             title: "Valign",
43772             opts : [[""],[ "top"],[ "middle"],[ "bottom"],[ "baseline"]],
43773             width: 80
43774         },
43775         colspan: {
43776             title: "Colspan",
43777             width: 20
43778             
43779         },
43780          'font-family'  : {
43781             title : "Font",
43782             style : 'fontFamily',
43783             displayField: 'display',
43784             optname : 'font-family',
43785             width: 140
43786         }
43787     },
43788     'INPUT' : {
43789         name : {
43790             title: "name",
43791             width: 120
43792         },
43793         value : {
43794             title: "Value",
43795             width: 120
43796         },
43797         width : {
43798             title: "Width",
43799             width: 40
43800         }
43801     },
43802     'LABEL' : {
43803         'for' : {
43804             title: "For",
43805             width: 120
43806         }
43807     },
43808     'TEXTAREA' : {
43809           name : {
43810             title: "name",
43811             width: 120
43812         },
43813         rows : {
43814             title: "Rows",
43815             width: 20
43816         },
43817         cols : {
43818             title: "Cols",
43819             width: 20
43820         }
43821     },
43822     'SELECT' : {
43823         name : {
43824             title: "name",
43825             width: 120
43826         },
43827         selectoptions : {
43828             title: "Options",
43829             width: 200
43830         }
43831     },
43832     
43833     // should we really allow this??
43834     // should this just be 
43835     'BODY' : {
43836         title : {
43837             title: "Title",
43838             width: 200,
43839             disabled : true
43840         }
43841     },
43842     'SPAN' : {
43843         'font-family'  : {
43844             title : "Font",
43845             style : 'fontFamily',
43846             displayField: 'display',
43847             optname : 'font-family',
43848             width: 140
43849         }
43850     },
43851     'DIV' : {
43852         'font-family'  : {
43853             title : "Font",
43854             style : 'fontFamily',
43855             displayField: 'display',
43856             optname : 'font-family',
43857             width: 140
43858         }
43859     },
43860      'P' : {
43861         'font-family'  : {
43862             title : "Font",
43863             style : 'fontFamily',
43864             displayField: 'display',
43865             optname : 'font-family',
43866             width: 140
43867         }
43868     },
43869     
43870     '*' : {
43871         // empty..
43872     }
43873
43874 };
43875
43876 // this should be configurable.. - you can either set it up using stores, or modify options somehwere..
43877 Roo.form.HtmlEditor.ToolbarContext.stores = false;
43878
43879 Roo.form.HtmlEditor.ToolbarContext.options = {
43880         'font-family'  : [ 
43881                 [ 'Helvetica,Arial,sans-serif', 'Helvetica'],
43882                 [ 'Courier New', 'Courier New'],
43883                 [ 'Tahoma', 'Tahoma'],
43884                 [ 'Times New Roman,serif', 'Times'],
43885                 [ 'Verdana','Verdana' ]
43886         ]
43887 };
43888
43889 // fixme - these need to be configurable..
43890  
43891
43892 Roo.form.HtmlEditor.ToolbarContext.types
43893
43894
43895 Roo.apply(Roo.form.HtmlEditor.ToolbarContext.prototype,  {
43896     
43897     tb: false,
43898     
43899     rendered: false,
43900     
43901     editor : false,
43902     editorcore : false,
43903     /**
43904      * @cfg {Object} disable  List of toolbar elements to disable
43905          
43906      */
43907     disable : false,
43908     /**
43909      * @cfg {Object} styles List of styles 
43910      *    eg. { '*' : [ 'headline' ] , 'TD' : [ 'underline', 'double-underline' ] } 
43911      *
43912      * These must be defined in the page, so they get rendered correctly..
43913      * .headline { }
43914      * TD.underline { }
43915      * 
43916      */
43917     styles : false,
43918     
43919     options: false,
43920     
43921     toolbars : false,
43922     
43923     init : function(editor)
43924     {
43925         this.editor = editor;
43926         this.editorcore = editor.editorcore ? editor.editorcore : editor;
43927         var editorcore = this.editorcore;
43928         
43929         var fid = editorcore.frameId;
43930         var etb = this;
43931         function btn(id, toggle, handler){
43932             var xid = fid + '-'+ id ;
43933             return {
43934                 id : xid,
43935                 cmd : id,
43936                 cls : 'x-btn-icon x-edit-'+id,
43937                 enableToggle:toggle !== false,
43938                 scope: editorcore, // was editor...
43939                 handler:handler||editorcore.relayBtnCmd,
43940                 clickEvent:'mousedown',
43941                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
43942                 tabIndex:-1
43943             };
43944         }
43945         // create a new element.
43946         var wdiv = editor.wrap.createChild({
43947                 tag: 'div'
43948             }, editor.wrap.dom.firstChild.nextSibling, true);
43949         
43950         // can we do this more than once??
43951         
43952          // stop form submits
43953       
43954  
43955         // disable everything...
43956         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
43957         this.toolbars = {};
43958            
43959         for (var i in  ty) {
43960           
43961             this.toolbars[i] = this.buildToolbar(ty[i],i);
43962         }
43963         this.tb = this.toolbars.BODY;
43964         this.tb.el.show();
43965         this.buildFooter();
43966         this.footer.show();
43967         editor.on('hide', function( ) { this.footer.hide() }, this);
43968         editor.on('show', function( ) { this.footer.show() }, this);
43969         
43970          
43971         this.rendered = true;
43972         
43973         // the all the btns;
43974         editor.on('editorevent', this.updateToolbar, this);
43975         // other toolbars need to implement this..
43976         //editor.on('editmodechange', this.updateToolbar, this);
43977     },
43978     
43979     
43980     
43981     /**
43982      * Protected method that will not generally be called directly. It triggers
43983      * a toolbar update by reading the markup state of the current selection in the editor.
43984      */
43985     updateToolbar: function(editor,ev,sel){
43986
43987         //Roo.log(ev);
43988         // capture mouse up - this is handy for selecting images..
43989         // perhaps should go somewhere else...
43990         if(!this.editorcore.activated){
43991              this.editor.onFirstFocus();
43992             return;
43993         }
43994         
43995         // http://developer.yahoo.com/yui/docs/simple-editor.js.html
43996         // selectNode - might want to handle IE?
43997         if (ev &&
43998             (ev.type == 'mouseup' || ev.type == 'click' ) &&
43999             ev.target && ev.target.tagName == 'IMG') {
44000             // they have click on an image...
44001             // let's see if we can change the selection...
44002             sel = ev.target;
44003          
44004               var nodeRange = sel.ownerDocument.createRange();
44005             try {
44006                 nodeRange.selectNode(sel);
44007             } catch (e) {
44008                 nodeRange.selectNodeContents(sel);
44009             }
44010             //nodeRange.collapse(true);
44011             var s = this.editorcore.win.getSelection();
44012             s.removeAllRanges();
44013             s.addRange(nodeRange);
44014         }  
44015         
44016       
44017         var updateFooter = sel ? false : true;
44018         
44019         
44020         var ans = this.editorcore.getAllAncestors();
44021         
44022         // pick
44023         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
44024         
44025         if (!sel) { 
44026             sel = ans.length ? (ans[0] ?  ans[0]  : ans[1]) : this.editorcore.doc.body;
44027             sel = sel ? sel : this.editorcore.doc.body;
44028             sel = sel.tagName.length ? sel : this.editorcore.doc.body;
44029             
44030         }
44031         // pick a menu that exists..
44032         var tn = sel.tagName.toUpperCase();
44033         //sel = typeof(ty[tn]) != 'undefined' ? sel : this.editor.doc.body;
44034         
44035         tn = sel.tagName.toUpperCase();
44036         
44037         var lastSel = this.tb.selectedNode
44038         
44039         this.tb.selectedNode = sel;
44040         
44041         // if current menu does not match..
44042         if ((this.tb.name != tn) || (lastSel != this.tb.selectedNode)) {
44043                 
44044             this.tb.el.hide();
44045             ///console.log("show: " + tn);
44046             this.tb =  typeof(ty[tn]) != 'undefined' ? this.toolbars[tn] : this.toolbars['*'];
44047             this.tb.el.show();
44048             // update name
44049             this.tb.items.first().el.innerHTML = tn + ':&nbsp;';
44050             
44051             
44052             // update attributes
44053             if (this.tb.fields) {
44054                 this.tb.fields.each(function(e) {
44055                     if (e.stylename) {
44056                         e.setValue(sel.style[e.stylename]);
44057                         return;
44058                     } 
44059                    e.setValue(sel.getAttribute(e.attrname));
44060                 });
44061             }
44062             
44063             var hasStyles = false;
44064             for(var i in this.styles) {
44065                 hasStyles = true;
44066                 break;
44067             }
44068             
44069             // update styles
44070             if (hasStyles) { 
44071                 var st = this.tb.fields.item(0);
44072                 
44073                 st.store.removeAll();
44074                
44075                 
44076                 var cn = sel.className.split(/\s+/);
44077                 
44078                 var avs = [];
44079                 if (this.styles['*']) {
44080                     
44081                     Roo.each(this.styles['*'], function(v) {
44082                         avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
44083                     });
44084                 }
44085                 if (this.styles[tn]) { 
44086                     Roo.each(this.styles[tn], function(v) {
44087                         avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
44088                     });
44089                 }
44090                 
44091                 st.store.loadData(avs);
44092                 st.collapse();
44093                 st.setValue(cn);
44094             }
44095             // flag our selected Node.
44096             this.tb.selectedNode = sel;
44097            
44098            
44099             Roo.menu.MenuMgr.hideAll();
44100
44101         }
44102         
44103         if (!updateFooter) {
44104             //this.footDisp.dom.innerHTML = ''; 
44105             return;
44106         }
44107         // update the footer
44108         //
44109         var html = '';
44110         
44111         this.footerEls = ans.reverse();
44112         Roo.each(this.footerEls, function(a,i) {
44113             if (!a) { return; }
44114             html += html.length ? ' &gt; '  :  '';
44115             
44116             html += '<span class="x-ed-loc-' + i + '">' + a.tagName + '</span>';
44117             
44118         });
44119        
44120         // 
44121         var sz = this.footDisp.up('td').getSize();
44122         this.footDisp.dom.style.width = (sz.width -10) + 'px';
44123         this.footDisp.dom.style.marginLeft = '5px';
44124         
44125         this.footDisp.dom.style.overflow = 'hidden';
44126         
44127         this.footDisp.dom.innerHTML = html;
44128             
44129         //this.editorsyncValue();
44130     },
44131      
44132     
44133    
44134        
44135     // private
44136     onDestroy : function(){
44137         if(this.rendered){
44138             
44139             this.tb.items.each(function(item){
44140                 if(item.menu){
44141                     item.menu.removeAll();
44142                     if(item.menu.el){
44143                         item.menu.el.destroy();
44144                     }
44145                 }
44146                 item.destroy();
44147             });
44148              
44149         }
44150     },
44151     onFirstFocus: function() {
44152         // need to do this for all the toolbars..
44153         this.tb.items.each(function(item){
44154            item.enable();
44155         });
44156     },
44157     buildToolbar: function(tlist, nm)
44158     {
44159         var editor = this.editor;
44160         var editorcore = this.editorcore;
44161          // create a new element.
44162         var wdiv = editor.wrap.createChild({
44163                 tag: 'div'
44164             }, editor.wrap.dom.firstChild.nextSibling, true);
44165         
44166        
44167         var tb = new Roo.Toolbar(wdiv);
44168         // add the name..
44169         
44170         tb.add(nm+ ":&nbsp;");
44171         
44172         var styles = [];
44173         for(var i in this.styles) {
44174             styles.push(i);
44175         }
44176         
44177         // styles...
44178         if (styles && styles.length) {
44179             
44180             // this needs a multi-select checkbox...
44181             tb.addField( new Roo.form.ComboBox({
44182                 store: new Roo.data.SimpleStore({
44183                     id : 'val',
44184                     fields: ['val', 'selected'],
44185                     data : [] 
44186                 }),
44187                 name : '-roo-edit-className',
44188                 attrname : 'className',
44189                 displayField: 'val',
44190                 typeAhead: false,
44191                 mode: 'local',
44192                 editable : false,
44193                 triggerAction: 'all',
44194                 emptyText:'Select Style',
44195                 selectOnFocus:true,
44196                 width: 130,
44197                 listeners : {
44198                     'select': function(c, r, i) {
44199                         // initial support only for on class per el..
44200                         tb.selectedNode.className =  r ? r.get('val') : '';
44201                         editorcore.syncValue();
44202                     }
44203                 }
44204     
44205             }));
44206         }
44207         
44208         var tbc = Roo.form.HtmlEditor.ToolbarContext;
44209         var tbops = tbc.options;
44210         
44211         for (var i in tlist) {
44212             
44213             var item = tlist[i];
44214             tb.add(item.title + ":&nbsp;");
44215             
44216             
44217             //optname == used so you can configure the options available..
44218             var opts = item.opts ? item.opts : false;
44219             if (item.optname) {
44220                 opts = tbops[item.optname];
44221            
44222             }
44223             
44224             if (opts) {
44225                 // opts == pulldown..
44226                 tb.addField( new Roo.form.ComboBox({
44227                     store:   typeof(tbc.stores[i]) != 'undefined' ?  Roo.factory(tbc.stores[i],Roo.data) : new Roo.data.SimpleStore({
44228                         id : 'val',
44229                         fields: ['val', 'display'],
44230                         data : opts  
44231                     }),
44232                     name : '-roo-edit-' + i,
44233                     attrname : i,
44234                     stylename : item.style ? item.style : false,
44235                     displayField: item.displayField ? item.displayField : 'val',
44236                     valueField :  'val',
44237                     typeAhead: false,
44238                     mode: typeof(tbc.stores[i]) != 'undefined'  ? 'remote' : 'local',
44239                     editable : false,
44240                     triggerAction: 'all',
44241                     emptyText:'Select',
44242                     selectOnFocus:true,
44243                     width: item.width ? item.width  : 130,
44244                     listeners : {
44245                         'select': function(c, r, i) {
44246                             if (c.stylename) {
44247                                 tb.selectedNode.style[c.stylename] =  r.get('val');
44248                                 return;
44249                             }
44250                             tb.selectedNode.setAttribute(c.attrname, r.get('val'));
44251                         }
44252                     }
44253
44254                 }));
44255                 continue;
44256                     
44257                  
44258                 
44259                 tb.addField( new Roo.form.TextField({
44260                     name: i,
44261                     width: 100,
44262                     //allowBlank:false,
44263                     value: ''
44264                 }));
44265                 continue;
44266             }
44267             tb.addField( new Roo.form.TextField({
44268                 name: '-roo-edit-' + i,
44269                 attrname : i,
44270                 
44271                 width: item.width,
44272                 //allowBlank:true,
44273                 value: '',
44274                 listeners: {
44275                     'change' : function(f, nv, ov) {
44276                         tb.selectedNode.setAttribute(f.attrname, nv);
44277                     }
44278                 }
44279             }));
44280              
44281         }
44282         tb.addFill();
44283         var _this = this;
44284         tb.addButton( {
44285             text: 'Remove Tag',
44286     
44287             listeners : {
44288                 click : function ()
44289                 {
44290                     // remove
44291                     // undo does not work.
44292                      
44293                     var sn = tb.selectedNode;
44294                     
44295                     var pn = sn.parentNode;
44296                     
44297                     var stn =  sn.childNodes[0];
44298                     var en = sn.childNodes[sn.childNodes.length - 1 ];
44299                     while (sn.childNodes.length) {
44300                         var node = sn.childNodes[0];
44301                         sn.removeChild(node);
44302                         //Roo.log(node);
44303                         pn.insertBefore(node, sn);
44304                         
44305                     }
44306                     pn.removeChild(sn);
44307                     var range = editorcore.createRange();
44308         
44309                     range.setStart(stn,0);
44310                     range.setEnd(en,0); //????
44311                     //range.selectNode(sel);
44312                     
44313                     
44314                     var selection = editorcore.getSelection();
44315                     selection.removeAllRanges();
44316                     selection.addRange(range);
44317                     
44318                     
44319                     
44320                     //_this.updateToolbar(null, null, pn);
44321                     _this.updateToolbar(null, null, null);
44322                     _this.footDisp.dom.innerHTML = ''; 
44323                 }
44324             }
44325             
44326                     
44327                 
44328             
44329         });
44330         
44331         
44332         tb.el.on('click', function(e){
44333             e.preventDefault(); // what does this do?
44334         });
44335         tb.el.setVisibilityMode( Roo.Element.DISPLAY);
44336         tb.el.hide();
44337         tb.name = nm;
44338         // dont need to disable them... as they will get hidden
44339         return tb;
44340          
44341         
44342     },
44343     buildFooter : function()
44344     {
44345         
44346         var fel = this.editor.wrap.createChild();
44347         this.footer = new Roo.Toolbar(fel);
44348         // toolbar has scrolly on left / right?
44349         var footDisp= new Roo.Toolbar.Fill();
44350         var _t = this;
44351         this.footer.add(
44352             {
44353                 text : '&lt;',
44354                 xtype: 'Button',
44355                 handler : function() {
44356                     _t.footDisp.scrollTo('left',0,true)
44357                 }
44358             }
44359         );
44360         this.footer.add( footDisp );
44361         this.footer.add( 
44362             {
44363                 text : '&gt;',
44364                 xtype: 'Button',
44365                 handler : function() {
44366                     // no animation..
44367                     _t.footDisp.select('span').last().scrollIntoView(_t.footDisp,true);
44368                 }
44369             }
44370         );
44371         var fel = Roo.get(footDisp.el);
44372         fel.addClass('x-editor-context');
44373         this.footDispWrap = fel; 
44374         this.footDispWrap.overflow  = 'hidden';
44375         
44376         this.footDisp = fel.createChild();
44377         this.footDispWrap.on('click', this.onContextClick, this)
44378         
44379         
44380     },
44381     onContextClick : function (ev,dom)
44382     {
44383         ev.preventDefault();
44384         var  cn = dom.className;
44385         //Roo.log(cn);
44386         if (!cn.match(/x-ed-loc-/)) {
44387             return;
44388         }
44389         var n = cn.split('-').pop();
44390         var ans = this.footerEls;
44391         var sel = ans[n];
44392         
44393          // pick
44394         var range = this.editorcore.createRange();
44395         
44396         range.selectNodeContents(sel);
44397         //range.selectNode(sel);
44398         
44399         
44400         var selection = this.editorcore.getSelection();
44401         selection.removeAllRanges();
44402         selection.addRange(range);
44403         
44404         
44405         
44406         this.updateToolbar(null, null, sel);
44407         
44408         
44409     }
44410     
44411     
44412     
44413     
44414     
44415 });
44416
44417
44418
44419
44420
44421 /*
44422  * Based on:
44423  * Ext JS Library 1.1.1
44424  * Copyright(c) 2006-2007, Ext JS, LLC.
44425  *
44426  * Originally Released Under LGPL - original licence link has changed is not relivant.
44427  *
44428  * Fork - LGPL
44429  * <script type="text/javascript">
44430  */
44431  
44432 /**
44433  * @class Roo.form.BasicForm
44434  * @extends Roo.util.Observable
44435  * Supplies the functionality to do "actions" on forms and initialize Roo.form.Field types on existing markup.
44436  * @constructor
44437  * @param {String/HTMLElement/Roo.Element} el The form element or its id
44438  * @param {Object} config Configuration options
44439  */
44440 Roo.form.BasicForm = function(el, config){
44441     this.allItems = [];
44442     this.childForms = [];
44443     Roo.apply(this, config);
44444     /*
44445      * The Roo.form.Field items in this form.
44446      * @type MixedCollection
44447      */
44448      
44449      
44450     this.items = new Roo.util.MixedCollection(false, function(o){
44451         return o.id || (o.id = Roo.id());
44452     });
44453     this.addEvents({
44454         /**
44455          * @event beforeaction
44456          * Fires before any action is performed. Return false to cancel the action.
44457          * @param {Form} this
44458          * @param {Action} action The action to be performed
44459          */
44460         beforeaction: true,
44461         /**
44462          * @event actionfailed
44463          * Fires when an action fails.
44464          * @param {Form} this
44465          * @param {Action} action The action that failed
44466          */
44467         actionfailed : true,
44468         /**
44469          * @event actioncomplete
44470          * Fires when an action is completed.
44471          * @param {Form} this
44472          * @param {Action} action The action that completed
44473          */
44474         actioncomplete : true
44475     });
44476     if(el){
44477         this.initEl(el);
44478     }
44479     Roo.form.BasicForm.superclass.constructor.call(this);
44480 };
44481
44482 Roo.extend(Roo.form.BasicForm, Roo.util.Observable, {
44483     /**
44484      * @cfg {String} method
44485      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
44486      */
44487     /**
44488      * @cfg {DataReader} reader
44489      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when executing "load" actions.
44490      * This is optional as there is built-in support for processing JSON.
44491      */
44492     /**
44493      * @cfg {DataReader} errorReader
44494      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when reading validation errors on "submit" actions.
44495      * This is completely optional as there is built-in support for processing JSON.
44496      */
44497     /**
44498      * @cfg {String} url
44499      * The URL to use for form actions if one isn't supplied in the action options.
44500      */
44501     /**
44502      * @cfg {Boolean} fileUpload
44503      * Set to true if this form is a file upload.
44504      */
44505      
44506     /**
44507      * @cfg {Object} baseParams
44508      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
44509      */
44510      /**
44511      
44512     /**
44513      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
44514      */
44515     timeout: 30,
44516
44517     // private
44518     activeAction : null,
44519
44520     /**
44521      * @cfg {Boolean} trackResetOnLoad If set to true, form.reset() resets to the last loaded
44522      * or setValues() data instead of when the form was first created.
44523      */
44524     trackResetOnLoad : false,
44525     
44526     
44527     /**
44528      * childForms - used for multi-tab forms
44529      * @type {Array}
44530      */
44531     childForms : false,
44532     
44533     /**
44534      * allItems - full list of fields.
44535      * @type {Array}
44536      */
44537     allItems : false,
44538     
44539     /**
44540      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
44541      * element by passing it or its id or mask the form itself by passing in true.
44542      * @type Mixed
44543      */
44544     waitMsgTarget : false,
44545
44546     // private
44547     initEl : function(el){
44548         this.el = Roo.get(el);
44549         this.id = this.el.id || Roo.id();
44550         this.el.on('submit', this.onSubmit, this);
44551         this.el.addClass('x-form');
44552     },
44553
44554     // private
44555     onSubmit : function(e){
44556         e.stopEvent();
44557     },
44558
44559     /**
44560      * Returns true if client-side validation on the form is successful.
44561      * @return Boolean
44562      */
44563     isValid : function(){
44564         var valid = true;
44565         this.items.each(function(f){
44566            if(!f.validate()){
44567                valid = false;
44568            }
44569         });
44570         return valid;
44571     },
44572
44573     /**
44574      * Returns true if any fields in this form have changed since their original load.
44575      * @return Boolean
44576      */
44577     isDirty : function(){
44578         var dirty = false;
44579         this.items.each(function(f){
44580            if(f.isDirty()){
44581                dirty = true;
44582                return false;
44583            }
44584         });
44585         return dirty;
44586     },
44587
44588     /**
44589      * Performs a predefined action (submit or load) or custom actions you define on this form.
44590      * @param {String} actionName The name of the action type
44591      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
44592      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
44593      * accept other config options):
44594      * <pre>
44595 Property          Type             Description
44596 ----------------  ---------------  ----------------------------------------------------------------------------------
44597 url               String           The url for the action (defaults to the form's url)
44598 method            String           The form method to use (defaults to the form's method, or POST if not defined)
44599 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
44600 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
44601                                    validate the form on the client (defaults to false)
44602      * </pre>
44603      * @return {BasicForm} this
44604      */
44605     doAction : function(action, options){
44606         if(typeof action == 'string'){
44607             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
44608         }
44609         if(this.fireEvent('beforeaction', this, action) !== false){
44610             this.beforeAction(action);
44611             action.run.defer(100, action);
44612         }
44613         return this;
44614     },
44615
44616     /**
44617      * Shortcut to do a submit action.
44618      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
44619      * @return {BasicForm} this
44620      */
44621     submit : function(options){
44622         this.doAction('submit', options);
44623         return this;
44624     },
44625
44626     /**
44627      * Shortcut to do a load action.
44628      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
44629      * @return {BasicForm} this
44630      */
44631     load : function(options){
44632         this.doAction('load', options);
44633         return this;
44634     },
44635
44636     /**
44637      * Persists the values in this form into the passed Roo.data.Record object in a beginEdit/endEdit block.
44638      * @param {Record} record The record to edit
44639      * @return {BasicForm} this
44640      */
44641     updateRecord : function(record){
44642         record.beginEdit();
44643         var fs = record.fields;
44644         fs.each(function(f){
44645             var field = this.findField(f.name);
44646             if(field){
44647                 record.set(f.name, field.getValue());
44648             }
44649         }, this);
44650         record.endEdit();
44651         return this;
44652     },
44653
44654     /**
44655      * Loads an Roo.data.Record into this form.
44656      * @param {Record} record The record to load
44657      * @return {BasicForm} this
44658      */
44659     loadRecord : function(record){
44660         this.setValues(record.data);
44661         return this;
44662     },
44663
44664     // private
44665     beforeAction : function(action){
44666         var o = action.options;
44667         
44668        
44669         if(this.waitMsgTarget === true){
44670             this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
44671         }else if(this.waitMsgTarget){
44672             this.waitMsgTarget = Roo.get(this.waitMsgTarget);
44673             this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
44674         }else {
44675             Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
44676         }
44677          
44678     },
44679
44680     // private
44681     afterAction : function(action, success){
44682         this.activeAction = null;
44683         var o = action.options;
44684         
44685         if(this.waitMsgTarget === true){
44686             this.el.unmask();
44687         }else if(this.waitMsgTarget){
44688             this.waitMsgTarget.unmask();
44689         }else{
44690             Roo.MessageBox.updateProgress(1);
44691             Roo.MessageBox.hide();
44692         }
44693          
44694         if(success){
44695             if(o.reset){
44696                 this.reset();
44697             }
44698             Roo.callback(o.success, o.scope, [this, action]);
44699             this.fireEvent('actioncomplete', this, action);
44700             
44701         }else{
44702             
44703             // failure condition..
44704             // we have a scenario where updates need confirming.
44705             // eg. if a locking scenario exists..
44706             // we look for { errors : { needs_confirm : true }} in the response.
44707             if (
44708                 (typeof(action.result) != 'undefined')  &&
44709                 (typeof(action.result.errors) != 'undefined')  &&
44710                 (typeof(action.result.errors.needs_confirm) != 'undefined')
44711            ){
44712                 var _t = this;
44713                 Roo.MessageBox.confirm(
44714                     "Change requires confirmation",
44715                     action.result.errorMsg,
44716                     function(r) {
44717                         if (r != 'yes') {
44718                             return;
44719                         }
44720                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
44721                     }
44722                     
44723                 );
44724                 
44725                 
44726                 
44727                 return;
44728             }
44729             
44730             Roo.callback(o.failure, o.scope, [this, action]);
44731             // show an error message if no failed handler is set..
44732             if (!this.hasListener('actionfailed')) {
44733                 Roo.MessageBox.alert("Error",
44734                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
44735                         action.result.errorMsg :
44736                         "Saving Failed, please check your entries or try again"
44737                 );
44738             }
44739             
44740             this.fireEvent('actionfailed', this, action);
44741         }
44742         
44743     },
44744
44745     /**
44746      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
44747      * @param {String} id The value to search for
44748      * @return Field
44749      */
44750     findField : function(id){
44751         var field = this.items.get(id);
44752         if(!field){
44753             this.items.each(function(f){
44754                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
44755                     field = f;
44756                     return false;
44757                 }
44758             });
44759         }
44760         return field || null;
44761     },
44762
44763     /**
44764      * Add a secondary form to this one, 
44765      * Used to provide tabbed forms. One form is primary, with hidden values 
44766      * which mirror the elements from the other forms.
44767      * 
44768      * @param {Roo.form.Form} form to add.
44769      * 
44770      */
44771     addForm : function(form)
44772     {
44773        
44774         if (this.childForms.indexOf(form) > -1) {
44775             // already added..
44776             return;
44777         }
44778         this.childForms.push(form);
44779         var n = '';
44780         Roo.each(form.allItems, function (fe) {
44781             
44782             n = typeof(fe.getName) == 'undefined' ? fe.name : fe.getName();
44783             if (this.findField(n)) { // already added..
44784                 return;
44785             }
44786             var add = new Roo.form.Hidden({
44787                 name : n
44788             });
44789             add.render(this.el);
44790             
44791             this.add( add );
44792         }, this);
44793         
44794     },
44795     /**
44796      * Mark fields in this form invalid in bulk.
44797      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
44798      * @return {BasicForm} this
44799      */
44800     markInvalid : function(errors){
44801         if(errors instanceof Array){
44802             for(var i = 0, len = errors.length; i < len; i++){
44803                 var fieldError = errors[i];
44804                 var f = this.findField(fieldError.id);
44805                 if(f){
44806                     f.markInvalid(fieldError.msg);
44807                 }
44808             }
44809         }else{
44810             var field, id;
44811             for(id in errors){
44812                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
44813                     field.markInvalid(errors[id]);
44814                 }
44815             }
44816         }
44817         Roo.each(this.childForms || [], function (f) {
44818             f.markInvalid(errors);
44819         });
44820         
44821         return this;
44822     },
44823
44824     /**
44825      * Set values for fields in this form in bulk.
44826      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
44827      * @return {BasicForm} this
44828      */
44829     setValues : function(values){
44830         if(values instanceof Array){ // array of objects
44831             for(var i = 0, len = values.length; i < len; i++){
44832                 var v = values[i];
44833                 var f = this.findField(v.id);
44834                 if(f){
44835                     f.setValue(v.value);
44836                     if(this.trackResetOnLoad){
44837                         f.originalValue = f.getValue();
44838                     }
44839                 }
44840             }
44841         }else{ // object hash
44842             var field, id;
44843             for(id in values){
44844                 if(typeof values[id] != 'function' && (field = this.findField(id))){
44845                     
44846                     if (field.setFromData && 
44847                         field.valueField && 
44848                         field.displayField &&
44849                         // combos' with local stores can 
44850                         // be queried via setValue()
44851                         // to set their value..
44852                         (field.store && !field.store.isLocal)
44853                         ) {
44854                         // it's a combo
44855                         var sd = { };
44856                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
44857                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
44858                         field.setFromData(sd);
44859                         
44860                     } else {
44861                         field.setValue(values[id]);
44862                     }
44863                     
44864                     
44865                     if(this.trackResetOnLoad){
44866                         field.originalValue = field.getValue();
44867                     }
44868                 }
44869             }
44870         }
44871          
44872         Roo.each(this.childForms || [], function (f) {
44873             f.setValues(values);
44874         });
44875                 
44876         return this;
44877     },
44878
44879     /**
44880      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
44881      * they are returned as an array.
44882      * @param {Boolean} asString
44883      * @return {Object}
44884      */
44885     getValues : function(asString){
44886         if (this.childForms) {
44887             // copy values from the child forms
44888             Roo.each(this.childForms, function (f) {
44889                 this.setValues(f.getValues());
44890             }, this);
44891         }
44892         
44893         
44894         
44895         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
44896         if(asString === true){
44897             return fs;
44898         }
44899         return Roo.urlDecode(fs);
44900     },
44901     
44902     /**
44903      * Returns the fields in this form as an object with key/value pairs. 
44904      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
44905      * @return {Object}
44906      */
44907     getFieldValues : function(with_hidden)
44908     {
44909         if (this.childForms) {
44910             // copy values from the child forms
44911             // should this call getFieldValues - probably not as we do not currently copy
44912             // hidden fields when we generate..
44913             Roo.each(this.childForms, function (f) {
44914                 this.setValues(f.getValues());
44915             }, this);
44916         }
44917         
44918         var ret = {};
44919         this.items.each(function(f){
44920             if (!f.getName()) {
44921                 return;
44922             }
44923             var v = f.getValue();
44924             if (f.inputType =='radio') {
44925                 if (typeof(ret[f.getName()]) == 'undefined') {
44926                     ret[f.getName()] = ''; // empty..
44927                 }
44928                 
44929                 if (!f.el.dom.checked) {
44930                     return;
44931                     
44932                 }
44933                 v = f.el.dom.value;
44934                 
44935             }
44936             
44937             // not sure if this supported any more..
44938             if ((typeof(v) == 'object') && f.getRawValue) {
44939                 v = f.getRawValue() ; // dates..
44940             }
44941             // combo boxes where name != hiddenName...
44942             if (f.name != f.getName()) {
44943                 ret[f.name] = f.getRawValue();
44944             }
44945             ret[f.getName()] = v;
44946         });
44947         
44948         return ret;
44949     },
44950
44951     /**
44952      * Clears all invalid messages in this form.
44953      * @return {BasicForm} this
44954      */
44955     clearInvalid : function(){
44956         this.items.each(function(f){
44957            f.clearInvalid();
44958         });
44959         
44960         Roo.each(this.childForms || [], function (f) {
44961             f.clearInvalid();
44962         });
44963         
44964         
44965         return this;
44966     },
44967
44968     /**
44969      * Resets this form.
44970      * @return {BasicForm} this
44971      */
44972     reset : function(){
44973         this.items.each(function(f){
44974             f.reset();
44975         });
44976         
44977         Roo.each(this.childForms || [], function (f) {
44978             f.reset();
44979         });
44980        
44981         
44982         return this;
44983     },
44984
44985     /**
44986      * Add Roo.form components to this form.
44987      * @param {Field} field1
44988      * @param {Field} field2 (optional)
44989      * @param {Field} etc (optional)
44990      * @return {BasicForm} this
44991      */
44992     add : function(){
44993         this.items.addAll(Array.prototype.slice.call(arguments, 0));
44994         return this;
44995     },
44996
44997
44998     /**
44999      * Removes a field from the items collection (does NOT remove its markup).
45000      * @param {Field} field
45001      * @return {BasicForm} this
45002      */
45003     remove : function(field){
45004         this.items.remove(field);
45005         return this;
45006     },
45007
45008     /**
45009      * Looks at the fields in this form, checks them for an id attribute,
45010      * and calls applyTo on the existing dom element with that id.
45011      * @return {BasicForm} this
45012      */
45013     render : function(){
45014         this.items.each(function(f){
45015             if(f.isFormField && !f.rendered && document.getElementById(f.id)){ // if the element exists
45016                 f.applyTo(f.id);
45017             }
45018         });
45019         return this;
45020     },
45021
45022     /**
45023      * Calls {@link Ext#apply} for all fields in this form with the passed object.
45024      * @param {Object} values
45025      * @return {BasicForm} this
45026      */
45027     applyToFields : function(o){
45028         this.items.each(function(f){
45029            Roo.apply(f, o);
45030         });
45031         return this;
45032     },
45033
45034     /**
45035      * Calls {@link Ext#applyIf} for all field in this form with the passed object.
45036      * @param {Object} values
45037      * @return {BasicForm} this
45038      */
45039     applyIfToFields : function(o){
45040         this.items.each(function(f){
45041            Roo.applyIf(f, o);
45042         });
45043         return this;
45044     }
45045 });
45046
45047 // back compat
45048 Roo.BasicForm = Roo.form.BasicForm;/*
45049  * Based on:
45050  * Ext JS Library 1.1.1
45051  * Copyright(c) 2006-2007, Ext JS, LLC.
45052  *
45053  * Originally Released Under LGPL - original licence link has changed is not relivant.
45054  *
45055  * Fork - LGPL
45056  * <script type="text/javascript">
45057  */
45058
45059 /**
45060  * @class Roo.form.Form
45061  * @extends Roo.form.BasicForm
45062  * Adds the ability to dynamically render forms with JavaScript to {@link Roo.form.BasicForm}.
45063  * @constructor
45064  * @param {Object} config Configuration options
45065  */
45066 Roo.form.Form = function(config){
45067     var xitems =  [];
45068     if (config.items) {
45069         xitems = config.items;
45070         delete config.items;
45071     }
45072    
45073     
45074     Roo.form.Form.superclass.constructor.call(this, null, config);
45075     this.url = this.url || this.action;
45076     if(!this.root){
45077         this.root = new Roo.form.Layout(Roo.applyIf({
45078             id: Roo.id()
45079         }, config));
45080     }
45081     this.active = this.root;
45082     /**
45083      * Array of all the buttons that have been added to this form via {@link addButton}
45084      * @type Array
45085      */
45086     this.buttons = [];
45087     this.allItems = [];
45088     this.addEvents({
45089         /**
45090          * @event clientvalidation
45091          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
45092          * @param {Form} this
45093          * @param {Boolean} valid true if the form has passed client-side validation
45094          */
45095         clientvalidation: true,
45096         /**
45097          * @event rendered
45098          * Fires when the form is rendered
45099          * @param {Roo.form.Form} form
45100          */
45101         rendered : true
45102     });
45103     
45104     if (this.progressUrl) {
45105             // push a hidden field onto the list of fields..
45106             this.addxtype( {
45107                     xns: Roo.form, 
45108                     xtype : 'Hidden', 
45109                     name : 'UPLOAD_IDENTIFIER' 
45110             });
45111         }
45112         
45113     
45114     Roo.each(xitems, this.addxtype, this);
45115     
45116     
45117     
45118 };
45119
45120 Roo.extend(Roo.form.Form, Roo.form.BasicForm, {
45121     /**
45122      * @cfg {Number} labelWidth The width of labels. This property cascades to child containers.
45123      */
45124     /**
45125      * @cfg {String} itemCls A css class to apply to the x-form-item of fields. This property cascades to child containers.
45126      */
45127     /**
45128      * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "center")
45129      */
45130     buttonAlign:'center',
45131
45132     /**
45133      * @cfg {Number} minButtonWidth Minimum width of all buttons in pixels (defaults to 75)
45134      */
45135     minButtonWidth:75,
45136
45137     /**
45138      * @cfg {String} labelAlign Valid values are "left," "top" and "right" (defaults to "left").
45139      * This property cascades to child containers if not set.
45140      */
45141     labelAlign:'left',
45142
45143     /**
45144      * @cfg {Boolean} monitorValid If true the form monitors its valid state <b>client-side</b> and
45145      * fires a looping event with that state. This is required to bind buttons to the valid
45146      * state using the config value formBind:true on the button.
45147      */
45148     monitorValid : false,
45149
45150     /**
45151      * @cfg {Number} monitorPoll The milliseconds to poll valid state, ignored if monitorValid is not true (defaults to 200)
45152      */
45153     monitorPoll : 200,
45154     
45155     /**
45156      * @cfg {String} progressUrl - Url to return progress data 
45157      */
45158     
45159     progressUrl : false,
45160   
45161     /**
45162      * Opens a new {@link Roo.form.Column} container in the layout stack. If fields are passed after the config, the
45163      * fields are added and the column is closed. If no fields are passed the column remains open
45164      * until end() is called.
45165      * @param {Object} config The config to pass to the column
45166      * @param {Field} field1 (optional)
45167      * @param {Field} field2 (optional)
45168      * @param {Field} etc (optional)
45169      * @return Column The column container object
45170      */
45171     column : function(c){
45172         var col = new Roo.form.Column(c);
45173         this.start(col);
45174         if(arguments.length > 1){ // duplicate code required because of Opera
45175             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
45176             this.end();
45177         }
45178         return col;
45179     },
45180
45181     /**
45182      * Opens a new {@link Roo.form.FieldSet} container in the layout stack. If fields are passed after the config, the
45183      * fields are added and the fieldset is closed. If no fields are passed the fieldset remains open
45184      * until end() is called.
45185      * @param {Object} config The config to pass to the fieldset
45186      * @param {Field} field1 (optional)
45187      * @param {Field} field2 (optional)
45188      * @param {Field} etc (optional)
45189      * @return FieldSet The fieldset container object
45190      */
45191     fieldset : function(c){
45192         var fs = new Roo.form.FieldSet(c);
45193         this.start(fs);
45194         if(arguments.length > 1){ // duplicate code required because of Opera
45195             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
45196             this.end();
45197         }
45198         return fs;
45199     },
45200
45201     /**
45202      * Opens a new {@link Roo.form.Layout} container in the layout stack. If fields are passed after the config, the
45203      * fields are added and the container is closed. If no fields are passed the container remains open
45204      * until end() is called.
45205      * @param {Object} config The config to pass to the Layout
45206      * @param {Field} field1 (optional)
45207      * @param {Field} field2 (optional)
45208      * @param {Field} etc (optional)
45209      * @return Layout The container object
45210      */
45211     container : function(c){
45212         var l = new Roo.form.Layout(c);
45213         this.start(l);
45214         if(arguments.length > 1){ // duplicate code required because of Opera
45215             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
45216             this.end();
45217         }
45218         return l;
45219     },
45220
45221     /**
45222      * Opens the passed container in the layout stack. The container can be any {@link Roo.form.Layout} or subclass.
45223      * @param {Object} container A Roo.form.Layout or subclass of Layout
45224      * @return {Form} this
45225      */
45226     start : function(c){
45227         // cascade label info
45228         Roo.applyIf(c, {'labelAlign': this.active.labelAlign, 'labelWidth': this.active.labelWidth, 'itemCls': this.active.itemCls});
45229         this.active.stack.push(c);
45230         c.ownerCt = this.active;
45231         this.active = c;
45232         return this;
45233     },
45234
45235     /**
45236      * Closes the current open container
45237      * @return {Form} this
45238      */
45239     end : function(){
45240         if(this.active == this.root){
45241             return this;
45242         }
45243         this.active = this.active.ownerCt;
45244         return this;
45245     },
45246
45247     /**
45248      * Add Roo.form components to the current open container (e.g. column, fieldset, etc.).  Fields added via this method
45249      * can also be passed with an additional property of fieldLabel, which if supplied, will provide the text to display
45250      * as the label of the field.
45251      * @param {Field} field1
45252      * @param {Field} field2 (optional)
45253      * @param {Field} etc. (optional)
45254      * @return {Form} this
45255      */
45256     add : function(){
45257         this.active.stack.push.apply(this.active.stack, arguments);
45258         this.allItems.push.apply(this.allItems,arguments);
45259         var r = [];
45260         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
45261             if(a[i].isFormField){
45262                 r.push(a[i]);
45263             }
45264         }
45265         if(r.length > 0){
45266             Roo.form.Form.superclass.add.apply(this, r);
45267         }
45268         return this;
45269     },
45270     
45271
45272     
45273     
45274     
45275      /**
45276      * Find any element that has been added to a form, using it's ID or name
45277      * This can include framesets, columns etc. along with regular fields..
45278      * @param {String} id - id or name to find.
45279      
45280      * @return {Element} e - or false if nothing found.
45281      */
45282     findbyId : function(id)
45283     {
45284         var ret = false;
45285         if (!id) {
45286             return ret;
45287         }
45288         Roo.each(this.allItems, function(f){
45289             if (f.id == id || f.name == id ){
45290                 ret = f;
45291                 return false;
45292             }
45293         });
45294         return ret;
45295     },
45296
45297     
45298     
45299     /**
45300      * Render this form into the passed container. This should only be called once!
45301      * @param {String/HTMLElement/Element} container The element this component should be rendered into
45302      * @return {Form} this
45303      */
45304     render : function(ct)
45305     {
45306         
45307         
45308         
45309         ct = Roo.get(ct);
45310         var o = this.autoCreate || {
45311             tag: 'form',
45312             method : this.method || 'POST',
45313             id : this.id || Roo.id()
45314         };
45315         this.initEl(ct.createChild(o));
45316
45317         this.root.render(this.el);
45318         
45319        
45320              
45321         this.items.each(function(f){
45322             f.render('x-form-el-'+f.id);
45323         });
45324
45325         if(this.buttons.length > 0){
45326             // tables are required to maintain order and for correct IE layout
45327             var tb = this.el.createChild({cls:'x-form-btns-ct', cn: {
45328                 cls:"x-form-btns x-form-btns-"+this.buttonAlign,
45329                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
45330             }}, null, true);
45331             var tr = tb.getElementsByTagName('tr')[0];
45332             for(var i = 0, len = this.buttons.length; i < len; i++) {
45333                 var b = this.buttons[i];
45334                 var td = document.createElement('td');
45335                 td.className = 'x-form-btn-td';
45336                 b.render(tr.appendChild(td));
45337             }
45338         }
45339         if(this.monitorValid){ // initialize after render
45340             this.startMonitoring();
45341         }
45342         this.fireEvent('rendered', this);
45343         return this;
45344     },
45345
45346     /**
45347      * Adds a button to the footer of the form - this <b>must</b> be called before the form is rendered.
45348      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
45349      * object or a valid Roo.DomHelper element config
45350      * @param {Function} handler The function called when the button is clicked
45351      * @param {Object} scope (optional) The scope of the handler function
45352      * @return {Roo.Button}
45353      */
45354     addButton : function(config, handler, scope){
45355         var bc = {
45356             handler: handler,
45357             scope: scope,
45358             minWidth: this.minButtonWidth,
45359             hideParent:true
45360         };
45361         if(typeof config == "string"){
45362             bc.text = config;
45363         }else{
45364             Roo.apply(bc, config);
45365         }
45366         var btn = new Roo.Button(null, bc);
45367         this.buttons.push(btn);
45368         return btn;
45369     },
45370
45371      /**
45372      * Adds a series of form elements (using the xtype property as the factory method.
45373      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column, (and 'end' to close a block)
45374      * @param {Object} config 
45375      */
45376     
45377     addxtype : function()
45378     {
45379         var ar = Array.prototype.slice.call(arguments, 0);
45380         var ret = false;
45381         for(var i = 0; i < ar.length; i++) {
45382             if (!ar[i]) {
45383                 continue; // skip -- if this happends something invalid got sent, we 
45384                 // should ignore it, as basically that interface element will not show up
45385                 // and that should be pretty obvious!!
45386             }
45387             
45388             if (Roo.form[ar[i].xtype]) {
45389                 ar[i].form = this;
45390                 var fe = Roo.factory(ar[i], Roo.form);
45391                 if (!ret) {
45392                     ret = fe;
45393                 }
45394                 fe.form = this;
45395                 if (fe.store) {
45396                     fe.store.form = this;
45397                 }
45398                 if (fe.isLayout) {  
45399                          
45400                     this.start(fe);
45401                     this.allItems.push(fe);
45402                     if (fe.items && fe.addxtype) {
45403                         fe.addxtype.apply(fe, fe.items);
45404                         delete fe.items;
45405                     }
45406                      this.end();
45407                     continue;
45408                 }
45409                 
45410                 
45411                  
45412                 this.add(fe);
45413               //  console.log('adding ' + ar[i].xtype);
45414             }
45415             if (ar[i].xtype == 'Button') {  
45416                 //console.log('adding button');
45417                 //console.log(ar[i]);
45418                 this.addButton(ar[i]);
45419                 this.allItems.push(fe);
45420                 continue;
45421             }
45422             
45423             if (ar[i].xtype == 'end') { // so we can add fieldsets... / layout etc.
45424                 alert('end is not supported on xtype any more, use items');
45425             //    this.end();
45426             //    //console.log('adding end');
45427             }
45428             
45429         }
45430         return ret;
45431     },
45432     
45433     /**
45434      * Starts monitoring of the valid state of this form. Usually this is done by passing the config
45435      * option "monitorValid"
45436      */
45437     startMonitoring : function(){
45438         if(!this.bound){
45439             this.bound = true;
45440             Roo.TaskMgr.start({
45441                 run : this.bindHandler,
45442                 interval : this.monitorPoll || 200,
45443                 scope: this
45444             });
45445         }
45446     },
45447
45448     /**
45449      * Stops monitoring of the valid state of this form
45450      */
45451     stopMonitoring : function(){
45452         this.bound = false;
45453     },
45454
45455     // private
45456     bindHandler : function(){
45457         if(!this.bound){
45458             return false; // stops binding
45459         }
45460         var valid = true;
45461         this.items.each(function(f){
45462             if(!f.isValid(true)){
45463                 valid = false;
45464                 return false;
45465             }
45466         });
45467         for(var i = 0, len = this.buttons.length; i < len; i++){
45468             var btn = this.buttons[i];
45469             if(btn.formBind === true && btn.disabled === valid){
45470                 btn.setDisabled(!valid);
45471             }
45472         }
45473         this.fireEvent('clientvalidation', this, valid);
45474     }
45475     
45476     
45477     
45478     
45479     
45480     
45481     
45482     
45483 });
45484
45485
45486 // back compat
45487 Roo.Form = Roo.form.Form;
45488 /*
45489  * Based on:
45490  * Ext JS Library 1.1.1
45491  * Copyright(c) 2006-2007, Ext JS, LLC.
45492  *
45493  * Originally Released Under LGPL - original licence link has changed is not relivant.
45494  *
45495  * Fork - LGPL
45496  * <script type="text/javascript">
45497  */
45498
45499 // as we use this in bootstrap.
45500 Roo.namespace('Roo.form');
45501  /**
45502  * @class Roo.form.Action
45503  * Internal Class used to handle form actions
45504  * @constructor
45505  * @param {Roo.form.BasicForm} el The form element or its id
45506  * @param {Object} config Configuration options
45507  */
45508
45509  
45510  
45511 // define the action interface
45512 Roo.form.Action = function(form, options){
45513     this.form = form;
45514     this.options = options || {};
45515 };
45516 /**
45517  * Client Validation Failed
45518  * @const 
45519  */
45520 Roo.form.Action.CLIENT_INVALID = 'client';
45521 /**
45522  * Server Validation Failed
45523  * @const 
45524  */
45525 Roo.form.Action.SERVER_INVALID = 'server';
45526  /**
45527  * Connect to Server Failed
45528  * @const 
45529  */
45530 Roo.form.Action.CONNECT_FAILURE = 'connect';
45531 /**
45532  * Reading Data from Server Failed
45533  * @const 
45534  */
45535 Roo.form.Action.LOAD_FAILURE = 'load';
45536
45537 Roo.form.Action.prototype = {
45538     type : 'default',
45539     failureType : undefined,
45540     response : undefined,
45541     result : undefined,
45542
45543     // interface method
45544     run : function(options){
45545
45546     },
45547
45548     // interface method
45549     success : function(response){
45550
45551     },
45552
45553     // interface method
45554     handleResponse : function(response){
45555
45556     },
45557
45558     // default connection failure
45559     failure : function(response){
45560         
45561         this.response = response;
45562         this.failureType = Roo.form.Action.CONNECT_FAILURE;
45563         this.form.afterAction(this, false);
45564     },
45565
45566     processResponse : function(response){
45567         this.response = response;
45568         if(!response.responseText){
45569             return true;
45570         }
45571         this.result = this.handleResponse(response);
45572         return this.result;
45573     },
45574
45575     // utility functions used internally
45576     getUrl : function(appendParams){
45577         var url = this.options.url || this.form.url || this.form.el.dom.action;
45578         if(appendParams){
45579             var p = this.getParams();
45580             if(p){
45581                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
45582             }
45583         }
45584         return url;
45585     },
45586
45587     getMethod : function(){
45588         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
45589     },
45590
45591     getParams : function(){
45592         var bp = this.form.baseParams;
45593         var p = this.options.params;
45594         if(p){
45595             if(typeof p == "object"){
45596                 p = Roo.urlEncode(Roo.applyIf(p, bp));
45597             }else if(typeof p == 'string' && bp){
45598                 p += '&' + Roo.urlEncode(bp);
45599             }
45600         }else if(bp){
45601             p = Roo.urlEncode(bp);
45602         }
45603         return p;
45604     },
45605
45606     createCallback : function(){
45607         return {
45608             success: this.success,
45609             failure: this.failure,
45610             scope: this,
45611             timeout: (this.form.timeout*1000),
45612             upload: this.form.fileUpload ? this.success : undefined
45613         };
45614     }
45615 };
45616
45617 Roo.form.Action.Submit = function(form, options){
45618     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
45619 };
45620
45621 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
45622     type : 'submit',
45623
45624     haveProgress : false,
45625     uploadComplete : false,
45626     
45627     // uploadProgress indicator.
45628     uploadProgress : function()
45629     {
45630         if (!this.form.progressUrl) {
45631             return;
45632         }
45633         
45634         if (!this.haveProgress) {
45635             Roo.MessageBox.progress("Uploading", "Uploading");
45636         }
45637         if (this.uploadComplete) {
45638            Roo.MessageBox.hide();
45639            return;
45640         }
45641         
45642         this.haveProgress = true;
45643    
45644         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
45645         
45646         var c = new Roo.data.Connection();
45647         c.request({
45648             url : this.form.progressUrl,
45649             params: {
45650                 id : uid
45651             },
45652             method: 'GET',
45653             success : function(req){
45654                //console.log(data);
45655                 var rdata = false;
45656                 var edata;
45657                 try  {
45658                    rdata = Roo.decode(req.responseText)
45659                 } catch (e) {
45660                     Roo.log("Invalid data from server..");
45661                     Roo.log(edata);
45662                     return;
45663                 }
45664                 if (!rdata || !rdata.success) {
45665                     Roo.log(rdata);
45666                     Roo.MessageBox.alert(Roo.encode(rdata));
45667                     return;
45668                 }
45669                 var data = rdata.data;
45670                 
45671                 if (this.uploadComplete) {
45672                    Roo.MessageBox.hide();
45673                    return;
45674                 }
45675                    
45676                 if (data){
45677                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
45678                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
45679                     );
45680                 }
45681                 this.uploadProgress.defer(2000,this);
45682             },
45683        
45684             failure: function(data) {
45685                 Roo.log('progress url failed ');
45686                 Roo.log(data);
45687             },
45688             scope : this
45689         });
45690            
45691     },
45692     
45693     
45694     run : function()
45695     {
45696         // run get Values on the form, so it syncs any secondary forms.
45697         this.form.getValues();
45698         
45699         var o = this.options;
45700         var method = this.getMethod();
45701         var isPost = method == 'POST';
45702         if(o.clientValidation === false || this.form.isValid()){
45703             
45704             if (this.form.progressUrl) {
45705                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
45706                     (new Date() * 1) + '' + Math.random());
45707                     
45708             } 
45709             
45710             
45711             Roo.Ajax.request(Roo.apply(this.createCallback(), {
45712                 form:this.form.el.dom,
45713                 url:this.getUrl(!isPost),
45714                 method: method,
45715                 params:isPost ? this.getParams() : null,
45716                 isUpload: this.form.fileUpload
45717             }));
45718             
45719             this.uploadProgress();
45720
45721         }else if (o.clientValidation !== false){ // client validation failed
45722             this.failureType = Roo.form.Action.CLIENT_INVALID;
45723             this.form.afterAction(this, false);
45724         }
45725     },
45726
45727     success : function(response)
45728     {
45729         this.uploadComplete= true;
45730         if (this.haveProgress) {
45731             Roo.MessageBox.hide();
45732         }
45733         
45734         
45735         var result = this.processResponse(response);
45736         if(result === true || result.success){
45737             this.form.afterAction(this, true);
45738             return;
45739         }
45740         if(result.errors){
45741             this.form.markInvalid(result.errors);
45742             this.failureType = Roo.form.Action.SERVER_INVALID;
45743         }
45744         this.form.afterAction(this, false);
45745     },
45746     failure : function(response)
45747     {
45748         this.uploadComplete= true;
45749         if (this.haveProgress) {
45750             Roo.MessageBox.hide();
45751         }
45752         
45753         this.response = response;
45754         this.failureType = Roo.form.Action.CONNECT_FAILURE;
45755         this.form.afterAction(this, false);
45756     },
45757     
45758     handleResponse : function(response){
45759         if(this.form.errorReader){
45760             var rs = this.form.errorReader.read(response);
45761             var errors = [];
45762             if(rs.records){
45763                 for(var i = 0, len = rs.records.length; i < len; i++) {
45764                     var r = rs.records[i];
45765                     errors[i] = r.data;
45766                 }
45767             }
45768             if(errors.length < 1){
45769                 errors = null;
45770             }
45771             return {
45772                 success : rs.success,
45773                 errors : errors
45774             };
45775         }
45776         var ret = false;
45777         try {
45778             ret = Roo.decode(response.responseText);
45779         } catch (e) {
45780             ret = {
45781                 success: false,
45782                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
45783                 errors : []
45784             };
45785         }
45786         return ret;
45787         
45788     }
45789 });
45790
45791
45792 Roo.form.Action.Load = function(form, options){
45793     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
45794     this.reader = this.form.reader;
45795 };
45796
45797 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
45798     type : 'load',
45799
45800     run : function(){
45801         
45802         Roo.Ajax.request(Roo.apply(
45803                 this.createCallback(), {
45804                     method:this.getMethod(),
45805                     url:this.getUrl(false),
45806                     params:this.getParams()
45807         }));
45808     },
45809
45810     success : function(response){
45811         
45812         var result = this.processResponse(response);
45813         if(result === true || !result.success || !result.data){
45814             this.failureType = Roo.form.Action.LOAD_FAILURE;
45815             this.form.afterAction(this, false);
45816             return;
45817         }
45818         this.form.clearInvalid();
45819         this.form.setValues(result.data);
45820         this.form.afterAction(this, true);
45821     },
45822
45823     handleResponse : function(response){
45824         if(this.form.reader){
45825             var rs = this.form.reader.read(response);
45826             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
45827             return {
45828                 success : rs.success,
45829                 data : data
45830             };
45831         }
45832         return Roo.decode(response.responseText);
45833     }
45834 });
45835
45836 Roo.form.Action.ACTION_TYPES = {
45837     'load' : Roo.form.Action.Load,
45838     'submit' : Roo.form.Action.Submit
45839 };/*
45840  * Based on:
45841  * Ext JS Library 1.1.1
45842  * Copyright(c) 2006-2007, Ext JS, LLC.
45843  *
45844  * Originally Released Under LGPL - original licence link has changed is not relivant.
45845  *
45846  * Fork - LGPL
45847  * <script type="text/javascript">
45848  */
45849  
45850 /**
45851  * @class Roo.form.Layout
45852  * @extends Roo.Component
45853  * Creates a container for layout and rendering of fields in an {@link Roo.form.Form}.
45854  * @constructor
45855  * @param {Object} config Configuration options
45856  */
45857 Roo.form.Layout = function(config){
45858     var xitems = [];
45859     if (config.items) {
45860         xitems = config.items;
45861         delete config.items;
45862     }
45863     Roo.form.Layout.superclass.constructor.call(this, config);
45864     this.stack = [];
45865     Roo.each(xitems, this.addxtype, this);
45866      
45867 };
45868
45869 Roo.extend(Roo.form.Layout, Roo.Component, {
45870     /**
45871      * @cfg {String/Object} autoCreate
45872      * A DomHelper element spec used to autocreate the layout (defaults to {tag: 'div', cls: 'x-form-ct'})
45873      */
45874     /**
45875      * @cfg {String/Object/Function} style
45876      * A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
45877      * a function which returns such a specification.
45878      */
45879     /**
45880      * @cfg {String} labelAlign
45881      * Valid values are "left," "top" and "right" (defaults to "left")
45882      */
45883     /**
45884      * @cfg {Number} labelWidth
45885      * Fixed width in pixels of all field labels (defaults to undefined)
45886      */
45887     /**
45888      * @cfg {Boolean} clear
45889      * True to add a clearing element at the end of this layout, equivalent to CSS clear: both (defaults to true)
45890      */
45891     clear : true,
45892     /**
45893      * @cfg {String} labelSeparator
45894      * The separator to use after field labels (defaults to ':')
45895      */
45896     labelSeparator : ':',
45897     /**
45898      * @cfg {Boolean} hideLabels
45899      * True to suppress the display of field labels in this layout (defaults to false)
45900      */
45901     hideLabels : false,
45902
45903     // private
45904     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct'},
45905     
45906     isLayout : true,
45907     
45908     // private
45909     onRender : function(ct, position){
45910         if(this.el){ // from markup
45911             this.el = Roo.get(this.el);
45912         }else {  // generate
45913             var cfg = this.getAutoCreate();
45914             this.el = ct.createChild(cfg, position);
45915         }
45916         if(this.style){
45917             this.el.applyStyles(this.style);
45918         }
45919         if(this.labelAlign){
45920             this.el.addClass('x-form-label-'+this.labelAlign);
45921         }
45922         if(this.hideLabels){
45923             this.labelStyle = "display:none";
45924             this.elementStyle = "padding-left:0;";
45925         }else{
45926             if(typeof this.labelWidth == 'number'){
45927                 this.labelStyle = "width:"+this.labelWidth+"px;";
45928                 this.elementStyle = "padding-left:"+((this.labelWidth+(typeof this.labelPad == 'number' ? this.labelPad : 5))+'px')+";";
45929             }
45930             if(this.labelAlign == 'top'){
45931                 this.labelStyle = "width:auto;";
45932                 this.elementStyle = "padding-left:0;";
45933             }
45934         }
45935         var stack = this.stack;
45936         var slen = stack.length;
45937         if(slen > 0){
45938             if(!this.fieldTpl){
45939                 var t = new Roo.Template(
45940                     '<div class="x-form-item {5}">',
45941                         '<label for="{0}" style="{2}">{1}{4}</label>',
45942                         '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
45943                         '</div>',
45944                     '</div><div class="x-form-clear-left"></div>'
45945                 );
45946                 t.disableFormats = true;
45947                 t.compile();
45948                 Roo.form.Layout.prototype.fieldTpl = t;
45949             }
45950             for(var i = 0; i < slen; i++) {
45951                 if(stack[i].isFormField){
45952                     this.renderField(stack[i]);
45953                 }else{
45954                     this.renderComponent(stack[i]);
45955                 }
45956             }
45957         }
45958         if(this.clear){
45959             this.el.createChild({cls:'x-form-clear'});
45960         }
45961     },
45962
45963     // private
45964     renderField : function(f){
45965         f.fieldEl = Roo.get(this.fieldTpl.append(this.el, [
45966                f.id, //0
45967                f.fieldLabel, //1
45968                f.labelStyle||this.labelStyle||'', //2
45969                this.elementStyle||'', //3
45970                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator, //4
45971                f.itemCls||this.itemCls||''  //5
45972        ], true).getPrevSibling());
45973     },
45974
45975     // private
45976     renderComponent : function(c){
45977         c.render(c.isLayout ? this.el : this.el.createChild());    
45978     },
45979     /**
45980      * Adds a object form elements (using the xtype property as the factory method.)
45981      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column
45982      * @param {Object} config 
45983      */
45984     addxtype : function(o)
45985     {
45986         // create the lement.
45987         o.form = this.form;
45988         var fe = Roo.factory(o, Roo.form);
45989         this.form.allItems.push(fe);
45990         this.stack.push(fe);
45991         
45992         if (fe.isFormField) {
45993             this.form.items.add(fe);
45994         }
45995          
45996         return fe;
45997     }
45998 });
45999
46000 /**
46001  * @class Roo.form.Column
46002  * @extends Roo.form.Layout
46003  * Creates a column container for layout and rendering of fields in an {@link Roo.form.Form}.
46004  * @constructor
46005  * @param {Object} config Configuration options
46006  */
46007 Roo.form.Column = function(config){
46008     Roo.form.Column.superclass.constructor.call(this, config);
46009 };
46010
46011 Roo.extend(Roo.form.Column, Roo.form.Layout, {
46012     /**
46013      * @cfg {Number/String} width
46014      * The fixed width of the column in pixels or CSS value (defaults to "auto")
46015      */
46016     /**
46017      * @cfg {String/Object} autoCreate
46018      * A DomHelper element spec used to autocreate the column (defaults to {tag: 'div', cls: 'x-form-ct x-form-column'})
46019      */
46020
46021     // private
46022     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-column'},
46023
46024     // private
46025     onRender : function(ct, position){
46026         Roo.form.Column.superclass.onRender.call(this, ct, position);
46027         if(this.width){
46028             this.el.setWidth(this.width);
46029         }
46030     }
46031 });
46032
46033
46034 /**
46035  * @class Roo.form.Row
46036  * @extends Roo.form.Layout
46037  * Creates a row container for layout and rendering of fields in an {@link Roo.form.Form}.
46038  * @constructor
46039  * @param {Object} config Configuration options
46040  */
46041
46042  
46043 Roo.form.Row = function(config){
46044     Roo.form.Row.superclass.constructor.call(this, config);
46045 };
46046  
46047 Roo.extend(Roo.form.Row, Roo.form.Layout, {
46048       /**
46049      * @cfg {Number/String} width
46050      * The fixed width of the column in pixels or CSS value (defaults to "auto")
46051      */
46052     /**
46053      * @cfg {Number/String} height
46054      * The fixed height of the column in pixels or CSS value (defaults to "auto")
46055      */
46056     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-row'},
46057     
46058     padWidth : 20,
46059     // private
46060     onRender : function(ct, position){
46061         //console.log('row render');
46062         if(!this.rowTpl){
46063             var t = new Roo.Template(
46064                 '<div class="x-form-item {5}" style="float:left;width:{6}px">',
46065                     '<label for="{0}" style="{2}">{1}{4}</label>',
46066                     '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
46067                     '</div>',
46068                 '</div>'
46069             );
46070             t.disableFormats = true;
46071             t.compile();
46072             Roo.form.Layout.prototype.rowTpl = t;
46073         }
46074         this.fieldTpl = this.rowTpl;
46075         
46076         //console.log('lw' + this.labelWidth +', la:' + this.labelAlign);
46077         var labelWidth = 100;
46078         
46079         if ((this.labelAlign != 'top')) {
46080             if (typeof this.labelWidth == 'number') {
46081                 labelWidth = this.labelWidth
46082             }
46083             this.padWidth =  20 + labelWidth;
46084             
46085         }
46086         
46087         Roo.form.Column.superclass.onRender.call(this, ct, position);
46088         if(this.width){
46089             this.el.setWidth(this.width);
46090         }
46091         if(this.height){
46092             this.el.setHeight(this.height);
46093         }
46094     },
46095     
46096     // private
46097     renderField : function(f){
46098         f.fieldEl = this.fieldTpl.append(this.el, [
46099                f.id, f.fieldLabel,
46100                f.labelStyle||this.labelStyle||'',
46101                this.elementStyle||'',
46102                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator,
46103                f.itemCls||this.itemCls||'',
46104                f.width ? f.width + this.padWidth : 160 + this.padWidth
46105        ],true);
46106     }
46107 });
46108  
46109
46110 /**
46111  * @class Roo.form.FieldSet
46112  * @extends Roo.form.Layout
46113  * Creates a fieldset container for layout and rendering of fields in an {@link Roo.form.Form}.
46114  * @constructor
46115  * @param {Object} config Configuration options
46116  */
46117 Roo.form.FieldSet = function(config){
46118     Roo.form.FieldSet.superclass.constructor.call(this, config);
46119 };
46120
46121 Roo.extend(Roo.form.FieldSet, Roo.form.Layout, {
46122     /**
46123      * @cfg {String} legend
46124      * The text to display as the legend for the FieldSet (defaults to '')
46125      */
46126     /**
46127      * @cfg {String/Object} autoCreate
46128      * A DomHelper element spec used to autocreate the fieldset (defaults to {tag: 'fieldset', cn: {tag:'legend'}})
46129      */
46130
46131     // private
46132     defaultAutoCreate : {tag: 'fieldset', cn: {tag:'legend'}},
46133
46134     // private
46135     onRender : function(ct, position){
46136         Roo.form.FieldSet.superclass.onRender.call(this, ct, position);
46137         if(this.legend){
46138             this.setLegend(this.legend);
46139         }
46140     },
46141
46142     // private
46143     setLegend : function(text){
46144         if(this.rendered){
46145             this.el.child('legend').update(text);
46146         }
46147     }
46148 });/*
46149  * Based on:
46150  * Ext JS Library 1.1.1
46151  * Copyright(c) 2006-2007, Ext JS, LLC.
46152  *
46153  * Originally Released Under LGPL - original licence link has changed is not relivant.
46154  *
46155  * Fork - LGPL
46156  * <script type="text/javascript">
46157  */
46158 /**
46159  * @class Roo.form.VTypes
46160  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
46161  * @singleton
46162  */
46163 Roo.form.VTypes = function(){
46164     // closure these in so they are only created once.
46165     var alpha = /^[a-zA-Z_]+$/;
46166     var alphanum = /^[a-zA-Z0-9_]+$/;
46167     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,4}$/;
46168     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
46169
46170     // All these messages and functions are configurable
46171     return {
46172         /**
46173          * The function used to validate email addresses
46174          * @param {String} value The email address
46175          */
46176         'email' : function(v){
46177             return email.test(v);
46178         },
46179         /**
46180          * The error text to display when the email validation function returns false
46181          * @type String
46182          */
46183         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
46184         /**
46185          * The keystroke filter mask to be applied on email input
46186          * @type RegExp
46187          */
46188         'emailMask' : /[a-z0-9_\.\-@]/i,
46189
46190         /**
46191          * The function used to validate URLs
46192          * @param {String} value The URL
46193          */
46194         'url' : function(v){
46195             return url.test(v);
46196         },
46197         /**
46198          * The error text to display when the url validation function returns false
46199          * @type String
46200          */
46201         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
46202         
46203         /**
46204          * The function used to validate alpha values
46205          * @param {String} value The value
46206          */
46207         'alpha' : function(v){
46208             return alpha.test(v);
46209         },
46210         /**
46211          * The error text to display when the alpha validation function returns false
46212          * @type String
46213          */
46214         'alphaText' : 'This field should only contain letters and _',
46215         /**
46216          * The keystroke filter mask to be applied on alpha input
46217          * @type RegExp
46218          */
46219         'alphaMask' : /[a-z_]/i,
46220
46221         /**
46222          * The function used to validate alphanumeric values
46223          * @param {String} value The value
46224          */
46225         'alphanum' : function(v){
46226             return alphanum.test(v);
46227         },
46228         /**
46229          * The error text to display when the alphanumeric validation function returns false
46230          * @type String
46231          */
46232         'alphanumText' : 'This field should only contain letters, numbers and _',
46233         /**
46234          * The keystroke filter mask to be applied on alphanumeric input
46235          * @type RegExp
46236          */
46237         'alphanumMask' : /[a-z0-9_]/i
46238     };
46239 }();//<script type="text/javascript">
46240
46241 /**
46242  * @class Roo.form.FCKeditor
46243  * @extends Roo.form.TextArea
46244  * Wrapper around the FCKEditor http://www.fckeditor.net
46245  * @constructor
46246  * Creates a new FCKeditor
46247  * @param {Object} config Configuration options
46248  */
46249 Roo.form.FCKeditor = function(config){
46250     Roo.form.FCKeditor.superclass.constructor.call(this, config);
46251     this.addEvents({
46252          /**
46253          * @event editorinit
46254          * Fired when the editor is initialized - you can add extra handlers here..
46255          * @param {FCKeditor} this
46256          * @param {Object} the FCK object.
46257          */
46258         editorinit : true
46259     });
46260     
46261     
46262 };
46263 Roo.form.FCKeditor.editors = { };
46264 Roo.extend(Roo.form.FCKeditor, Roo.form.TextArea,
46265 {
46266     //defaultAutoCreate : {
46267     //    tag : "textarea",style   : "width:100px;height:60px;" ,autocomplete    : "off"
46268     //},
46269     // private
46270     /**
46271      * @cfg {Object} fck options - see fck manual for details.
46272      */
46273     fckconfig : false,
46274     
46275     /**
46276      * @cfg {Object} fck toolbar set (Basic or Default)
46277      */
46278     toolbarSet : 'Basic',
46279     /**
46280      * @cfg {Object} fck BasePath
46281      */ 
46282     basePath : '/fckeditor/',
46283     
46284     
46285     frame : false,
46286     
46287     value : '',
46288     
46289    
46290     onRender : function(ct, position)
46291     {
46292         if(!this.el){
46293             this.defaultAutoCreate = {
46294                 tag: "textarea",
46295                 style:"width:300px;height:60px;",
46296                 autocomplete: "off"
46297             };
46298         }
46299         Roo.form.FCKeditor.superclass.onRender.call(this, ct, position);
46300         /*
46301         if(this.grow){
46302             this.textSizeEl = Roo.DomHelper.append(document.body, {tag: "pre", cls: "x-form-grow-sizer"});
46303             if(this.preventScrollbars){
46304                 this.el.setStyle("overflow", "hidden");
46305             }
46306             this.el.setHeight(this.growMin);
46307         }
46308         */
46309         //console.log('onrender' + this.getId() );
46310         Roo.form.FCKeditor.editors[this.getId()] = this;
46311          
46312
46313         this.replaceTextarea() ;
46314         
46315     },
46316     
46317     getEditor : function() {
46318         return this.fckEditor;
46319     },
46320     /**
46321      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
46322      * @param {Mixed} value The value to set
46323      */
46324     
46325     
46326     setValue : function(value)
46327     {
46328         //console.log('setValue: ' + value);
46329         
46330         if(typeof(value) == 'undefined') { // not sure why this is happending...
46331             return;
46332         }
46333         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
46334         
46335         //if(!this.el || !this.getEditor()) {
46336         //    this.value = value;
46337             //this.setValue.defer(100,this,[value]);    
46338         //    return;
46339         //} 
46340         
46341         if(!this.getEditor()) {
46342             return;
46343         }
46344         
46345         this.getEditor().SetData(value);
46346         
46347         //
46348
46349     },
46350
46351     /**
46352      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
46353      * @return {Mixed} value The field value
46354      */
46355     getValue : function()
46356     {
46357         
46358         if (this.frame && this.frame.dom.style.display == 'none') {
46359             return Roo.form.FCKeditor.superclass.getValue.call(this);
46360         }
46361         
46362         if(!this.el || !this.getEditor()) {
46363            
46364            // this.getValue.defer(100,this); 
46365             return this.value;
46366         }
46367        
46368         
46369         var value=this.getEditor().GetData();
46370         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
46371         return Roo.form.FCKeditor.superclass.getValue.call(this);
46372         
46373
46374     },
46375
46376     /**
46377      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
46378      * @return {Mixed} value The field value
46379      */
46380     getRawValue : function()
46381     {
46382         if (this.frame && this.frame.dom.style.display == 'none') {
46383             return Roo.form.FCKeditor.superclass.getRawValue.call(this);
46384         }
46385         
46386         if(!this.el || !this.getEditor()) {
46387             //this.getRawValue.defer(100,this); 
46388             return this.value;
46389             return;
46390         }
46391         
46392         
46393         
46394         var value=this.getEditor().GetData();
46395         Roo.form.FCKeditor.superclass.setRawValue.apply(this,[value]);
46396         return Roo.form.FCKeditor.superclass.getRawValue.call(this);
46397          
46398     },
46399     
46400     setSize : function(w,h) {
46401         
46402         
46403         
46404         //if (this.frame && this.frame.dom.style.display == 'none') {
46405         //    Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
46406         //    return;
46407         //}
46408         //if(!this.el || !this.getEditor()) {
46409         //    this.setSize.defer(100,this, [w,h]); 
46410         //    return;
46411         //}
46412         
46413         
46414         
46415         Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
46416         
46417         this.frame.dom.setAttribute('width', w);
46418         this.frame.dom.setAttribute('height', h);
46419         this.frame.setSize(w,h);
46420         
46421     },
46422     
46423     toggleSourceEdit : function(value) {
46424         
46425       
46426          
46427         this.el.dom.style.display = value ? '' : 'none';
46428         this.frame.dom.style.display = value ?  'none' : '';
46429         
46430     },
46431     
46432     
46433     focus: function(tag)
46434     {
46435         if (this.frame.dom.style.display == 'none') {
46436             return Roo.form.FCKeditor.superclass.focus.call(this);
46437         }
46438         if(!this.el || !this.getEditor()) {
46439             this.focus.defer(100,this, [tag]); 
46440             return;
46441         }
46442         
46443         
46444         
46445         
46446         var tgs = this.getEditor().EditorDocument.getElementsByTagName(tag);
46447         this.getEditor().Focus();
46448         if (tgs.length) {
46449             if (!this.getEditor().Selection.GetSelection()) {
46450                 this.focus.defer(100,this, [tag]); 
46451                 return;
46452             }
46453             
46454             
46455             var r = this.getEditor().EditorDocument.createRange();
46456             r.setStart(tgs[0],0);
46457             r.setEnd(tgs[0],0);
46458             this.getEditor().Selection.GetSelection().removeAllRanges();
46459             this.getEditor().Selection.GetSelection().addRange(r);
46460             this.getEditor().Focus();
46461         }
46462         
46463     },
46464     
46465     
46466     
46467     replaceTextarea : function()
46468     {
46469         if ( document.getElementById( this.getId() + '___Frame' ) )
46470             return ;
46471         //if ( !this.checkBrowser || this._isCompatibleBrowser() )
46472         //{
46473             // We must check the elements firstly using the Id and then the name.
46474         var oTextarea = document.getElementById( this.getId() );
46475         
46476         var colElementsByName = document.getElementsByName( this.getId() ) ;
46477          
46478         oTextarea.style.display = 'none' ;
46479
46480         if ( oTextarea.tabIndex ) {            
46481             this.TabIndex = oTextarea.tabIndex ;
46482         }
46483         
46484         this._insertHtmlBefore( this._getConfigHtml(), oTextarea ) ;
46485         this._insertHtmlBefore( this._getIFrameHtml(), oTextarea ) ;
46486         this.frame = Roo.get(this.getId() + '___Frame')
46487     },
46488     
46489     _getConfigHtml : function()
46490     {
46491         var sConfig = '' ;
46492
46493         for ( var o in this.fckconfig ) {
46494             sConfig += sConfig.length > 0  ? '&amp;' : '';
46495             sConfig += encodeURIComponent( o ) + '=' + encodeURIComponent( this.fckconfig[o] ) ;
46496         }
46497
46498         return '<input type="hidden" id="' + this.getId() + '___Config" value="' + sConfig + '" style="display:none" />' ;
46499     },
46500     
46501     
46502     _getIFrameHtml : function()
46503     {
46504         var sFile = 'fckeditor.html' ;
46505         /* no idea what this is about..
46506         try
46507         {
46508             if ( (/fcksource=true/i).test( window.top.location.search ) )
46509                 sFile = 'fckeditor.original.html' ;
46510         }
46511         catch (e) { 
46512         */
46513
46514         var sLink = this.basePath + 'editor/' + sFile + '?InstanceName=' + encodeURIComponent( this.getId() ) ;
46515         sLink += this.toolbarSet ? ( '&amp;Toolbar=' + this.toolbarSet)  : '';
46516         
46517         
46518         var html = '<iframe id="' + this.getId() +
46519             '___Frame" src="' + sLink +
46520             '" width="' + this.width +
46521             '" height="' + this.height + '"' +
46522             (this.tabIndex ?  ' tabindex="' + this.tabIndex + '"' :'' ) +
46523             ' frameborder="0" scrolling="no"></iframe>' ;
46524
46525         return html ;
46526     },
46527     
46528     _insertHtmlBefore : function( html, element )
46529     {
46530         if ( element.insertAdjacentHTML )       {
46531             // IE
46532             element.insertAdjacentHTML( 'beforeBegin', html ) ;
46533         } else { // Gecko
46534             var oRange = document.createRange() ;
46535             oRange.setStartBefore( element ) ;
46536             var oFragment = oRange.createContextualFragment( html );
46537             element.parentNode.insertBefore( oFragment, element ) ;
46538         }
46539     }
46540     
46541     
46542   
46543     
46544     
46545     
46546     
46547
46548 });
46549
46550 //Roo.reg('fckeditor', Roo.form.FCKeditor);
46551
46552 function FCKeditor_OnComplete(editorInstance){
46553     var f = Roo.form.FCKeditor.editors[editorInstance.Name];
46554     f.fckEditor = editorInstance;
46555     //console.log("loaded");
46556     f.fireEvent('editorinit', f, editorInstance);
46557
46558   
46559
46560  
46561
46562
46563
46564
46565
46566
46567
46568
46569
46570
46571
46572
46573
46574
46575
46576 //<script type="text/javascript">
46577 /**
46578  * @class Roo.form.GridField
46579  * @extends Roo.form.Field
46580  * Embed a grid (or editable grid into a form)
46581  * STATUS ALPHA
46582  * 
46583  * This embeds a grid in a form, the value of the field should be the json encoded array of rows
46584  * it needs 
46585  * xgrid.store = Roo.data.Store
46586  * xgrid.store.proxy = Roo.data.MemoryProxy (data = [] )
46587  * xgrid.store.reader = Roo.data.JsonReader 
46588  * 
46589  * 
46590  * @constructor
46591  * Creates a new GridField
46592  * @param {Object} config Configuration options
46593  */
46594 Roo.form.GridField = function(config){
46595     Roo.form.GridField.superclass.constructor.call(this, config);
46596      
46597 };
46598
46599 Roo.extend(Roo.form.GridField, Roo.form.Field,  {
46600     /**
46601      * @cfg {Number} width  - used to restrict width of grid..
46602      */
46603     width : 100,
46604     /**
46605      * @cfg {Number} height - used to restrict height of grid..
46606      */
46607     height : 50,
46608      /**
46609      * @cfg {Object} xgrid (xtype'd description of grid) { xtype : 'Grid', dataSource: .... }
46610          * 
46611          *}
46612      */
46613     xgrid : false, 
46614     /**
46615      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
46616      * {tag: "input", type: "checkbox", autocomplete: "off"})
46617      */
46618    // defaultAutoCreate : { tag: 'div' },
46619     defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
46620     /**
46621      * @cfg {String} addTitle Text to include for adding a title.
46622      */
46623     addTitle : false,
46624     //
46625     onResize : function(){
46626         Roo.form.Field.superclass.onResize.apply(this, arguments);
46627     },
46628
46629     initEvents : function(){
46630         // Roo.form.Checkbox.superclass.initEvents.call(this);
46631         // has no events...
46632        
46633     },
46634
46635
46636     getResizeEl : function(){
46637         return this.wrap;
46638     },
46639
46640     getPositionEl : function(){
46641         return this.wrap;
46642     },
46643
46644     // private
46645     onRender : function(ct, position){
46646         
46647         this.style = this.style || 'overflow: hidden; border:1px solid #c3daf9;';
46648         var style = this.style;
46649         delete this.style;
46650         
46651         Roo.form.GridField.superclass.onRender.call(this, ct, position);
46652         this.wrap = this.el.wrap({cls: ''}); // not sure why ive done thsi...
46653         this.viewEl = this.wrap.createChild({ tag: 'div' });
46654         if (style) {
46655             this.viewEl.applyStyles(style);
46656         }
46657         if (this.width) {
46658             this.viewEl.setWidth(this.width);
46659         }
46660         if (this.height) {
46661             this.viewEl.setHeight(this.height);
46662         }
46663         //if(this.inputValue !== undefined){
46664         //this.setValue(this.value);
46665         
46666         
46667         this.grid = new Roo.grid[this.xgrid.xtype](this.viewEl, this.xgrid);
46668         
46669         
46670         this.grid.render();
46671         this.grid.getDataSource().on('remove', this.refreshValue, this);
46672         this.grid.getDataSource().on('update', this.refreshValue, this);
46673         this.grid.on('afteredit', this.refreshValue, this);
46674  
46675     },
46676      
46677     
46678     /**
46679      * Sets the value of the item. 
46680      * @param {String} either an object  or a string..
46681      */
46682     setValue : function(v){
46683         //this.value = v;
46684         v = v || []; // empty set..
46685         // this does not seem smart - it really only affects memoryproxy grids..
46686         if (this.grid && this.grid.getDataSource() && typeof(v) != 'undefined') {
46687             var ds = this.grid.getDataSource();
46688             // assumes a json reader..
46689             var data = {}
46690             data[ds.reader.meta.root ] =  typeof(v) == 'string' ? Roo.decode(v) : v;
46691             ds.loadData( data);
46692         }
46693         // clear selection so it does not get stale.
46694         if (this.grid.sm) { 
46695             this.grid.sm.clearSelections();
46696         }
46697         
46698         Roo.form.GridField.superclass.setValue.call(this, v);
46699         this.refreshValue();
46700         // should load data in the grid really....
46701     },
46702     
46703     // private
46704     refreshValue: function() {
46705          var val = [];
46706         this.grid.getDataSource().each(function(r) {
46707             val.push(r.data);
46708         });
46709         this.el.dom.value = Roo.encode(val);
46710     }
46711     
46712      
46713     
46714     
46715 });/*
46716  * Based on:
46717  * Ext JS Library 1.1.1
46718  * Copyright(c) 2006-2007, Ext JS, LLC.
46719  *
46720  * Originally Released Under LGPL - original licence link has changed is not relivant.
46721  *
46722  * Fork - LGPL
46723  * <script type="text/javascript">
46724  */
46725 /**
46726  * @class Roo.form.DisplayField
46727  * @extends Roo.form.Field
46728  * A generic Field to display non-editable data.
46729  * @constructor
46730  * Creates a new Display Field item.
46731  * @param {Object} config Configuration options
46732  */
46733 Roo.form.DisplayField = function(config){
46734     Roo.form.DisplayField.superclass.constructor.call(this, config);
46735     
46736 };
46737
46738 Roo.extend(Roo.form.DisplayField, Roo.form.TextField,  {
46739     inputType:      'hidden',
46740     allowBlank:     true,
46741     readOnly:         true,
46742     
46743  
46744     /**
46745      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
46746      */
46747     focusClass : undefined,
46748     /**
46749      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
46750      */
46751     fieldClass: 'x-form-field',
46752     
46753      /**
46754      * @cfg {Function} valueRenderer The renderer for the field (so you can reformat output). should return raw HTML
46755      */
46756     valueRenderer: undefined,
46757     
46758     width: 100,
46759     /**
46760      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
46761      * {tag: "input", type: "checkbox", autocomplete: "off"})
46762      */
46763      
46764  //   defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
46765
46766     onResize : function(){
46767         Roo.form.DisplayField.superclass.onResize.apply(this, arguments);
46768         
46769     },
46770
46771     initEvents : function(){
46772         // Roo.form.Checkbox.superclass.initEvents.call(this);
46773         // has no events...
46774        
46775     },
46776
46777
46778     getResizeEl : function(){
46779         return this.wrap;
46780     },
46781
46782     getPositionEl : function(){
46783         return this.wrap;
46784     },
46785
46786     // private
46787     onRender : function(ct, position){
46788         
46789         Roo.form.DisplayField.superclass.onRender.call(this, ct, position);
46790         //if(this.inputValue !== undefined){
46791         this.wrap = this.el.wrap();
46792         
46793         this.viewEl = this.wrap.createChild({ tag: 'div', cls: 'x-form-displayfield'});
46794         
46795         if (this.bodyStyle) {
46796             this.viewEl.applyStyles(this.bodyStyle);
46797         }
46798         //this.viewEl.setStyle('padding', '2px');
46799         
46800         this.setValue(this.value);
46801         
46802     },
46803 /*
46804     // private
46805     initValue : Roo.emptyFn,
46806
46807   */
46808
46809         // private
46810     onClick : function(){
46811         
46812     },
46813
46814     /**
46815      * Sets the checked state of the checkbox.
46816      * @param {Boolean/String} checked True, 'true', '1', or 'on' to check the checkbox, any other value will uncheck it.
46817      */
46818     setValue : function(v){
46819         this.value = v;
46820         var html = this.valueRenderer ?  this.valueRenderer(v) : String.format('{0}', v);
46821         // this might be called before we have a dom element..
46822         if (!this.viewEl) {
46823             return;
46824         }
46825         this.viewEl.dom.innerHTML = html;
46826         Roo.form.DisplayField.superclass.setValue.call(this, v);
46827
46828     }
46829 });/*
46830  * 
46831  * Licence- LGPL
46832  * 
46833  */
46834
46835 /**
46836  * @class Roo.form.DayPicker
46837  * @extends Roo.form.Field
46838  * A Day picker show [M] [T] [W] ....
46839  * @constructor
46840  * Creates a new Day Picker
46841  * @param {Object} config Configuration options
46842  */
46843 Roo.form.DayPicker= function(config){
46844     Roo.form.DayPicker.superclass.constructor.call(this, config);
46845      
46846 };
46847
46848 Roo.extend(Roo.form.DayPicker, Roo.form.Field,  {
46849     /**
46850      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
46851      */
46852     focusClass : undefined,
46853     /**
46854      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
46855      */
46856     fieldClass: "x-form-field",
46857    
46858     /**
46859      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
46860      * {tag: "input", type: "checkbox", autocomplete: "off"})
46861      */
46862     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "off"},
46863     
46864    
46865     actionMode : 'viewEl', 
46866     //
46867     // private
46868  
46869     inputType : 'hidden',
46870     
46871      
46872     inputElement: false, // real input element?
46873     basedOn: false, // ????
46874     
46875     isFormField: true, // not sure where this is needed!!!!
46876
46877     onResize : function(){
46878         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
46879         if(!this.boxLabel){
46880             this.el.alignTo(this.wrap, 'c-c');
46881         }
46882     },
46883
46884     initEvents : function(){
46885         Roo.form.Checkbox.superclass.initEvents.call(this);
46886         this.el.on("click", this.onClick,  this);
46887         this.el.on("change", this.onClick,  this);
46888     },
46889
46890
46891     getResizeEl : function(){
46892         return this.wrap;
46893     },
46894
46895     getPositionEl : function(){
46896         return this.wrap;
46897     },
46898
46899     
46900     // private
46901     onRender : function(ct, position){
46902         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
46903        
46904         this.wrap = this.el.wrap({cls: 'x-form-daypick-item '});
46905         
46906         var r1 = '<table><tr>';
46907         var r2 = '<tr class="x-form-daypick-icons">';
46908         for (var i=0; i < 7; i++) {
46909             r1+= '<td><div>' + Date.dayNames[i].substring(0,3) + '</div></td>';
46910             r2+= '<td><img class="x-menu-item-icon" src="' + Roo.BLANK_IMAGE_URL  +'"></td>';
46911         }
46912         
46913         var viewEl = this.wrap.createChild( r1 + '</tr>' + r2 + '</tr></table>');
46914         viewEl.select('img').on('click', this.onClick, this);
46915         this.viewEl = viewEl;   
46916         
46917         
46918         // this will not work on Chrome!!!
46919         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
46920         this.el.on('propertychange', this.setFromHidden,  this);  //ie
46921         
46922         
46923           
46924
46925     },
46926
46927     // private
46928     initValue : Roo.emptyFn,
46929
46930     /**
46931      * Returns the checked state of the checkbox.
46932      * @return {Boolean} True if checked, else false
46933      */
46934     getValue : function(){
46935         return this.el.dom.value;
46936         
46937     },
46938
46939         // private
46940     onClick : function(e){ 
46941         //this.setChecked(!this.checked);
46942         Roo.get(e.target).toggleClass('x-menu-item-checked');
46943         this.refreshValue();
46944         //if(this.el.dom.checked != this.checked){
46945         //    this.setValue(this.el.dom.checked);
46946        // }
46947     },
46948     
46949     // private
46950     refreshValue : function()
46951     {
46952         var val = '';
46953         this.viewEl.select('img',true).each(function(e,i,n)  {
46954             val += e.is(".x-menu-item-checked") ? String(n) : '';
46955         });
46956         this.setValue(val, true);
46957     },
46958
46959     /**
46960      * Sets the checked state of the checkbox.
46961      * On is always based on a string comparison between inputValue and the param.
46962      * @param {Boolean/String} value - the value to set 
46963      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
46964      */
46965     setValue : function(v,suppressEvent){
46966         if (!this.el.dom) {
46967             return;
46968         }
46969         var old = this.el.dom.value ;
46970         this.el.dom.value = v;
46971         if (suppressEvent) {
46972             return ;
46973         }
46974          
46975         // update display..
46976         this.viewEl.select('img',true).each(function(e,i,n)  {
46977             
46978             var on = e.is(".x-menu-item-checked");
46979             var newv = v.indexOf(String(n)) > -1;
46980             if (on != newv) {
46981                 e.toggleClass('x-menu-item-checked');
46982             }
46983             
46984         });
46985         
46986         
46987         this.fireEvent('change', this, v, old);
46988         
46989         
46990     },
46991    
46992     // handle setting of hidden value by some other method!!?!?
46993     setFromHidden: function()
46994     {
46995         if(!this.el){
46996             return;
46997         }
46998         //console.log("SET FROM HIDDEN");
46999         //alert('setFrom hidden');
47000         this.setValue(this.el.dom.value);
47001     },
47002     
47003     onDestroy : function()
47004     {
47005         if(this.viewEl){
47006             Roo.get(this.viewEl).remove();
47007         }
47008          
47009         Roo.form.DayPicker.superclass.onDestroy.call(this);
47010     }
47011
47012 });/*
47013  * RooJS Library 1.1.1
47014  * Copyright(c) 2008-2011  Alan Knowles
47015  *
47016  * License - LGPL
47017  */
47018  
47019
47020 /**
47021  * @class Roo.form.ComboCheck
47022  * @extends Roo.form.ComboBox
47023  * A combobox for multiple select items.
47024  *
47025  * FIXME - could do with a reset button..
47026  * 
47027  * @constructor
47028  * Create a new ComboCheck
47029  * @param {Object} config Configuration options
47030  */
47031 Roo.form.ComboCheck = function(config){
47032     Roo.form.ComboCheck.superclass.constructor.call(this, config);
47033     // should verify some data...
47034     // like
47035     // hiddenName = required..
47036     // displayField = required
47037     // valudField == required
47038     var req= [ 'hiddenName', 'displayField', 'valueField' ];
47039     var _t = this;
47040     Roo.each(req, function(e) {
47041         if ((typeof(_t[e]) == 'undefined' ) || !_t[e].length) {
47042             throw "Roo.form.ComboCheck : missing value for: " + e;
47043         }
47044     });
47045     
47046     
47047 };
47048
47049 Roo.extend(Roo.form.ComboCheck, Roo.form.ComboBox, {
47050      
47051      
47052     editable : false,
47053      
47054     selectedClass: 'x-menu-item-checked', 
47055     
47056     // private
47057     onRender : function(ct, position){
47058         var _t = this;
47059         
47060         
47061         
47062         if(!this.tpl){
47063             var cls = 'x-combo-list';
47064
47065             
47066             this.tpl =  new Roo.Template({
47067                 html :  '<div class="'+cls+'-item x-menu-check-item">' +
47068                    '<img class="x-menu-item-icon" style="margin: 0px;" src="' + Roo.BLANK_IMAGE_URL + '">' + 
47069                    '<span>{' + this.displayField + '}</span>' +
47070                     '</div>' 
47071                 
47072             });
47073         }
47074  
47075         
47076         Roo.form.ComboCheck.superclass.onRender.call(this, ct, position);
47077         this.view.singleSelect = false;
47078         this.view.multiSelect = true;
47079         this.view.toggleSelect = true;
47080         this.pageTb.add(new Roo.Toolbar.Fill(), {
47081             
47082             text: 'Done',
47083             handler: function()
47084             {
47085                 _t.collapse();
47086             }
47087         });
47088     },
47089     
47090     onViewOver : function(e, t){
47091         // do nothing...
47092         return;
47093         
47094     },
47095     
47096     onViewClick : function(doFocus,index){
47097         return;
47098         
47099     },
47100     select: function () {
47101         //Roo.log("SELECT CALLED");
47102     },
47103      
47104     selectByValue : function(xv, scrollIntoView){
47105         var ar = this.getValueArray();
47106         var sels = [];
47107         
47108         Roo.each(ar, function(v) {
47109             if(v === undefined || v === null){
47110                 return;
47111             }
47112             var r = this.findRecord(this.valueField, v);
47113             if(r){
47114                 sels.push(this.store.indexOf(r))
47115                 
47116             }
47117         },this);
47118         this.view.select(sels);
47119         return false;
47120     },
47121     
47122     
47123     
47124     onSelect : function(record, index){
47125        // Roo.log("onselect Called");
47126        // this is only called by the clear button now..
47127         this.view.clearSelections();
47128         this.setValue('[]');
47129         if (this.value != this.valueBefore) {
47130             this.fireEvent('change', this, this.value, this.valueBefore);
47131             this.valueBefore = this.value;
47132         }
47133     },
47134     getValueArray : function()
47135     {
47136         var ar = [] ;
47137         
47138         try {
47139             //Roo.log(this.value);
47140             if (typeof(this.value) == 'undefined') {
47141                 return [];
47142             }
47143             var ar = Roo.decode(this.value);
47144             return  ar instanceof Array ? ar : []; //?? valid?
47145             
47146         } catch(e) {
47147             Roo.log(e + "\nRoo.form.ComboCheck:getValueArray  invalid data:" + this.getValue());
47148             return [];
47149         }
47150          
47151     },
47152     expand : function ()
47153     {
47154         
47155         Roo.form.ComboCheck.superclass.expand.call(this);
47156         this.valueBefore = typeof(this.value) == 'undefined' ? '' : this.value;
47157         //this.valueBefore = typeof(this.valueBefore) == 'undefined' ? '' : this.valueBefore;
47158         
47159
47160     },
47161     
47162     collapse : function(){
47163         Roo.form.ComboCheck.superclass.collapse.call(this);
47164         var sl = this.view.getSelectedIndexes();
47165         var st = this.store;
47166         var nv = [];
47167         var tv = [];
47168         var r;
47169         Roo.each(sl, function(i) {
47170             r = st.getAt(i);
47171             nv.push(r.get(this.valueField));
47172         },this);
47173         this.setValue(Roo.encode(nv));
47174         if (this.value != this.valueBefore) {
47175
47176             this.fireEvent('change', this, this.value, this.valueBefore);
47177             this.valueBefore = this.value;
47178         }
47179         
47180     },
47181     
47182     setValue : function(v){
47183         // Roo.log(v);
47184         this.value = v;
47185         
47186         var vals = this.getValueArray();
47187         var tv = [];
47188         Roo.each(vals, function(k) {
47189             var r = this.findRecord(this.valueField, k);
47190             if(r){
47191                 tv.push(r.data[this.displayField]);
47192             }else if(this.valueNotFoundText !== undefined){
47193                 tv.push( this.valueNotFoundText );
47194             }
47195         },this);
47196        // Roo.log(tv);
47197         
47198         Roo.form.ComboBox.superclass.setValue.call(this, tv.join(', '));
47199         this.hiddenField.value = v;
47200         this.value = v;
47201     }
47202     
47203 });/*
47204  * Based on:
47205  * Ext JS Library 1.1.1
47206  * Copyright(c) 2006-2007, Ext JS, LLC.
47207  *
47208  * Originally Released Under LGPL - original licence link has changed is not relivant.
47209  *
47210  * Fork - LGPL
47211  * <script type="text/javascript">
47212  */
47213  
47214 /**
47215  * @class Roo.form.Signature
47216  * @extends Roo.form.Field
47217  * Signature field.  
47218  * @constructor
47219  * 
47220  * @param {Object} config Configuration options
47221  */
47222
47223 Roo.form.Signature = function(config){
47224     Roo.form.Signature.superclass.constructor.call(this, config);
47225     
47226     this.addEvents({// not in used??
47227          /**
47228          * @event confirm
47229          * Fires when the 'confirm' icon is pressed (add a listener to enable add button)
47230              * @param {Roo.form.Signature} combo This combo box
47231              */
47232         'confirm' : true,
47233         /**
47234          * @event reset
47235          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
47236              * @param {Roo.form.ComboBox} combo This combo box
47237              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
47238              */
47239         'reset' : true
47240     });
47241 };
47242
47243 Roo.extend(Roo.form.Signature, Roo.form.Field,  {
47244     /**
47245      * @cfg {Object} labels Label to use when rendering a form.
47246      * defaults to 
47247      * labels : { 
47248      *      clear : "Clear",
47249      *      confirm : "Confirm"
47250      *  }
47251      */
47252     labels : { 
47253         clear : "Clear",
47254         confirm : "Confirm"
47255     },
47256     /**
47257      * @cfg {Number} width The signature panel width (defaults to 300)
47258      */
47259     width: 300,
47260     /**
47261      * @cfg {Number} height The signature panel height (defaults to 100)
47262      */
47263     height : 100,
47264     /**
47265      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to false)
47266      */
47267     allowBlank : false,
47268     
47269     //private
47270     // {Object} signPanel The signature SVG panel element (defaults to {})
47271     signPanel : {},
47272     // {Boolean} isMouseDown False to validate that the mouse down event (defaults to false)
47273     isMouseDown : false,
47274     // {Boolean} isConfirmed validate the signature is confirmed or not for submitting form (defaults to false)
47275     isConfirmed : false,
47276     // {String} signatureTmp SVG mapping string (defaults to empty string)
47277     signatureTmp : '',
47278     
47279     
47280     defaultAutoCreate : { // modified by initCompnoent..
47281         tag: "input",
47282         type:"hidden"
47283     },
47284
47285     // private
47286     onRender : function(ct, position){
47287         
47288         Roo.form.Signature.superclass.onRender.call(this, ct, position);
47289         
47290         this.wrap = this.el.wrap({
47291             cls:'x-form-signature-wrap', style : 'width: ' + this.width + 'px', cn:{cls:'x-form-signature'}
47292         });
47293         
47294         this.createToolbar(this);
47295         this.signPanel = this.wrap.createChild({
47296                 tag: 'div',
47297                 style: 'width: ' + this.width + 'px; height: ' + this.height + 'px; border: 0;'
47298             }, this.el
47299         );
47300             
47301         this.svgID = Roo.id();
47302         this.svgEl = this.signPanel.createChild({
47303               xmlns : 'http://www.w3.org/2000/svg',
47304               tag : 'svg',
47305               id : this.svgID + "-svg",
47306               width: this.width,
47307               height: this.height,
47308               viewBox: '0 0 '+this.width+' '+this.height,
47309               cn : [
47310                 {
47311                     tag: "rect",
47312                     id: this.svgID + "-svg-r",
47313                     width: this.width,
47314                     height: this.height,
47315                     fill: "#ffa"
47316                 },
47317                 {
47318                     tag: "line",
47319                     id: this.svgID + "-svg-l",
47320                     x1: "0", // start
47321                     y1: (this.height*0.8), // start set the line in 80% of height
47322                     x2: this.width, // end
47323                     y2: (this.height*0.8), // end set the line in 80% of height
47324                     'stroke': "#666",
47325                     'stroke-width': "1",
47326                     'stroke-dasharray': "3",
47327                     'shape-rendering': "crispEdges",
47328                     'pointer-events': "none"
47329                 },
47330                 {
47331                     tag: "path",
47332                     id: this.svgID + "-svg-p",
47333                     'stroke': "navy",
47334                     'stroke-width': "3",
47335                     'fill': "none",
47336                     'pointer-events': 'none'
47337                 }
47338               ]
47339         });
47340         this.createSVG();
47341         this.svgBox = this.svgEl.dom.getScreenCTM();
47342     },
47343     createSVG : function(){ 
47344         var svg = this.signPanel;
47345         var r = svg.select('#'+ this.svgID + '-svg-r', true).first().dom;
47346         var t = this;
47347
47348         r.addEventListener('mousedown', function(e) { return t.down(e); }, false);
47349         r.addEventListener('mousemove', function(e) { return t.move(e); }, false);
47350         r.addEventListener('mouseup', function(e) { return t.up(e); }, false);
47351         r.addEventListener('mouseout', function(e) { return t.up(e); }, false);
47352         r.addEventListener('touchstart', function(e) { return t.down(e); }, false);
47353         r.addEventListener('touchmove', function(e) { return t.move(e); }, false);
47354         r.addEventListener('touchend', function(e) { return t.up(e); }, false);
47355         
47356     },
47357     isTouchEvent : function(e){
47358         return e.type.match(/^touch/);
47359     },
47360     getCoords : function (e) {
47361         var pt    = this.svgEl.dom.createSVGPoint();
47362         pt.x = e.clientX; 
47363         pt.y = e.clientY;
47364         if (this.isTouchEvent(e)) {
47365             pt.x =  e.targetTouches[0].clientX 
47366             pt.y = e.targetTouches[0].clientY;
47367         }
47368         var a = this.svgEl.dom.getScreenCTM();
47369         var b = a.inverse();
47370         var mx = pt.matrixTransform(b);
47371         return mx.x + ',' + mx.y;
47372     },
47373     //mouse event headler 
47374     down : function (e) {
47375         this.signatureTmp += 'M' + this.getCoords(e) + ' ';
47376         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr('d', this.signatureTmp);
47377         
47378         this.isMouseDown = true;
47379         
47380         e.preventDefault();
47381     },
47382     move : function (e) {
47383         if (this.isMouseDown) {
47384             this.signatureTmp += 'L' + this.getCoords(e) + ' ';
47385             this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', this.signatureTmp);
47386         }
47387         
47388         e.preventDefault();
47389     },
47390     up : function (e) {
47391         this.isMouseDown = false;
47392         var sp = this.signatureTmp.split(' ');
47393         
47394         if(sp.length > 1){
47395             if(!sp[sp.length-2].match(/^L/)){
47396                 sp.pop();
47397                 sp.pop();
47398                 sp.push("");
47399                 this.signatureTmp = sp.join(" ");
47400             }
47401         }
47402         if(this.getValue() != this.signatureTmp){
47403             this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
47404             this.isConfirmed = false;
47405         }
47406         e.preventDefault();
47407     },
47408     
47409     /**
47410      * Protected method that will not generally be called directly. It
47411      * is called when the editor creates its toolbar. Override this method if you need to
47412      * add custom toolbar buttons.
47413      * @param {HtmlEditor} editor
47414      */
47415     createToolbar : function(editor){
47416          function btn(id, toggle, handler){
47417             var xid = fid + '-'+ id ;
47418             return {
47419                 id : xid,
47420                 cmd : id,
47421                 cls : 'x-btn-icon x-edit-'+id,
47422                 enableToggle:toggle !== false,
47423                 scope: editor, // was editor...
47424                 handler:handler||editor.relayBtnCmd,
47425                 clickEvent:'mousedown',
47426                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
47427                 tabIndex:-1
47428             };
47429         }
47430         
47431         
47432         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
47433         this.tb = tb;
47434         this.tb.add(
47435            {
47436                 cls : ' x-signature-btn x-signature-'+id,
47437                 scope: editor, // was editor...
47438                 handler: this.reset,
47439                 clickEvent:'mousedown',
47440                 text: this.labels.clear
47441             },
47442             {
47443                  xtype : 'Fill',
47444                  xns: Roo.Toolbar
47445             }, 
47446             {
47447                 cls : '  x-signature-btn x-signature-'+id,
47448                 scope: editor, // was editor...
47449                 handler: this.confirmHandler,
47450                 clickEvent:'mousedown',
47451                 text: this.labels.confirm
47452             }
47453         );
47454     
47455     },
47456     //public
47457     /**
47458      * when user is clicked confirm then show this image.....
47459      * 
47460      * @return {String} Image Data URI
47461      */
47462     getImageDataURI : function(){
47463         var svg = this.svgEl.dom.parentNode.innerHTML;
47464         var src = 'data:image/svg+xml;base64,'+window.btoa(svg);
47465         return src; 
47466     },
47467     /**
47468      * 
47469      * @return {Boolean} this.isConfirmed
47470      */
47471     getConfirmed : function(){
47472         return this.isConfirmed;
47473     },
47474     /**
47475      * 
47476      * @return {Number} this.width
47477      */
47478     getWidth : function(){
47479         return this.width;
47480     },
47481     /**
47482      * 
47483      * @return {Number} this.height
47484      */
47485     getHeight : function(){
47486         return this.height;
47487     },
47488     // private
47489     getSignature : function(){
47490         return this.signatureTmp;
47491     },
47492     // private
47493     reset : function(){
47494         this.signatureTmp = '';
47495         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
47496         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', '');
47497         this.isConfirmed = false;
47498         Roo.form.Signature.superclass.reset.call(this);
47499     },
47500     setSignature : function(s){
47501         this.signatureTmp = s;
47502         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
47503         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', s);
47504         this.setValue(s);
47505         this.isConfirmed = false;
47506         Roo.form.Signature.superclass.reset.call(this);
47507     }, 
47508     test : function(){
47509 //        Roo.log(this.signPanel.dom.contentWindow.up())
47510     },
47511     //private
47512     setConfirmed : function(){
47513         
47514         
47515         
47516 //        Roo.log(Roo.get(this.signPanel.dom.contentWindow.r).attr('fill', '#cfc'));
47517     },
47518     // private
47519     confirmHandler : function(){
47520         if(!this.getSignature()){
47521             return;
47522         }
47523         
47524         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#cfc');
47525         this.setValue(this.getSignature());
47526         this.isConfirmed = true;
47527         
47528         this.fireEvent('confirm', this);
47529     },
47530     // private
47531     // Subclasses should provide the validation implementation by overriding this
47532     validateValue : function(value){
47533         if(this.allowBlank){
47534             return true;
47535         }
47536         
47537         if(this.isConfirmed){
47538             return true;
47539         }
47540         return false;
47541     }
47542 });/*
47543  * Based on:
47544  * Ext JS Library 1.1.1
47545  * Copyright(c) 2006-2007, Ext JS, LLC.
47546  *
47547  * Originally Released Under LGPL - original licence link has changed is not relivant.
47548  *
47549  * Fork - LGPL
47550  * <script type="text/javascript">
47551  */
47552  
47553
47554 /**
47555  * @class Roo.form.ComboBox
47556  * @extends Roo.form.TriggerField
47557  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
47558  * @constructor
47559  * Create a new ComboBox.
47560  * @param {Object} config Configuration options
47561  */
47562 Roo.form.Select = function(config){
47563     Roo.form.Select.superclass.constructor.call(this, config);
47564      
47565 };
47566
47567 Roo.extend(Roo.form.Select , Roo.form.ComboBox, {
47568     /**
47569      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
47570      */
47571     /**
47572      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
47573      * rendering into an Roo.Editor, defaults to false)
47574      */
47575     /**
47576      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
47577      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
47578      */
47579     /**
47580      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
47581      */
47582     /**
47583      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
47584      * the dropdown list (defaults to undefined, with no header element)
47585      */
47586
47587      /**
47588      * @cfg {String/Roo.Template} tpl The template to use to render the output
47589      */
47590      
47591     // private
47592     defaultAutoCreate : {tag: "select"  },
47593     /**
47594      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
47595      */
47596     listWidth: undefined,
47597     /**
47598      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
47599      * mode = 'remote' or 'text' if mode = 'local')
47600      */
47601     displayField: undefined,
47602     /**
47603      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
47604      * mode = 'remote' or 'value' if mode = 'local'). 
47605      * Note: use of a valueField requires the user make a selection
47606      * in order for a value to be mapped.
47607      */
47608     valueField: undefined,
47609     
47610     
47611     /**
47612      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
47613      * field's data value (defaults to the underlying DOM element's name)
47614      */
47615     hiddenName: undefined,
47616     /**
47617      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
47618      */
47619     listClass: '',
47620     /**
47621      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
47622      */
47623     selectedClass: 'x-combo-selected',
47624     /**
47625      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
47626      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
47627      * which displays a downward arrow icon).
47628      */
47629     triggerClass : 'x-form-arrow-trigger',
47630     /**
47631      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
47632      */
47633     shadow:'sides',
47634     /**
47635      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
47636      * anchor positions (defaults to 'tl-bl')
47637      */
47638     listAlign: 'tl-bl?',
47639     /**
47640      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
47641      */
47642     maxHeight: 300,
47643     /**
47644      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
47645      * query specified by the allQuery config option (defaults to 'query')
47646      */
47647     triggerAction: 'query',
47648     /**
47649      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
47650      * (defaults to 4, does not apply if editable = false)
47651      */
47652     minChars : 4,
47653     /**
47654      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
47655      * delay (typeAheadDelay) if it matches a known value (defaults to false)
47656      */
47657     typeAhead: false,
47658     /**
47659      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
47660      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
47661      */
47662     queryDelay: 500,
47663     /**
47664      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
47665      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
47666      */
47667     pageSize: 0,
47668     /**
47669      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
47670      * when editable = true (defaults to false)
47671      */
47672     selectOnFocus:false,
47673     /**
47674      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
47675      */
47676     queryParam: 'query',
47677     /**
47678      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
47679      * when mode = 'remote' (defaults to 'Loading...')
47680      */
47681     loadingText: 'Loading...',
47682     /**
47683      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
47684      */
47685     resizable: false,
47686     /**
47687      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
47688      */
47689     handleHeight : 8,
47690     /**
47691      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
47692      * traditional select (defaults to true)
47693      */
47694     editable: true,
47695     /**
47696      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
47697      */
47698     allQuery: '',
47699     /**
47700      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
47701      */
47702     mode: 'remote',
47703     /**
47704      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
47705      * listWidth has a higher value)
47706      */
47707     minListWidth : 70,
47708     /**
47709      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
47710      * allow the user to set arbitrary text into the field (defaults to false)
47711      */
47712     forceSelection:false,
47713     /**
47714      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
47715      * if typeAhead = true (defaults to 250)
47716      */
47717     typeAheadDelay : 250,
47718     /**
47719      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
47720      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
47721      */
47722     valueNotFoundText : undefined,
47723     
47724     /**
47725      * @cfg {String} defaultValue The value displayed after loading the store.
47726      */
47727     defaultValue: '',
47728     
47729     /**
47730      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
47731      */
47732     blockFocus : false,
47733     
47734     /**
47735      * @cfg {Boolean} disableClear Disable showing of clear button.
47736      */
47737     disableClear : false,
47738     /**
47739      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
47740      */
47741     alwaysQuery : false,
47742     
47743     //private
47744     addicon : false,
47745     editicon: false,
47746     
47747     // element that contains real text value.. (when hidden is used..)
47748      
47749     // private
47750     onRender : function(ct, position){
47751         Roo.form.Field.prototype.onRender.call(this, ct, position);
47752         
47753         if(this.store){
47754             this.store.on('beforeload', this.onBeforeLoad, this);
47755             this.store.on('load', this.onLoad, this);
47756             this.store.on('loadexception', this.onLoadException, this);
47757             this.store.load({});
47758         }
47759         
47760         
47761         
47762     },
47763
47764     // private
47765     initEvents : function(){
47766         //Roo.form.ComboBox.superclass.initEvents.call(this);
47767  
47768     },
47769
47770     onDestroy : function(){
47771        
47772         if(this.store){
47773             this.store.un('beforeload', this.onBeforeLoad, this);
47774             this.store.un('load', this.onLoad, this);
47775             this.store.un('loadexception', this.onLoadException, this);
47776         }
47777         //Roo.form.ComboBox.superclass.onDestroy.call(this);
47778     },
47779
47780     // private
47781     fireKey : function(e){
47782         if(e.isNavKeyPress() && !this.list.isVisible()){
47783             this.fireEvent("specialkey", this, e);
47784         }
47785     },
47786
47787     // private
47788     onResize: function(w, h){
47789         
47790         return; 
47791     
47792         
47793     },
47794
47795     /**
47796      * Allow or prevent the user from directly editing the field text.  If false is passed,
47797      * the user will only be able to select from the items defined in the dropdown list.  This method
47798      * is the runtime equivalent of setting the 'editable' config option at config time.
47799      * @param {Boolean} value True to allow the user to directly edit the field text
47800      */
47801     setEditable : function(value){
47802          
47803     },
47804
47805     // private
47806     onBeforeLoad : function(){
47807         
47808         Roo.log("Select before load");
47809         return;
47810     
47811         this.innerList.update(this.loadingText ?
47812                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
47813         //this.restrictHeight();
47814         this.selectedIndex = -1;
47815     },
47816
47817     // private
47818     onLoad : function(){
47819
47820     
47821         var dom = this.el.dom;
47822         dom.innerHTML = '';
47823          var od = dom.ownerDocument;
47824          
47825         if (this.emptyText) {
47826             var op = od.createElement('option');
47827             op.setAttribute('value', '');
47828             op.innerHTML = String.format('{0}', this.emptyText);
47829             dom.appendChild(op);
47830         }
47831         if(this.store.getCount() > 0){
47832            
47833             var vf = this.valueField;
47834             var df = this.displayField;
47835             this.store.data.each(function(r) {
47836                 // which colmsn to use... testing - cdoe / title..
47837                 var op = od.createElement('option');
47838                 op.setAttribute('value', r.data[vf]);
47839                 op.innerHTML = String.format('{0}', r.data[df]);
47840                 dom.appendChild(op);
47841             });
47842             if (typeof(this.defaultValue != 'undefined')) {
47843                 this.setValue(this.defaultValue);
47844             }
47845             
47846              
47847         }else{
47848             //this.onEmptyResults();
47849         }
47850         //this.el.focus();
47851     },
47852     // private
47853     onLoadException : function()
47854     {
47855         dom.innerHTML = '';
47856             
47857         Roo.log("Select on load exception");
47858         return;
47859     
47860         this.collapse();
47861         Roo.log(this.store.reader.jsonData);
47862         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
47863             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
47864         }
47865         
47866         
47867     },
47868     // private
47869     onTypeAhead : function(){
47870          
47871     },
47872
47873     // private
47874     onSelect : function(record, index){
47875         Roo.log('on select?');
47876         return;
47877         if(this.fireEvent('beforeselect', this, record, index) !== false){
47878             this.setFromData(index > -1 ? record.data : false);
47879             this.collapse();
47880             this.fireEvent('select', this, record, index);
47881         }
47882     },
47883
47884     /**
47885      * Returns the currently selected field value or empty string if no value is set.
47886      * @return {String} value The selected value
47887      */
47888     getValue : function(){
47889         var dom = this.el.dom;
47890         this.value = dom.options[dom.selectedIndex].value;
47891         return this.value;
47892         
47893     },
47894
47895     /**
47896      * Clears any text/value currently set in the field
47897      */
47898     clearValue : function(){
47899         this.value = '';
47900         this.el.dom.selectedIndex = this.emptyText ? 0 : -1;
47901         
47902     },
47903
47904     /**
47905      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
47906      * will be displayed in the field.  If the value does not match the data value of an existing item,
47907      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
47908      * Otherwise the field will be blank (although the value will still be set).
47909      * @param {String} value The value to match
47910      */
47911     setValue : function(v){
47912         var d = this.el.dom;
47913         for (var i =0; i < d.options.length;i++) {
47914             if (v == d.options[i].value) {
47915                 d.selectedIndex = i;
47916                 this.value = v;
47917                 return;
47918             }
47919         }
47920         this.clearValue();
47921     },
47922     /**
47923      * @property {Object} the last set data for the element
47924      */
47925     
47926     lastData : false,
47927     /**
47928      * Sets the value of the field based on a object which is related to the record format for the store.
47929      * @param {Object} value the value to set as. or false on reset?
47930      */
47931     setFromData : function(o){
47932         Roo.log('setfrom data?');
47933          
47934         
47935         
47936     },
47937     // private
47938     reset : function(){
47939         this.clearValue();
47940     },
47941     // private
47942     findRecord : function(prop, value){
47943         
47944         return false;
47945     
47946         var record;
47947         if(this.store.getCount() > 0){
47948             this.store.each(function(r){
47949                 if(r.data[prop] == value){
47950                     record = r;
47951                     return false;
47952                 }
47953                 return true;
47954             });
47955         }
47956         return record;
47957     },
47958     
47959     getName: function()
47960     {
47961         // returns hidden if it's set..
47962         if (!this.rendered) {return ''};
47963         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
47964         
47965     },
47966      
47967
47968     
47969
47970     // private
47971     onEmptyResults : function(){
47972         Roo.log('empty results');
47973         //this.collapse();
47974     },
47975
47976     /**
47977      * Returns true if the dropdown list is expanded, else false.
47978      */
47979     isExpanded : function(){
47980         return false;
47981     },
47982
47983     /**
47984      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
47985      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
47986      * @param {String} value The data value of the item to select
47987      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
47988      * selected item if it is not currently in view (defaults to true)
47989      * @return {Boolean} True if the value matched an item in the list, else false
47990      */
47991     selectByValue : function(v, scrollIntoView){
47992         Roo.log('select By Value');
47993         return false;
47994     
47995         if(v !== undefined && v !== null){
47996             var r = this.findRecord(this.valueField || this.displayField, v);
47997             if(r){
47998                 this.select(this.store.indexOf(r), scrollIntoView);
47999                 return true;
48000             }
48001         }
48002         return false;
48003     },
48004
48005     /**
48006      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
48007      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
48008      * @param {Number} index The zero-based index of the list item to select
48009      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
48010      * selected item if it is not currently in view (defaults to true)
48011      */
48012     select : function(index, scrollIntoView){
48013         Roo.log('select ');
48014         return  ;
48015         
48016         this.selectedIndex = index;
48017         this.view.select(index);
48018         if(scrollIntoView !== false){
48019             var el = this.view.getNode(index);
48020             if(el){
48021                 this.innerList.scrollChildIntoView(el, false);
48022             }
48023         }
48024     },
48025
48026       
48027
48028     // private
48029     validateBlur : function(){
48030         
48031         return;
48032         
48033     },
48034
48035     // private
48036     initQuery : function(){
48037         this.doQuery(this.getRawValue());
48038     },
48039
48040     // private
48041     doForce : function(){
48042         if(this.el.dom.value.length > 0){
48043             this.el.dom.value =
48044                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
48045              
48046         }
48047     },
48048
48049     /**
48050      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
48051      * query allowing the query action to be canceled if needed.
48052      * @param {String} query The SQL query to execute
48053      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
48054      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
48055      * saved in the current store (defaults to false)
48056      */
48057     doQuery : function(q, forceAll){
48058         
48059         Roo.log('doQuery?');
48060         if(q === undefined || q === null){
48061             q = '';
48062         }
48063         var qe = {
48064             query: q,
48065             forceAll: forceAll,
48066             combo: this,
48067             cancel:false
48068         };
48069         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
48070             return false;
48071         }
48072         q = qe.query;
48073         forceAll = qe.forceAll;
48074         if(forceAll === true || (q.length >= this.minChars)){
48075             if(this.lastQuery != q || this.alwaysQuery){
48076                 this.lastQuery = q;
48077                 if(this.mode == 'local'){
48078                     this.selectedIndex = -1;
48079                     if(forceAll){
48080                         this.store.clearFilter();
48081                     }else{
48082                         this.store.filter(this.displayField, q);
48083                     }
48084                     this.onLoad();
48085                 }else{
48086                     this.store.baseParams[this.queryParam] = q;
48087                     this.store.load({
48088                         params: this.getParams(q)
48089                     });
48090                     this.expand();
48091                 }
48092             }else{
48093                 this.selectedIndex = -1;
48094                 this.onLoad();   
48095             }
48096         }
48097     },
48098
48099     // private
48100     getParams : function(q){
48101         var p = {};
48102         //p[this.queryParam] = q;
48103         if(this.pageSize){
48104             p.start = 0;
48105             p.limit = this.pageSize;
48106         }
48107         return p;
48108     },
48109
48110     /**
48111      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
48112      */
48113     collapse : function(){
48114         
48115     },
48116
48117     // private
48118     collapseIf : function(e){
48119         
48120     },
48121
48122     /**
48123      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
48124      */
48125     expand : function(){
48126         
48127     } ,
48128
48129     // private
48130      
48131
48132     /** 
48133     * @cfg {Boolean} grow 
48134     * @hide 
48135     */
48136     /** 
48137     * @cfg {Number} growMin 
48138     * @hide 
48139     */
48140     /** 
48141     * @cfg {Number} growMax 
48142     * @hide 
48143     */
48144     /**
48145      * @hide
48146      * @method autoSize
48147      */
48148     
48149     setWidth : function()
48150     {
48151         
48152     },
48153     getResizeEl : function(){
48154         return this.el;
48155     }
48156 });//<script type="text/javasscript">
48157  
48158
48159 /**
48160  * @class Roo.DDView
48161  * A DnD enabled version of Roo.View.
48162  * @param {Element/String} container The Element in which to create the View.
48163  * @param {String} tpl The template string used to create the markup for each element of the View
48164  * @param {Object} config The configuration properties. These include all the config options of
48165  * {@link Roo.View} plus some specific to this class.<br>
48166  * <p>
48167  * Drag/drop is implemented by adding {@link Roo.data.Record}s to the target DDView. If copying is
48168  * not being performed, the original {@link Roo.data.Record} is removed from the source DDView.<br>
48169  * <p>
48170  * The following extra CSS rules are needed to provide insertion point highlighting:<pre><code>
48171 .x-view-drag-insert-above {
48172         border-top:1px dotted #3366cc;
48173 }
48174 .x-view-drag-insert-below {
48175         border-bottom:1px dotted #3366cc;
48176 }
48177 </code></pre>
48178  * 
48179  */
48180  
48181 Roo.DDView = function(container, tpl, config) {
48182     Roo.DDView.superclass.constructor.apply(this, arguments);
48183     this.getEl().setStyle("outline", "0px none");
48184     this.getEl().unselectable();
48185     if (this.dragGroup) {
48186                 this.setDraggable(this.dragGroup.split(","));
48187     }
48188     if (this.dropGroup) {
48189                 this.setDroppable(this.dropGroup.split(","));
48190     }
48191     if (this.deletable) {
48192         this.setDeletable();
48193     }
48194     this.isDirtyFlag = false;
48195         this.addEvents({
48196                 "drop" : true
48197         });
48198 };
48199
48200 Roo.extend(Roo.DDView, Roo.View, {
48201 /**     @cfg {String/Array} dragGroup The ddgroup name(s) for the View's DragZone. */
48202 /**     @cfg {String/Array} dropGroup The ddgroup name(s) for the View's DropZone. */
48203 /**     @cfg {Boolean} copy Causes drag operations to copy nodes rather than move. */
48204 /**     @cfg {Boolean} allowCopy Causes ctrl/drag operations to copy nodes rather than move. */
48205
48206         isFormField: true,
48207
48208         reset: Roo.emptyFn,
48209         
48210         clearInvalid: Roo.form.Field.prototype.clearInvalid,
48211
48212         validate: function() {
48213                 return true;
48214         },
48215         
48216         destroy: function() {
48217                 this.purgeListeners();
48218                 this.getEl.removeAllListeners();
48219                 this.getEl().remove();
48220                 if (this.dragZone) {
48221                         if (this.dragZone.destroy) {
48222                                 this.dragZone.destroy();
48223                         }
48224                 }
48225                 if (this.dropZone) {
48226                         if (this.dropZone.destroy) {
48227                                 this.dropZone.destroy();
48228                         }
48229                 }
48230         },
48231
48232 /**     Allows this class to be an Roo.form.Field so it can be found using {@link Roo.form.BasicForm#findField}. */
48233         getName: function() {
48234                 return this.name;
48235         },
48236
48237 /**     Loads the View from a JSON string representing the Records to put into the Store. */
48238         setValue: function(v) {
48239                 if (!this.store) {
48240                         throw "DDView.setValue(). DDView must be constructed with a valid Store";
48241                 }
48242                 var data = {};
48243                 data[this.store.reader.meta.root] = v ? [].concat(v) : [];
48244                 this.store.proxy = new Roo.data.MemoryProxy(data);
48245                 this.store.load();
48246         },
48247
48248 /**     @return {String} a parenthesised list of the ids of the Records in the View. */
48249         getValue: function() {
48250                 var result = '(';
48251                 this.store.each(function(rec) {
48252                         result += rec.id + ',';
48253                 });
48254                 return result.substr(0, result.length - 1) + ')';
48255         },
48256         
48257         getIds: function() {
48258                 var i = 0, result = new Array(this.store.getCount());
48259                 this.store.each(function(rec) {
48260                         result[i++] = rec.id;
48261                 });
48262                 return result;
48263         },
48264         
48265         isDirty: function() {
48266                 return this.isDirtyFlag;
48267         },
48268
48269 /**
48270  *      Part of the Roo.dd.DropZone interface. If no target node is found, the
48271  *      whole Element becomes the target, and this causes the drop gesture to append.
48272  */
48273     getTargetFromEvent : function(e) {
48274                 var target = e.getTarget();
48275                 while ((target !== null) && (target.parentNode != this.el.dom)) {
48276                 target = target.parentNode;
48277                 }
48278                 if (!target) {
48279                         target = this.el.dom.lastChild || this.el.dom;
48280                 }
48281                 return target;
48282     },
48283
48284 /**
48285  *      Create the drag data which consists of an object which has the property "ddel" as
48286  *      the drag proxy element. 
48287  */
48288     getDragData : function(e) {
48289         var target = this.findItemFromChild(e.getTarget());
48290                 if(target) {
48291                         this.handleSelection(e);
48292                         var selNodes = this.getSelectedNodes();
48293             var dragData = {
48294                 source: this,
48295                 copy: this.copy || (this.allowCopy && e.ctrlKey),
48296                 nodes: selNodes,
48297                 records: []
48298                         };
48299                         var selectedIndices = this.getSelectedIndexes();
48300                         for (var i = 0; i < selectedIndices.length; i++) {
48301                                 dragData.records.push(this.store.getAt(selectedIndices[i]));
48302                         }
48303                         if (selNodes.length == 1) {
48304                                 dragData.ddel = target.cloneNode(true); // the div element
48305                         } else {
48306                                 var div = document.createElement('div'); // create the multi element drag "ghost"
48307                                 div.className = 'multi-proxy';
48308                                 for (var i = 0, len = selNodes.length; i < len; i++) {
48309                                         div.appendChild(selNodes[i].cloneNode(true));
48310                                 }
48311                                 dragData.ddel = div;
48312                         }
48313             //console.log(dragData)
48314             //console.log(dragData.ddel.innerHTML)
48315                         return dragData;
48316                 }
48317         //console.log('nodragData')
48318                 return false;
48319     },
48320     
48321 /**     Specify to which ddGroup items in this DDView may be dragged. */
48322     setDraggable: function(ddGroup) {
48323         if (ddGroup instanceof Array) {
48324                 Roo.each(ddGroup, this.setDraggable, this);
48325                 return;
48326         }
48327         if (this.dragZone) {
48328                 this.dragZone.addToGroup(ddGroup);
48329         } else {
48330                         this.dragZone = new Roo.dd.DragZone(this.getEl(), {
48331                                 containerScroll: true,
48332                                 ddGroup: ddGroup 
48333
48334                         });
48335 //                      Draggability implies selection. DragZone's mousedown selects the element.
48336                         if (!this.multiSelect) { this.singleSelect = true; }
48337
48338 //                      Wire the DragZone's handlers up to methods in *this*
48339                         this.dragZone.getDragData = this.getDragData.createDelegate(this);
48340                 }
48341     },
48342
48343 /**     Specify from which ddGroup this DDView accepts drops. */
48344     setDroppable: function(ddGroup) {
48345         if (ddGroup instanceof Array) {
48346                 Roo.each(ddGroup, this.setDroppable, this);
48347                 return;
48348         }
48349         if (this.dropZone) {
48350                 this.dropZone.addToGroup(ddGroup);
48351         } else {
48352                         this.dropZone = new Roo.dd.DropZone(this.getEl(), {
48353                                 containerScroll: true,
48354                                 ddGroup: ddGroup
48355                         });
48356
48357 //                      Wire the DropZone's handlers up to methods in *this*
48358                         this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
48359                         this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
48360                         this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
48361                         this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
48362                         this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
48363                 }
48364     },
48365
48366 /**     Decide whether to drop above or below a View node. */
48367     getDropPoint : function(e, n, dd){
48368         if (n == this.el.dom) { return "above"; }
48369                 var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
48370                 var c = t + (b - t) / 2;
48371                 var y = Roo.lib.Event.getPageY(e);
48372                 if(y <= c) {
48373                         return "above";
48374                 }else{
48375                         return "below";
48376                 }
48377     },
48378
48379     onNodeEnter : function(n, dd, e, data){
48380                 return false;
48381     },
48382     
48383     onNodeOver : function(n, dd, e, data){
48384                 var pt = this.getDropPoint(e, n, dd);
48385                 // set the insert point style on the target node
48386                 var dragElClass = this.dropNotAllowed;
48387                 if (pt) {
48388                         var targetElClass;
48389                         if (pt == "above"){
48390                                 dragElClass = n.previousSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-above";
48391                                 targetElClass = "x-view-drag-insert-above";
48392                         } else {
48393                                 dragElClass = n.nextSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-below";
48394                                 targetElClass = "x-view-drag-insert-below";
48395                         }
48396                         if (this.lastInsertClass != targetElClass){
48397                                 Roo.fly(n).replaceClass(this.lastInsertClass, targetElClass);
48398                                 this.lastInsertClass = targetElClass;
48399                         }
48400                 }
48401                 return dragElClass;
48402         },
48403
48404     onNodeOut : function(n, dd, e, data){
48405                 this.removeDropIndicators(n);
48406     },
48407
48408     onNodeDrop : function(n, dd, e, data){
48409         if (this.fireEvent("drop", this, n, dd, e, data) === false) {
48410                 return false;
48411         }
48412         var pt = this.getDropPoint(e, n, dd);
48413                 var insertAt = (n == this.el.dom) ? this.nodes.length : n.nodeIndex;
48414                 if (pt == "below") { insertAt++; }
48415                 for (var i = 0; i < data.records.length; i++) {
48416                         var r = data.records[i];
48417                         var dup = this.store.getById(r.id);
48418                         if (dup && (dd != this.dragZone)) {
48419                                 Roo.fly(this.getNode(this.store.indexOf(dup))).frame("red", 1);
48420                         } else {
48421                                 if (data.copy) {
48422                                         this.store.insert(insertAt++, r.copy());
48423                                 } else {
48424                                         data.source.isDirtyFlag = true;
48425                                         r.store.remove(r);
48426                                         this.store.insert(insertAt++, r);
48427                                 }
48428                                 this.isDirtyFlag = true;
48429                         }
48430                 }
48431                 this.dragZone.cachedTarget = null;
48432                 return true;
48433     },
48434
48435     removeDropIndicators : function(n){
48436                 if(n){
48437                         Roo.fly(n).removeClass([
48438                                 "x-view-drag-insert-above",
48439                                 "x-view-drag-insert-below"]);
48440                         this.lastInsertClass = "_noclass";
48441                 }
48442     },
48443
48444 /**
48445  *      Utility method. Add a delete option to the DDView's context menu.
48446  *      @param {String} imageUrl The URL of the "delete" icon image.
48447  */
48448         setDeletable: function(imageUrl) {
48449                 if (!this.singleSelect && !this.multiSelect) {
48450                         this.singleSelect = true;
48451                 }
48452                 var c = this.getContextMenu();
48453                 this.contextMenu.on("itemclick", function(item) {
48454                         switch (item.id) {
48455                                 case "delete":
48456                                         this.remove(this.getSelectedIndexes());
48457                                         break;
48458                         }
48459                 }, this);
48460                 this.contextMenu.add({
48461                         icon: imageUrl,
48462                         id: "delete",
48463                         text: 'Delete'
48464                 });
48465         },
48466         
48467 /**     Return the context menu for this DDView. */
48468         getContextMenu: function() {
48469                 if (!this.contextMenu) {
48470 //                      Create the View's context menu
48471                         this.contextMenu = new Roo.menu.Menu({
48472                                 id: this.id + "-contextmenu"
48473                         });
48474                         this.el.on("contextmenu", this.showContextMenu, this);
48475                 }
48476                 return this.contextMenu;
48477         },
48478         
48479         disableContextMenu: function() {
48480                 if (this.contextMenu) {
48481                         this.el.un("contextmenu", this.showContextMenu, this);
48482                 }
48483         },
48484
48485         showContextMenu: function(e, item) {
48486         item = this.findItemFromChild(e.getTarget());
48487                 if (item) {
48488                         e.stopEvent();
48489                         this.select(this.getNode(item), this.multiSelect && e.ctrlKey, true);
48490                         this.contextMenu.showAt(e.getXY());
48491             }
48492     },
48493
48494 /**
48495  *      Remove {@link Roo.data.Record}s at the specified indices.
48496  *      @param {Array/Number} selectedIndices The index (or Array of indices) of Records to remove.
48497  */
48498     remove: function(selectedIndices) {
48499                 selectedIndices = [].concat(selectedIndices);
48500                 for (var i = 0; i < selectedIndices.length; i++) {
48501                         var rec = this.store.getAt(selectedIndices[i]);
48502                         this.store.remove(rec);
48503                 }
48504     },
48505
48506 /**
48507  *      Double click fires the event, but also, if this is draggable, and there is only one other
48508  *      related DropZone, it transfers the selected node.
48509  */
48510     onDblClick : function(e){
48511         var item = this.findItemFromChild(e.getTarget());
48512         if(item){
48513             if (this.fireEvent("dblclick", this, this.indexOf(item), item, e) === false) {
48514                 return false;
48515             }
48516             if (this.dragGroup) {
48517                     var targets = Roo.dd.DragDropMgr.getRelated(this.dragZone, true);
48518                     while (targets.indexOf(this.dropZone) > -1) {
48519                             targets.remove(this.dropZone);
48520                                 }
48521                     if (targets.length == 1) {
48522                                         this.dragZone.cachedTarget = null;
48523                         var el = Roo.get(targets[0].getEl());
48524                         var box = el.getBox(true);
48525                         targets[0].onNodeDrop(el.dom, {
48526                                 target: el.dom,
48527                                 xy: [box.x, box.y + box.height - 1]
48528                         }, null, this.getDragData(e));
48529                     }
48530                 }
48531         }
48532     },
48533     
48534     handleSelection: function(e) {
48535                 this.dragZone.cachedTarget = null;
48536         var item = this.findItemFromChild(e.getTarget());
48537         if (!item) {
48538                 this.clearSelections(true);
48539                 return;
48540         }
48541                 if (item && (this.multiSelect || this.singleSelect)){
48542                         if(this.multiSelect && e.shiftKey && (!e.ctrlKey) && this.lastSelection){
48543                                 this.select(this.getNodes(this.indexOf(this.lastSelection), item.nodeIndex), false);
48544                         }else if (this.isSelected(this.getNode(item)) && e.ctrlKey){
48545                                 this.unselect(item);
48546                         } else {
48547                                 this.select(item, this.multiSelect && e.ctrlKey);
48548                                 this.lastSelection = item;
48549                         }
48550                 }
48551     },
48552
48553     onItemClick : function(item, index, e){
48554                 if(this.fireEvent("beforeclick", this, index, item, e) === false){
48555                         return false;
48556                 }
48557                 return true;
48558     },
48559
48560     unselect : function(nodeInfo, suppressEvent){
48561                 var node = this.getNode(nodeInfo);
48562                 if(node && this.isSelected(node)){
48563                         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
48564                                 Roo.fly(node).removeClass(this.selectedClass);
48565                                 this.selections.remove(node);
48566                                 if(!suppressEvent){
48567                                         this.fireEvent("selectionchange", this, this.selections);
48568                                 }
48569                         }
48570                 }
48571     }
48572 });
48573 /*
48574  * Based on:
48575  * Ext JS Library 1.1.1
48576  * Copyright(c) 2006-2007, Ext JS, LLC.
48577  *
48578  * Originally Released Under LGPL - original licence link has changed is not relivant.
48579  *
48580  * Fork - LGPL
48581  * <script type="text/javascript">
48582  */
48583  
48584 /**
48585  * @class Roo.LayoutManager
48586  * @extends Roo.util.Observable
48587  * Base class for layout managers.
48588  */
48589 Roo.LayoutManager = function(container, config){
48590     Roo.LayoutManager.superclass.constructor.call(this);
48591     this.el = Roo.get(container);
48592     // ie scrollbar fix
48593     if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
48594         document.body.scroll = "no";
48595     }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
48596         this.el.position('relative');
48597     }
48598     this.id = this.el.id;
48599     this.el.addClass("x-layout-container");
48600     /** false to disable window resize monitoring @type Boolean */
48601     this.monitorWindowResize = true;
48602     this.regions = {};
48603     this.addEvents({
48604         /**
48605          * @event layout
48606          * Fires when a layout is performed. 
48607          * @param {Roo.LayoutManager} this
48608          */
48609         "layout" : true,
48610         /**
48611          * @event regionresized
48612          * Fires when the user resizes a region. 
48613          * @param {Roo.LayoutRegion} region The resized region
48614          * @param {Number} newSize The new size (width for east/west, height for north/south)
48615          */
48616         "regionresized" : true,
48617         /**
48618          * @event regioncollapsed
48619          * Fires when a region is collapsed. 
48620          * @param {Roo.LayoutRegion} region The collapsed region
48621          */
48622         "regioncollapsed" : true,
48623         /**
48624          * @event regionexpanded
48625          * Fires when a region is expanded.  
48626          * @param {Roo.LayoutRegion} region The expanded region
48627          */
48628         "regionexpanded" : true
48629     });
48630     this.updating = false;
48631     Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
48632 };
48633
48634 Roo.extend(Roo.LayoutManager, Roo.util.Observable, {
48635     /**
48636      * Returns true if this layout is currently being updated
48637      * @return {Boolean}
48638      */
48639     isUpdating : function(){
48640         return this.updating; 
48641     },
48642     
48643     /**
48644      * Suspend the LayoutManager from doing auto-layouts while
48645      * making multiple add or remove calls
48646      */
48647     beginUpdate : function(){
48648         this.updating = true;    
48649     },
48650     
48651     /**
48652      * Restore auto-layouts and optionally disable the manager from performing a layout
48653      * @param {Boolean} noLayout true to disable a layout update 
48654      */
48655     endUpdate : function(noLayout){
48656         this.updating = false;
48657         if(!noLayout){
48658             this.layout();
48659         }    
48660     },
48661     
48662     layout: function(){
48663         
48664     },
48665     
48666     onRegionResized : function(region, newSize){
48667         this.fireEvent("regionresized", region, newSize);
48668         this.layout();
48669     },
48670     
48671     onRegionCollapsed : function(region){
48672         this.fireEvent("regioncollapsed", region);
48673     },
48674     
48675     onRegionExpanded : function(region){
48676         this.fireEvent("regionexpanded", region);
48677     },
48678         
48679     /**
48680      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
48681      * performs box-model adjustments.
48682      * @return {Object} The size as an object {width: (the width), height: (the height)}
48683      */
48684     getViewSize : function(){
48685         var size;
48686         if(this.el.dom != document.body){
48687             size = this.el.getSize();
48688         }else{
48689             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
48690         }
48691         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
48692         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
48693         return size;
48694     },
48695     
48696     /**
48697      * Returns the Element this layout is bound to.
48698      * @return {Roo.Element}
48699      */
48700     getEl : function(){
48701         return this.el;
48702     },
48703     
48704     /**
48705      * Returns the specified region.
48706      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
48707      * @return {Roo.LayoutRegion}
48708      */
48709     getRegion : function(target){
48710         return this.regions[target.toLowerCase()];
48711     },
48712     
48713     onWindowResize : function(){
48714         if(this.monitorWindowResize){
48715             this.layout();
48716         }
48717     }
48718 });/*
48719  * Based on:
48720  * Ext JS Library 1.1.1
48721  * Copyright(c) 2006-2007, Ext JS, LLC.
48722  *
48723  * Originally Released Under LGPL - original licence link has changed is not relivant.
48724  *
48725  * Fork - LGPL
48726  * <script type="text/javascript">
48727  */
48728 /**
48729  * @class Roo.BorderLayout
48730  * @extends Roo.LayoutManager
48731  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
48732  * please see: <br><br>
48733  * <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>
48734  * <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>
48735  * Example:
48736  <pre><code>
48737  var layout = new Roo.BorderLayout(document.body, {
48738     north: {
48739         initialSize: 25,
48740         titlebar: false
48741     },
48742     west: {
48743         split:true,
48744         initialSize: 200,
48745         minSize: 175,
48746         maxSize: 400,
48747         titlebar: true,
48748         collapsible: true
48749     },
48750     east: {
48751         split:true,
48752         initialSize: 202,
48753         minSize: 175,
48754         maxSize: 400,
48755         titlebar: true,
48756         collapsible: true
48757     },
48758     south: {
48759         split:true,
48760         initialSize: 100,
48761         minSize: 100,
48762         maxSize: 200,
48763         titlebar: true,
48764         collapsible: true
48765     },
48766     center: {
48767         titlebar: true,
48768         autoScroll:true,
48769         resizeTabs: true,
48770         minTabWidth: 50,
48771         preferredTabWidth: 150
48772     }
48773 });
48774
48775 // shorthand
48776 var CP = Roo.ContentPanel;
48777
48778 layout.beginUpdate();
48779 layout.add("north", new CP("north", "North"));
48780 layout.add("south", new CP("south", {title: "South", closable: true}));
48781 layout.add("west", new CP("west", {title: "West"}));
48782 layout.add("east", new CP("autoTabs", {title: "Auto Tabs", closable: true}));
48783 layout.add("center", new CP("center1", {title: "Close Me", closable: true}));
48784 layout.add("center", new CP("center2", {title: "Center Panel", closable: false}));
48785 layout.getRegion("center").showPanel("center1");
48786 layout.endUpdate();
48787 </code></pre>
48788
48789 <b>The container the layout is rendered into can be either the body element or any other element.
48790 If it is not the body element, the container needs to either be an absolute positioned element,
48791 or you will need to add "position:relative" to the css of the container.  You will also need to specify
48792 the container size if it is not the body element.</b>
48793
48794 * @constructor
48795 * Create a new BorderLayout
48796 * @param {String/HTMLElement/Element} container The container this layout is bound to
48797 * @param {Object} config Configuration options
48798  */
48799 Roo.BorderLayout = function(container, config){
48800     config = config || {};
48801     Roo.BorderLayout.superclass.constructor.call(this, container, config);
48802     this.factory = config.factory || Roo.BorderLayout.RegionFactory;
48803     for(var i = 0, len = this.factory.validRegions.length; i < len; i++) {
48804         var target = this.factory.validRegions[i];
48805         if(config[target]){
48806             this.addRegion(target, config[target]);
48807         }
48808     }
48809 };
48810
48811 Roo.extend(Roo.BorderLayout, Roo.LayoutManager, {
48812     /**
48813      * Creates and adds a new region if it doesn't already exist.
48814      * @param {String} target The target region key (north, south, east, west or center).
48815      * @param {Object} config The regions config object
48816      * @return {BorderLayoutRegion} The new region
48817      */
48818     addRegion : function(target, config){
48819         if(!this.regions[target]){
48820             var r = this.factory.create(target, this, config);
48821             this.bindRegion(target, r);
48822         }
48823         return this.regions[target];
48824     },
48825
48826     // private (kinda)
48827     bindRegion : function(name, r){
48828         this.regions[name] = r;
48829         r.on("visibilitychange", this.layout, this);
48830         r.on("paneladded", this.layout, this);
48831         r.on("panelremoved", this.layout, this);
48832         r.on("invalidated", this.layout, this);
48833         r.on("resized", this.onRegionResized, this);
48834         r.on("collapsed", this.onRegionCollapsed, this);
48835         r.on("expanded", this.onRegionExpanded, this);
48836     },
48837
48838     /**
48839      * Performs a layout update.
48840      */
48841     layout : function(){
48842         if(this.updating) return;
48843         var size = this.getViewSize();
48844         var w = size.width;
48845         var h = size.height;
48846         var centerW = w;
48847         var centerH = h;
48848         var centerY = 0;
48849         var centerX = 0;
48850         //var x = 0, y = 0;
48851
48852         var rs = this.regions;
48853         var north = rs["north"];
48854         var south = rs["south"]; 
48855         var west = rs["west"];
48856         var east = rs["east"];
48857         var center = rs["center"];
48858         //if(this.hideOnLayout){ // not supported anymore
48859             //c.el.setStyle("display", "none");
48860         //}
48861         if(north && north.isVisible()){
48862             var b = north.getBox();
48863             var m = north.getMargins();
48864             b.width = w - (m.left+m.right);
48865             b.x = m.left;
48866             b.y = m.top;
48867             centerY = b.height + b.y + m.bottom;
48868             centerH -= centerY;
48869             north.updateBox(this.safeBox(b));
48870         }
48871         if(south && south.isVisible()){
48872             var b = south.getBox();
48873             var m = south.getMargins();
48874             b.width = w - (m.left+m.right);
48875             b.x = m.left;
48876             var totalHeight = (b.height + m.top + m.bottom);
48877             b.y = h - totalHeight + m.top;
48878             centerH -= totalHeight;
48879             south.updateBox(this.safeBox(b));
48880         }
48881         if(west && west.isVisible()){
48882             var b = west.getBox();
48883             var m = west.getMargins();
48884             b.height = centerH - (m.top+m.bottom);
48885             b.x = m.left;
48886             b.y = centerY + m.top;
48887             var totalWidth = (b.width + m.left + m.right);
48888             centerX += totalWidth;
48889             centerW -= totalWidth;
48890             west.updateBox(this.safeBox(b));
48891         }
48892         if(east && east.isVisible()){
48893             var b = east.getBox();
48894             var m = east.getMargins();
48895             b.height = centerH - (m.top+m.bottom);
48896             var totalWidth = (b.width + m.left + m.right);
48897             b.x = w - totalWidth + m.left;
48898             b.y = centerY + m.top;
48899             centerW -= totalWidth;
48900             east.updateBox(this.safeBox(b));
48901         }
48902         if(center){
48903             var m = center.getMargins();
48904             var centerBox = {
48905                 x: centerX + m.left,
48906                 y: centerY + m.top,
48907                 width: centerW - (m.left+m.right),
48908                 height: centerH - (m.top+m.bottom)
48909             };
48910             //if(this.hideOnLayout){
48911                 //center.el.setStyle("display", "block");
48912             //}
48913             center.updateBox(this.safeBox(centerBox));
48914         }
48915         this.el.repaint();
48916         this.fireEvent("layout", this);
48917     },
48918
48919     // private
48920     safeBox : function(box){
48921         box.width = Math.max(0, box.width);
48922         box.height = Math.max(0, box.height);
48923         return box;
48924     },
48925
48926     /**
48927      * Adds a ContentPanel (or subclass) to this layout.
48928      * @param {String} target The target region key (north, south, east, west or center).
48929      * @param {Roo.ContentPanel} panel The panel to add
48930      * @return {Roo.ContentPanel} The added panel
48931      */
48932     add : function(target, panel){
48933          
48934         target = target.toLowerCase();
48935         return this.regions[target].add(panel);
48936     },
48937
48938     /**
48939      * Remove a ContentPanel (or subclass) to this layout.
48940      * @param {String} target The target region key (north, south, east, west or center).
48941      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
48942      * @return {Roo.ContentPanel} The removed panel
48943      */
48944     remove : function(target, panel){
48945         target = target.toLowerCase();
48946         return this.regions[target].remove(panel);
48947     },
48948
48949     /**
48950      * Searches all regions for a panel with the specified id
48951      * @param {String} panelId
48952      * @return {Roo.ContentPanel} The panel or null if it wasn't found
48953      */
48954     findPanel : function(panelId){
48955         var rs = this.regions;
48956         for(var target in rs){
48957             if(typeof rs[target] != "function"){
48958                 var p = rs[target].getPanel(panelId);
48959                 if(p){
48960                     return p;
48961                 }
48962             }
48963         }
48964         return null;
48965     },
48966
48967     /**
48968      * Searches all regions for a panel with the specified id and activates (shows) it.
48969      * @param {String/ContentPanel} panelId The panels id or the panel itself
48970      * @return {Roo.ContentPanel} The shown panel or null
48971      */
48972     showPanel : function(panelId) {
48973       var rs = this.regions;
48974       for(var target in rs){
48975          var r = rs[target];
48976          if(typeof r != "function"){
48977             if(r.hasPanel(panelId)){
48978                return r.showPanel(panelId);
48979             }
48980          }
48981       }
48982       return null;
48983    },
48984
48985    /**
48986      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
48987      * @param {Roo.state.Provider} provider (optional) An alternate state provider
48988      */
48989     restoreState : function(provider){
48990         if(!provider){
48991             provider = Roo.state.Manager;
48992         }
48993         var sm = new Roo.LayoutStateManager();
48994         sm.init(this, provider);
48995     },
48996
48997     /**
48998      * Adds a batch of multiple ContentPanels dynamically by passing a special regions config object.  This config
48999      * object should contain properties for each region to add ContentPanels to, and each property's value should be
49000      * a valid ContentPanel config object.  Example:
49001      * <pre><code>
49002 // Create the main layout
49003 var layout = new Roo.BorderLayout('main-ct', {
49004     west: {
49005         split:true,
49006         minSize: 175,
49007         titlebar: true
49008     },
49009     center: {
49010         title:'Components'
49011     }
49012 }, 'main-ct');
49013
49014 // Create and add multiple ContentPanels at once via configs
49015 layout.batchAdd({
49016    west: {
49017        id: 'source-files',
49018        autoCreate:true,
49019        title:'Ext Source Files',
49020        autoScroll:true,
49021        fitToFrame:true
49022    },
49023    center : {
49024        el: cview,
49025        autoScroll:true,
49026        fitToFrame:true,
49027        toolbar: tb,
49028        resizeEl:'cbody'
49029    }
49030 });
49031 </code></pre>
49032      * @param {Object} regions An object containing ContentPanel configs by region name
49033      */
49034     batchAdd : function(regions){
49035         this.beginUpdate();
49036         for(var rname in regions){
49037             var lr = this.regions[rname];
49038             if(lr){
49039                 this.addTypedPanels(lr, regions[rname]);
49040             }
49041         }
49042         this.endUpdate();
49043     },
49044
49045     // private
49046     addTypedPanels : function(lr, ps){
49047         if(typeof ps == 'string'){
49048             lr.add(new Roo.ContentPanel(ps));
49049         }
49050         else if(ps instanceof Array){
49051             for(var i =0, len = ps.length; i < len; i++){
49052                 this.addTypedPanels(lr, ps[i]);
49053             }
49054         }
49055         else if(!ps.events){ // raw config?
49056             var el = ps.el;
49057             delete ps.el; // prevent conflict
49058             lr.add(new Roo.ContentPanel(el || Roo.id(), ps));
49059         }
49060         else {  // panel object assumed!
49061             lr.add(ps);
49062         }
49063     },
49064     /**
49065      * Adds a xtype elements to the layout.
49066      * <pre><code>
49067
49068 layout.addxtype({
49069        xtype : 'ContentPanel',
49070        region: 'west',
49071        items: [ .... ]
49072    }
49073 );
49074
49075 layout.addxtype({
49076         xtype : 'NestedLayoutPanel',
49077         region: 'west',
49078         layout: {
49079            center: { },
49080            west: { }   
49081         },
49082         items : [ ... list of content panels or nested layout panels.. ]
49083    }
49084 );
49085 </code></pre>
49086      * @param {Object} cfg Xtype definition of item to add.
49087      */
49088     addxtype : function(cfg)
49089     {
49090         // basically accepts a pannel...
49091         // can accept a layout region..!?!?
49092         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
49093         
49094         if (!cfg.xtype.match(/Panel$/)) {
49095             return false;
49096         }
49097         var ret = false;
49098         
49099         if (typeof(cfg.region) == 'undefined') {
49100             Roo.log("Failed to add Panel, region was not set");
49101             Roo.log(cfg);
49102             return false;
49103         }
49104         var region = cfg.region;
49105         delete cfg.region;
49106         
49107           
49108         var xitems = [];
49109         if (cfg.items) {
49110             xitems = cfg.items;
49111             delete cfg.items;
49112         }
49113         var nb = false;
49114         
49115         switch(cfg.xtype) 
49116         {
49117             case 'ContentPanel':  // ContentPanel (el, cfg)
49118             case 'ScrollPanel':  // ContentPanel (el, cfg)
49119             case 'ViewPanel': 
49120                 if(cfg.autoCreate) {
49121                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
49122                 } else {
49123                     var el = this.el.createChild();
49124                     ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
49125                 }
49126                 
49127                 this.add(region, ret);
49128                 break;
49129             
49130             
49131             case 'TreePanel': // our new panel!
49132                 cfg.el = this.el.createChild();
49133                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
49134                 this.add(region, ret);
49135                 break;
49136             
49137             case 'NestedLayoutPanel': 
49138                 // create a new Layout (which is  a Border Layout...
49139                 var el = this.el.createChild();
49140                 var clayout = cfg.layout;
49141                 delete cfg.layout;
49142                 clayout.items   = clayout.items  || [];
49143                 // replace this exitems with the clayout ones..
49144                 xitems = clayout.items;
49145                  
49146                 
49147                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
49148                     cfg.background = false;
49149                 }
49150                 var layout = new Roo.BorderLayout(el, clayout);
49151                 
49152                 ret = new Roo[cfg.xtype](layout, cfg); // new panel!!!!!
49153                 //console.log('adding nested layout panel '  + cfg.toSource());
49154                 this.add(region, ret);
49155                 nb = {}; /// find first...
49156                 break;
49157                 
49158             case 'GridPanel': 
49159             
49160                 // needs grid and region
49161                 
49162                 //var el = this.getRegion(region).el.createChild();
49163                 var el = this.el.createChild();
49164                 // create the grid first...
49165                 
49166                 var grid = new Roo.grid[cfg.grid.xtype](el, cfg.grid);
49167                 delete cfg.grid;
49168                 if (region == 'center' && this.active ) {
49169                     cfg.background = false;
49170                 }
49171                 ret = new Roo[cfg.xtype](grid, cfg); // new panel!!!!!
49172                 
49173                 this.add(region, ret);
49174                 if (cfg.background) {
49175                     ret.on('activate', function(gp) {
49176                         if (!gp.grid.rendered) {
49177                             gp.grid.render();
49178                         }
49179                     });
49180                 } else {
49181                     grid.render();
49182                 }
49183                 break;
49184            
49185            
49186            
49187                 
49188                 
49189                 
49190             default:
49191                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
49192                     
49193                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
49194                     this.add(region, ret);
49195                 } else {
49196                 
49197                     alert("Can not add '" + cfg.xtype + "' to BorderLayout");
49198                     return null;
49199                 }
49200                 
49201              // GridPanel (grid, cfg)
49202             
49203         }
49204         this.beginUpdate();
49205         // add children..
49206         var region = '';
49207         var abn = {};
49208         Roo.each(xitems, function(i)  {
49209             region = nb && i.region ? i.region : false;
49210             
49211             var add = ret.addxtype(i);
49212            
49213             if (region) {
49214                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
49215                 if (!i.background) {
49216                     abn[region] = nb[region] ;
49217                 }
49218             }
49219             
49220         });
49221         this.endUpdate();
49222
49223         // make the last non-background panel active..
49224         //if (nb) { Roo.log(abn); }
49225         if (nb) {
49226             
49227             for(var r in abn) {
49228                 region = this.getRegion(r);
49229                 if (region) {
49230                     // tried using nb[r], but it does not work..
49231                      
49232                     region.showPanel(abn[r]);
49233                    
49234                 }
49235             }
49236         }
49237         return ret;
49238         
49239     }
49240 });
49241
49242 /**
49243  * Shortcut for creating a new BorderLayout object and adding one or more ContentPanels to it in a single step, handling
49244  * the beginUpdate and endUpdate calls internally.  The key to this method is the <b>panels</b> property that can be
49245  * provided with each region config, which allows you to add ContentPanel configs in addition to the region configs
49246  * during creation.  The following code is equivalent to the constructor-based example at the beginning of this class:
49247  * <pre><code>
49248 // shorthand
49249 var CP = Roo.ContentPanel;
49250
49251 var layout = Roo.BorderLayout.create({
49252     north: {
49253         initialSize: 25,
49254         titlebar: false,
49255         panels: [new CP("north", "North")]
49256     },
49257     west: {
49258         split:true,
49259         initialSize: 200,
49260         minSize: 175,
49261         maxSize: 400,
49262         titlebar: true,
49263         collapsible: true,
49264         panels: [new CP("west", {title: "West"})]
49265     },
49266     east: {
49267         split:true,
49268         initialSize: 202,
49269         minSize: 175,
49270         maxSize: 400,
49271         titlebar: true,
49272         collapsible: true,
49273         panels: [new CP("autoTabs", {title: "Auto Tabs", closable: true})]
49274     },
49275     south: {
49276         split:true,
49277         initialSize: 100,
49278         minSize: 100,
49279         maxSize: 200,
49280         titlebar: true,
49281         collapsible: true,
49282         panels: [new CP("south", {title: "South", closable: true})]
49283     },
49284     center: {
49285         titlebar: true,
49286         autoScroll:true,
49287         resizeTabs: true,
49288         minTabWidth: 50,
49289         preferredTabWidth: 150,
49290         panels: [
49291             new CP("center1", {title: "Close Me", closable: true}),
49292             new CP("center2", {title: "Center Panel", closable: false})
49293         ]
49294     }
49295 }, document.body);
49296
49297 layout.getRegion("center").showPanel("center1");
49298 </code></pre>
49299  * @param config
49300  * @param targetEl
49301  */
49302 Roo.BorderLayout.create = function(config, targetEl){
49303     var layout = new Roo.BorderLayout(targetEl || document.body, config);
49304     layout.beginUpdate();
49305     var regions = Roo.BorderLayout.RegionFactory.validRegions;
49306     for(var j = 0, jlen = regions.length; j < jlen; j++){
49307         var lr = regions[j];
49308         if(layout.regions[lr] && config[lr].panels){
49309             var r = layout.regions[lr];
49310             var ps = config[lr].panels;
49311             layout.addTypedPanels(r, ps);
49312         }
49313     }
49314     layout.endUpdate();
49315     return layout;
49316 };
49317
49318 // private
49319 Roo.BorderLayout.RegionFactory = {
49320     // private
49321     validRegions : ["north","south","east","west","center"],
49322
49323     // private
49324     create : function(target, mgr, config){
49325         target = target.toLowerCase();
49326         if(config.lightweight || config.basic){
49327             return new Roo.BasicLayoutRegion(mgr, config, target);
49328         }
49329         switch(target){
49330             case "north":
49331                 return new Roo.NorthLayoutRegion(mgr, config);
49332             case "south":
49333                 return new Roo.SouthLayoutRegion(mgr, config);
49334             case "east":
49335                 return new Roo.EastLayoutRegion(mgr, config);
49336             case "west":
49337                 return new Roo.WestLayoutRegion(mgr, config);
49338             case "center":
49339                 return new Roo.CenterLayoutRegion(mgr, config);
49340         }
49341         throw 'Layout region "'+target+'" not supported.';
49342     }
49343 };/*
49344  * Based on:
49345  * Ext JS Library 1.1.1
49346  * Copyright(c) 2006-2007, Ext JS, LLC.
49347  *
49348  * Originally Released Under LGPL - original licence link has changed is not relivant.
49349  *
49350  * Fork - LGPL
49351  * <script type="text/javascript">
49352  */
49353  
49354 /**
49355  * @class Roo.BasicLayoutRegion
49356  * @extends Roo.util.Observable
49357  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
49358  * and does not have a titlebar, tabs or any other features. All it does is size and position 
49359  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
49360  */
49361 Roo.BasicLayoutRegion = function(mgr, config, pos, skipConfig){
49362     this.mgr = mgr;
49363     this.position  = pos;
49364     this.events = {
49365         /**
49366          * @scope Roo.BasicLayoutRegion
49367          */
49368         
49369         /**
49370          * @event beforeremove
49371          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
49372          * @param {Roo.LayoutRegion} this
49373          * @param {Roo.ContentPanel} panel The panel
49374          * @param {Object} e The cancel event object
49375          */
49376         "beforeremove" : true,
49377         /**
49378          * @event invalidated
49379          * Fires when the layout for this region is changed.
49380          * @param {Roo.LayoutRegion} this
49381          */
49382         "invalidated" : true,
49383         /**
49384          * @event visibilitychange
49385          * Fires when this region is shown or hidden 
49386          * @param {Roo.LayoutRegion} this
49387          * @param {Boolean} visibility true or false
49388          */
49389         "visibilitychange" : true,
49390         /**
49391          * @event paneladded
49392          * Fires when a panel is added. 
49393          * @param {Roo.LayoutRegion} this
49394          * @param {Roo.ContentPanel} panel The panel
49395          */
49396         "paneladded" : true,
49397         /**
49398          * @event panelremoved
49399          * Fires when a panel is removed. 
49400          * @param {Roo.LayoutRegion} this
49401          * @param {Roo.ContentPanel} panel The panel
49402          */
49403         "panelremoved" : true,
49404         /**
49405          * @event collapsed
49406          * Fires when this region is collapsed.
49407          * @param {Roo.LayoutRegion} this
49408          */
49409         "collapsed" : true,
49410         /**
49411          * @event expanded
49412          * Fires when this region is expanded.
49413          * @param {Roo.LayoutRegion} this
49414          */
49415         "expanded" : true,
49416         /**
49417          * @event slideshow
49418          * Fires when this region is slid into view.
49419          * @param {Roo.LayoutRegion} this
49420          */
49421         "slideshow" : true,
49422         /**
49423          * @event slidehide
49424          * Fires when this region slides out of view. 
49425          * @param {Roo.LayoutRegion} this
49426          */
49427         "slidehide" : true,
49428         /**
49429          * @event panelactivated
49430          * Fires when a panel is activated. 
49431          * @param {Roo.LayoutRegion} this
49432          * @param {Roo.ContentPanel} panel The activated panel
49433          */
49434         "panelactivated" : true,
49435         /**
49436          * @event resized
49437          * Fires when the user resizes this region. 
49438          * @param {Roo.LayoutRegion} this
49439          * @param {Number} newSize The new size (width for east/west, height for north/south)
49440          */
49441         "resized" : true
49442     };
49443     /** A collection of panels in this region. @type Roo.util.MixedCollection */
49444     this.panels = new Roo.util.MixedCollection();
49445     this.panels.getKey = this.getPanelId.createDelegate(this);
49446     this.box = null;
49447     this.activePanel = null;
49448     // ensure listeners are added...
49449     
49450     if (config.listeners || config.events) {
49451         Roo.BasicLayoutRegion.superclass.constructor.call(this, {
49452             listeners : config.listeners || {},
49453             events : config.events || {}
49454         });
49455     }
49456     
49457     if(skipConfig !== true){
49458         this.applyConfig(config);
49459     }
49460 };
49461
49462 Roo.extend(Roo.BasicLayoutRegion, Roo.util.Observable, {
49463     getPanelId : function(p){
49464         return p.getId();
49465     },
49466     
49467     applyConfig : function(config){
49468         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
49469         this.config = config;
49470         
49471     },
49472     
49473     /**
49474      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
49475      * the width, for horizontal (north, south) the height.
49476      * @param {Number} newSize The new width or height
49477      */
49478     resizeTo : function(newSize){
49479         var el = this.el ? this.el :
49480                  (this.activePanel ? this.activePanel.getEl() : null);
49481         if(el){
49482             switch(this.position){
49483                 case "east":
49484                 case "west":
49485                     el.setWidth(newSize);
49486                     this.fireEvent("resized", this, newSize);
49487                 break;
49488                 case "north":
49489                 case "south":
49490                     el.setHeight(newSize);
49491                     this.fireEvent("resized", this, newSize);
49492                 break;                
49493             }
49494         }
49495     },
49496     
49497     getBox : function(){
49498         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
49499     },
49500     
49501     getMargins : function(){
49502         return this.margins;
49503     },
49504     
49505     updateBox : function(box){
49506         this.box = box;
49507         var el = this.activePanel.getEl();
49508         el.dom.style.left = box.x + "px";
49509         el.dom.style.top = box.y + "px";
49510         this.activePanel.setSize(box.width, box.height);
49511     },
49512     
49513     /**
49514      * Returns the container element for this region.
49515      * @return {Roo.Element}
49516      */
49517     getEl : function(){
49518         return this.activePanel;
49519     },
49520     
49521     /**
49522      * Returns true if this region is currently visible.
49523      * @return {Boolean}
49524      */
49525     isVisible : function(){
49526         return this.activePanel ? true : false;
49527     },
49528     
49529     setActivePanel : function(panel){
49530         panel = this.getPanel(panel);
49531         if(this.activePanel && this.activePanel != panel){
49532             this.activePanel.setActiveState(false);
49533             this.activePanel.getEl().setLeftTop(-10000,-10000);
49534         }
49535         this.activePanel = panel;
49536         panel.setActiveState(true);
49537         if(this.box){
49538             panel.setSize(this.box.width, this.box.height);
49539         }
49540         this.fireEvent("panelactivated", this, panel);
49541         this.fireEvent("invalidated");
49542     },
49543     
49544     /**
49545      * Show the specified panel.
49546      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
49547      * @return {Roo.ContentPanel} The shown panel or null
49548      */
49549     showPanel : function(panel){
49550         if(panel = this.getPanel(panel)){
49551             this.setActivePanel(panel);
49552         }
49553         return panel;
49554     },
49555     
49556     /**
49557      * Get the active panel for this region.
49558      * @return {Roo.ContentPanel} The active panel or null
49559      */
49560     getActivePanel : function(){
49561         return this.activePanel;
49562     },
49563     
49564     /**
49565      * Add the passed ContentPanel(s)
49566      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
49567      * @return {Roo.ContentPanel} The panel added (if only one was added)
49568      */
49569     add : function(panel){
49570         if(arguments.length > 1){
49571             for(var i = 0, len = arguments.length; i < len; i++) {
49572                 this.add(arguments[i]);
49573             }
49574             return null;
49575         }
49576         if(this.hasPanel(panel)){
49577             this.showPanel(panel);
49578             return panel;
49579         }
49580         var el = panel.getEl();
49581         if(el.dom.parentNode != this.mgr.el.dom){
49582             this.mgr.el.dom.appendChild(el.dom);
49583         }
49584         if(panel.setRegion){
49585             panel.setRegion(this);
49586         }
49587         this.panels.add(panel);
49588         el.setStyle("position", "absolute");
49589         if(!panel.background){
49590             this.setActivePanel(panel);
49591             if(this.config.initialSize && this.panels.getCount()==1){
49592                 this.resizeTo(this.config.initialSize);
49593             }
49594         }
49595         this.fireEvent("paneladded", this, panel);
49596         return panel;
49597     },
49598     
49599     /**
49600      * Returns true if the panel is in this region.
49601      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
49602      * @return {Boolean}
49603      */
49604     hasPanel : function(panel){
49605         if(typeof panel == "object"){ // must be panel obj
49606             panel = panel.getId();
49607         }
49608         return this.getPanel(panel) ? true : false;
49609     },
49610     
49611     /**
49612      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
49613      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
49614      * @param {Boolean} preservePanel Overrides the config preservePanel option
49615      * @return {Roo.ContentPanel} The panel that was removed
49616      */
49617     remove : function(panel, preservePanel){
49618         panel = this.getPanel(panel);
49619         if(!panel){
49620             return null;
49621         }
49622         var e = {};
49623         this.fireEvent("beforeremove", this, panel, e);
49624         if(e.cancel === true){
49625             return null;
49626         }
49627         var panelId = panel.getId();
49628         this.panels.removeKey(panelId);
49629         return panel;
49630     },
49631     
49632     /**
49633      * Returns the panel specified or null if it's not in this region.
49634      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
49635      * @return {Roo.ContentPanel}
49636      */
49637     getPanel : function(id){
49638         if(typeof id == "object"){ // must be panel obj
49639             return id;
49640         }
49641         return this.panels.get(id);
49642     },
49643     
49644     /**
49645      * Returns this regions position (north/south/east/west/center).
49646      * @return {String} 
49647      */
49648     getPosition: function(){
49649         return this.position;    
49650     }
49651 });/*
49652  * Based on:
49653  * Ext JS Library 1.1.1
49654  * Copyright(c) 2006-2007, Ext JS, LLC.
49655  *
49656  * Originally Released Under LGPL - original licence link has changed is not relivant.
49657  *
49658  * Fork - LGPL
49659  * <script type="text/javascript">
49660  */
49661  
49662 /**
49663  * @class Roo.LayoutRegion
49664  * @extends Roo.BasicLayoutRegion
49665  * This class represents a region in a layout manager.
49666  * @cfg {Boolean}   collapsible     False to disable collapsing (defaults to true)
49667  * @cfg {Boolean}   collapsed       True to set the initial display to collapsed (defaults to false)
49668  * @cfg {Boolean}   floatable       False to disable floating (defaults to true)
49669  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
49670  * @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})
49671  * @cfg {String}    tabPosition     "top" or "bottom" (defaults to "bottom")
49672  * @cfg {String}    collapsedTitle  Optional string message to display in the collapsed block of a north or south region
49673  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
49674  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
49675  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
49676  * @cfg {String}    title           The title for the region (overrides panel titles)
49677  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
49678  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
49679  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
49680  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
49681  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
49682  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
49683  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
49684  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
49685  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
49686  * @cfg {Boolean}   showPin         True to show a pin button
49687  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
49688  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
49689  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
49690  * @cfg {Number}    width           For East/West panels
49691  * @cfg {Number}    height          For North/South panels
49692  * @cfg {Boolean}   split           To show the splitter
49693  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
49694  */
49695 Roo.LayoutRegion = function(mgr, config, pos){
49696     Roo.LayoutRegion.superclass.constructor.call(this, mgr, config, pos, true);
49697     var dh = Roo.DomHelper;
49698     /** This region's container element 
49699     * @type Roo.Element */
49700     this.el = dh.append(mgr.el.dom, {tag: "div", cls: "x-layout-panel x-layout-panel-" + this.position}, true);
49701     /** This region's title element 
49702     * @type Roo.Element */
49703
49704     this.titleEl = dh.append(this.el.dom, {tag: "div", unselectable: "on", cls: "x-unselectable x-layout-panel-hd x-layout-title-"+this.position, children:[
49705         {tag: "span", cls: "x-unselectable x-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
49706         {tag: "div", cls: "x-unselectable x-layout-panel-hd-tools", unselectable: "on"}
49707     ]}, true);
49708     this.titleEl.enableDisplayMode();
49709     /** This region's title text element 
49710     * @type HTMLElement */
49711     this.titleTextEl = this.titleEl.dom.firstChild;
49712     this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
49713     this.closeBtn = this.createTool(this.tools.dom, "x-layout-close");
49714     this.closeBtn.enableDisplayMode();
49715     this.closeBtn.on("click", this.closeClicked, this);
49716     this.closeBtn.hide();
49717
49718     this.createBody(config);
49719     this.visible = true;
49720     this.collapsed = false;
49721
49722     if(config.hideWhenEmpty){
49723         this.hide();
49724         this.on("paneladded", this.validateVisibility, this);
49725         this.on("panelremoved", this.validateVisibility, this);
49726     }
49727     this.applyConfig(config);
49728 };
49729
49730 Roo.extend(Roo.LayoutRegion, Roo.BasicLayoutRegion, {
49731
49732     createBody : function(){
49733         /** This region's body element 
49734         * @type Roo.Element */
49735         this.bodyEl = this.el.createChild({tag: "div", cls: "x-layout-panel-body"});
49736     },
49737
49738     applyConfig : function(c){
49739         if(c.collapsible && this.position != "center" && !this.collapsedEl){
49740             var dh = Roo.DomHelper;
49741             if(c.titlebar !== false){
49742                 this.collapseBtn = this.createTool(this.tools.dom, "x-layout-collapse-"+this.position);
49743                 this.collapseBtn.on("click", this.collapse, this);
49744                 this.collapseBtn.enableDisplayMode();
49745
49746                 if(c.showPin === true || this.showPin){
49747                     this.stickBtn = this.createTool(this.tools.dom, "x-layout-stick");
49748                     this.stickBtn.enableDisplayMode();
49749                     this.stickBtn.on("click", this.expand, this);
49750                     this.stickBtn.hide();
49751                 }
49752             }
49753             /** This region's collapsed element
49754             * @type Roo.Element */
49755             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
49756                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
49757             ]}, true);
49758             if(c.floatable !== false){
49759                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
49760                this.collapsedEl.on("click", this.collapseClick, this);
49761             }
49762
49763             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
49764                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
49765                    id: "message", unselectable: "on", style:{"float":"left"}});
49766                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
49767              }
49768             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
49769             this.expandBtn.on("click", this.expand, this);
49770         }
49771         if(this.collapseBtn){
49772             this.collapseBtn.setVisible(c.collapsible == true);
49773         }
49774         this.cmargins = c.cmargins || this.cmargins ||
49775                          (this.position == "west" || this.position == "east" ?
49776                              {top: 0, left: 2, right:2, bottom: 0} :
49777                              {top: 2, left: 0, right:0, bottom: 2});
49778         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
49779         this.bottomTabs = c.tabPosition != "top";
49780         this.autoScroll = c.autoScroll || false;
49781         if(this.autoScroll){
49782             this.bodyEl.setStyle("overflow", "auto");
49783         }else{
49784             this.bodyEl.setStyle("overflow", "hidden");
49785         }
49786         //if(c.titlebar !== false){
49787             if((!c.titlebar && !c.title) || c.titlebar === false){
49788                 this.titleEl.hide();
49789             }else{
49790                 this.titleEl.show();
49791                 if(c.title){
49792                     this.titleTextEl.innerHTML = c.title;
49793                 }
49794             }
49795         //}
49796         this.duration = c.duration || .30;
49797         this.slideDuration = c.slideDuration || .45;
49798         this.config = c;
49799         if(c.collapsed){
49800             this.collapse(true);
49801         }
49802         if(c.hidden){
49803             this.hide();
49804         }
49805     },
49806     /**
49807      * Returns true if this region is currently visible.
49808      * @return {Boolean}
49809      */
49810     isVisible : function(){
49811         return this.visible;
49812     },
49813
49814     /**
49815      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
49816      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
49817      */
49818     setCollapsedTitle : function(title){
49819         title = title || "&#160;";
49820         if(this.collapsedTitleTextEl){
49821             this.collapsedTitleTextEl.innerHTML = title;
49822         }
49823     },
49824
49825     getBox : function(){
49826         var b;
49827         if(!this.collapsed){
49828             b = this.el.getBox(false, true);
49829         }else{
49830             b = this.collapsedEl.getBox(false, true);
49831         }
49832         return b;
49833     },
49834
49835     getMargins : function(){
49836         return this.collapsed ? this.cmargins : this.margins;
49837     },
49838
49839     highlight : function(){
49840         this.el.addClass("x-layout-panel-dragover");
49841     },
49842
49843     unhighlight : function(){
49844         this.el.removeClass("x-layout-panel-dragover");
49845     },
49846
49847     updateBox : function(box){
49848         this.box = box;
49849         if(!this.collapsed){
49850             this.el.dom.style.left = box.x + "px";
49851             this.el.dom.style.top = box.y + "px";
49852             this.updateBody(box.width, box.height);
49853         }else{
49854             this.collapsedEl.dom.style.left = box.x + "px";
49855             this.collapsedEl.dom.style.top = box.y + "px";
49856             this.collapsedEl.setSize(box.width, box.height);
49857         }
49858         if(this.tabs){
49859             this.tabs.autoSizeTabs();
49860         }
49861     },
49862
49863     updateBody : function(w, h){
49864         if(w !== null){
49865             this.el.setWidth(w);
49866             w -= this.el.getBorderWidth("rl");
49867             if(this.config.adjustments){
49868                 w += this.config.adjustments[0];
49869             }
49870         }
49871         if(h !== null){
49872             this.el.setHeight(h);
49873             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
49874             h -= this.el.getBorderWidth("tb");
49875             if(this.config.adjustments){
49876                 h += this.config.adjustments[1];
49877             }
49878             this.bodyEl.setHeight(h);
49879             if(this.tabs){
49880                 h = this.tabs.syncHeight(h);
49881             }
49882         }
49883         if(this.panelSize){
49884             w = w !== null ? w : this.panelSize.width;
49885             h = h !== null ? h : this.panelSize.height;
49886         }
49887         if(this.activePanel){
49888             var el = this.activePanel.getEl();
49889             w = w !== null ? w : el.getWidth();
49890             h = h !== null ? h : el.getHeight();
49891             this.panelSize = {width: w, height: h};
49892             this.activePanel.setSize(w, h);
49893         }
49894         if(Roo.isIE && this.tabs){
49895             this.tabs.el.repaint();
49896         }
49897     },
49898
49899     /**
49900      * Returns the container element for this region.
49901      * @return {Roo.Element}
49902      */
49903     getEl : function(){
49904         return this.el;
49905     },
49906
49907     /**
49908      * Hides this region.
49909      */
49910     hide : function(){
49911         if(!this.collapsed){
49912             this.el.dom.style.left = "-2000px";
49913             this.el.hide();
49914         }else{
49915             this.collapsedEl.dom.style.left = "-2000px";
49916             this.collapsedEl.hide();
49917         }
49918         this.visible = false;
49919         this.fireEvent("visibilitychange", this, false);
49920     },
49921
49922     /**
49923      * Shows this region if it was previously hidden.
49924      */
49925     show : function(){
49926         if(!this.collapsed){
49927             this.el.show();
49928         }else{
49929             this.collapsedEl.show();
49930         }
49931         this.visible = true;
49932         this.fireEvent("visibilitychange", this, true);
49933     },
49934
49935     closeClicked : function(){
49936         if(this.activePanel){
49937             this.remove(this.activePanel);
49938         }
49939     },
49940
49941     collapseClick : function(e){
49942         if(this.isSlid){
49943            e.stopPropagation();
49944            this.slideIn();
49945         }else{
49946            e.stopPropagation();
49947            this.slideOut();
49948         }
49949     },
49950
49951     /**
49952      * Collapses this region.
49953      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
49954      */
49955     collapse : function(skipAnim){
49956         if(this.collapsed) return;
49957         this.collapsed = true;
49958         if(this.split){
49959             this.split.el.hide();
49960         }
49961         if(this.config.animate && skipAnim !== true){
49962             this.fireEvent("invalidated", this);
49963             this.animateCollapse();
49964         }else{
49965             this.el.setLocation(-20000,-20000);
49966             this.el.hide();
49967             this.collapsedEl.show();
49968             this.fireEvent("collapsed", this);
49969             this.fireEvent("invalidated", this);
49970         }
49971     },
49972
49973     animateCollapse : function(){
49974         // overridden
49975     },
49976
49977     /**
49978      * Expands this region if it was previously collapsed.
49979      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
49980      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
49981      */
49982     expand : function(e, skipAnim){
49983         if(e) e.stopPropagation();
49984         if(!this.collapsed || this.el.hasActiveFx()) return;
49985         if(this.isSlid){
49986             this.afterSlideIn();
49987             skipAnim = true;
49988         }
49989         this.collapsed = false;
49990         if(this.config.animate && skipAnim !== true){
49991             this.animateExpand();
49992         }else{
49993             this.el.show();
49994             if(this.split){
49995                 this.split.el.show();
49996             }
49997             this.collapsedEl.setLocation(-2000,-2000);
49998             this.collapsedEl.hide();
49999             this.fireEvent("invalidated", this);
50000             this.fireEvent("expanded", this);
50001         }
50002     },
50003
50004     animateExpand : function(){
50005         // overridden
50006     },
50007
50008     initTabs : function()
50009     {
50010         this.bodyEl.setStyle("overflow", "hidden");
50011         var ts = new Roo.TabPanel(
50012                 this.bodyEl.dom,
50013                 {
50014                     tabPosition: this.bottomTabs ? 'bottom' : 'top',
50015                     disableTooltips: this.config.disableTabTips,
50016                     toolbar : this.config.toolbar
50017                 }
50018         );
50019         if(this.config.hideTabs){
50020             ts.stripWrap.setDisplayed(false);
50021         }
50022         this.tabs = ts;
50023         ts.resizeTabs = this.config.resizeTabs === true;
50024         ts.minTabWidth = this.config.minTabWidth || 40;
50025         ts.maxTabWidth = this.config.maxTabWidth || 250;
50026         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
50027         ts.monitorResize = false;
50028         ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
50029         ts.bodyEl.addClass('x-layout-tabs-body');
50030         this.panels.each(this.initPanelAsTab, this);
50031     },
50032
50033     initPanelAsTab : function(panel){
50034         var ti = this.tabs.addTab(panel.getEl().id, panel.getTitle(), null,
50035                     this.config.closeOnTab && panel.isClosable());
50036         if(panel.tabTip !== undefined){
50037             ti.setTooltip(panel.tabTip);
50038         }
50039         ti.on("activate", function(){
50040               this.setActivePanel(panel);
50041         }, this);
50042         if(this.config.closeOnTab){
50043             ti.on("beforeclose", function(t, e){
50044                 e.cancel = true;
50045                 this.remove(panel);
50046             }, this);
50047         }
50048         return ti;
50049     },
50050
50051     updatePanelTitle : function(panel, title){
50052         if(this.activePanel == panel){
50053             this.updateTitle(title);
50054         }
50055         if(this.tabs){
50056             var ti = this.tabs.getTab(panel.getEl().id);
50057             ti.setText(title);
50058             if(panel.tabTip !== undefined){
50059                 ti.setTooltip(panel.tabTip);
50060             }
50061         }
50062     },
50063
50064     updateTitle : function(title){
50065         if(this.titleTextEl && !this.config.title){
50066             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
50067         }
50068     },
50069
50070     setActivePanel : function(panel){
50071         panel = this.getPanel(panel);
50072         if(this.activePanel && this.activePanel != panel){
50073             this.activePanel.setActiveState(false);
50074         }
50075         this.activePanel = panel;
50076         panel.setActiveState(true);
50077         if(this.panelSize){
50078             panel.setSize(this.panelSize.width, this.panelSize.height);
50079         }
50080         if(this.closeBtn){
50081             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
50082         }
50083         this.updateTitle(panel.getTitle());
50084         if(this.tabs){
50085             this.fireEvent("invalidated", this);
50086         }
50087         this.fireEvent("panelactivated", this, panel);
50088     },
50089
50090     /**
50091      * Shows the specified panel.
50092      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
50093      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
50094      */
50095     showPanel : function(panel){
50096         if(panel = this.getPanel(panel)){
50097             if(this.tabs){
50098                 var tab = this.tabs.getTab(panel.getEl().id);
50099                 if(tab.isHidden()){
50100                     this.tabs.unhideTab(tab.id);
50101                 }
50102                 tab.activate();
50103             }else{
50104                 this.setActivePanel(panel);
50105             }
50106         }
50107         return panel;
50108     },
50109
50110     /**
50111      * Get the active panel for this region.
50112      * @return {Roo.ContentPanel} The active panel or null
50113      */
50114     getActivePanel : function(){
50115         return this.activePanel;
50116     },
50117
50118     validateVisibility : function(){
50119         if(this.panels.getCount() < 1){
50120             this.updateTitle("&#160;");
50121             this.closeBtn.hide();
50122             this.hide();
50123         }else{
50124             if(!this.isVisible()){
50125                 this.show();
50126             }
50127         }
50128     },
50129
50130     /**
50131      * Adds the passed ContentPanel(s) to this region.
50132      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
50133      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
50134      */
50135     add : function(panel){
50136         if(arguments.length > 1){
50137             for(var i = 0, len = arguments.length; i < len; i++) {
50138                 this.add(arguments[i]);
50139             }
50140             return null;
50141         }
50142         if(this.hasPanel(panel)){
50143             this.showPanel(panel);
50144             return panel;
50145         }
50146         panel.setRegion(this);
50147         this.panels.add(panel);
50148         if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
50149             this.bodyEl.dom.appendChild(panel.getEl().dom);
50150             if(panel.background !== true){
50151                 this.setActivePanel(panel);
50152             }
50153             this.fireEvent("paneladded", this, panel);
50154             return panel;
50155         }
50156         if(!this.tabs){
50157             this.initTabs();
50158         }else{
50159             this.initPanelAsTab(panel);
50160         }
50161         if(panel.background !== true){
50162             this.tabs.activate(panel.getEl().id);
50163         }
50164         this.fireEvent("paneladded", this, panel);
50165         return panel;
50166     },
50167
50168     /**
50169      * Hides the tab for the specified panel.
50170      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
50171      */
50172     hidePanel : function(panel){
50173         if(this.tabs && (panel = this.getPanel(panel))){
50174             this.tabs.hideTab(panel.getEl().id);
50175         }
50176     },
50177
50178     /**
50179      * Unhides the tab for a previously hidden panel.
50180      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
50181      */
50182     unhidePanel : function(panel){
50183         if(this.tabs && (panel = this.getPanel(panel))){
50184             this.tabs.unhideTab(panel.getEl().id);
50185         }
50186     },
50187
50188     clearPanels : function(){
50189         while(this.panels.getCount() > 0){
50190              this.remove(this.panels.first());
50191         }
50192     },
50193
50194     /**
50195      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
50196      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
50197      * @param {Boolean} preservePanel Overrides the config preservePanel option
50198      * @return {Roo.ContentPanel} The panel that was removed
50199      */
50200     remove : function(panel, preservePanel){
50201         panel = this.getPanel(panel);
50202         if(!panel){
50203             return null;
50204         }
50205         var e = {};
50206         this.fireEvent("beforeremove", this, panel, e);
50207         if(e.cancel === true){
50208             return null;
50209         }
50210         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
50211         var panelId = panel.getId();
50212         this.panels.removeKey(panelId);
50213         if(preservePanel){
50214             document.body.appendChild(panel.getEl().dom);
50215         }
50216         if(this.tabs){
50217             this.tabs.removeTab(panel.getEl().id);
50218         }else if (!preservePanel){
50219             this.bodyEl.dom.removeChild(panel.getEl().dom);
50220         }
50221         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
50222             var p = this.panels.first();
50223             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
50224             tempEl.appendChild(p.getEl().dom);
50225             this.bodyEl.update("");
50226             this.bodyEl.dom.appendChild(p.getEl().dom);
50227             tempEl = null;
50228             this.updateTitle(p.getTitle());
50229             this.tabs = null;
50230             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
50231             this.setActivePanel(p);
50232         }
50233         panel.setRegion(null);
50234         if(this.activePanel == panel){
50235             this.activePanel = null;
50236         }
50237         if(this.config.autoDestroy !== false && preservePanel !== true){
50238             try{panel.destroy();}catch(e){}
50239         }
50240         this.fireEvent("panelremoved", this, panel);
50241         return panel;
50242     },
50243
50244     /**
50245      * Returns the TabPanel component used by this region
50246      * @return {Roo.TabPanel}
50247      */
50248     getTabs : function(){
50249         return this.tabs;
50250     },
50251
50252     createTool : function(parentEl, className){
50253         var btn = Roo.DomHelper.append(parentEl, {tag: "div", cls: "x-layout-tools-button",
50254             children: [{tag: "div", cls: "x-layout-tools-button-inner " + className, html: "&#160;"}]}, true);
50255         btn.addClassOnOver("x-layout-tools-button-over");
50256         return btn;
50257     }
50258 });/*
50259  * Based on:
50260  * Ext JS Library 1.1.1
50261  * Copyright(c) 2006-2007, Ext JS, LLC.
50262  *
50263  * Originally Released Under LGPL - original licence link has changed is not relivant.
50264  *
50265  * Fork - LGPL
50266  * <script type="text/javascript">
50267  */
50268  
50269
50270
50271 /**
50272  * @class Roo.SplitLayoutRegion
50273  * @extends Roo.LayoutRegion
50274  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
50275  */
50276 Roo.SplitLayoutRegion = function(mgr, config, pos, cursor){
50277     this.cursor = cursor;
50278     Roo.SplitLayoutRegion.superclass.constructor.call(this, mgr, config, pos);
50279 };
50280
50281 Roo.extend(Roo.SplitLayoutRegion, Roo.LayoutRegion, {
50282     splitTip : "Drag to resize.",
50283     collapsibleSplitTip : "Drag to resize. Double click to hide.",
50284     useSplitTips : false,
50285
50286     applyConfig : function(config){
50287         Roo.SplitLayoutRegion.superclass.applyConfig.call(this, config);
50288         if(config.split){
50289             if(!this.split){
50290                 var splitEl = Roo.DomHelper.append(this.mgr.el.dom, 
50291                         {tag: "div", id: this.el.id + "-split", cls: "x-layout-split x-layout-split-"+this.position, html: "&#160;"});
50292                 /** The SplitBar for this region 
50293                 * @type Roo.SplitBar */
50294                 this.split = new Roo.SplitBar(splitEl, this.el, this.orientation);
50295                 this.split.on("moved", this.onSplitMove, this);
50296                 this.split.useShim = config.useShim === true;
50297                 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
50298                 if(this.useSplitTips){
50299                     this.split.el.dom.title = config.collapsible ? this.collapsibleSplitTip : this.splitTip;
50300                 }
50301                 if(config.collapsible){
50302                     this.split.el.on("dblclick", this.collapse,  this);
50303                 }
50304             }
50305             if(typeof config.minSize != "undefined"){
50306                 this.split.minSize = config.minSize;
50307             }
50308             if(typeof config.maxSize != "undefined"){
50309                 this.split.maxSize = config.maxSize;
50310             }
50311             if(config.hideWhenEmpty || config.hidden || config.collapsed){
50312                 this.hideSplitter();
50313             }
50314         }
50315     },
50316
50317     getHMaxSize : function(){
50318          var cmax = this.config.maxSize || 10000;
50319          var center = this.mgr.getRegion("center");
50320          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
50321     },
50322
50323     getVMaxSize : function(){
50324          var cmax = this.config.maxSize || 10000;
50325          var center = this.mgr.getRegion("center");
50326          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
50327     },
50328
50329     onSplitMove : function(split, newSize){
50330         this.fireEvent("resized", this, newSize);
50331     },
50332     
50333     /** 
50334      * Returns the {@link Roo.SplitBar} for this region.
50335      * @return {Roo.SplitBar}
50336      */
50337     getSplitBar : function(){
50338         return this.split;
50339     },
50340     
50341     hide : function(){
50342         this.hideSplitter();
50343         Roo.SplitLayoutRegion.superclass.hide.call(this);
50344     },
50345
50346     hideSplitter : function(){
50347         if(this.split){
50348             this.split.el.setLocation(-2000,-2000);
50349             this.split.el.hide();
50350         }
50351     },
50352
50353     show : function(){
50354         if(this.split){
50355             this.split.el.show();
50356         }
50357         Roo.SplitLayoutRegion.superclass.show.call(this);
50358     },
50359     
50360     beforeSlide: function(){
50361         if(Roo.isGecko){// firefox overflow auto bug workaround
50362             this.bodyEl.clip();
50363             if(this.tabs) this.tabs.bodyEl.clip();
50364             if(this.activePanel){
50365                 this.activePanel.getEl().clip();
50366                 
50367                 if(this.activePanel.beforeSlide){
50368                     this.activePanel.beforeSlide();
50369                 }
50370             }
50371         }
50372     },
50373     
50374     afterSlide : function(){
50375         if(Roo.isGecko){// firefox overflow auto bug workaround
50376             this.bodyEl.unclip();
50377             if(this.tabs) this.tabs.bodyEl.unclip();
50378             if(this.activePanel){
50379                 this.activePanel.getEl().unclip();
50380                 if(this.activePanel.afterSlide){
50381                     this.activePanel.afterSlide();
50382                 }
50383             }
50384         }
50385     },
50386
50387     initAutoHide : function(){
50388         if(this.autoHide !== false){
50389             if(!this.autoHideHd){
50390                 var st = new Roo.util.DelayedTask(this.slideIn, this);
50391                 this.autoHideHd = {
50392                     "mouseout": function(e){
50393                         if(!e.within(this.el, true)){
50394                             st.delay(500);
50395                         }
50396                     },
50397                     "mouseover" : function(e){
50398                         st.cancel();
50399                     },
50400                     scope : this
50401                 };
50402             }
50403             this.el.on(this.autoHideHd);
50404         }
50405     },
50406
50407     clearAutoHide : function(){
50408         if(this.autoHide !== false){
50409             this.el.un("mouseout", this.autoHideHd.mouseout);
50410             this.el.un("mouseover", this.autoHideHd.mouseover);
50411         }
50412     },
50413
50414     clearMonitor : function(){
50415         Roo.get(document).un("click", this.slideInIf, this);
50416     },
50417
50418     // these names are backwards but not changed for compat
50419     slideOut : function(){
50420         if(this.isSlid || this.el.hasActiveFx()){
50421             return;
50422         }
50423         this.isSlid = true;
50424         if(this.collapseBtn){
50425             this.collapseBtn.hide();
50426         }
50427         this.closeBtnState = this.closeBtn.getStyle('display');
50428         this.closeBtn.hide();
50429         if(this.stickBtn){
50430             this.stickBtn.show();
50431         }
50432         this.el.show();
50433         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
50434         this.beforeSlide();
50435         this.el.setStyle("z-index", 10001);
50436         this.el.slideIn(this.getSlideAnchor(), {
50437             callback: function(){
50438                 this.afterSlide();
50439                 this.initAutoHide();
50440                 Roo.get(document).on("click", this.slideInIf, this);
50441                 this.fireEvent("slideshow", this);
50442             },
50443             scope: this,
50444             block: true
50445         });
50446     },
50447
50448     afterSlideIn : function(){
50449         this.clearAutoHide();
50450         this.isSlid = false;
50451         this.clearMonitor();
50452         this.el.setStyle("z-index", "");
50453         if(this.collapseBtn){
50454             this.collapseBtn.show();
50455         }
50456         this.closeBtn.setStyle('display', this.closeBtnState);
50457         if(this.stickBtn){
50458             this.stickBtn.hide();
50459         }
50460         this.fireEvent("slidehide", this);
50461     },
50462
50463     slideIn : function(cb){
50464         if(!this.isSlid || this.el.hasActiveFx()){
50465             Roo.callback(cb);
50466             return;
50467         }
50468         this.isSlid = false;
50469         this.beforeSlide();
50470         this.el.slideOut(this.getSlideAnchor(), {
50471             callback: function(){
50472                 this.el.setLeftTop(-10000, -10000);
50473                 this.afterSlide();
50474                 this.afterSlideIn();
50475                 Roo.callback(cb);
50476             },
50477             scope: this,
50478             block: true
50479         });
50480     },
50481     
50482     slideInIf : function(e){
50483         if(!e.within(this.el)){
50484             this.slideIn();
50485         }
50486     },
50487
50488     animateCollapse : function(){
50489         this.beforeSlide();
50490         this.el.setStyle("z-index", 20000);
50491         var anchor = this.getSlideAnchor();
50492         this.el.slideOut(anchor, {
50493             callback : function(){
50494                 this.el.setStyle("z-index", "");
50495                 this.collapsedEl.slideIn(anchor, {duration:.3});
50496                 this.afterSlide();
50497                 this.el.setLocation(-10000,-10000);
50498                 this.el.hide();
50499                 this.fireEvent("collapsed", this);
50500             },
50501             scope: this,
50502             block: true
50503         });
50504     },
50505
50506     animateExpand : function(){
50507         this.beforeSlide();
50508         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
50509         this.el.setStyle("z-index", 20000);
50510         this.collapsedEl.hide({
50511             duration:.1
50512         });
50513         this.el.slideIn(this.getSlideAnchor(), {
50514             callback : function(){
50515                 this.el.setStyle("z-index", "");
50516                 this.afterSlide();
50517                 if(this.split){
50518                     this.split.el.show();
50519                 }
50520                 this.fireEvent("invalidated", this);
50521                 this.fireEvent("expanded", this);
50522             },
50523             scope: this,
50524             block: true
50525         });
50526     },
50527
50528     anchors : {
50529         "west" : "left",
50530         "east" : "right",
50531         "north" : "top",
50532         "south" : "bottom"
50533     },
50534
50535     sanchors : {
50536         "west" : "l",
50537         "east" : "r",
50538         "north" : "t",
50539         "south" : "b"
50540     },
50541
50542     canchors : {
50543         "west" : "tl-tr",
50544         "east" : "tr-tl",
50545         "north" : "tl-bl",
50546         "south" : "bl-tl"
50547     },
50548
50549     getAnchor : function(){
50550         return this.anchors[this.position];
50551     },
50552
50553     getCollapseAnchor : function(){
50554         return this.canchors[this.position];
50555     },
50556
50557     getSlideAnchor : function(){
50558         return this.sanchors[this.position];
50559     },
50560
50561     getAlignAdj : function(){
50562         var cm = this.cmargins;
50563         switch(this.position){
50564             case "west":
50565                 return [0, 0];
50566             break;
50567             case "east":
50568                 return [0, 0];
50569             break;
50570             case "north":
50571                 return [0, 0];
50572             break;
50573             case "south":
50574                 return [0, 0];
50575             break;
50576         }
50577     },
50578
50579     getExpandAdj : function(){
50580         var c = this.collapsedEl, cm = this.cmargins;
50581         switch(this.position){
50582             case "west":
50583                 return [-(cm.right+c.getWidth()+cm.left), 0];
50584             break;
50585             case "east":
50586                 return [cm.right+c.getWidth()+cm.left, 0];
50587             break;
50588             case "north":
50589                 return [0, -(cm.top+cm.bottom+c.getHeight())];
50590             break;
50591             case "south":
50592                 return [0, cm.top+cm.bottom+c.getHeight()];
50593             break;
50594         }
50595     }
50596 });/*
50597  * Based on:
50598  * Ext JS Library 1.1.1
50599  * Copyright(c) 2006-2007, Ext JS, LLC.
50600  *
50601  * Originally Released Under LGPL - original licence link has changed is not relivant.
50602  *
50603  * Fork - LGPL
50604  * <script type="text/javascript">
50605  */
50606 /*
50607  * These classes are private internal classes
50608  */
50609 Roo.CenterLayoutRegion = function(mgr, config){
50610     Roo.LayoutRegion.call(this, mgr, config, "center");
50611     this.visible = true;
50612     this.minWidth = config.minWidth || 20;
50613     this.minHeight = config.minHeight || 20;
50614 };
50615
50616 Roo.extend(Roo.CenterLayoutRegion, Roo.LayoutRegion, {
50617     hide : function(){
50618         // center panel can't be hidden
50619     },
50620     
50621     show : function(){
50622         // center panel can't be hidden
50623     },
50624     
50625     getMinWidth: function(){
50626         return this.minWidth;
50627     },
50628     
50629     getMinHeight: function(){
50630         return this.minHeight;
50631     }
50632 });
50633
50634
50635 Roo.NorthLayoutRegion = function(mgr, config){
50636     Roo.LayoutRegion.call(this, mgr, config, "north", "n-resize");
50637     if(this.split){
50638         this.split.placement = Roo.SplitBar.TOP;
50639         this.split.orientation = Roo.SplitBar.VERTICAL;
50640         this.split.el.addClass("x-layout-split-v");
50641     }
50642     var size = config.initialSize || config.height;
50643     if(typeof size != "undefined"){
50644         this.el.setHeight(size);
50645     }
50646 };
50647 Roo.extend(Roo.NorthLayoutRegion, Roo.SplitLayoutRegion, {
50648     orientation: Roo.SplitBar.VERTICAL,
50649     getBox : function(){
50650         if(this.collapsed){
50651             return this.collapsedEl.getBox();
50652         }
50653         var box = this.el.getBox();
50654         if(this.split){
50655             box.height += this.split.el.getHeight();
50656         }
50657         return box;
50658     },
50659     
50660     updateBox : function(box){
50661         if(this.split && !this.collapsed){
50662             box.height -= this.split.el.getHeight();
50663             this.split.el.setLeft(box.x);
50664             this.split.el.setTop(box.y+box.height);
50665             this.split.el.setWidth(box.width);
50666         }
50667         if(this.collapsed){
50668             this.updateBody(box.width, null);
50669         }
50670         Roo.LayoutRegion.prototype.updateBox.call(this, box);
50671     }
50672 });
50673
50674 Roo.SouthLayoutRegion = function(mgr, config){
50675     Roo.SplitLayoutRegion.call(this, mgr, config, "south", "s-resize");
50676     if(this.split){
50677         this.split.placement = Roo.SplitBar.BOTTOM;
50678         this.split.orientation = Roo.SplitBar.VERTICAL;
50679         this.split.el.addClass("x-layout-split-v");
50680     }
50681     var size = config.initialSize || config.height;
50682     if(typeof size != "undefined"){
50683         this.el.setHeight(size);
50684     }
50685 };
50686 Roo.extend(Roo.SouthLayoutRegion, Roo.SplitLayoutRegion, {
50687     orientation: Roo.SplitBar.VERTICAL,
50688     getBox : function(){
50689         if(this.collapsed){
50690             return this.collapsedEl.getBox();
50691         }
50692         var box = this.el.getBox();
50693         if(this.split){
50694             var sh = this.split.el.getHeight();
50695             box.height += sh;
50696             box.y -= sh;
50697         }
50698         return box;
50699     },
50700     
50701     updateBox : function(box){
50702         if(this.split && !this.collapsed){
50703             var sh = this.split.el.getHeight();
50704             box.height -= sh;
50705             box.y += sh;
50706             this.split.el.setLeft(box.x);
50707             this.split.el.setTop(box.y-sh);
50708             this.split.el.setWidth(box.width);
50709         }
50710         if(this.collapsed){
50711             this.updateBody(box.width, null);
50712         }
50713         Roo.LayoutRegion.prototype.updateBox.call(this, box);
50714     }
50715 });
50716
50717 Roo.EastLayoutRegion = function(mgr, config){
50718     Roo.SplitLayoutRegion.call(this, mgr, config, "east", "e-resize");
50719     if(this.split){
50720         this.split.placement = Roo.SplitBar.RIGHT;
50721         this.split.orientation = Roo.SplitBar.HORIZONTAL;
50722         this.split.el.addClass("x-layout-split-h");
50723     }
50724     var size = config.initialSize || config.width;
50725     if(typeof size != "undefined"){
50726         this.el.setWidth(size);
50727     }
50728 };
50729 Roo.extend(Roo.EastLayoutRegion, Roo.SplitLayoutRegion, {
50730     orientation: Roo.SplitBar.HORIZONTAL,
50731     getBox : function(){
50732         if(this.collapsed){
50733             return this.collapsedEl.getBox();
50734         }
50735         var box = this.el.getBox();
50736         if(this.split){
50737             var sw = this.split.el.getWidth();
50738             box.width += sw;
50739             box.x -= sw;
50740         }
50741         return box;
50742     },
50743
50744     updateBox : function(box){
50745         if(this.split && !this.collapsed){
50746             var sw = this.split.el.getWidth();
50747             box.width -= sw;
50748             this.split.el.setLeft(box.x);
50749             this.split.el.setTop(box.y);
50750             this.split.el.setHeight(box.height);
50751             box.x += sw;
50752         }
50753         if(this.collapsed){
50754             this.updateBody(null, box.height);
50755         }
50756         Roo.LayoutRegion.prototype.updateBox.call(this, box);
50757     }
50758 });
50759
50760 Roo.WestLayoutRegion = function(mgr, config){
50761     Roo.SplitLayoutRegion.call(this, mgr, config, "west", "w-resize");
50762     if(this.split){
50763         this.split.placement = Roo.SplitBar.LEFT;
50764         this.split.orientation = Roo.SplitBar.HORIZONTAL;
50765         this.split.el.addClass("x-layout-split-h");
50766     }
50767     var size = config.initialSize || config.width;
50768     if(typeof size != "undefined"){
50769         this.el.setWidth(size);
50770     }
50771 };
50772 Roo.extend(Roo.WestLayoutRegion, Roo.SplitLayoutRegion, {
50773     orientation: Roo.SplitBar.HORIZONTAL,
50774     getBox : function(){
50775         if(this.collapsed){
50776             return this.collapsedEl.getBox();
50777         }
50778         var box = this.el.getBox();
50779         if(this.split){
50780             box.width += this.split.el.getWidth();
50781         }
50782         return box;
50783     },
50784     
50785     updateBox : function(box){
50786         if(this.split && !this.collapsed){
50787             var sw = this.split.el.getWidth();
50788             box.width -= sw;
50789             this.split.el.setLeft(box.x+box.width);
50790             this.split.el.setTop(box.y);
50791             this.split.el.setHeight(box.height);
50792         }
50793         if(this.collapsed){
50794             this.updateBody(null, box.height);
50795         }
50796         Roo.LayoutRegion.prototype.updateBox.call(this, box);
50797     }
50798 });
50799 /*
50800  * Based on:
50801  * Ext JS Library 1.1.1
50802  * Copyright(c) 2006-2007, Ext JS, LLC.
50803  *
50804  * Originally Released Under LGPL - original licence link has changed is not relivant.
50805  *
50806  * Fork - LGPL
50807  * <script type="text/javascript">
50808  */
50809  
50810  
50811 /*
50812  * Private internal class for reading and applying state
50813  */
50814 Roo.LayoutStateManager = function(layout){
50815      // default empty state
50816      this.state = {
50817         north: {},
50818         south: {},
50819         east: {},
50820         west: {}       
50821     };
50822 };
50823
50824 Roo.LayoutStateManager.prototype = {
50825     init : function(layout, provider){
50826         this.provider = provider;
50827         var state = provider.get(layout.id+"-layout-state");
50828         if(state){
50829             var wasUpdating = layout.isUpdating();
50830             if(!wasUpdating){
50831                 layout.beginUpdate();
50832             }
50833             for(var key in state){
50834                 if(typeof state[key] != "function"){
50835                     var rstate = state[key];
50836                     var r = layout.getRegion(key);
50837                     if(r && rstate){
50838                         if(rstate.size){
50839                             r.resizeTo(rstate.size);
50840                         }
50841                         if(rstate.collapsed == true){
50842                             r.collapse(true);
50843                         }else{
50844                             r.expand(null, true);
50845                         }
50846                     }
50847                 }
50848             }
50849             if(!wasUpdating){
50850                 layout.endUpdate();
50851             }
50852             this.state = state; 
50853         }
50854         this.layout = layout;
50855         layout.on("regionresized", this.onRegionResized, this);
50856         layout.on("regioncollapsed", this.onRegionCollapsed, this);
50857         layout.on("regionexpanded", this.onRegionExpanded, this);
50858     },
50859     
50860     storeState : function(){
50861         this.provider.set(this.layout.id+"-layout-state", this.state);
50862     },
50863     
50864     onRegionResized : function(region, newSize){
50865         this.state[region.getPosition()].size = newSize;
50866         this.storeState();
50867     },
50868     
50869     onRegionCollapsed : function(region){
50870         this.state[region.getPosition()].collapsed = true;
50871         this.storeState();
50872     },
50873     
50874     onRegionExpanded : function(region){
50875         this.state[region.getPosition()].collapsed = false;
50876         this.storeState();
50877     }
50878 };/*
50879  * Based on:
50880  * Ext JS Library 1.1.1
50881  * Copyright(c) 2006-2007, Ext JS, LLC.
50882  *
50883  * Originally Released Under LGPL - original licence link has changed is not relivant.
50884  *
50885  * Fork - LGPL
50886  * <script type="text/javascript">
50887  */
50888 /**
50889  * @class Roo.ContentPanel
50890  * @extends Roo.util.Observable
50891  * A basic ContentPanel element.
50892  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
50893  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
50894  * @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
50895  * @cfg {Boolean}   closable      True if the panel can be closed/removed
50896  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
50897  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
50898  * @cfg {Toolbar}   toolbar       A toolbar for this panel
50899  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
50900  * @cfg {String} title          The title for this panel
50901  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
50902  * @cfg {String} url            Calls {@link #setUrl} with this value
50903  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
50904  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
50905  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
50906  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
50907
50908  * @constructor
50909  * Create a new ContentPanel.
50910  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
50911  * @param {String/Object} config A string to set only the title or a config object
50912  * @param {String} content (optional) Set the HTML content for this panel
50913  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
50914  */
50915 Roo.ContentPanel = function(el, config, content){
50916     
50917      
50918     /*
50919     if(el.autoCreate || el.xtype){ // xtype is available if this is called from factory
50920         config = el;
50921         el = Roo.id();
50922     }
50923     if (config && config.parentLayout) { 
50924         el = config.parentLayout.el.createChild(); 
50925     }
50926     */
50927     if(el.autoCreate){ // xtype is available if this is called from factory
50928         config = el;
50929         el = Roo.id();
50930     }
50931     this.el = Roo.get(el);
50932     if(!this.el && config && config.autoCreate){
50933         if(typeof config.autoCreate == "object"){
50934             if(!config.autoCreate.id){
50935                 config.autoCreate.id = config.id||el;
50936             }
50937             this.el = Roo.DomHelper.append(document.body,
50938                         config.autoCreate, true);
50939         }else{
50940             this.el = Roo.DomHelper.append(document.body,
50941                         {tag: "div", cls: "x-layout-inactive-content", id: config.id||el}, true);
50942         }
50943     }
50944     this.closable = false;
50945     this.loaded = false;
50946     this.active = false;
50947     if(typeof config == "string"){
50948         this.title = config;
50949     }else{
50950         Roo.apply(this, config);
50951     }
50952     
50953     if (this.toolbar && !this.toolbar.el && this.toolbar.xtype) {
50954         this.wrapEl = this.el.wrap();
50955         this.toolbar.container = this.el.insertSibling(false, 'before');
50956         this.toolbar = new Roo.Toolbar(this.toolbar);
50957     }
50958     
50959     // xtype created footer. - not sure if will work as we normally have to render first..
50960     if (this.footer && !this.footer.el && this.footer.xtype) {
50961         if (!this.wrapEl) {
50962             this.wrapEl = this.el.wrap();
50963         }
50964     
50965         this.footer.container = this.wrapEl.createChild();
50966          
50967         this.footer = Roo.factory(this.footer, Roo);
50968         
50969     }
50970     
50971     if(this.resizeEl){
50972         this.resizeEl = Roo.get(this.resizeEl, true);
50973     }else{
50974         this.resizeEl = this.el;
50975     }
50976     // handle view.xtype
50977     
50978  
50979     
50980     
50981     this.addEvents({
50982         /**
50983          * @event activate
50984          * Fires when this panel is activated. 
50985          * @param {Roo.ContentPanel} this
50986          */
50987         "activate" : true,
50988         /**
50989          * @event deactivate
50990          * Fires when this panel is activated. 
50991          * @param {Roo.ContentPanel} this
50992          */
50993         "deactivate" : true,
50994
50995         /**
50996          * @event resize
50997          * Fires when this panel is resized if fitToFrame is true.
50998          * @param {Roo.ContentPanel} this
50999          * @param {Number} width The width after any component adjustments
51000          * @param {Number} height The height after any component adjustments
51001          */
51002         "resize" : true,
51003         
51004          /**
51005          * @event render
51006          * Fires when this tab is created
51007          * @param {Roo.ContentPanel} this
51008          */
51009         "render" : true
51010         
51011         
51012         
51013     });
51014     
51015
51016     
51017     
51018     if(this.autoScroll){
51019         this.resizeEl.setStyle("overflow", "auto");
51020     } else {
51021         // fix randome scrolling
51022         this.el.on('scroll', function() {
51023             Roo.log('fix random scolling');
51024             this.scrollTo('top',0); 
51025         });
51026     }
51027     content = content || this.content;
51028     if(content){
51029         this.setContent(content);
51030     }
51031     if(config && config.url){
51032         this.setUrl(this.url, this.params, this.loadOnce);
51033     }
51034     
51035     
51036     
51037     Roo.ContentPanel.superclass.constructor.call(this);
51038     
51039     if (this.view && typeof(this.view.xtype) != 'undefined') {
51040         this.view.el = this.el.appendChild(document.createElement("div"));
51041         this.view = Roo.factory(this.view); 
51042         this.view.render  &&  this.view.render(false, '');  
51043     }
51044     
51045     
51046     this.fireEvent('render', this);
51047 };
51048
51049 Roo.extend(Roo.ContentPanel, Roo.util.Observable, {
51050     tabTip:'',
51051     setRegion : function(region){
51052         this.region = region;
51053         if(region){
51054            this.el.replaceClass("x-layout-inactive-content", "x-layout-active-content");
51055         }else{
51056            this.el.replaceClass("x-layout-active-content", "x-layout-inactive-content");
51057         } 
51058     },
51059     
51060     /**
51061      * Returns the toolbar for this Panel if one was configured. 
51062      * @return {Roo.Toolbar} 
51063      */
51064     getToolbar : function(){
51065         return this.toolbar;
51066     },
51067     
51068     setActiveState : function(active){
51069         this.active = active;
51070         if(!active){
51071             this.fireEvent("deactivate", this);
51072         }else{
51073             this.fireEvent("activate", this);
51074         }
51075     },
51076     /**
51077      * Updates this panel's element
51078      * @param {String} content The new content
51079      * @param {Boolean} loadScripts (optional) true to look for and process scripts
51080     */
51081     setContent : function(content, loadScripts){
51082         this.el.update(content, loadScripts);
51083     },
51084
51085     ignoreResize : function(w, h){
51086         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
51087             return true;
51088         }else{
51089             this.lastSize = {width: w, height: h};
51090             return false;
51091         }
51092     },
51093     /**
51094      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
51095      * @return {Roo.UpdateManager} The UpdateManager
51096      */
51097     getUpdateManager : function(){
51098         return this.el.getUpdateManager();
51099     },
51100      /**
51101      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
51102      * @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:
51103 <pre><code>
51104 panel.load({
51105     url: "your-url.php",
51106     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
51107     callback: yourFunction,
51108     scope: yourObject, //(optional scope)
51109     discardUrl: false,
51110     nocache: false,
51111     text: "Loading...",
51112     timeout: 30,
51113     scripts: false
51114 });
51115 </code></pre>
51116      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
51117      * 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.
51118      * @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}
51119      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
51120      * @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.
51121      * @return {Roo.ContentPanel} this
51122      */
51123     load : function(){
51124         var um = this.el.getUpdateManager();
51125         um.update.apply(um, arguments);
51126         return this;
51127     },
51128
51129
51130     /**
51131      * 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.
51132      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
51133      * @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)
51134      * @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)
51135      * @return {Roo.UpdateManager} The UpdateManager
51136      */
51137     setUrl : function(url, params, loadOnce){
51138         if(this.refreshDelegate){
51139             this.removeListener("activate", this.refreshDelegate);
51140         }
51141         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
51142         this.on("activate", this.refreshDelegate);
51143         return this.el.getUpdateManager();
51144     },
51145     
51146     _handleRefresh : function(url, params, loadOnce){
51147         if(!loadOnce || !this.loaded){
51148             var updater = this.el.getUpdateManager();
51149             updater.update(url, params, this._setLoaded.createDelegate(this));
51150         }
51151     },
51152     
51153     _setLoaded : function(){
51154         this.loaded = true;
51155     }, 
51156     
51157     /**
51158      * Returns this panel's id
51159      * @return {String} 
51160      */
51161     getId : function(){
51162         return this.el.id;
51163     },
51164     
51165     /** 
51166      * Returns this panel's element - used by regiosn to add.
51167      * @return {Roo.Element} 
51168      */
51169     getEl : function(){
51170         return this.wrapEl || this.el;
51171     },
51172     
51173     adjustForComponents : function(width, height)
51174     {
51175         //Roo.log('adjustForComponents ');
51176         if(this.resizeEl != this.el){
51177             width -= this.el.getFrameWidth('lr');
51178             height -= this.el.getFrameWidth('tb');
51179         }
51180         if(this.toolbar){
51181             var te = this.toolbar.getEl();
51182             height -= te.getHeight();
51183             te.setWidth(width);
51184         }
51185         if(this.footer){
51186             var te = this.footer.getEl();
51187             Roo.log("footer:" + te.getHeight());
51188             
51189             height -= te.getHeight();
51190             te.setWidth(width);
51191         }
51192         
51193         
51194         if(this.adjustments){
51195             width += this.adjustments[0];
51196             height += this.adjustments[1];
51197         }
51198         return {"width": width, "height": height};
51199     },
51200     
51201     setSize : function(width, height){
51202         if(this.fitToFrame && !this.ignoreResize(width, height)){
51203             if(this.fitContainer && this.resizeEl != this.el){
51204                 this.el.setSize(width, height);
51205             }
51206             var size = this.adjustForComponents(width, height);
51207             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
51208             this.fireEvent('resize', this, size.width, size.height);
51209         }
51210     },
51211     
51212     /**
51213      * Returns this panel's title
51214      * @return {String} 
51215      */
51216     getTitle : function(){
51217         return this.title;
51218     },
51219     
51220     /**
51221      * Set this panel's title
51222      * @param {String} title
51223      */
51224     setTitle : function(title){
51225         this.title = title;
51226         if(this.region){
51227             this.region.updatePanelTitle(this, title);
51228         }
51229     },
51230     
51231     /**
51232      * Returns true is this panel was configured to be closable
51233      * @return {Boolean} 
51234      */
51235     isClosable : function(){
51236         return this.closable;
51237     },
51238     
51239     beforeSlide : function(){
51240         this.el.clip();
51241         this.resizeEl.clip();
51242     },
51243     
51244     afterSlide : function(){
51245         this.el.unclip();
51246         this.resizeEl.unclip();
51247     },
51248     
51249     /**
51250      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
51251      *   Will fail silently if the {@link #setUrl} method has not been called.
51252      *   This does not activate the panel, just updates its content.
51253      */
51254     refresh : function(){
51255         if(this.refreshDelegate){
51256            this.loaded = false;
51257            this.refreshDelegate();
51258         }
51259     },
51260     
51261     /**
51262      * Destroys this panel
51263      */
51264     destroy : function(){
51265         this.el.removeAllListeners();
51266         var tempEl = document.createElement("span");
51267         tempEl.appendChild(this.el.dom);
51268         tempEl.innerHTML = "";
51269         this.el.remove();
51270         this.el = null;
51271     },
51272     
51273     /**
51274      * form - if the content panel contains a form - this is a reference to it.
51275      * @type {Roo.form.Form}
51276      */
51277     form : false,
51278     /**
51279      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
51280      *    This contains a reference to it.
51281      * @type {Roo.View}
51282      */
51283     view : false,
51284     
51285       /**
51286      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
51287      * <pre><code>
51288
51289 layout.addxtype({
51290        xtype : 'Form',
51291        items: [ .... ]
51292    }
51293 );
51294
51295 </code></pre>
51296      * @param {Object} cfg Xtype definition of item to add.
51297      */
51298     
51299     addxtype : function(cfg) {
51300         // add form..
51301         if (cfg.xtype.match(/^Form$/)) {
51302             
51303             var el;
51304             //if (this.footer) {
51305             //    el = this.footer.container.insertSibling(false, 'before');
51306             //} else {
51307                 el = this.el.createChild();
51308             //}
51309
51310             this.form = new  Roo.form.Form(cfg);
51311             
51312             
51313             if ( this.form.allItems.length) this.form.render(el.dom);
51314             return this.form;
51315         }
51316         // should only have one of theses..
51317         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
51318             // views.. should not be just added - used named prop 'view''
51319             
51320             cfg.el = this.el.appendChild(document.createElement("div"));
51321             // factory?
51322             
51323             var ret = new Roo.factory(cfg);
51324              
51325              ret.render && ret.render(false, ''); // render blank..
51326             this.view = ret;
51327             return ret;
51328         }
51329         return false;
51330     }
51331 });
51332
51333 /**
51334  * @class Roo.GridPanel
51335  * @extends Roo.ContentPanel
51336  * @constructor
51337  * Create a new GridPanel.
51338  * @param {Roo.grid.Grid} grid The grid for this panel
51339  * @param {String/Object} config A string to set only the panel's title, or a config object
51340  */
51341 Roo.GridPanel = function(grid, config){
51342     
51343   
51344     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
51345         {tag: "div", cls: "x-layout-grid-wrapper x-layout-inactive-content"}, true);
51346         
51347     this.wrapper.dom.appendChild(grid.getGridEl().dom);
51348     
51349     Roo.GridPanel.superclass.constructor.call(this, this.wrapper, config);
51350     
51351     if(this.toolbar){
51352         this.toolbar.el.insertBefore(this.wrapper.dom.firstChild);
51353     }
51354     // xtype created footer. - not sure if will work as we normally have to render first..
51355     if (this.footer && !this.footer.el && this.footer.xtype) {
51356         
51357         this.footer.container = this.grid.getView().getFooterPanel(true);
51358         this.footer.dataSource = this.grid.dataSource;
51359         this.footer = Roo.factory(this.footer, Roo);
51360         
51361     }
51362     
51363     grid.monitorWindowResize = false; // turn off autosizing
51364     grid.autoHeight = false;
51365     grid.autoWidth = false;
51366     this.grid = grid;
51367     this.grid.getGridEl().replaceClass("x-layout-inactive-content", "x-layout-component-panel");
51368 };
51369
51370 Roo.extend(Roo.GridPanel, Roo.ContentPanel, {
51371     getId : function(){
51372         return this.grid.id;
51373     },
51374     
51375     /**
51376      * Returns the grid for this panel
51377      * @return {Roo.grid.Grid} 
51378      */
51379     getGrid : function(){
51380         return this.grid;    
51381     },
51382     
51383     setSize : function(width, height){
51384         if(!this.ignoreResize(width, height)){
51385             var grid = this.grid;
51386             var size = this.adjustForComponents(width, height);
51387             grid.getGridEl().setSize(size.width, size.height);
51388             grid.autoSize();
51389         }
51390     },
51391     
51392     beforeSlide : function(){
51393         this.grid.getView().scroller.clip();
51394     },
51395     
51396     afterSlide : function(){
51397         this.grid.getView().scroller.unclip();
51398     },
51399     
51400     destroy : function(){
51401         this.grid.destroy();
51402         delete this.grid;
51403         Roo.GridPanel.superclass.destroy.call(this); 
51404     }
51405 });
51406
51407
51408 /**
51409  * @class Roo.NestedLayoutPanel
51410  * @extends Roo.ContentPanel
51411  * @constructor
51412  * Create a new NestedLayoutPanel.
51413  * 
51414  * 
51415  * @param {Roo.BorderLayout} layout The layout for this panel
51416  * @param {String/Object} config A string to set only the title or a config object
51417  */
51418 Roo.NestedLayoutPanel = function(layout, config)
51419 {
51420     // construct with only one argument..
51421     /* FIXME - implement nicer consturctors
51422     if (layout.layout) {
51423         config = layout;
51424         layout = config.layout;
51425         delete config.layout;
51426     }
51427     if (layout.xtype && !layout.getEl) {
51428         // then layout needs constructing..
51429         layout = Roo.factory(layout, Roo);
51430     }
51431     */
51432     
51433     
51434     Roo.NestedLayoutPanel.superclass.constructor.call(this, layout.getEl(), config);
51435     
51436     layout.monitorWindowResize = false; // turn off autosizing
51437     this.layout = layout;
51438     this.layout.getEl().addClass("x-layout-nested-layout");
51439     
51440     
51441     
51442     
51443 };
51444
51445 Roo.extend(Roo.NestedLayoutPanel, Roo.ContentPanel, {
51446
51447     setSize : function(width, height){
51448         if(!this.ignoreResize(width, height)){
51449             var size = this.adjustForComponents(width, height);
51450             var el = this.layout.getEl();
51451             el.setSize(size.width, size.height);
51452             var touch = el.dom.offsetWidth;
51453             this.layout.layout();
51454             // ie requires a double layout on the first pass
51455             if(Roo.isIE && !this.initialized){
51456                 this.initialized = true;
51457                 this.layout.layout();
51458             }
51459         }
51460     },
51461     
51462     // activate all subpanels if not currently active..
51463     
51464     setActiveState : function(active){
51465         this.active = active;
51466         if(!active){
51467             this.fireEvent("deactivate", this);
51468             return;
51469         }
51470         
51471         this.fireEvent("activate", this);
51472         // not sure if this should happen before or after..
51473         if (!this.layout) {
51474             return; // should not happen..
51475         }
51476         var reg = false;
51477         for (var r in this.layout.regions) {
51478             reg = this.layout.getRegion(r);
51479             if (reg.getActivePanel()) {
51480                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
51481                 reg.setActivePanel(reg.getActivePanel());
51482                 continue;
51483             }
51484             if (!reg.panels.length) {
51485                 continue;
51486             }
51487             reg.showPanel(reg.getPanel(0));
51488         }
51489         
51490         
51491         
51492         
51493     },
51494     
51495     /**
51496      * Returns the nested BorderLayout for this panel
51497      * @return {Roo.BorderLayout} 
51498      */
51499     getLayout : function(){
51500         return this.layout;
51501     },
51502     
51503      /**
51504      * Adds a xtype elements to the layout of the nested panel
51505      * <pre><code>
51506
51507 panel.addxtype({
51508        xtype : 'ContentPanel',
51509        region: 'west',
51510        items: [ .... ]
51511    }
51512 );
51513
51514 panel.addxtype({
51515         xtype : 'NestedLayoutPanel',
51516         region: 'west',
51517         layout: {
51518            center: { },
51519            west: { }   
51520         },
51521         items : [ ... list of content panels or nested layout panels.. ]
51522    }
51523 );
51524 </code></pre>
51525      * @param {Object} cfg Xtype definition of item to add.
51526      */
51527     addxtype : function(cfg) {
51528         return this.layout.addxtype(cfg);
51529     
51530     }
51531 });
51532
51533 Roo.ScrollPanel = function(el, config, content){
51534     config = config || {};
51535     config.fitToFrame = true;
51536     Roo.ScrollPanel.superclass.constructor.call(this, el, config, content);
51537     
51538     this.el.dom.style.overflow = "hidden";
51539     var wrap = this.el.wrap({cls: "x-scroller x-layout-inactive-content"});
51540     this.el.removeClass("x-layout-inactive-content");
51541     this.el.on("mousewheel", this.onWheel, this);
51542
51543     var up = wrap.createChild({cls: "x-scroller-up", html: "&#160;"}, this.el.dom);
51544     var down = wrap.createChild({cls: "x-scroller-down", html: "&#160;"});
51545     up.unselectable(); down.unselectable();
51546     up.on("click", this.scrollUp, this);
51547     down.on("click", this.scrollDown, this);
51548     up.addClassOnOver("x-scroller-btn-over");
51549     down.addClassOnOver("x-scroller-btn-over");
51550     up.addClassOnClick("x-scroller-btn-click");
51551     down.addClassOnClick("x-scroller-btn-click");
51552     this.adjustments = [0, -(up.getHeight() + down.getHeight())];
51553
51554     this.resizeEl = this.el;
51555     this.el = wrap; this.up = up; this.down = down;
51556 };
51557
51558 Roo.extend(Roo.ScrollPanel, Roo.ContentPanel, {
51559     increment : 100,
51560     wheelIncrement : 5,
51561     scrollUp : function(){
51562         this.resizeEl.scroll("up", this.increment, {callback: this.afterScroll, scope: this});
51563     },
51564
51565     scrollDown : function(){
51566         this.resizeEl.scroll("down", this.increment, {callback: this.afterScroll, scope: this});
51567     },
51568
51569     afterScroll : function(){
51570         var el = this.resizeEl;
51571         var t = el.dom.scrollTop, h = el.dom.scrollHeight, ch = el.dom.clientHeight;
51572         this.up[t == 0 ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
51573         this.down[h - t <= ch ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
51574     },
51575
51576     setSize : function(){
51577         Roo.ScrollPanel.superclass.setSize.apply(this, arguments);
51578         this.afterScroll();
51579     },
51580
51581     onWheel : function(e){
51582         var d = e.getWheelDelta();
51583         this.resizeEl.dom.scrollTop -= (d*this.wheelIncrement);
51584         this.afterScroll();
51585         e.stopEvent();
51586     },
51587
51588     setContent : function(content, loadScripts){
51589         this.resizeEl.update(content, loadScripts);
51590     }
51591
51592 });
51593
51594
51595
51596
51597
51598
51599
51600
51601
51602 /**
51603  * @class Roo.TreePanel
51604  * @extends Roo.ContentPanel
51605  * @constructor
51606  * Create a new TreePanel. - defaults to fit/scoll contents.
51607  * @param {String/Object} config A string to set only the panel's title, or a config object
51608  * @cfg {Roo.tree.TreePanel} tree The tree TreePanel, with config etc.
51609  */
51610 Roo.TreePanel = function(config){
51611     var el = config.el;
51612     var tree = config.tree;
51613     delete config.tree; 
51614     delete config.el; // hopefull!
51615     
51616     // wrapper for IE7 strict & safari scroll issue
51617     
51618     var treeEl = el.createChild();
51619     config.resizeEl = treeEl;
51620     
51621     
51622     
51623     Roo.TreePanel.superclass.constructor.call(this, el, config);
51624  
51625  
51626     this.tree = new Roo.tree.TreePanel(treeEl , tree);
51627     //console.log(tree);
51628     this.on('activate', function()
51629     {
51630         if (this.tree.rendered) {
51631             return;
51632         }
51633         //console.log('render tree');
51634         this.tree.render();
51635     });
51636     // this should not be needed.. - it's actually the 'el' that resizes?
51637     // actuall it breaks the containerScroll - dragging nodes auto scroll at top
51638     
51639     //this.on('resize',  function (cp, w, h) {
51640     //        this.tree.innerCt.setWidth(w);
51641     //        this.tree.innerCt.setHeight(h);
51642     //        //this.tree.innerCt.setStyle('overflow-y', 'auto');
51643     //});
51644
51645         
51646     
51647 };
51648
51649 Roo.extend(Roo.TreePanel, Roo.ContentPanel, {   
51650     fitToFrame : true,
51651     autoScroll : true
51652 });
51653
51654
51655
51656
51657
51658
51659
51660
51661
51662
51663
51664 /*
51665  * Based on:
51666  * Ext JS Library 1.1.1
51667  * Copyright(c) 2006-2007, Ext JS, LLC.
51668  *
51669  * Originally Released Under LGPL - original licence link has changed is not relivant.
51670  *
51671  * Fork - LGPL
51672  * <script type="text/javascript">
51673  */
51674  
51675
51676 /**
51677  * @class Roo.ReaderLayout
51678  * @extends Roo.BorderLayout
51679  * This is a pre-built layout that represents a classic, 5-pane application.  It consists of a header, a primary
51680  * center region containing two nested regions (a top one for a list view and one for item preview below),
51681  * and regions on either side that can be used for navigation, application commands, informational displays, etc.
51682  * The setup and configuration work exactly the same as it does for a {@link Roo.BorderLayout} - this class simply
51683  * expedites the setup of the overall layout and regions for this common application style.
51684  * Example:
51685  <pre><code>
51686 var reader = new Roo.ReaderLayout();
51687 var CP = Roo.ContentPanel;  // shortcut for adding
51688
51689 reader.beginUpdate();
51690 reader.add("north", new CP("north", "North"));
51691 reader.add("west", new CP("west", {title: "West"}));
51692 reader.add("east", new CP("east", {title: "East"}));
51693
51694 reader.regions.listView.add(new CP("listView", "List"));
51695 reader.regions.preview.add(new CP("preview", "Preview"));
51696 reader.endUpdate();
51697 </code></pre>
51698 * @constructor
51699 * Create a new ReaderLayout
51700 * @param {Object} config Configuration options
51701 * @param {String/HTMLElement/Element} container (optional) The container this layout is bound to (defaults to
51702 * document.body if omitted)
51703 */
51704 Roo.ReaderLayout = function(config, renderTo){
51705     var c = config || {size:{}};
51706     Roo.ReaderLayout.superclass.constructor.call(this, renderTo || document.body, {
51707         north: c.north !== false ? Roo.apply({
51708             split:false,
51709             initialSize: 32,
51710             titlebar: false
51711         }, c.north) : false,
51712         west: c.west !== false ? Roo.apply({
51713             split:true,
51714             initialSize: 200,
51715             minSize: 175,
51716             maxSize: 400,
51717             titlebar: true,
51718             collapsible: true,
51719             animate: true,
51720             margins:{left:5,right:0,bottom:5,top:5},
51721             cmargins:{left:5,right:5,bottom:5,top:5}
51722         }, c.west) : false,
51723         east: c.east !== false ? Roo.apply({
51724             split:true,
51725             initialSize: 200,
51726             minSize: 175,
51727             maxSize: 400,
51728             titlebar: true,
51729             collapsible: true,
51730             animate: true,
51731             margins:{left:0,right:5,bottom:5,top:5},
51732             cmargins:{left:5,right:5,bottom:5,top:5}
51733         }, c.east) : false,
51734         center: Roo.apply({
51735             tabPosition: 'top',
51736             autoScroll:false,
51737             closeOnTab: true,
51738             titlebar:false,
51739             margins:{left:c.west!==false ? 0 : 5,right:c.east!==false ? 0 : 5,bottom:5,top:2}
51740         }, c.center)
51741     });
51742
51743     this.el.addClass('x-reader');
51744
51745     this.beginUpdate();
51746
51747     var inner = new Roo.BorderLayout(Roo.get(document.body).createChild(), {
51748         south: c.preview !== false ? Roo.apply({
51749             split:true,
51750             initialSize: 200,
51751             minSize: 100,
51752             autoScroll:true,
51753             collapsible:true,
51754             titlebar: true,
51755             cmargins:{top:5,left:0, right:0, bottom:0}
51756         }, c.preview) : false,
51757         center: Roo.apply({
51758             autoScroll:false,
51759             titlebar:false,
51760             minHeight:200
51761         }, c.listView)
51762     });
51763     this.add('center', new Roo.NestedLayoutPanel(inner,
51764             Roo.apply({title: c.mainTitle || '',tabTip:''},c.innerPanelCfg)));
51765
51766     this.endUpdate();
51767
51768     this.regions.preview = inner.getRegion('south');
51769     this.regions.listView = inner.getRegion('center');
51770 };
51771
51772 Roo.extend(Roo.ReaderLayout, Roo.BorderLayout);/*
51773  * Based on:
51774  * Ext JS Library 1.1.1
51775  * Copyright(c) 2006-2007, Ext JS, LLC.
51776  *
51777  * Originally Released Under LGPL - original licence link has changed is not relivant.
51778  *
51779  * Fork - LGPL
51780  * <script type="text/javascript">
51781  */
51782  
51783 /**
51784  * @class Roo.grid.Grid
51785  * @extends Roo.util.Observable
51786  * This class represents the primary interface of a component based grid control.
51787  * <br><br>Usage:<pre><code>
51788  var grid = new Roo.grid.Grid("my-container-id", {
51789      ds: myDataStore,
51790      cm: myColModel,
51791      selModel: mySelectionModel,
51792      autoSizeColumns: true,
51793      monitorWindowResize: false,
51794      trackMouseOver: true
51795  });
51796  // set any options
51797  grid.render();
51798  * </code></pre>
51799  * <b>Common Problems:</b><br/>
51800  * - Grid does not resize properly when going smaller: Setting overflow hidden on the container
51801  * element will correct this<br/>
51802  * - If you get el.style[camel]= NaNpx or -2px or something related, be certain you have given your container element
51803  * dimensions. The grid adapts to your container's size, if your container has no size defined then the results
51804  * are unpredictable.<br/>
51805  * - Do not render the grid into an element with display:none. Try using visibility:hidden. Otherwise there is no way for the
51806  * grid to calculate dimensions/offsets.<br/>
51807   * @constructor
51808  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
51809  * The container MUST have some type of size defined for the grid to fill. The container will be
51810  * automatically set to position relative if it isn't already.
51811  * @param {Object} config A config object that sets properties on this grid.
51812  */
51813 Roo.grid.Grid = function(container, config){
51814         // initialize the container
51815         this.container = Roo.get(container);
51816         this.container.update("");
51817         this.container.setStyle("overflow", "hidden");
51818     this.container.addClass('x-grid-container');
51819
51820     this.id = this.container.id;
51821
51822     Roo.apply(this, config);
51823     // check and correct shorthanded configs
51824     if(this.ds){
51825         this.dataSource = this.ds;
51826         delete this.ds;
51827     }
51828     if(this.cm){
51829         this.colModel = this.cm;
51830         delete this.cm;
51831     }
51832     if(this.sm){
51833         this.selModel = this.sm;
51834         delete this.sm;
51835     }
51836
51837     if (this.selModel) {
51838         this.selModel = Roo.factory(this.selModel, Roo.grid);
51839         this.sm = this.selModel;
51840         this.sm.xmodule = this.xmodule || false;
51841     }
51842     if (typeof(this.colModel.config) == 'undefined') {
51843         this.colModel = new Roo.grid.ColumnModel(this.colModel);
51844         this.cm = this.colModel;
51845         this.cm.xmodule = this.xmodule || false;
51846     }
51847     if (this.dataSource) {
51848         this.dataSource= Roo.factory(this.dataSource, Roo.data);
51849         this.ds = this.dataSource;
51850         this.ds.xmodule = this.xmodule || false;
51851          
51852     }
51853     
51854     
51855     
51856     if(this.width){
51857         this.container.setWidth(this.width);
51858     }
51859
51860     if(this.height){
51861         this.container.setHeight(this.height);
51862     }
51863     /** @private */
51864         this.addEvents({
51865         // raw events
51866         /**
51867          * @event click
51868          * The raw click event for the entire grid.
51869          * @param {Roo.EventObject} e
51870          */
51871         "click" : true,
51872         /**
51873          * @event dblclick
51874          * The raw dblclick event for the entire grid.
51875          * @param {Roo.EventObject} e
51876          */
51877         "dblclick" : true,
51878         /**
51879          * @event contextmenu
51880          * The raw contextmenu event for the entire grid.
51881          * @param {Roo.EventObject} e
51882          */
51883         "contextmenu" : true,
51884         /**
51885          * @event mousedown
51886          * The raw mousedown event for the entire grid.
51887          * @param {Roo.EventObject} e
51888          */
51889         "mousedown" : true,
51890         /**
51891          * @event mouseup
51892          * The raw mouseup event for the entire grid.
51893          * @param {Roo.EventObject} e
51894          */
51895         "mouseup" : true,
51896         /**
51897          * @event mouseover
51898          * The raw mouseover event for the entire grid.
51899          * @param {Roo.EventObject} e
51900          */
51901         "mouseover" : true,
51902         /**
51903          * @event mouseout
51904          * The raw mouseout event for the entire grid.
51905          * @param {Roo.EventObject} e
51906          */
51907         "mouseout" : true,
51908         /**
51909          * @event keypress
51910          * The raw keypress event for the entire grid.
51911          * @param {Roo.EventObject} e
51912          */
51913         "keypress" : true,
51914         /**
51915          * @event keydown
51916          * The raw keydown event for the entire grid.
51917          * @param {Roo.EventObject} e
51918          */
51919         "keydown" : true,
51920
51921         // custom events
51922
51923         /**
51924          * @event cellclick
51925          * Fires when a cell is clicked
51926          * @param {Grid} this
51927          * @param {Number} rowIndex
51928          * @param {Number} columnIndex
51929          * @param {Roo.EventObject} e
51930          */
51931         "cellclick" : true,
51932         /**
51933          * @event celldblclick
51934          * Fires when a cell is double clicked
51935          * @param {Grid} this
51936          * @param {Number} rowIndex
51937          * @param {Number} columnIndex
51938          * @param {Roo.EventObject} e
51939          */
51940         "celldblclick" : true,
51941         /**
51942          * @event rowclick
51943          * Fires when a row is clicked
51944          * @param {Grid} this
51945          * @param {Number} rowIndex
51946          * @param {Roo.EventObject} e
51947          */
51948         "rowclick" : true,
51949         /**
51950          * @event rowdblclick
51951          * Fires when a row is double clicked
51952          * @param {Grid} this
51953          * @param {Number} rowIndex
51954          * @param {Roo.EventObject} e
51955          */
51956         "rowdblclick" : true,
51957         /**
51958          * @event headerclick
51959          * Fires when a header is clicked
51960          * @param {Grid} this
51961          * @param {Number} columnIndex
51962          * @param {Roo.EventObject} e
51963          */
51964         "headerclick" : true,
51965         /**
51966          * @event headerdblclick
51967          * Fires when a header cell is double clicked
51968          * @param {Grid} this
51969          * @param {Number} columnIndex
51970          * @param {Roo.EventObject} e
51971          */
51972         "headerdblclick" : true,
51973         /**
51974          * @event rowcontextmenu
51975          * Fires when a row is right clicked
51976          * @param {Grid} this
51977          * @param {Number} rowIndex
51978          * @param {Roo.EventObject} e
51979          */
51980         "rowcontextmenu" : true,
51981         /**
51982          * @event cellcontextmenu
51983          * Fires when a cell is right clicked
51984          * @param {Grid} this
51985          * @param {Number} rowIndex
51986          * @param {Number} cellIndex
51987          * @param {Roo.EventObject} e
51988          */
51989          "cellcontextmenu" : true,
51990         /**
51991          * @event headercontextmenu
51992          * Fires when a header is right clicked
51993          * @param {Grid} this
51994          * @param {Number} columnIndex
51995          * @param {Roo.EventObject} e
51996          */
51997         "headercontextmenu" : true,
51998         /**
51999          * @event bodyscroll
52000          * Fires when the body element is scrolled
52001          * @param {Number} scrollLeft
52002          * @param {Number} scrollTop
52003          */
52004         "bodyscroll" : true,
52005         /**
52006          * @event columnresize
52007          * Fires when the user resizes a column
52008          * @param {Number} columnIndex
52009          * @param {Number} newSize
52010          */
52011         "columnresize" : true,
52012         /**
52013          * @event columnmove
52014          * Fires when the user moves a column
52015          * @param {Number} oldIndex
52016          * @param {Number} newIndex
52017          */
52018         "columnmove" : true,
52019         /**
52020          * @event startdrag
52021          * Fires when row(s) start being dragged
52022          * @param {Grid} this
52023          * @param {Roo.GridDD} dd The drag drop object
52024          * @param {event} e The raw browser event
52025          */
52026         "startdrag" : true,
52027         /**
52028          * @event enddrag
52029          * Fires when a drag operation is complete
52030          * @param {Grid} this
52031          * @param {Roo.GridDD} dd The drag drop object
52032          * @param {event} e The raw browser event
52033          */
52034         "enddrag" : true,
52035         /**
52036          * @event dragdrop
52037          * Fires when dragged row(s) are dropped on a valid DD target
52038          * @param {Grid} this
52039          * @param {Roo.GridDD} dd The drag drop object
52040          * @param {String} targetId The target drag drop object
52041          * @param {event} e The raw browser event
52042          */
52043         "dragdrop" : true,
52044         /**
52045          * @event dragover
52046          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
52047          * @param {Grid} this
52048          * @param {Roo.GridDD} dd The drag drop object
52049          * @param {String} targetId The target drag drop object
52050          * @param {event} e The raw browser event
52051          */
52052         "dragover" : true,
52053         /**
52054          * @event dragenter
52055          *  Fires when the dragged row(s) first cross another DD target while being dragged
52056          * @param {Grid} this
52057          * @param {Roo.GridDD} dd The drag drop object
52058          * @param {String} targetId The target drag drop object
52059          * @param {event} e The raw browser event
52060          */
52061         "dragenter" : true,
52062         /**
52063          * @event dragout
52064          * Fires when the dragged row(s) leave another DD target while being dragged
52065          * @param {Grid} this
52066          * @param {Roo.GridDD} dd The drag drop object
52067          * @param {String} targetId The target drag drop object
52068          * @param {event} e The raw browser event
52069          */
52070         "dragout" : true,
52071         /**
52072          * @event rowclass
52073          * Fires when a row is rendered, so you can change add a style to it.
52074          * @param {GridView} gridview   The grid view
52075          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
52076          */
52077         'rowclass' : true,
52078
52079         /**
52080          * @event render
52081          * Fires when the grid is rendered
52082          * @param {Grid} grid
52083          */
52084         'render' : true
52085     });
52086
52087     Roo.grid.Grid.superclass.constructor.call(this);
52088 };
52089 Roo.extend(Roo.grid.Grid, Roo.util.Observable, {
52090     
52091     /**
52092      * @cfg {String} ddGroup - drag drop group.
52093      */
52094
52095     /**
52096      * @cfg {Number} minColumnWidth The minimum width a column can be resized to. Default is 25.
52097      */
52098     minColumnWidth : 25,
52099
52100     /**
52101      * @cfg {Boolean} autoSizeColumns True to automatically resize the columns to fit their content
52102      * <b>on initial render.</b> It is more efficient to explicitly size the columns
52103      * through the ColumnModel's {@link Roo.grid.ColumnModel#width} config option.  Default is false.
52104      */
52105     autoSizeColumns : false,
52106
52107     /**
52108      * @cfg {Boolean} autoSizeHeaders True to measure headers with column data when auto sizing columns. Default is true.
52109      */
52110     autoSizeHeaders : true,
52111
52112     /**
52113      * @cfg {Boolean} monitorWindowResize True to autoSize the grid when the window resizes. Default is true.
52114      */
52115     monitorWindowResize : true,
52116
52117     /**
52118      * @cfg {Boolean} maxRowsToMeasure If autoSizeColumns is on, maxRowsToMeasure can be used to limit the number of
52119      * rows measured to get a columns size. Default is 0 (all rows).
52120      */
52121     maxRowsToMeasure : 0,
52122
52123     /**
52124      * @cfg {Boolean} trackMouseOver True to highlight rows when the mouse is over. Default is true.
52125      */
52126     trackMouseOver : true,
52127
52128     /**
52129     * @cfg {Boolean} enableDrag  True to enable drag of rows. Default is false. (double check if this is needed?)
52130     */
52131     
52132     /**
52133     * @cfg {Boolean} enableDragDrop True to enable drag and drop of rows. Default is false.
52134     */
52135     enableDragDrop : false,
52136     
52137     /**
52138     * @cfg {Boolean} enableColumnMove True to enable drag and drop reorder of columns. Default is true.
52139     */
52140     enableColumnMove : true,
52141     
52142     /**
52143     * @cfg {Boolean} enableColumnHide True to enable hiding of columns with the header context menu. Default is true.
52144     */
52145     enableColumnHide : true,
52146     
52147     /**
52148     * @cfg {Boolean} enableRowHeightSync True to manually sync row heights across locked and not locked rows. Default is false.
52149     */
52150     enableRowHeightSync : false,
52151     
52152     /**
52153     * @cfg {Boolean} stripeRows True to stripe the rows.  Default is true.
52154     */
52155     stripeRows : true,
52156     
52157     /**
52158     * @cfg {Boolean} autoHeight True to fit the height of the grid container to the height of the data. Default is false.
52159     */
52160     autoHeight : false,
52161
52162     /**
52163      * @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.
52164      */
52165     autoExpandColumn : false,
52166
52167     /**
52168     * @cfg {Number} autoExpandMin The minimum width the autoExpandColumn can have (if enabled).
52169     * Default is 50.
52170     */
52171     autoExpandMin : 50,
52172
52173     /**
52174     * @cfg {Number} autoExpandMax The maximum width the autoExpandColumn can have (if enabled). Default is 1000.
52175     */
52176     autoExpandMax : 1000,
52177
52178     /**
52179     * @cfg {Object} view The {@link Roo.grid.GridView} used by the grid. This can be set before a call to render().
52180     */
52181     view : null,
52182
52183     /**
52184     * @cfg {Object} loadMask An {@link Roo.LoadMask} config or true to mask the grid while loading. Default is false.
52185     */
52186     loadMask : false,
52187     /**
52188     * @cfg {Roo.dd.DropTarget} dropTarget An {@link Roo.dd.DropTarget} config
52189     */
52190     dropTarget: false,
52191     
52192    
52193     
52194     // private
52195     rendered : false,
52196
52197     /**
52198     * @cfg {Boolean} autoWidth True to set the grid's width to the default total width of the grid's columns instead
52199     * of a fixed width. Default is false.
52200     */
52201     /**
52202     * @cfg {Number} maxHeight Sets the maximum height of the grid - ignored if autoHeight is not on.
52203     */
52204     /**
52205      * Called once after all setup has been completed and the grid is ready to be rendered.
52206      * @return {Roo.grid.Grid} this
52207      */
52208     render : function()
52209     {
52210         var c = this.container;
52211         // try to detect autoHeight/width mode
52212         if((!c.dom.offsetHeight || c.dom.offsetHeight < 20) || c.getStyle("height") == "auto"){
52213             this.autoHeight = true;
52214         }
52215         var view = this.getView();
52216         view.init(this);
52217
52218         c.on("click", this.onClick, this);
52219         c.on("dblclick", this.onDblClick, this);
52220         c.on("contextmenu", this.onContextMenu, this);
52221         c.on("keydown", this.onKeyDown, this);
52222         if (Roo.isTouch) {
52223             c.on("touchstart", this.onTouchStart, this);
52224         }
52225
52226         this.relayEvents(c, ["mousedown","mouseup","mouseover","mouseout","keypress"]);
52227
52228         this.getSelectionModel().init(this);
52229
52230         view.render();
52231
52232         if(this.loadMask){
52233             this.loadMask = new Roo.LoadMask(this.container,
52234                     Roo.apply({store:this.dataSource}, this.loadMask));
52235         }
52236         
52237         
52238         if (this.toolbar && this.toolbar.xtype) {
52239             this.toolbar.container = this.getView().getHeaderPanel(true);
52240             this.toolbar = new Roo.Toolbar(this.toolbar);
52241         }
52242         if (this.footer && this.footer.xtype) {
52243             this.footer.dataSource = this.getDataSource();
52244             this.footer.container = this.getView().getFooterPanel(true);
52245             this.footer = Roo.factory(this.footer, Roo);
52246         }
52247         if (this.dropTarget && this.dropTarget.xtype) {
52248             delete this.dropTarget.xtype;
52249             this.dropTarget =  new Roo.dd.DropTarget(this.getView().mainBody, this.dropTarget);
52250         }
52251         
52252         
52253         this.rendered = true;
52254         this.fireEvent('render', this);
52255         return this;
52256     },
52257
52258         /**
52259          * Reconfigures the grid to use a different Store and Column Model.
52260          * The View will be bound to the new objects and refreshed.
52261          * @param {Roo.data.Store} dataSource The new {@link Roo.data.Store} object
52262          * @param {Roo.grid.ColumnModel} The new {@link Roo.grid.ColumnModel} object
52263          */
52264     reconfigure : function(dataSource, colModel){
52265         if(this.loadMask){
52266             this.loadMask.destroy();
52267             this.loadMask = new Roo.LoadMask(this.container,
52268                     Roo.apply({store:dataSource}, this.loadMask));
52269         }
52270         this.view.bind(dataSource, colModel);
52271         this.dataSource = dataSource;
52272         this.colModel = colModel;
52273         this.view.refresh(true);
52274     },
52275
52276     // private
52277     onKeyDown : function(e){
52278         this.fireEvent("keydown", e);
52279     },
52280
52281     /**
52282      * Destroy this grid.
52283      * @param {Boolean} removeEl True to remove the element
52284      */
52285     destroy : function(removeEl, keepListeners){
52286         if(this.loadMask){
52287             this.loadMask.destroy();
52288         }
52289         var c = this.container;
52290         c.removeAllListeners();
52291         this.view.destroy();
52292         this.colModel.purgeListeners();
52293         if(!keepListeners){
52294             this.purgeListeners();
52295         }
52296         c.update("");
52297         if(removeEl === true){
52298             c.remove();
52299         }
52300     },
52301
52302     // private
52303     processEvent : function(name, e){
52304         // does this fire select???
52305         Roo.log('grid:processEvent '  + name);
52306         
52307         if (name != 'touchstart' ) {
52308             this.fireEvent(name, e);    
52309         }
52310         
52311         var t = e.getTarget();
52312         var v = this.view;
52313         var header = v.findHeaderIndex(t);
52314         if(header !== false){
52315             var ename = name == 'touchstart' ? 'click' : name;
52316              
52317             this.fireEvent("header" + ename, this, header, e);
52318         }else{
52319             var row = v.findRowIndex(t);
52320             var cell = v.findCellIndex(t);
52321             if (name == 'touchstart') {
52322                 // first touch is always a click.
52323                 // hopefull this happens after selection is updated.?
52324                 name = false;
52325                 
52326                 if (typeof(this.selModel.getSelectedCell) != 'undefined') {
52327                     var cs = this.selModel.getSelectedCell();
52328                     if (row == cs[0] && cell == cs[1]){
52329                         name = 'dblclick';
52330                     }
52331                 }
52332                 if (typeof(this.selModel.getSelections) != 'undefined') {
52333                     var cs = this.selModel.getSelections();
52334                     var ds = this.dataSource;
52335                     if (cs.length == 1 && ds.getAt(row) == cs[0]){
52336                         name = 'dblclick';
52337                     }
52338                 }
52339                 if (!name) {
52340                     return;
52341                 }
52342             }
52343             
52344             
52345             if(row !== false){
52346                 this.fireEvent("row" + name, this, row, e);
52347                 if(cell !== false){
52348                     this.fireEvent("cell" + name, this, row, cell, e);
52349                 }
52350             }
52351         }
52352     },
52353
52354     // private
52355     onClick : function(e){
52356         this.processEvent("click", e);
52357     },
52358    // private
52359     onTouchStart : function(e){
52360         this.processEvent("touchstart", e);
52361     },
52362
52363     // private
52364     onContextMenu : function(e, t){
52365         this.processEvent("contextmenu", e);
52366     },
52367
52368     // private
52369     onDblClick : function(e){
52370         this.processEvent("dblclick", e);
52371     },
52372
52373     // private
52374     walkCells : function(row, col, step, fn, scope){
52375         var cm = this.colModel, clen = cm.getColumnCount();
52376         var ds = this.dataSource, rlen = ds.getCount(), first = true;
52377         if(step < 0){
52378             if(col < 0){
52379                 row--;
52380                 first = false;
52381             }
52382             while(row >= 0){
52383                 if(!first){
52384                     col = clen-1;
52385                 }
52386                 first = false;
52387                 while(col >= 0){
52388                     if(fn.call(scope || this, row, col, cm) === true){
52389                         return [row, col];
52390                     }
52391                     col--;
52392                 }
52393                 row--;
52394             }
52395         } else {
52396             if(col >= clen){
52397                 row++;
52398                 first = false;
52399             }
52400             while(row < rlen){
52401                 if(!first){
52402                     col = 0;
52403                 }
52404                 first = false;
52405                 while(col < clen){
52406                     if(fn.call(scope || this, row, col, cm) === true){
52407                         return [row, col];
52408                     }
52409                     col++;
52410                 }
52411                 row++;
52412             }
52413         }
52414         return null;
52415     },
52416
52417     // private
52418     getSelections : function(){
52419         return this.selModel.getSelections();
52420     },
52421
52422     /**
52423      * Causes the grid to manually recalculate its dimensions. Generally this is done automatically,
52424      * but if manual update is required this method will initiate it.
52425      */
52426     autoSize : function(){
52427         if(this.rendered){
52428             this.view.layout();
52429             if(this.view.adjustForScroll){
52430                 this.view.adjustForScroll();
52431             }
52432         }
52433     },
52434
52435     /**
52436      * Returns the grid's underlying element.
52437      * @return {Element} The element
52438      */
52439     getGridEl : function(){
52440         return this.container;
52441     },
52442
52443     // private for compatibility, overridden by editor grid
52444     stopEditing : function(){},
52445
52446     /**
52447      * Returns the grid's SelectionModel.
52448      * @return {SelectionModel}
52449      */
52450     getSelectionModel : function(){
52451         if(!this.selModel){
52452             this.selModel = new Roo.grid.RowSelectionModel();
52453         }
52454         return this.selModel;
52455     },
52456
52457     /**
52458      * Returns the grid's DataSource.
52459      * @return {DataSource}
52460      */
52461     getDataSource : function(){
52462         return this.dataSource;
52463     },
52464
52465     /**
52466      * Returns the grid's ColumnModel.
52467      * @return {ColumnModel}
52468      */
52469     getColumnModel : function(){
52470         return this.colModel;
52471     },
52472
52473     /**
52474      * Returns the grid's GridView object.
52475      * @return {GridView}
52476      */
52477     getView : function(){
52478         if(!this.view){
52479             this.view = new Roo.grid.GridView(this.viewConfig);
52480         }
52481         return this.view;
52482     },
52483     /**
52484      * Called to get grid's drag proxy text, by default returns this.ddText.
52485      * @return {String}
52486      */
52487     getDragDropText : function(){
52488         var count = this.selModel.getCount();
52489         return String.format(this.ddText, count, count == 1 ? '' : 's');
52490     }
52491 });
52492 /**
52493  * Configures the text is the drag proxy (defaults to "%0 selected row(s)").
52494  * %0 is replaced with the number of selected rows.
52495  * @type String
52496  */
52497 Roo.grid.Grid.prototype.ddText = "{0} selected row{1}";/*
52498  * Based on:
52499  * Ext JS Library 1.1.1
52500  * Copyright(c) 2006-2007, Ext JS, LLC.
52501  *
52502  * Originally Released Under LGPL - original licence link has changed is not relivant.
52503  *
52504  * Fork - LGPL
52505  * <script type="text/javascript">
52506  */
52507  
52508 Roo.grid.AbstractGridView = function(){
52509         this.grid = null;
52510         
52511         this.events = {
52512             "beforerowremoved" : true,
52513             "beforerowsinserted" : true,
52514             "beforerefresh" : true,
52515             "rowremoved" : true,
52516             "rowsinserted" : true,
52517             "rowupdated" : true,
52518             "refresh" : true
52519         };
52520     Roo.grid.AbstractGridView.superclass.constructor.call(this);
52521 };
52522
52523 Roo.extend(Roo.grid.AbstractGridView, Roo.util.Observable, {
52524     rowClass : "x-grid-row",
52525     cellClass : "x-grid-cell",
52526     tdClass : "x-grid-td",
52527     hdClass : "x-grid-hd",
52528     splitClass : "x-grid-hd-split",
52529     
52530     init: function(grid){
52531         this.grid = grid;
52532                 var cid = this.grid.getGridEl().id;
52533         this.colSelector = "#" + cid + " ." + this.cellClass + "-";
52534         this.tdSelector = "#" + cid + " ." + this.tdClass + "-";
52535         this.hdSelector = "#" + cid + " ." + this.hdClass + "-";
52536         this.splitSelector = "#" + cid + " ." + this.splitClass + "-";
52537         },
52538         
52539     getColumnRenderers : function(){
52540         var renderers = [];
52541         var cm = this.grid.colModel;
52542         var colCount = cm.getColumnCount();
52543         for(var i = 0; i < colCount; i++){
52544             renderers[i] = cm.getRenderer(i);
52545         }
52546         return renderers;
52547     },
52548     
52549     getColumnIds : function(){
52550         var ids = [];
52551         var cm = this.grid.colModel;
52552         var colCount = cm.getColumnCount();
52553         for(var i = 0; i < colCount; i++){
52554             ids[i] = cm.getColumnId(i);
52555         }
52556         return ids;
52557     },
52558     
52559     getDataIndexes : function(){
52560         if(!this.indexMap){
52561             this.indexMap = this.buildIndexMap();
52562         }
52563         return this.indexMap.colToData;
52564     },
52565     
52566     getColumnIndexByDataIndex : function(dataIndex){
52567         if(!this.indexMap){
52568             this.indexMap = this.buildIndexMap();
52569         }
52570         return this.indexMap.dataToCol[dataIndex];
52571     },
52572     
52573     /**
52574      * Set a css style for a column dynamically. 
52575      * @param {Number} colIndex The index of the column
52576      * @param {String} name The css property name
52577      * @param {String} value The css value
52578      */
52579     setCSSStyle : function(colIndex, name, value){
52580         var selector = "#" + this.grid.id + " .x-grid-col-" + colIndex;
52581         Roo.util.CSS.updateRule(selector, name, value);
52582     },
52583     
52584     generateRules : function(cm){
52585         var ruleBuf = [], rulesId = this.grid.id + '-cssrules';
52586         Roo.util.CSS.removeStyleSheet(rulesId);
52587         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
52588             var cid = cm.getColumnId(i);
52589             ruleBuf.push(this.colSelector, cid, " {\n", cm.config[i].css, "}\n",
52590                          this.tdSelector, cid, " {\n}\n",
52591                          this.hdSelector, cid, " {\n}\n",
52592                          this.splitSelector, cid, " {\n}\n");
52593         }
52594         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
52595     }
52596 });/*
52597  * Based on:
52598  * Ext JS Library 1.1.1
52599  * Copyright(c) 2006-2007, Ext JS, LLC.
52600  *
52601  * Originally Released Under LGPL - original licence link has changed is not relivant.
52602  *
52603  * Fork - LGPL
52604  * <script type="text/javascript">
52605  */
52606
52607 // private
52608 // This is a support class used internally by the Grid components
52609 Roo.grid.HeaderDragZone = function(grid, hd, hd2){
52610     this.grid = grid;
52611     this.view = grid.getView();
52612     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
52613     Roo.grid.HeaderDragZone.superclass.constructor.call(this, hd);
52614     if(hd2){
52615         this.setHandleElId(Roo.id(hd));
52616         this.setOuterHandleElId(Roo.id(hd2));
52617     }
52618     this.scroll = false;
52619 };
52620 Roo.extend(Roo.grid.HeaderDragZone, Roo.dd.DragZone, {
52621     maxDragWidth: 120,
52622     getDragData : function(e){
52623         var t = Roo.lib.Event.getTarget(e);
52624         var h = this.view.findHeaderCell(t);
52625         if(h){
52626             return {ddel: h.firstChild, header:h};
52627         }
52628         return false;
52629     },
52630
52631     onInitDrag : function(e){
52632         this.view.headersDisabled = true;
52633         var clone = this.dragData.ddel.cloneNode(true);
52634         clone.id = Roo.id();
52635         clone.style.width = Math.min(this.dragData.header.offsetWidth,this.maxDragWidth) + "px";
52636         this.proxy.update(clone);
52637         return true;
52638     },
52639
52640     afterValidDrop : function(){
52641         var v = this.view;
52642         setTimeout(function(){
52643             v.headersDisabled = false;
52644         }, 50);
52645     },
52646
52647     afterInvalidDrop : function(){
52648         var v = this.view;
52649         setTimeout(function(){
52650             v.headersDisabled = false;
52651         }, 50);
52652     }
52653 });
52654 /*
52655  * Based on:
52656  * Ext JS Library 1.1.1
52657  * Copyright(c) 2006-2007, Ext JS, LLC.
52658  *
52659  * Originally Released Under LGPL - original licence link has changed is not relivant.
52660  *
52661  * Fork - LGPL
52662  * <script type="text/javascript">
52663  */
52664 // private
52665 // This is a support class used internally by the Grid components
52666 Roo.grid.HeaderDropZone = function(grid, hd, hd2){
52667     this.grid = grid;
52668     this.view = grid.getView();
52669     // split the proxies so they don't interfere with mouse events
52670     this.proxyTop = Roo.DomHelper.append(document.body, {
52671         cls:"col-move-top", html:"&#160;"
52672     }, true);
52673     this.proxyBottom = Roo.DomHelper.append(document.body, {
52674         cls:"col-move-bottom", html:"&#160;"
52675     }, true);
52676     this.proxyTop.hide = this.proxyBottom.hide = function(){
52677         this.setLeftTop(-100,-100);
52678         this.setStyle("visibility", "hidden");
52679     };
52680     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
52681     // temporarily disabled
52682     //Roo.dd.ScrollManager.register(this.view.scroller.dom);
52683     Roo.grid.HeaderDropZone.superclass.constructor.call(this, grid.getGridEl().dom);
52684 };
52685 Roo.extend(Roo.grid.HeaderDropZone, Roo.dd.DropZone, {
52686     proxyOffsets : [-4, -9],
52687     fly: Roo.Element.fly,
52688
52689     getTargetFromEvent : function(e){
52690         var t = Roo.lib.Event.getTarget(e);
52691         var cindex = this.view.findCellIndex(t);
52692         if(cindex !== false){
52693             return this.view.getHeaderCell(cindex);
52694         }
52695         return null;
52696     },
52697
52698     nextVisible : function(h){
52699         var v = this.view, cm = this.grid.colModel;
52700         h = h.nextSibling;
52701         while(h){
52702             if(!cm.isHidden(v.getCellIndex(h))){
52703                 return h;
52704             }
52705             h = h.nextSibling;
52706         }
52707         return null;
52708     },
52709
52710     prevVisible : function(h){
52711         var v = this.view, cm = this.grid.colModel;
52712         h = h.prevSibling;
52713         while(h){
52714             if(!cm.isHidden(v.getCellIndex(h))){
52715                 return h;
52716             }
52717             h = h.prevSibling;
52718         }
52719         return null;
52720     },
52721
52722     positionIndicator : function(h, n, e){
52723         var x = Roo.lib.Event.getPageX(e);
52724         var r = Roo.lib.Dom.getRegion(n.firstChild);
52725         var px, pt, py = r.top + this.proxyOffsets[1];
52726         if((r.right - x) <= (r.right-r.left)/2){
52727             px = r.right+this.view.borderWidth;
52728             pt = "after";
52729         }else{
52730             px = r.left;
52731             pt = "before";
52732         }
52733         var oldIndex = this.view.getCellIndex(h);
52734         var newIndex = this.view.getCellIndex(n);
52735
52736         if(this.grid.colModel.isFixed(newIndex)){
52737             return false;
52738         }
52739
52740         var locked = this.grid.colModel.isLocked(newIndex);
52741
52742         if(pt == "after"){
52743             newIndex++;
52744         }
52745         if(oldIndex < newIndex){
52746             newIndex--;
52747         }
52748         if(oldIndex == newIndex && (locked == this.grid.colModel.isLocked(oldIndex))){
52749             return false;
52750         }
52751         px +=  this.proxyOffsets[0];
52752         this.proxyTop.setLeftTop(px, py);
52753         this.proxyTop.show();
52754         if(!this.bottomOffset){
52755             this.bottomOffset = this.view.mainHd.getHeight();
52756         }
52757         this.proxyBottom.setLeftTop(px, py+this.proxyTop.dom.offsetHeight+this.bottomOffset);
52758         this.proxyBottom.show();
52759         return pt;
52760     },
52761
52762     onNodeEnter : function(n, dd, e, data){
52763         if(data.header != n){
52764             this.positionIndicator(data.header, n, e);
52765         }
52766     },
52767
52768     onNodeOver : function(n, dd, e, data){
52769         var result = false;
52770         if(data.header != n){
52771             result = this.positionIndicator(data.header, n, e);
52772         }
52773         if(!result){
52774             this.proxyTop.hide();
52775             this.proxyBottom.hide();
52776         }
52777         return result ? this.dropAllowed : this.dropNotAllowed;
52778     },
52779
52780     onNodeOut : function(n, dd, e, data){
52781         this.proxyTop.hide();
52782         this.proxyBottom.hide();
52783     },
52784
52785     onNodeDrop : function(n, dd, e, data){
52786         var h = data.header;
52787         if(h != n){
52788             var cm = this.grid.colModel;
52789             var x = Roo.lib.Event.getPageX(e);
52790             var r = Roo.lib.Dom.getRegion(n.firstChild);
52791             var pt = (r.right - x) <= ((r.right-r.left)/2) ? "after" : "before";
52792             var oldIndex = this.view.getCellIndex(h);
52793             var newIndex = this.view.getCellIndex(n);
52794             var locked = cm.isLocked(newIndex);
52795             if(pt == "after"){
52796                 newIndex++;
52797             }
52798             if(oldIndex < newIndex){
52799                 newIndex--;
52800             }
52801             if(oldIndex == newIndex && (locked == cm.isLocked(oldIndex))){
52802                 return false;
52803             }
52804             cm.setLocked(oldIndex, locked, true);
52805             cm.moveColumn(oldIndex, newIndex);
52806             this.grid.fireEvent("columnmove", oldIndex, newIndex);
52807             return true;
52808         }
52809         return false;
52810     }
52811 });
52812 /*
52813  * Based on:
52814  * Ext JS Library 1.1.1
52815  * Copyright(c) 2006-2007, Ext JS, LLC.
52816  *
52817  * Originally Released Under LGPL - original licence link has changed is not relivant.
52818  *
52819  * Fork - LGPL
52820  * <script type="text/javascript">
52821  */
52822   
52823 /**
52824  * @class Roo.grid.GridView
52825  * @extends Roo.util.Observable
52826  *
52827  * @constructor
52828  * @param {Object} config
52829  */
52830 Roo.grid.GridView = function(config){
52831     Roo.grid.GridView.superclass.constructor.call(this);
52832     this.el = null;
52833
52834     Roo.apply(this, config);
52835 };
52836
52837 Roo.extend(Roo.grid.GridView, Roo.grid.AbstractGridView, {
52838
52839     unselectable :  'unselectable="on"',
52840     unselectableCls :  'x-unselectable',
52841     
52842     
52843     rowClass : "x-grid-row",
52844
52845     cellClass : "x-grid-col",
52846
52847     tdClass : "x-grid-td",
52848
52849     hdClass : "x-grid-hd",
52850
52851     splitClass : "x-grid-split",
52852
52853     sortClasses : ["sort-asc", "sort-desc"],
52854
52855     enableMoveAnim : false,
52856
52857     hlColor: "C3DAF9",
52858
52859     dh : Roo.DomHelper,
52860
52861     fly : Roo.Element.fly,
52862
52863     css : Roo.util.CSS,
52864
52865     borderWidth: 1,
52866
52867     splitOffset: 3,
52868
52869     scrollIncrement : 22,
52870
52871     cellRE: /(?:.*?)x-grid-(?:hd|cell|csplit)-(?:[\d]+)-([\d]+)(?:.*?)/,
52872
52873     findRE: /\s?(?:x-grid-hd|x-grid-col|x-grid-csplit)\s/,
52874
52875     bind : function(ds, cm){
52876         if(this.ds){
52877             this.ds.un("load", this.onLoad, this);
52878             this.ds.un("datachanged", this.onDataChange, this);
52879             this.ds.un("add", this.onAdd, this);
52880             this.ds.un("remove", this.onRemove, this);
52881             this.ds.un("update", this.onUpdate, this);
52882             this.ds.un("clear", this.onClear, this);
52883         }
52884         if(ds){
52885             ds.on("load", this.onLoad, this);
52886             ds.on("datachanged", this.onDataChange, this);
52887             ds.on("add", this.onAdd, this);
52888             ds.on("remove", this.onRemove, this);
52889             ds.on("update", this.onUpdate, this);
52890             ds.on("clear", this.onClear, this);
52891         }
52892         this.ds = ds;
52893
52894         if(this.cm){
52895             this.cm.un("widthchange", this.onColWidthChange, this);
52896             this.cm.un("headerchange", this.onHeaderChange, this);
52897             this.cm.un("hiddenchange", this.onHiddenChange, this);
52898             this.cm.un("columnmoved", this.onColumnMove, this);
52899             this.cm.un("columnlockchange", this.onColumnLock, this);
52900         }
52901         if(cm){
52902             this.generateRules(cm);
52903             cm.on("widthchange", this.onColWidthChange, this);
52904             cm.on("headerchange", this.onHeaderChange, this);
52905             cm.on("hiddenchange", this.onHiddenChange, this);
52906             cm.on("columnmoved", this.onColumnMove, this);
52907             cm.on("columnlockchange", this.onColumnLock, this);
52908         }
52909         this.cm = cm;
52910     },
52911
52912     init: function(grid){
52913         Roo.grid.GridView.superclass.init.call(this, grid);
52914
52915         this.bind(grid.dataSource, grid.colModel);
52916
52917         grid.on("headerclick", this.handleHeaderClick, this);
52918
52919         if(grid.trackMouseOver){
52920             grid.on("mouseover", this.onRowOver, this);
52921             grid.on("mouseout", this.onRowOut, this);
52922         }
52923         grid.cancelTextSelection = function(){};
52924         this.gridId = grid.id;
52925
52926         var tpls = this.templates || {};
52927
52928         if(!tpls.master){
52929             tpls.master = new Roo.Template(
52930                '<div class="x-grid" hidefocus="true">',
52931                 '<a href="#" class="x-grid-focus" tabIndex="-1"></a>',
52932                   '<div class="x-grid-topbar"></div>',
52933                   '<div class="x-grid-scroller"><div></div></div>',
52934                   '<div class="x-grid-locked">',
52935                       '<div class="x-grid-header">{lockedHeader}</div>',
52936                       '<div class="x-grid-body">{lockedBody}</div>',
52937                   "</div>",
52938                   '<div class="x-grid-viewport">',
52939                       '<div class="x-grid-header">{header}</div>',
52940                       '<div class="x-grid-body">{body}</div>',
52941                   "</div>",
52942                   '<div class="x-grid-bottombar"></div>',
52943                  
52944                   '<div class="x-grid-resize-proxy">&#160;</div>',
52945                "</div>"
52946             );
52947             tpls.master.disableformats = true;
52948         }
52949
52950         if(!tpls.header){
52951             tpls.header = new Roo.Template(
52952                '<table border="0" cellspacing="0" cellpadding="0">',
52953                '<tbody><tr class="x-grid-hd-row">{cells}</tr></tbody>',
52954                "</table>{splits}"
52955             );
52956             tpls.header.disableformats = true;
52957         }
52958         tpls.header.compile();
52959
52960         if(!tpls.hcell){
52961             tpls.hcell = new Roo.Template(
52962                 '<td class="x-grid-hd x-grid-td-{id} {cellId}"><div title="{title}" class="x-grid-hd-inner x-grid-hd-{id}">',
52963                 '<div class="x-grid-hd-text ' + this.unselectableCls +  '" ' + this.unselectable +'>{value}<img class="x-grid-sort-icon" src="', Roo.BLANK_IMAGE_URL, '" /></div>',
52964                 "</div></td>"
52965              );
52966              tpls.hcell.disableFormats = true;
52967         }
52968         tpls.hcell.compile();
52969
52970         if(!tpls.hsplit){
52971             tpls.hsplit = new Roo.Template('<div class="x-grid-split {splitId} x-grid-split-{id}" style="{style} ' +
52972                                             this.unselectableCls +  '" ' + this.unselectable +'>&#160;</div>');
52973             tpls.hsplit.disableFormats = true;
52974         }
52975         tpls.hsplit.compile();
52976
52977         if(!tpls.body){
52978             tpls.body = new Roo.Template(
52979                '<table border="0" cellspacing="0" cellpadding="0">',
52980                "<tbody>{rows}</tbody>",
52981                "</table>"
52982             );
52983             tpls.body.disableFormats = true;
52984         }
52985         tpls.body.compile();
52986
52987         if(!tpls.row){
52988             tpls.row = new Roo.Template('<tr class="x-grid-row {alt}">{cells}</tr>');
52989             tpls.row.disableFormats = true;
52990         }
52991         tpls.row.compile();
52992
52993         if(!tpls.cell){
52994             tpls.cell = new Roo.Template(
52995                 '<td class="x-grid-col x-grid-td-{id} {cellId} {css}" tabIndex="0">',
52996                 '<div class="x-grid-col-{id} x-grid-cell-inner"><div class="x-grid-cell-text ' +
52997                     this.unselectableCls +  '" ' + this.unselectable +'" {attr}>{value}</div></div>',
52998                 "</td>"
52999             );
53000             tpls.cell.disableFormats = true;
53001         }
53002         tpls.cell.compile();
53003
53004         this.templates = tpls;
53005     },
53006
53007     // remap these for backwards compat
53008     onColWidthChange : function(){
53009         this.updateColumns.apply(this, arguments);
53010     },
53011     onHeaderChange : function(){
53012         this.updateHeaders.apply(this, arguments);
53013     }, 
53014     onHiddenChange : function(){
53015         this.handleHiddenChange.apply(this, arguments);
53016     },
53017     onColumnMove : function(){
53018         this.handleColumnMove.apply(this, arguments);
53019     },
53020     onColumnLock : function(){
53021         this.handleLockChange.apply(this, arguments);
53022     },
53023
53024     onDataChange : function(){
53025         this.refresh();
53026         this.updateHeaderSortState();
53027     },
53028
53029     onClear : function(){
53030         this.refresh();
53031     },
53032
53033     onUpdate : function(ds, record){
53034         this.refreshRow(record);
53035     },
53036
53037     refreshRow : function(record){
53038         var ds = this.ds, index;
53039         if(typeof record == 'number'){
53040             index = record;
53041             record = ds.getAt(index);
53042         }else{
53043             index = ds.indexOf(record);
53044         }
53045         this.insertRows(ds, index, index, true);
53046         this.onRemove(ds, record, index+1, true);
53047         this.syncRowHeights(index, index);
53048         this.layout();
53049         this.fireEvent("rowupdated", this, index, record);
53050     },
53051
53052     onAdd : function(ds, records, index){
53053         this.insertRows(ds, index, index + (records.length-1));
53054     },
53055
53056     onRemove : function(ds, record, index, isUpdate){
53057         if(isUpdate !== true){
53058             this.fireEvent("beforerowremoved", this, index, record);
53059         }
53060         var bt = this.getBodyTable(), lt = this.getLockedTable();
53061         if(bt.rows[index]){
53062             bt.firstChild.removeChild(bt.rows[index]);
53063         }
53064         if(lt.rows[index]){
53065             lt.firstChild.removeChild(lt.rows[index]);
53066         }
53067         if(isUpdate !== true){
53068             this.stripeRows(index);
53069             this.syncRowHeights(index, index);
53070             this.layout();
53071             this.fireEvent("rowremoved", this, index, record);
53072         }
53073     },
53074
53075     onLoad : function(){
53076         this.scrollToTop();
53077     },
53078
53079     /**
53080      * Scrolls the grid to the top
53081      */
53082     scrollToTop : function(){
53083         if(this.scroller){
53084             this.scroller.dom.scrollTop = 0;
53085             this.syncScroll();
53086         }
53087     },
53088
53089     /**
53090      * Gets a panel in the header of the grid that can be used for toolbars etc.
53091      * After modifying the contents of this panel a call to grid.autoSize() may be
53092      * required to register any changes in size.
53093      * @param {Boolean} doShow By default the header is hidden. Pass true to show the panel
53094      * @return Roo.Element
53095      */
53096     getHeaderPanel : function(doShow){
53097         if(doShow){
53098             this.headerPanel.show();
53099         }
53100         return this.headerPanel;
53101     },
53102
53103     /**
53104      * Gets a panel in the footer of the grid that can be used for toolbars etc.
53105      * After modifying the contents of this panel a call to grid.autoSize() may be
53106      * required to register any changes in size.
53107      * @param {Boolean} doShow By default the footer is hidden. Pass true to show the panel
53108      * @return Roo.Element
53109      */
53110     getFooterPanel : function(doShow){
53111         if(doShow){
53112             this.footerPanel.show();
53113         }
53114         return this.footerPanel;
53115     },
53116
53117     initElements : function(){
53118         var E = Roo.Element;
53119         var el = this.grid.getGridEl().dom.firstChild;
53120         var cs = el.childNodes;
53121
53122         this.el = new E(el);
53123         
53124          this.focusEl = new E(el.firstChild);
53125         this.focusEl.swallowEvent("click", true);
53126         
53127         this.headerPanel = new E(cs[1]);
53128         this.headerPanel.enableDisplayMode("block");
53129
53130         this.scroller = new E(cs[2]);
53131         this.scrollSizer = new E(this.scroller.dom.firstChild);
53132
53133         this.lockedWrap = new E(cs[3]);
53134         this.lockedHd = new E(this.lockedWrap.dom.firstChild);
53135         this.lockedBody = new E(this.lockedWrap.dom.childNodes[1]);
53136
53137         this.mainWrap = new E(cs[4]);
53138         this.mainHd = new E(this.mainWrap.dom.firstChild);
53139         this.mainBody = new E(this.mainWrap.dom.childNodes[1]);
53140
53141         this.footerPanel = new E(cs[5]);
53142         this.footerPanel.enableDisplayMode("block");
53143
53144         this.resizeProxy = new E(cs[6]);
53145
53146         this.headerSelector = String.format(
53147            '#{0} td.x-grid-hd, #{1} td.x-grid-hd',
53148            this.lockedHd.id, this.mainHd.id
53149         );
53150
53151         this.splitterSelector = String.format(
53152            '#{0} div.x-grid-split, #{1} div.x-grid-split',
53153            this.idToCssName(this.lockedHd.id), this.idToCssName(this.mainHd.id)
53154         );
53155     },
53156     idToCssName : function(s)
53157     {
53158         return s.replace(/[^a-z0-9]+/ig, '-');
53159     },
53160
53161     getHeaderCell : function(index){
53162         return Roo.DomQuery.select(this.headerSelector)[index];
53163     },
53164
53165     getHeaderCellMeasure : function(index){
53166         return this.getHeaderCell(index).firstChild;
53167     },
53168
53169     getHeaderCellText : function(index){
53170         return this.getHeaderCell(index).firstChild.firstChild;
53171     },
53172
53173     getLockedTable : function(){
53174         return this.lockedBody.dom.firstChild;
53175     },
53176
53177     getBodyTable : function(){
53178         return this.mainBody.dom.firstChild;
53179     },
53180
53181     getLockedRow : function(index){
53182         return this.getLockedTable().rows[index];
53183     },
53184
53185     getRow : function(index){
53186         return this.getBodyTable().rows[index];
53187     },
53188
53189     getRowComposite : function(index){
53190         if(!this.rowEl){
53191             this.rowEl = new Roo.CompositeElementLite();
53192         }
53193         var els = [], lrow, mrow;
53194         if(lrow = this.getLockedRow(index)){
53195             els.push(lrow);
53196         }
53197         if(mrow = this.getRow(index)){
53198             els.push(mrow);
53199         }
53200         this.rowEl.elements = els;
53201         return this.rowEl;
53202     },
53203     /**
53204      * Gets the 'td' of the cell
53205      * 
53206      * @param {Integer} rowIndex row to select
53207      * @param {Integer} colIndex column to select
53208      * 
53209      * @return {Object} 
53210      */
53211     getCell : function(rowIndex, colIndex){
53212         var locked = this.cm.getLockedCount();
53213         var source;
53214         if(colIndex < locked){
53215             source = this.lockedBody.dom.firstChild;
53216         }else{
53217             source = this.mainBody.dom.firstChild;
53218             colIndex -= locked;
53219         }
53220         return source.rows[rowIndex].childNodes[colIndex];
53221     },
53222
53223     getCellText : function(rowIndex, colIndex){
53224         return this.getCell(rowIndex, colIndex).firstChild.firstChild;
53225     },
53226
53227     getCellBox : function(cell){
53228         var b = this.fly(cell).getBox();
53229         if(Roo.isOpera){ // opera fails to report the Y
53230             b.y = cell.offsetTop + this.mainBody.getY();
53231         }
53232         return b;
53233     },
53234
53235     getCellIndex : function(cell){
53236         var id = String(cell.className).match(this.cellRE);
53237         if(id){
53238             return parseInt(id[1], 10);
53239         }
53240         return 0;
53241     },
53242
53243     findHeaderIndex : function(n){
53244         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
53245         return r ? this.getCellIndex(r) : false;
53246     },
53247
53248     findHeaderCell : function(n){
53249         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
53250         return r ? r : false;
53251     },
53252
53253     findRowIndex : function(n){
53254         if(!n){
53255             return false;
53256         }
53257         var r = Roo.fly(n).findParent("tr." + this.rowClass, 6);
53258         return r ? r.rowIndex : false;
53259     },
53260
53261     findCellIndex : function(node){
53262         var stop = this.el.dom;
53263         while(node && node != stop){
53264             if(this.findRE.test(node.className)){
53265                 return this.getCellIndex(node);
53266             }
53267             node = node.parentNode;
53268         }
53269         return false;
53270     },
53271
53272     getColumnId : function(index){
53273         return this.cm.getColumnId(index);
53274     },
53275
53276     getSplitters : function()
53277     {
53278         if(this.splitterSelector){
53279            return Roo.DomQuery.select(this.splitterSelector);
53280         }else{
53281             return null;
53282       }
53283     },
53284
53285     getSplitter : function(index){
53286         return this.getSplitters()[index];
53287     },
53288
53289     onRowOver : function(e, t){
53290         var row;
53291         if((row = this.findRowIndex(t)) !== false){
53292             this.getRowComposite(row).addClass("x-grid-row-over");
53293         }
53294     },
53295
53296     onRowOut : function(e, t){
53297         var row;
53298         if((row = this.findRowIndex(t)) !== false && row !== this.findRowIndex(e.getRelatedTarget())){
53299             this.getRowComposite(row).removeClass("x-grid-row-over");
53300         }
53301     },
53302
53303     renderHeaders : function(){
53304         var cm = this.cm;
53305         var ct = this.templates.hcell, ht = this.templates.header, st = this.templates.hsplit;
53306         var cb = [], lb = [], sb = [], lsb = [], p = {};
53307         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
53308             p.cellId = "x-grid-hd-0-" + i;
53309             p.splitId = "x-grid-csplit-0-" + i;
53310             p.id = cm.getColumnId(i);
53311             p.title = cm.getColumnTooltip(i) || "";
53312             p.value = cm.getColumnHeader(i) || "";
53313             p.style = (this.grid.enableColumnResize === false || !cm.isResizable(i) || cm.isFixed(i)) ? 'cursor:default' : '';
53314             if(!cm.isLocked(i)){
53315                 cb[cb.length] = ct.apply(p);
53316                 sb[sb.length] = st.apply(p);
53317             }else{
53318                 lb[lb.length] = ct.apply(p);
53319                 lsb[lsb.length] = st.apply(p);
53320             }
53321         }
53322         return [ht.apply({cells: lb.join(""), splits:lsb.join("")}),
53323                 ht.apply({cells: cb.join(""), splits:sb.join("")})];
53324     },
53325
53326     updateHeaders : function(){
53327         var html = this.renderHeaders();
53328         this.lockedHd.update(html[0]);
53329         this.mainHd.update(html[1]);
53330     },
53331
53332     /**
53333      * Focuses the specified row.
53334      * @param {Number} row The row index
53335      */
53336     focusRow : function(row)
53337     {
53338         //Roo.log('GridView.focusRow');
53339         var x = this.scroller.dom.scrollLeft;
53340         this.focusCell(row, 0, false);
53341         this.scroller.dom.scrollLeft = x;
53342     },
53343
53344     /**
53345      * Focuses the specified cell.
53346      * @param {Number} row The row index
53347      * @param {Number} col The column index
53348      * @param {Boolean} hscroll false to disable horizontal scrolling
53349      */
53350     focusCell : function(row, col, hscroll)
53351     {
53352         //Roo.log('GridView.focusCell');
53353         var el = this.ensureVisible(row, col, hscroll);
53354         this.focusEl.alignTo(el, "tl-tl");
53355         if(Roo.isGecko){
53356             this.focusEl.focus();
53357         }else{
53358             this.focusEl.focus.defer(1, this.focusEl);
53359         }
53360     },
53361
53362     /**
53363      * Scrolls the specified cell into view
53364      * @param {Number} row The row index
53365      * @param {Number} col The column index
53366      * @param {Boolean} hscroll false to disable horizontal scrolling
53367      */
53368     ensureVisible : function(row, col, hscroll)
53369     {
53370         //Roo.log('GridView.ensureVisible,' + row + ',' + col);
53371         //return null; //disable for testing.
53372         if(typeof row != "number"){
53373             row = row.rowIndex;
53374         }
53375         if(row < 0 && row >= this.ds.getCount()){
53376             return  null;
53377         }
53378         col = (col !== undefined ? col : 0);
53379         var cm = this.grid.colModel;
53380         while(cm.isHidden(col)){
53381             col++;
53382         }
53383
53384         var el = this.getCell(row, col);
53385         if(!el){
53386             return null;
53387         }
53388         var c = this.scroller.dom;
53389
53390         var ctop = parseInt(el.offsetTop, 10);
53391         var cleft = parseInt(el.offsetLeft, 10);
53392         var cbot = ctop + el.offsetHeight;
53393         var cright = cleft + el.offsetWidth;
53394         
53395         var ch = c.clientHeight - this.mainHd.dom.offsetHeight;
53396         var stop = parseInt(c.scrollTop, 10);
53397         var sleft = parseInt(c.scrollLeft, 10);
53398         var sbot = stop + ch;
53399         var sright = sleft + c.clientWidth;
53400         /*
53401         Roo.log('GridView.ensureVisible:' +
53402                 ' ctop:' + ctop +
53403                 ' c.clientHeight:' + c.clientHeight +
53404                 ' this.mainHd.dom.offsetHeight:' + this.mainHd.dom.offsetHeight +
53405                 ' stop:' + stop +
53406                 ' cbot:' + cbot +
53407                 ' sbot:' + sbot +
53408                 ' ch:' + ch  
53409                 );
53410         */
53411         if(ctop < stop){
53412              c.scrollTop = ctop;
53413             //Roo.log("set scrolltop to ctop DISABLE?");
53414         }else if(cbot > sbot){
53415             //Roo.log("set scrolltop to cbot-ch");
53416             c.scrollTop = cbot-ch;
53417         }
53418         
53419         if(hscroll !== false){
53420             if(cleft < sleft){
53421                 c.scrollLeft = cleft;
53422             }else if(cright > sright){
53423                 c.scrollLeft = cright-c.clientWidth;
53424             }
53425         }
53426          
53427         return el;
53428     },
53429
53430     updateColumns : function(){
53431         this.grid.stopEditing();
53432         var cm = this.grid.colModel, colIds = this.getColumnIds();
53433         //var totalWidth = cm.getTotalWidth();
53434         var pos = 0;
53435         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
53436             //if(cm.isHidden(i)) continue;
53437             var w = cm.getColumnWidth(i);
53438             this.css.updateRule(this.colSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
53439             this.css.updateRule(this.hdSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
53440         }
53441         this.updateSplitters();
53442     },
53443
53444     generateRules : function(cm){
53445         var ruleBuf = [], rulesId = this.idToCssName(this.grid.id)+ '-cssrules';
53446         Roo.util.CSS.removeStyleSheet(rulesId);
53447         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
53448             var cid = cm.getColumnId(i);
53449             var align = '';
53450             if(cm.config[i].align){
53451                 align = 'text-align:'+cm.config[i].align+';';
53452             }
53453             var hidden = '';
53454             if(cm.isHidden(i)){
53455                 hidden = 'display:none;';
53456             }
53457             var width = "width:" + (cm.getColumnWidth(i) - this.borderWidth) + "px;";
53458             ruleBuf.push(
53459                     this.colSelector, cid, " {\n", cm.config[i].css, align, width, "\n}\n",
53460                     this.hdSelector, cid, " {\n", align, width, "}\n",
53461                     this.tdSelector, cid, " {\n",hidden,"\n}\n",
53462                     this.splitSelector, cid, " {\n", hidden , "\n}\n");
53463         }
53464         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
53465     },
53466
53467     updateSplitters : function(){
53468         var cm = this.cm, s = this.getSplitters();
53469         if(s){ // splitters not created yet
53470             var pos = 0, locked = true;
53471             for(var i = 0, len = cm.getColumnCount(); i < len; i++){
53472                 if(cm.isHidden(i)) continue;
53473                 var w = cm.getColumnWidth(i); // make sure it's a number
53474                 if(!cm.isLocked(i) && locked){
53475                     pos = 0;
53476                     locked = false;
53477                 }
53478                 pos += w;
53479                 s[i].style.left = (pos-this.splitOffset) + "px";
53480             }
53481         }
53482     },
53483
53484     handleHiddenChange : function(colModel, colIndex, hidden){
53485         if(hidden){
53486             this.hideColumn(colIndex);
53487         }else{
53488             this.unhideColumn(colIndex);
53489         }
53490     },
53491
53492     hideColumn : function(colIndex){
53493         var cid = this.getColumnId(colIndex);
53494         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "none");
53495         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "none");
53496         if(Roo.isSafari){
53497             this.updateHeaders();
53498         }
53499         this.updateSplitters();
53500         this.layout();
53501     },
53502
53503     unhideColumn : function(colIndex){
53504         var cid = this.getColumnId(colIndex);
53505         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "");
53506         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "");
53507
53508         if(Roo.isSafari){
53509             this.updateHeaders();
53510         }
53511         this.updateSplitters();
53512         this.layout();
53513     },
53514
53515     insertRows : function(dm, firstRow, lastRow, isUpdate){
53516         if(firstRow == 0 && lastRow == dm.getCount()-1){
53517             this.refresh();
53518         }else{
53519             if(!isUpdate){
53520                 this.fireEvent("beforerowsinserted", this, firstRow, lastRow);
53521             }
53522             var s = this.getScrollState();
53523             var markup = this.renderRows(firstRow, lastRow);
53524             this.bufferRows(markup[0], this.getLockedTable(), firstRow);
53525             this.bufferRows(markup[1], this.getBodyTable(), firstRow);
53526             this.restoreScroll(s);
53527             if(!isUpdate){
53528                 this.fireEvent("rowsinserted", this, firstRow, lastRow);
53529                 this.syncRowHeights(firstRow, lastRow);
53530                 this.stripeRows(firstRow);
53531                 this.layout();
53532             }
53533         }
53534     },
53535
53536     bufferRows : function(markup, target, index){
53537         var before = null, trows = target.rows, tbody = target.tBodies[0];
53538         if(index < trows.length){
53539             before = trows[index];
53540         }
53541         var b = document.createElement("div");
53542         b.innerHTML = "<table><tbody>"+markup+"</tbody></table>";
53543         var rows = b.firstChild.rows;
53544         for(var i = 0, len = rows.length; i < len; i++){
53545             if(before){
53546                 tbody.insertBefore(rows[0], before);
53547             }else{
53548                 tbody.appendChild(rows[0]);
53549             }
53550         }
53551         b.innerHTML = "";
53552         b = null;
53553     },
53554
53555     deleteRows : function(dm, firstRow, lastRow){
53556         if(dm.getRowCount()<1){
53557             this.fireEvent("beforerefresh", this);
53558             this.mainBody.update("");
53559             this.lockedBody.update("");
53560             this.fireEvent("refresh", this);
53561         }else{
53562             this.fireEvent("beforerowsdeleted", this, firstRow, lastRow);
53563             var bt = this.getBodyTable();
53564             var tbody = bt.firstChild;
53565             var rows = bt.rows;
53566             for(var rowIndex = firstRow; rowIndex <= lastRow; rowIndex++){
53567                 tbody.removeChild(rows[firstRow]);
53568             }
53569             this.stripeRows(firstRow);
53570             this.fireEvent("rowsdeleted", this, firstRow, lastRow);
53571         }
53572     },
53573
53574     updateRows : function(dataSource, firstRow, lastRow){
53575         var s = this.getScrollState();
53576         this.refresh();
53577         this.restoreScroll(s);
53578     },
53579
53580     handleSort : function(dataSource, sortColumnIndex, sortDir, noRefresh){
53581         if(!noRefresh){
53582            this.refresh();
53583         }
53584         this.updateHeaderSortState();
53585     },
53586
53587     getScrollState : function(){
53588         
53589         var sb = this.scroller.dom;
53590         return {left: sb.scrollLeft, top: sb.scrollTop};
53591     },
53592
53593     stripeRows : function(startRow){
53594         if(!this.grid.stripeRows || this.ds.getCount() < 1){
53595             return;
53596         }
53597         startRow = startRow || 0;
53598         var rows = this.getBodyTable().rows;
53599         var lrows = this.getLockedTable().rows;
53600         var cls = ' x-grid-row-alt ';
53601         for(var i = startRow, len = rows.length; i < len; i++){
53602             var row = rows[i], lrow = lrows[i];
53603             var isAlt = ((i+1) % 2 == 0);
53604             var hasAlt = (' '+row.className + ' ').indexOf(cls) != -1;
53605             if(isAlt == hasAlt){
53606                 continue;
53607             }
53608             if(isAlt){
53609                 row.className += " x-grid-row-alt";
53610             }else{
53611                 row.className = row.className.replace("x-grid-row-alt", "");
53612             }
53613             if(lrow){
53614                 lrow.className = row.className;
53615             }
53616         }
53617     },
53618
53619     restoreScroll : function(state){
53620         //Roo.log('GridView.restoreScroll');
53621         var sb = this.scroller.dom;
53622         sb.scrollLeft = state.left;
53623         sb.scrollTop = state.top;
53624         this.syncScroll();
53625     },
53626
53627     syncScroll : function(){
53628         //Roo.log('GridView.syncScroll');
53629         var sb = this.scroller.dom;
53630         var sh = this.mainHd.dom;
53631         var bs = this.mainBody.dom;
53632         var lv = this.lockedBody.dom;
53633         sh.scrollLeft = bs.scrollLeft = sb.scrollLeft;
53634         lv.scrollTop = bs.scrollTop = sb.scrollTop;
53635     },
53636
53637     handleScroll : function(e){
53638         this.syncScroll();
53639         var sb = this.scroller.dom;
53640         this.grid.fireEvent("bodyscroll", sb.scrollLeft, sb.scrollTop);
53641         e.stopEvent();
53642     },
53643
53644     handleWheel : function(e){
53645         var d = e.getWheelDelta();
53646         this.scroller.dom.scrollTop -= d*22;
53647         // set this here to prevent jumpy scrolling on large tables
53648         this.lockedBody.dom.scrollTop = this.mainBody.dom.scrollTop = this.scroller.dom.scrollTop;
53649         e.stopEvent();
53650     },
53651
53652     renderRows : function(startRow, endRow){
53653         // pull in all the crap needed to render rows
53654         var g = this.grid, cm = g.colModel, ds = g.dataSource, stripe = g.stripeRows;
53655         var colCount = cm.getColumnCount();
53656
53657         if(ds.getCount() < 1){
53658             return ["", ""];
53659         }
53660
53661         // build a map for all the columns
53662         var cs = [];
53663         for(var i = 0; i < colCount; i++){
53664             var name = cm.getDataIndex(i);
53665             cs[i] = {
53666                 name : typeof name == 'undefined' ? ds.fields.get(i).name : name,
53667                 renderer : cm.getRenderer(i),
53668                 id : cm.getColumnId(i),
53669                 locked : cm.isLocked(i)
53670             };
53671         }
53672
53673         startRow = startRow || 0;
53674         endRow = typeof endRow == "undefined"? ds.getCount()-1 : endRow;
53675
53676         // records to render
53677         var rs = ds.getRange(startRow, endRow);
53678
53679         return this.doRender(cs, rs, ds, startRow, colCount, stripe);
53680     },
53681
53682     // As much as I hate to duplicate code, this was branched because FireFox really hates
53683     // [].join("") on strings. The performance difference was substantial enough to
53684     // branch this function
53685     doRender : Roo.isGecko ?
53686             function(cs, rs, ds, startRow, colCount, stripe){
53687                 var ts = this.templates, ct = ts.cell, rt = ts.row;
53688                 // buffers
53689                 var buf = "", lbuf = "", cb, lcb, c, p = {}, rp = {}, r, rowIndex;
53690                 
53691                 var hasListener = this.grid.hasListener('rowclass');
53692                 var rowcfg = {};
53693                 for(var j = 0, len = rs.length; j < len; j++){
53694                     r = rs[j]; cb = ""; lcb = ""; rowIndex = (j+startRow);
53695                     for(var i = 0; i < colCount; i++){
53696                         c = cs[i];
53697                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
53698                         p.id = c.id;
53699                         p.css = p.attr = "";
53700                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
53701                         if(p.value == undefined || p.value === "") p.value = "&#160;";
53702                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
53703                             p.css += p.css ? ' x-grid-dirty-cell' : 'x-grid-dirty-cell';
53704                         }
53705                         var markup = ct.apply(p);
53706                         if(!c.locked){
53707                             cb+= markup;
53708                         }else{
53709                             lcb+= markup;
53710                         }
53711                     }
53712                     var alt = [];
53713                     if(stripe && ((rowIndex+1) % 2 == 0)){
53714                         alt.push("x-grid-row-alt")
53715                     }
53716                     if(r.dirty){
53717                         alt.push(  " x-grid-dirty-row");
53718                     }
53719                     rp.cells = lcb;
53720                     if(this.getRowClass){
53721                         alt.push(this.getRowClass(r, rowIndex));
53722                     }
53723                     if (hasListener) {
53724                         rowcfg = {
53725                              
53726                             record: r,
53727                             rowIndex : rowIndex,
53728                             rowClass : ''
53729                         }
53730                         this.grid.fireEvent('rowclass', this, rowcfg);
53731                         alt.push(rowcfg.rowClass);
53732                     }
53733                     rp.alt = alt.join(" ");
53734                     lbuf+= rt.apply(rp);
53735                     rp.cells = cb;
53736                     buf+=  rt.apply(rp);
53737                 }
53738                 return [lbuf, buf];
53739             } :
53740             function(cs, rs, ds, startRow, colCount, stripe){
53741                 var ts = this.templates, ct = ts.cell, rt = ts.row;
53742                 // buffers
53743                 var buf = [], lbuf = [], cb, lcb, c, p = {}, rp = {}, r, rowIndex;
53744                 var hasListener = this.grid.hasListener('rowclass');
53745  
53746                 var rowcfg = {};
53747                 for(var j = 0, len = rs.length; j < len; j++){
53748                     r = rs[j]; cb = []; lcb = []; rowIndex = (j+startRow);
53749                     for(var i = 0; i < colCount; i++){
53750                         c = cs[i];
53751                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
53752                         p.id = c.id;
53753                         p.css = p.attr = "";
53754                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
53755                         if(p.value == undefined || p.value === "") p.value = "&#160;";
53756                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
53757                             p.css += p.css ? ' x-grid-dirty-cell' : 'x-grid-dirty-cell';
53758                         }
53759                         
53760                         var markup = ct.apply(p);
53761                         if(!c.locked){
53762                             cb[cb.length] = markup;
53763                         }else{
53764                             lcb[lcb.length] = markup;
53765                         }
53766                     }
53767                     var alt = [];
53768                     if(stripe && ((rowIndex+1) % 2 == 0)){
53769                         alt.push( "x-grid-row-alt");
53770                     }
53771                     if(r.dirty){
53772                         alt.push(" x-grid-dirty-row");
53773                     }
53774                     rp.cells = lcb;
53775                     if(this.getRowClass){
53776                         alt.push( this.getRowClass(r, rowIndex));
53777                     }
53778                     if (hasListener) {
53779                         rowcfg = {
53780                              
53781                             record: r,
53782                             rowIndex : rowIndex,
53783                             rowClass : ''
53784                         }
53785                         this.grid.fireEvent('rowclass', this, rowcfg);
53786                         alt.push(rowcfg.rowClass);
53787                     }
53788                     rp.alt = alt.join(" ");
53789                     rp.cells = lcb.join("");
53790                     lbuf[lbuf.length] = rt.apply(rp);
53791                     rp.cells = cb.join("");
53792                     buf[buf.length] =  rt.apply(rp);
53793                 }
53794                 return [lbuf.join(""), buf.join("")];
53795             },
53796
53797     renderBody : function(){
53798         var markup = this.renderRows();
53799         var bt = this.templates.body;
53800         return [bt.apply({rows: markup[0]}), bt.apply({rows: markup[1]})];
53801     },
53802
53803     /**
53804      * Refreshes the grid
53805      * @param {Boolean} headersToo
53806      */
53807     refresh : function(headersToo){
53808         this.fireEvent("beforerefresh", this);
53809         this.grid.stopEditing();
53810         var result = this.renderBody();
53811         this.lockedBody.update(result[0]);
53812         this.mainBody.update(result[1]);
53813         if(headersToo === true){
53814             this.updateHeaders();
53815             this.updateColumns();
53816             this.updateSplitters();
53817             this.updateHeaderSortState();
53818         }
53819         this.syncRowHeights();
53820         this.layout();
53821         this.fireEvent("refresh", this);
53822     },
53823
53824     handleColumnMove : function(cm, oldIndex, newIndex){
53825         this.indexMap = null;
53826         var s = this.getScrollState();
53827         this.refresh(true);
53828         this.restoreScroll(s);
53829         this.afterMove(newIndex);
53830     },
53831
53832     afterMove : function(colIndex){
53833         if(this.enableMoveAnim && Roo.enableFx){
53834             this.fly(this.getHeaderCell(colIndex).firstChild).highlight(this.hlColor);
53835         }
53836         // if multisort - fix sortOrder, and reload..
53837         if (this.grid.dataSource.multiSort) {
53838             // the we can call sort again..
53839             var dm = this.grid.dataSource;
53840             var cm = this.grid.colModel;
53841             var so = [];
53842             for(var i = 0; i < cm.config.length; i++ ) {
53843                 
53844                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined')) {
53845                     continue; // dont' bother, it's not in sort list or being set.
53846                 }
53847                 
53848                 so.push(cm.config[i].dataIndex);
53849             };
53850             dm.sortOrder = so;
53851             dm.load(dm.lastOptions);
53852             
53853             
53854         }
53855         
53856     },
53857
53858     updateCell : function(dm, rowIndex, dataIndex){
53859         var colIndex = this.getColumnIndexByDataIndex(dataIndex);
53860         if(typeof colIndex == "undefined"){ // not present in grid
53861             return;
53862         }
53863         var cm = this.grid.colModel;
53864         var cell = this.getCell(rowIndex, colIndex);
53865         var cellText = this.getCellText(rowIndex, colIndex);
53866
53867         var p = {
53868             cellId : "x-grid-cell-" + rowIndex + "-" + colIndex,
53869             id : cm.getColumnId(colIndex),
53870             css: colIndex == cm.getColumnCount()-1 ? "x-grid-col-last" : ""
53871         };
53872         var renderer = cm.getRenderer(colIndex);
53873         var val = renderer(dm.getValueAt(rowIndex, dataIndex), p, rowIndex, colIndex, dm);
53874         if(typeof val == "undefined" || val === "") val = "&#160;";
53875         cellText.innerHTML = val;
53876         cell.className = this.cellClass + " " + this.idToCssName(p.cellId) + " " + p.css;
53877         this.syncRowHeights(rowIndex, rowIndex);
53878     },
53879
53880     calcColumnWidth : function(colIndex, maxRowsToMeasure){
53881         var maxWidth = 0;
53882         if(this.grid.autoSizeHeaders){
53883             var h = this.getHeaderCellMeasure(colIndex);
53884             maxWidth = Math.max(maxWidth, h.scrollWidth);
53885         }
53886         var tb, index;
53887         if(this.cm.isLocked(colIndex)){
53888             tb = this.getLockedTable();
53889             index = colIndex;
53890         }else{
53891             tb = this.getBodyTable();
53892             index = colIndex - this.cm.getLockedCount();
53893         }
53894         if(tb && tb.rows){
53895             var rows = tb.rows;
53896             var stopIndex = Math.min(maxRowsToMeasure || rows.length, rows.length);
53897             for(var i = 0; i < stopIndex; i++){
53898                 var cell = rows[i].childNodes[index].firstChild;
53899                 maxWidth = Math.max(maxWidth, cell.scrollWidth);
53900             }
53901         }
53902         return maxWidth + /*margin for error in IE*/ 5;
53903     },
53904     /**
53905      * Autofit a column to its content.
53906      * @param {Number} colIndex
53907      * @param {Boolean} forceMinSize true to force the column to go smaller if possible
53908      */
53909      autoSizeColumn : function(colIndex, forceMinSize, suppressEvent){
53910          if(this.cm.isHidden(colIndex)){
53911              return; // can't calc a hidden column
53912          }
53913         if(forceMinSize){
53914             var cid = this.cm.getColumnId(colIndex);
53915             this.css.updateRule(this.colSelector +this.idToCssName( cid), "width", this.grid.minColumnWidth + "px");
53916            if(this.grid.autoSizeHeaders){
53917                this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", this.grid.minColumnWidth + "px");
53918            }
53919         }
53920         var newWidth = this.calcColumnWidth(colIndex);
53921         this.cm.setColumnWidth(colIndex,
53922             Math.max(this.grid.minColumnWidth, newWidth), suppressEvent);
53923         if(!suppressEvent){
53924             this.grid.fireEvent("columnresize", colIndex, newWidth);
53925         }
53926     },
53927
53928     /**
53929      * Autofits all columns to their content and then expands to fit any extra space in the grid
53930      */
53931      autoSizeColumns : function(){
53932         var cm = this.grid.colModel;
53933         var colCount = cm.getColumnCount();
53934         for(var i = 0; i < colCount; i++){
53935             this.autoSizeColumn(i, true, true);
53936         }
53937         if(cm.getTotalWidth() < this.scroller.dom.clientWidth){
53938             this.fitColumns();
53939         }else{
53940             this.updateColumns();
53941             this.layout();
53942         }
53943     },
53944
53945     /**
53946      * Autofits all columns to the grid's width proportionate with their current size
53947      * @param {Boolean} reserveScrollSpace Reserve space for a scrollbar
53948      */
53949     fitColumns : function(reserveScrollSpace){
53950         var cm = this.grid.colModel;
53951         var colCount = cm.getColumnCount();
53952         var cols = [];
53953         var width = 0;
53954         var i, w;
53955         for (i = 0; i < colCount; i++){
53956             if(!cm.isHidden(i) && !cm.isFixed(i)){
53957                 w = cm.getColumnWidth(i);
53958                 cols.push(i);
53959                 cols.push(w);
53960                 width += w;
53961             }
53962         }
53963         var avail = Math.min(this.scroller.dom.clientWidth, this.el.getWidth());
53964         if(reserveScrollSpace){
53965             avail -= 17;
53966         }
53967         var frac = (avail - cm.getTotalWidth())/width;
53968         while (cols.length){
53969             w = cols.pop();
53970             i = cols.pop();
53971             cm.setColumnWidth(i, Math.floor(w + w*frac), true);
53972         }
53973         this.updateColumns();
53974         this.layout();
53975     },
53976
53977     onRowSelect : function(rowIndex){
53978         var row = this.getRowComposite(rowIndex);
53979         row.addClass("x-grid-row-selected");
53980     },
53981
53982     onRowDeselect : function(rowIndex){
53983         var row = this.getRowComposite(rowIndex);
53984         row.removeClass("x-grid-row-selected");
53985     },
53986
53987     onCellSelect : function(row, col){
53988         var cell = this.getCell(row, col);
53989         if(cell){
53990             Roo.fly(cell).addClass("x-grid-cell-selected");
53991         }
53992     },
53993
53994     onCellDeselect : function(row, col){
53995         var cell = this.getCell(row, col);
53996         if(cell){
53997             Roo.fly(cell).removeClass("x-grid-cell-selected");
53998         }
53999     },
54000
54001     updateHeaderSortState : function(){
54002         
54003         // sort state can be single { field: xxx, direction : yyy}
54004         // or   { xxx=>ASC , yyy : DESC ..... }
54005         
54006         var mstate = {};
54007         if (!this.ds.multiSort) { 
54008             var state = this.ds.getSortState();
54009             if(!state){
54010                 return;
54011             }
54012             mstate[state.field] = state.direction;
54013             // FIXME... - this is not used here.. but might be elsewhere..
54014             this.sortState = state;
54015             
54016         } else {
54017             mstate = this.ds.sortToggle;
54018         }
54019         //remove existing sort classes..
54020         
54021         var sc = this.sortClasses;
54022         var hds = this.el.select(this.headerSelector).removeClass(sc);
54023         
54024         for(var f in mstate) {
54025         
54026             var sortColumn = this.cm.findColumnIndex(f);
54027             
54028             if(sortColumn != -1){
54029                 var sortDir = mstate[f];        
54030                 hds.item(sortColumn).addClass(sc[sortDir == "DESC" ? 1 : 0]);
54031             }
54032         }
54033         
54034          
54035         
54036     },
54037
54038
54039     handleHeaderClick : function(g, index,e){
54040         
54041         Roo.log("header click");
54042         
54043         if (Roo.isTouch) {
54044             // touch events on header are handled by context
54045             this.handleHdCtx(g,index,e);
54046             return;
54047         }
54048         
54049         
54050         if(this.headersDisabled){
54051             return;
54052         }
54053         var dm = g.dataSource, cm = g.colModel;
54054         if(!cm.isSortable(index)){
54055             return;
54056         }
54057         g.stopEditing();
54058         
54059         if (dm.multiSort) {
54060             // update the sortOrder
54061             var so = [];
54062             for(var i = 0; i < cm.config.length; i++ ) {
54063                 
54064                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined') && (index != i)) {
54065                     continue; // dont' bother, it's not in sort list or being set.
54066                 }
54067                 
54068                 so.push(cm.config[i].dataIndex);
54069             };
54070             dm.sortOrder = so;
54071         }
54072         
54073         
54074         dm.sort(cm.getDataIndex(index));
54075     },
54076
54077
54078     destroy : function(){
54079         if(this.colMenu){
54080             this.colMenu.removeAll();
54081             Roo.menu.MenuMgr.unregister(this.colMenu);
54082             this.colMenu.getEl().remove();
54083             delete this.colMenu;
54084         }
54085         if(this.hmenu){
54086             this.hmenu.removeAll();
54087             Roo.menu.MenuMgr.unregister(this.hmenu);
54088             this.hmenu.getEl().remove();
54089             delete this.hmenu;
54090         }
54091         if(this.grid.enableColumnMove){
54092             var dds = Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
54093             if(dds){
54094                 for(var dd in dds){
54095                     if(!dds[dd].config.isTarget && dds[dd].dragElId){
54096                         var elid = dds[dd].dragElId;
54097                         dds[dd].unreg();
54098                         Roo.get(elid).remove();
54099                     } else if(dds[dd].config.isTarget){
54100                         dds[dd].proxyTop.remove();
54101                         dds[dd].proxyBottom.remove();
54102                         dds[dd].unreg();
54103                     }
54104                     if(Roo.dd.DDM.locationCache[dd]){
54105                         delete Roo.dd.DDM.locationCache[dd];
54106                     }
54107                 }
54108                 delete Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
54109             }
54110         }
54111         Roo.util.CSS.removeStyleSheet(this.idToCssName(this.grid.id) + '-cssrules');
54112         this.bind(null, null);
54113         Roo.EventManager.removeResizeListener(this.onWindowResize, this);
54114     },
54115
54116     handleLockChange : function(){
54117         this.refresh(true);
54118     },
54119
54120     onDenyColumnLock : function(){
54121
54122     },
54123
54124     onDenyColumnHide : function(){
54125
54126     },
54127
54128     handleHdMenuClick : function(item){
54129         var index = this.hdCtxIndex;
54130         var cm = this.cm, ds = this.ds;
54131         switch(item.id){
54132             case "asc":
54133                 ds.sort(cm.getDataIndex(index), "ASC");
54134                 break;
54135             case "desc":
54136                 ds.sort(cm.getDataIndex(index), "DESC");
54137                 break;
54138             case "lock":
54139                 var lc = cm.getLockedCount();
54140                 if(cm.getColumnCount(true) <= lc+1){
54141                     this.onDenyColumnLock();
54142                     return;
54143                 }
54144                 if(lc != index){
54145                     cm.setLocked(index, true, true);
54146                     cm.moveColumn(index, lc);
54147                     this.grid.fireEvent("columnmove", index, lc);
54148                 }else{
54149                     cm.setLocked(index, true);
54150                 }
54151             break;
54152             case "unlock":
54153                 var lc = cm.getLockedCount();
54154                 if((lc-1) != index){
54155                     cm.setLocked(index, false, true);
54156                     cm.moveColumn(index, lc-1);
54157                     this.grid.fireEvent("columnmove", index, lc-1);
54158                 }else{
54159                     cm.setLocked(index, false);
54160                 }
54161             break;
54162             case 'wider': // used to expand cols on touch..
54163             case 'narrow':
54164                 var cw = cm.getColumnWidth(index);
54165                 cw += (item.id == 'wider' ? 1 : -1) * 50;
54166                 cw = Math.max(0, cw);
54167                 cw = Math.min(cw,4000);
54168                 cm.setColumnWidth(index, cw);
54169                 break;
54170                 
54171             default:
54172                 index = cm.getIndexById(item.id.substr(4));
54173                 if(index != -1){
54174                     if(item.checked && cm.getColumnCount(true) <= 1){
54175                         this.onDenyColumnHide();
54176                         return false;
54177                     }
54178                     cm.setHidden(index, item.checked);
54179                 }
54180         }
54181         return true;
54182     },
54183
54184     beforeColMenuShow : function(){
54185         var cm = this.cm,  colCount = cm.getColumnCount();
54186         this.colMenu.removeAll();
54187         for(var i = 0; i < colCount; i++){
54188             this.colMenu.add(new Roo.menu.CheckItem({
54189                 id: "col-"+cm.getColumnId(i),
54190                 text: cm.getColumnHeader(i),
54191                 checked: !cm.isHidden(i),
54192                 hideOnClick:false
54193             }));
54194         }
54195     },
54196
54197     handleHdCtx : function(g, index, e){
54198         e.stopEvent();
54199         var hd = this.getHeaderCell(index);
54200         this.hdCtxIndex = index;
54201         var ms = this.hmenu.items, cm = this.cm;
54202         ms.get("asc").setDisabled(!cm.isSortable(index));
54203         ms.get("desc").setDisabled(!cm.isSortable(index));
54204         if(this.grid.enableColLock !== false){
54205             ms.get("lock").setDisabled(cm.isLocked(index));
54206             ms.get("unlock").setDisabled(!cm.isLocked(index));
54207         }
54208         this.hmenu.show(hd, "tl-bl");
54209     },
54210
54211     handleHdOver : function(e){
54212         var hd = this.findHeaderCell(e.getTarget());
54213         if(hd && !this.headersDisabled){
54214             if(this.grid.colModel.isSortable(this.getCellIndex(hd))){
54215                this.fly(hd).addClass("x-grid-hd-over");
54216             }
54217         }
54218     },
54219
54220     handleHdOut : function(e){
54221         var hd = this.findHeaderCell(e.getTarget());
54222         if(hd){
54223             this.fly(hd).removeClass("x-grid-hd-over");
54224         }
54225     },
54226
54227     handleSplitDblClick : function(e, t){
54228         var i = this.getCellIndex(t);
54229         if(this.grid.enableColumnResize !== false && this.cm.isResizable(i) && !this.cm.isFixed(i)){
54230             this.autoSizeColumn(i, true);
54231             this.layout();
54232         }
54233     },
54234
54235     render : function(){
54236
54237         var cm = this.cm;
54238         var colCount = cm.getColumnCount();
54239
54240         if(this.grid.monitorWindowResize === true){
54241             Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
54242         }
54243         var header = this.renderHeaders();
54244         var body = this.templates.body.apply({rows:""});
54245         var html = this.templates.master.apply({
54246             lockedBody: body,
54247             body: body,
54248             lockedHeader: header[0],
54249             header: header[1]
54250         });
54251
54252         //this.updateColumns();
54253
54254         this.grid.getGridEl().dom.innerHTML = html;
54255
54256         this.initElements();
54257         
54258         // a kludge to fix the random scolling effect in webkit
54259         this.el.on("scroll", function() {
54260             this.el.dom.scrollTop=0; // hopefully not recursive..
54261         },this);
54262
54263         this.scroller.on("scroll", this.handleScroll, this);
54264         this.lockedBody.on("mousewheel", this.handleWheel, this);
54265         this.mainBody.on("mousewheel", this.handleWheel, this);
54266
54267         this.mainHd.on("mouseover", this.handleHdOver, this);
54268         this.mainHd.on("mouseout", this.handleHdOut, this);
54269         this.mainHd.on("dblclick", this.handleSplitDblClick, this,
54270                 {delegate: "."+this.splitClass});
54271
54272         this.lockedHd.on("mouseover", this.handleHdOver, this);
54273         this.lockedHd.on("mouseout", this.handleHdOut, this);
54274         this.lockedHd.on("dblclick", this.handleSplitDblClick, this,
54275                 {delegate: "."+this.splitClass});
54276
54277         if(this.grid.enableColumnResize !== false && Roo.grid.SplitDragZone){
54278             new Roo.grid.SplitDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
54279         }
54280
54281         this.updateSplitters();
54282
54283         if(this.grid.enableColumnMove && Roo.grid.HeaderDragZone){
54284             new Roo.grid.HeaderDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
54285             new Roo.grid.HeaderDropZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
54286         }
54287
54288         if(this.grid.enableCtxMenu !== false && Roo.menu.Menu){
54289             this.hmenu = new Roo.menu.Menu({id: this.grid.id + "-hctx"});
54290             this.hmenu.add(
54291                 {id:"asc", text: this.sortAscText, cls: "xg-hmenu-sort-asc"},
54292                 {id:"desc", text: this.sortDescText, cls: "xg-hmenu-sort-desc"}
54293             );
54294             if(this.grid.enableColLock !== false){
54295                 this.hmenu.add('-',
54296                     {id:"lock", text: this.lockText, cls: "xg-hmenu-lock"},
54297                     {id:"unlock", text: this.unlockText, cls: "xg-hmenu-unlock"}
54298                 );
54299             }
54300             if (Roo.isTouch) {
54301                  this.hmenu.add('-',
54302                     {id:"wider", text: this.columnsWiderText},
54303                     {id:"narrow", text: this.columnsNarrowText }
54304                 );
54305                 
54306                  
54307             }
54308             
54309             if(this.grid.enableColumnHide !== false){
54310
54311                 this.colMenu = new Roo.menu.Menu({id:this.grid.id + "-hcols-menu"});
54312                 this.colMenu.on("beforeshow", this.beforeColMenuShow, this);
54313                 this.colMenu.on("itemclick", this.handleHdMenuClick, this);
54314
54315                 this.hmenu.add('-',
54316                     {id:"columns", text: this.columnsText, menu: this.colMenu}
54317                 );
54318             }
54319             this.hmenu.on("itemclick", this.handleHdMenuClick, this);
54320
54321             this.grid.on("headercontextmenu", this.handleHdCtx, this);
54322         }
54323
54324         if((this.grid.enableDragDrop || this.grid.enableDrag) && Roo.grid.GridDragZone){
54325             this.dd = new Roo.grid.GridDragZone(this.grid, {
54326                 ddGroup : this.grid.ddGroup || 'GridDD'
54327             });
54328             
54329         }
54330
54331         /*
54332         for(var i = 0; i < colCount; i++){
54333             if(cm.isHidden(i)){
54334                 this.hideColumn(i);
54335             }
54336             if(cm.config[i].align){
54337                 this.css.updateRule(this.colSelector + i, "textAlign", cm.config[i].align);
54338                 this.css.updateRule(this.hdSelector + i, "textAlign", cm.config[i].align);
54339             }
54340         }*/
54341         
54342         this.updateHeaderSortState();
54343
54344         this.beforeInitialResize();
54345         this.layout(true);
54346
54347         // two part rendering gives faster view to the user
54348         this.renderPhase2.defer(1, this);
54349     },
54350
54351     renderPhase2 : function(){
54352         // render the rows now
54353         this.refresh();
54354         if(this.grid.autoSizeColumns){
54355             this.autoSizeColumns();
54356         }
54357     },
54358
54359     beforeInitialResize : function(){
54360
54361     },
54362
54363     onColumnSplitterMoved : function(i, w){
54364         this.userResized = true;
54365         var cm = this.grid.colModel;
54366         cm.setColumnWidth(i, w, true);
54367         var cid = cm.getColumnId(i);
54368         this.css.updateRule(this.colSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
54369         this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
54370         this.updateSplitters();
54371         this.layout();
54372         this.grid.fireEvent("columnresize", i, w);
54373     },
54374
54375     syncRowHeights : function(startIndex, endIndex){
54376         if(this.grid.enableRowHeightSync === true && this.cm.getLockedCount() > 0){
54377             startIndex = startIndex || 0;
54378             var mrows = this.getBodyTable().rows;
54379             var lrows = this.getLockedTable().rows;
54380             var len = mrows.length-1;
54381             endIndex = Math.min(endIndex || len, len);
54382             for(var i = startIndex; i <= endIndex; i++){
54383                 var m = mrows[i], l = lrows[i];
54384                 var h = Math.max(m.offsetHeight, l.offsetHeight);
54385                 m.style.height = l.style.height = h + "px";
54386             }
54387         }
54388     },
54389
54390     layout : function(initialRender, is2ndPass){
54391         var g = this.grid;
54392         var auto = g.autoHeight;
54393         var scrollOffset = 16;
54394         var c = g.getGridEl(), cm = this.cm,
54395                 expandCol = g.autoExpandColumn,
54396                 gv = this;
54397         //c.beginMeasure();
54398
54399         if(!c.dom.offsetWidth){ // display:none?
54400             if(initialRender){
54401                 this.lockedWrap.show();
54402                 this.mainWrap.show();
54403             }
54404             return;
54405         }
54406
54407         var hasLock = this.cm.isLocked(0);
54408
54409         var tbh = this.headerPanel.getHeight();
54410         var bbh = this.footerPanel.getHeight();
54411
54412         if(auto){
54413             var ch = this.getBodyTable().offsetHeight + tbh + bbh + this.mainHd.getHeight();
54414             var newHeight = ch + c.getBorderWidth("tb");
54415             if(g.maxHeight){
54416                 newHeight = Math.min(g.maxHeight, newHeight);
54417             }
54418             c.setHeight(newHeight);
54419         }
54420
54421         if(g.autoWidth){
54422             c.setWidth(cm.getTotalWidth()+c.getBorderWidth('lr'));
54423         }
54424
54425         var s = this.scroller;
54426
54427         var csize = c.getSize(true);
54428
54429         this.el.setSize(csize.width, csize.height);
54430
54431         this.headerPanel.setWidth(csize.width);
54432         this.footerPanel.setWidth(csize.width);
54433
54434         var hdHeight = this.mainHd.getHeight();
54435         var vw = csize.width;
54436         var vh = csize.height - (tbh + bbh);
54437
54438         s.setSize(vw, vh);
54439
54440         var bt = this.getBodyTable();
54441         var ltWidth = hasLock ?
54442                       Math.max(this.getLockedTable().offsetWidth, this.lockedHd.dom.firstChild.offsetWidth) : 0;
54443
54444         var scrollHeight = bt.offsetHeight;
54445         var scrollWidth = ltWidth + bt.offsetWidth;
54446         var vscroll = false, hscroll = false;
54447
54448         this.scrollSizer.setSize(scrollWidth, scrollHeight+hdHeight);
54449
54450         var lw = this.lockedWrap, mw = this.mainWrap;
54451         var lb = this.lockedBody, mb = this.mainBody;
54452
54453         setTimeout(function(){
54454             var t = s.dom.offsetTop;
54455             var w = s.dom.clientWidth,
54456                 h = s.dom.clientHeight;
54457
54458             lw.setTop(t);
54459             lw.setSize(ltWidth, h);
54460
54461             mw.setLeftTop(ltWidth, t);
54462             mw.setSize(w-ltWidth, h);
54463
54464             lb.setHeight(h-hdHeight);
54465             mb.setHeight(h-hdHeight);
54466
54467             if(is2ndPass !== true && !gv.userResized && expandCol){
54468                 // high speed resize without full column calculation
54469                 
54470                 var ci = cm.getIndexById(expandCol);
54471                 if (ci < 0) {
54472                     ci = cm.findColumnIndex(expandCol);
54473                 }
54474                 ci = Math.max(0, ci); // make sure it's got at least the first col.
54475                 var expandId = cm.getColumnId(ci);
54476                 var  tw = cm.getTotalWidth(false);
54477                 var currentWidth = cm.getColumnWidth(ci);
54478                 var cw = Math.min(Math.max(((w-tw)+currentWidth-2)-/*scrollbar*/(w <= s.dom.offsetWidth ? 0 : 18), g.autoExpandMin), g.autoExpandMax);
54479                 if(currentWidth != cw){
54480                     cm.setColumnWidth(ci, cw, true);
54481                     gv.css.updateRule(gv.colSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
54482                     gv.css.updateRule(gv.hdSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
54483                     gv.updateSplitters();
54484                     gv.layout(false, true);
54485                 }
54486             }
54487
54488             if(initialRender){
54489                 lw.show();
54490                 mw.show();
54491             }
54492             //c.endMeasure();
54493         }, 10);
54494     },
54495
54496     onWindowResize : function(){
54497         if(!this.grid.monitorWindowResize || this.grid.autoHeight){
54498             return;
54499         }
54500         this.layout();
54501     },
54502
54503     appendFooter : function(parentEl){
54504         return null;
54505     },
54506
54507     sortAscText : "Sort Ascending",
54508     sortDescText : "Sort Descending",
54509     lockText : "Lock Column",
54510     unlockText : "Unlock Column",
54511     columnsText : "Columns",
54512  
54513     columnsWiderText : "Wider",
54514     columnsNarrowText : "Thinner"
54515 });
54516
54517
54518 Roo.grid.GridView.ColumnDragZone = function(grid, hd){
54519     Roo.grid.GridView.ColumnDragZone.superclass.constructor.call(this, grid, hd, null);
54520     this.proxy.el.addClass('x-grid3-col-dd');
54521 };
54522
54523 Roo.extend(Roo.grid.GridView.ColumnDragZone, Roo.grid.HeaderDragZone, {
54524     handleMouseDown : function(e){
54525
54526     },
54527
54528     callHandleMouseDown : function(e){
54529         Roo.grid.GridView.ColumnDragZone.superclass.handleMouseDown.call(this, e);
54530     }
54531 });
54532 /*
54533  * Based on:
54534  * Ext JS Library 1.1.1
54535  * Copyright(c) 2006-2007, Ext JS, LLC.
54536  *
54537  * Originally Released Under LGPL - original licence link has changed is not relivant.
54538  *
54539  * Fork - LGPL
54540  * <script type="text/javascript">
54541  */
54542  
54543 // private
54544 // This is a support class used internally by the Grid components
54545 Roo.grid.SplitDragZone = function(grid, hd, hd2){
54546     this.grid = grid;
54547     this.view = grid.getView();
54548     this.proxy = this.view.resizeProxy;
54549     Roo.grid.SplitDragZone.superclass.constructor.call(this, hd,
54550         "gridSplitters" + this.grid.getGridEl().id, {
54551         dragElId : Roo.id(this.proxy.dom), resizeFrame:false
54552     });
54553     this.setHandleElId(Roo.id(hd));
54554     this.setOuterHandleElId(Roo.id(hd2));
54555     this.scroll = false;
54556 };
54557 Roo.extend(Roo.grid.SplitDragZone, Roo.dd.DDProxy, {
54558     fly: Roo.Element.fly,
54559
54560     b4StartDrag : function(x, y){
54561         this.view.headersDisabled = true;
54562         this.proxy.setHeight(this.view.mainWrap.getHeight());
54563         var w = this.cm.getColumnWidth(this.cellIndex);
54564         var minw = Math.max(w-this.grid.minColumnWidth, 0);
54565         this.resetConstraints();
54566         this.setXConstraint(minw, 1000);
54567         this.setYConstraint(0, 0);
54568         this.minX = x - minw;
54569         this.maxX = x + 1000;
54570         this.startPos = x;
54571         Roo.dd.DDProxy.prototype.b4StartDrag.call(this, x, y);
54572     },
54573
54574
54575     handleMouseDown : function(e){
54576         ev = Roo.EventObject.setEvent(e);
54577         var t = this.fly(ev.getTarget());
54578         if(t.hasClass("x-grid-split")){
54579             this.cellIndex = this.view.getCellIndex(t.dom);
54580             this.split = t.dom;
54581             this.cm = this.grid.colModel;
54582             if(this.cm.isResizable(this.cellIndex) && !this.cm.isFixed(this.cellIndex)){
54583                 Roo.grid.SplitDragZone.superclass.handleMouseDown.apply(this, arguments);
54584             }
54585         }
54586     },
54587
54588     endDrag : function(e){
54589         this.view.headersDisabled = false;
54590         var endX = Math.max(this.minX, Roo.lib.Event.getPageX(e));
54591         var diff = endX - this.startPos;
54592         this.view.onColumnSplitterMoved(this.cellIndex, this.cm.getColumnWidth(this.cellIndex)+diff);
54593     },
54594
54595     autoOffset : function(){
54596         this.setDelta(0,0);
54597     }
54598 });/*
54599  * Based on:
54600  * Ext JS Library 1.1.1
54601  * Copyright(c) 2006-2007, Ext JS, LLC.
54602  *
54603  * Originally Released Under LGPL - original licence link has changed is not relivant.
54604  *
54605  * Fork - LGPL
54606  * <script type="text/javascript">
54607  */
54608  
54609 // private
54610 // This is a support class used internally by the Grid components
54611 Roo.grid.GridDragZone = function(grid, config){
54612     this.view = grid.getView();
54613     Roo.grid.GridDragZone.superclass.constructor.call(this, this.view.mainBody.dom, config);
54614     if(this.view.lockedBody){
54615         this.setHandleElId(Roo.id(this.view.mainBody.dom));
54616         this.setOuterHandleElId(Roo.id(this.view.lockedBody.dom));
54617     }
54618     this.scroll = false;
54619     this.grid = grid;
54620     this.ddel = document.createElement('div');
54621     this.ddel.className = 'x-grid-dd-wrap';
54622 };
54623
54624 Roo.extend(Roo.grid.GridDragZone, Roo.dd.DragZone, {
54625     ddGroup : "GridDD",
54626
54627     getDragData : function(e){
54628         var t = Roo.lib.Event.getTarget(e);
54629         var rowIndex = this.view.findRowIndex(t);
54630         var sm = this.grid.selModel;
54631             
54632         //Roo.log(rowIndex);
54633         
54634         if (sm.getSelectedCell) {
54635             // cell selection..
54636             if (!sm.getSelectedCell()) {
54637                 return false;
54638             }
54639             if (rowIndex != sm.getSelectedCell()[0]) {
54640                 return false;
54641             }
54642         
54643         }
54644         
54645         if(rowIndex !== false){
54646             
54647             // if editorgrid.. 
54648             
54649             
54650             //Roo.log([ sm.getSelectedCell() ? sm.getSelectedCell()[0] : 'NO' , rowIndex ]);
54651                
54652             //if(!sm.isSelected(rowIndex) || e.hasModifier()){
54653               //  
54654             //}
54655             if (e.hasModifier()){
54656                 sm.handleMouseDown(e, t); // non modifier buttons are handled by row select.
54657             }
54658             
54659             Roo.log("getDragData");
54660             
54661             return {
54662                 grid: this.grid,
54663                 ddel: this.ddel,
54664                 rowIndex: rowIndex,
54665                 selections:sm.getSelections ? sm.getSelections() : (
54666                     sm.getSelectedCell() ? [ this.grid.ds.getAt(sm.getSelectedCell()[0]) ] : []
54667                 )
54668             };
54669         }
54670         return false;
54671     },
54672
54673     onInitDrag : function(e){
54674         var data = this.dragData;
54675         this.ddel.innerHTML = this.grid.getDragDropText();
54676         this.proxy.update(this.ddel);
54677         // fire start drag?
54678     },
54679
54680     afterRepair : function(){
54681         this.dragging = false;
54682     },
54683
54684     getRepairXY : function(e, data){
54685         return false;
54686     },
54687
54688     onEndDrag : function(data, e){
54689         // fire end drag?
54690     },
54691
54692     onValidDrop : function(dd, e, id){
54693         // fire drag drop?
54694         this.hideProxy();
54695     },
54696
54697     beforeInvalidDrop : function(e, id){
54698
54699     }
54700 });/*
54701  * Based on:
54702  * Ext JS Library 1.1.1
54703  * Copyright(c) 2006-2007, Ext JS, LLC.
54704  *
54705  * Originally Released Under LGPL - original licence link has changed is not relivant.
54706  *
54707  * Fork - LGPL
54708  * <script type="text/javascript">
54709  */
54710  
54711
54712 /**
54713  * @class Roo.grid.ColumnModel
54714  * @extends Roo.util.Observable
54715  * This is the default implementation of a ColumnModel used by the Grid. It defines
54716  * the columns in the grid.
54717  * <br>Usage:<br>
54718  <pre><code>
54719  var colModel = new Roo.grid.ColumnModel([
54720         {header: "Ticker", width: 60, sortable: true, locked: true},
54721         {header: "Company Name", width: 150, sortable: true},
54722         {header: "Market Cap.", width: 100, sortable: true},
54723         {header: "$ Sales", width: 100, sortable: true, renderer: money},
54724         {header: "Employees", width: 100, sortable: true, resizable: false}
54725  ]);
54726  </code></pre>
54727  * <p>
54728  
54729  * The config options listed for this class are options which may appear in each
54730  * individual column definition.
54731  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
54732  * @constructor
54733  * @param {Object} config An Array of column config objects. See this class's
54734  * config objects for details.
54735 */
54736 Roo.grid.ColumnModel = function(config){
54737         /**
54738      * The config passed into the constructor
54739      */
54740     this.config = config;
54741     this.lookup = {};
54742
54743     // if no id, create one
54744     // if the column does not have a dataIndex mapping,
54745     // map it to the order it is in the config
54746     for(var i = 0, len = config.length; i < len; i++){
54747         var c = config[i];
54748         if(typeof c.dataIndex == "undefined"){
54749             c.dataIndex = i;
54750         }
54751         if(typeof c.renderer == "string"){
54752             c.renderer = Roo.util.Format[c.renderer];
54753         }
54754         if(typeof c.id == "undefined"){
54755             c.id = Roo.id();
54756         }
54757         if(c.editor && c.editor.xtype){
54758             c.editor  = Roo.factory(c.editor, Roo.grid);
54759         }
54760         if(c.editor && c.editor.isFormField){
54761             c.editor = new Roo.grid.GridEditor(c.editor);
54762         }
54763         this.lookup[c.id] = c;
54764     }
54765
54766     /**
54767      * The width of columns which have no width specified (defaults to 100)
54768      * @type Number
54769      */
54770     this.defaultWidth = 100;
54771
54772     /**
54773      * Default sortable of columns which have no sortable specified (defaults to false)
54774      * @type Boolean
54775      */
54776     this.defaultSortable = false;
54777
54778     this.addEvents({
54779         /**
54780              * @event widthchange
54781              * Fires when the width of a column changes.
54782              * @param {ColumnModel} this
54783              * @param {Number} columnIndex The column index
54784              * @param {Number} newWidth The new width
54785              */
54786             "widthchange": true,
54787         /**
54788              * @event headerchange
54789              * Fires when the text of a header changes.
54790              * @param {ColumnModel} this
54791              * @param {Number} columnIndex The column index
54792              * @param {Number} newText The new header text
54793              */
54794             "headerchange": true,
54795         /**
54796              * @event hiddenchange
54797              * Fires when a column is hidden or "unhidden".
54798              * @param {ColumnModel} this
54799              * @param {Number} columnIndex The column index
54800              * @param {Boolean} hidden true if hidden, false otherwise
54801              */
54802             "hiddenchange": true,
54803             /**
54804          * @event columnmoved
54805          * Fires when a column is moved.
54806          * @param {ColumnModel} this
54807          * @param {Number} oldIndex
54808          * @param {Number} newIndex
54809          */
54810         "columnmoved" : true,
54811         /**
54812          * @event columlockchange
54813          * Fires when a column's locked state is changed
54814          * @param {ColumnModel} this
54815          * @param {Number} colIndex
54816          * @param {Boolean} locked true if locked
54817          */
54818         "columnlockchange" : true
54819     });
54820     Roo.grid.ColumnModel.superclass.constructor.call(this);
54821 };
54822 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
54823     /**
54824      * @cfg {String} header The header text to display in the Grid view.
54825      */
54826     /**
54827      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
54828      * {@link Roo.data.Record} definition from which to draw the column's value. If not
54829      * specified, the column's index is used as an index into the Record's data Array.
54830      */
54831     /**
54832      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
54833      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
54834      */
54835     /**
54836      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
54837      * Defaults to the value of the {@link #defaultSortable} property.
54838      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
54839      */
54840     /**
54841      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
54842      */
54843     /**
54844      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
54845      */
54846     /**
54847      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
54848      */
54849     /**
54850      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
54851      */
54852     /**
54853      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
54854      * given the cell's data value. See {@link #setRenderer}. If not specified, the
54855      * default renderer uses the raw data value. If an object is returned (bootstrap only)
54856      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
54857      */
54858        /**
54859      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
54860      */
54861     /**
54862      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
54863      */
54864
54865     /**
54866      * Returns the id of the column at the specified index.
54867      * @param {Number} index The column index
54868      * @return {String} the id
54869      */
54870     getColumnId : function(index){
54871         return this.config[index].id;
54872     },
54873
54874     /**
54875      * Returns the column for a specified id.
54876      * @param {String} id The column id
54877      * @return {Object} the column
54878      */
54879     getColumnById : function(id){
54880         return this.lookup[id];
54881     },
54882
54883     
54884     /**
54885      * Returns the column for a specified dataIndex.
54886      * @param {String} dataIndex The column dataIndex
54887      * @return {Object|Boolean} the column or false if not found
54888      */
54889     getColumnByDataIndex: function(dataIndex){
54890         var index = this.findColumnIndex(dataIndex);
54891         return index > -1 ? this.config[index] : false;
54892     },
54893     
54894     /**
54895      * Returns the index for a specified column id.
54896      * @param {String} id The column id
54897      * @return {Number} the index, or -1 if not found
54898      */
54899     getIndexById : function(id){
54900         for(var i = 0, len = this.config.length; i < len; i++){
54901             if(this.config[i].id == id){
54902                 return i;
54903             }
54904         }
54905         return -1;
54906     },
54907     
54908     /**
54909      * Returns the index for a specified column dataIndex.
54910      * @param {String} dataIndex The column dataIndex
54911      * @return {Number} the index, or -1 if not found
54912      */
54913     
54914     findColumnIndex : function(dataIndex){
54915         for(var i = 0, len = this.config.length; i < len; i++){
54916             if(this.config[i].dataIndex == dataIndex){
54917                 return i;
54918             }
54919         }
54920         return -1;
54921     },
54922     
54923     
54924     moveColumn : function(oldIndex, newIndex){
54925         var c = this.config[oldIndex];
54926         this.config.splice(oldIndex, 1);
54927         this.config.splice(newIndex, 0, c);
54928         this.dataMap = null;
54929         this.fireEvent("columnmoved", this, oldIndex, newIndex);
54930     },
54931
54932     isLocked : function(colIndex){
54933         return this.config[colIndex].locked === true;
54934     },
54935
54936     setLocked : function(colIndex, value, suppressEvent){
54937         if(this.isLocked(colIndex) == value){
54938             return;
54939         }
54940         this.config[colIndex].locked = value;
54941         if(!suppressEvent){
54942             this.fireEvent("columnlockchange", this, colIndex, value);
54943         }
54944     },
54945
54946     getTotalLockedWidth : function(){
54947         var totalWidth = 0;
54948         for(var i = 0; i < this.config.length; i++){
54949             if(this.isLocked(i) && !this.isHidden(i)){
54950                 this.totalWidth += this.getColumnWidth(i);
54951             }
54952         }
54953         return totalWidth;
54954     },
54955
54956     getLockedCount : function(){
54957         for(var i = 0, len = this.config.length; i < len; i++){
54958             if(!this.isLocked(i)){
54959                 return i;
54960             }
54961         }
54962     },
54963
54964     /**
54965      * Returns the number of columns.
54966      * @return {Number}
54967      */
54968     getColumnCount : function(visibleOnly){
54969         if(visibleOnly === true){
54970             var c = 0;
54971             for(var i = 0, len = this.config.length; i < len; i++){
54972                 if(!this.isHidden(i)){
54973                     c++;
54974                 }
54975             }
54976             return c;
54977         }
54978         return this.config.length;
54979     },
54980
54981     /**
54982      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
54983      * @param {Function} fn
54984      * @param {Object} scope (optional)
54985      * @return {Array} result
54986      */
54987     getColumnsBy : function(fn, scope){
54988         var r = [];
54989         for(var i = 0, len = this.config.length; i < len; i++){
54990             var c = this.config[i];
54991             if(fn.call(scope||this, c, i) === true){
54992                 r[r.length] = c;
54993             }
54994         }
54995         return r;
54996     },
54997
54998     /**
54999      * Returns true if the specified column is sortable.
55000      * @param {Number} col The column index
55001      * @return {Boolean}
55002      */
55003     isSortable : function(col){
55004         if(typeof this.config[col].sortable == "undefined"){
55005             return this.defaultSortable;
55006         }
55007         return this.config[col].sortable;
55008     },
55009
55010     /**
55011      * Returns the rendering (formatting) function defined for the column.
55012      * @param {Number} col The column index.
55013      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
55014      */
55015     getRenderer : function(col){
55016         if(!this.config[col].renderer){
55017             return Roo.grid.ColumnModel.defaultRenderer;
55018         }
55019         return this.config[col].renderer;
55020     },
55021
55022     /**
55023      * Sets the rendering (formatting) function for a column.
55024      * @param {Number} col The column index
55025      * @param {Function} fn The function to use to process the cell's raw data
55026      * to return HTML markup for the grid view. The render function is called with
55027      * the following parameters:<ul>
55028      * <li>Data value.</li>
55029      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
55030      * <li>css A CSS style string to apply to the table cell.</li>
55031      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
55032      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
55033      * <li>Row index</li>
55034      * <li>Column index</li>
55035      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
55036      */
55037     setRenderer : function(col, fn){
55038         this.config[col].renderer = fn;
55039     },
55040
55041     /**
55042      * Returns the width for the specified column.
55043      * @param {Number} col The column index
55044      * @return {Number}
55045      */
55046     getColumnWidth : function(col){
55047         return this.config[col].width * 1 || this.defaultWidth;
55048     },
55049
55050     /**
55051      * Sets the width for a column.
55052      * @param {Number} col The column index
55053      * @param {Number} width The new width
55054      */
55055     setColumnWidth : function(col, width, suppressEvent){
55056         this.config[col].width = width;
55057         this.totalWidth = null;
55058         if(!suppressEvent){
55059              this.fireEvent("widthchange", this, col, width);
55060         }
55061     },
55062
55063     /**
55064      * Returns the total width of all columns.
55065      * @param {Boolean} includeHidden True to include hidden column widths
55066      * @return {Number}
55067      */
55068     getTotalWidth : function(includeHidden){
55069         if(!this.totalWidth){
55070             this.totalWidth = 0;
55071             for(var i = 0, len = this.config.length; i < len; i++){
55072                 if(includeHidden || !this.isHidden(i)){
55073                     this.totalWidth += this.getColumnWidth(i);
55074                 }
55075             }
55076         }
55077         return this.totalWidth;
55078     },
55079
55080     /**
55081      * Returns the header for the specified column.
55082      * @param {Number} col The column index
55083      * @return {String}
55084      */
55085     getColumnHeader : function(col){
55086         return this.config[col].header;
55087     },
55088
55089     /**
55090      * Sets the header for a column.
55091      * @param {Number} col The column index
55092      * @param {String} header The new header
55093      */
55094     setColumnHeader : function(col, header){
55095         this.config[col].header = header;
55096         this.fireEvent("headerchange", this, col, header);
55097     },
55098
55099     /**
55100      * Returns the tooltip for the specified column.
55101      * @param {Number} col The column index
55102      * @return {String}
55103      */
55104     getColumnTooltip : function(col){
55105             return this.config[col].tooltip;
55106     },
55107     /**
55108      * Sets the tooltip for a column.
55109      * @param {Number} col The column index
55110      * @param {String} tooltip The new tooltip
55111      */
55112     setColumnTooltip : function(col, tooltip){
55113             this.config[col].tooltip = tooltip;
55114     },
55115
55116     /**
55117      * Returns the dataIndex for the specified column.
55118      * @param {Number} col The column index
55119      * @return {Number}
55120      */
55121     getDataIndex : function(col){
55122         return this.config[col].dataIndex;
55123     },
55124
55125     /**
55126      * Sets the dataIndex for a column.
55127      * @param {Number} col The column index
55128      * @param {Number} dataIndex The new dataIndex
55129      */
55130     setDataIndex : function(col, dataIndex){
55131         this.config[col].dataIndex = dataIndex;
55132     },
55133
55134     
55135     
55136     /**
55137      * Returns true if the cell is editable.
55138      * @param {Number} colIndex The column index
55139      * @param {Number} rowIndex The row index
55140      * @return {Boolean}
55141      */
55142     isCellEditable : function(colIndex, rowIndex){
55143         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
55144     },
55145
55146     /**
55147      * Returns the editor defined for the cell/column.
55148      * return false or null to disable editing.
55149      * @param {Number} colIndex The column index
55150      * @param {Number} rowIndex The row index
55151      * @return {Object}
55152      */
55153     getCellEditor : function(colIndex, rowIndex){
55154         return this.config[colIndex].editor;
55155     },
55156
55157     /**
55158      * Sets if a column is editable.
55159      * @param {Number} col The column index
55160      * @param {Boolean} editable True if the column is editable
55161      */
55162     setEditable : function(col, editable){
55163         this.config[col].editable = editable;
55164     },
55165
55166
55167     /**
55168      * Returns true if the column is hidden.
55169      * @param {Number} colIndex The column index
55170      * @return {Boolean}
55171      */
55172     isHidden : function(colIndex){
55173         return this.config[colIndex].hidden;
55174     },
55175
55176
55177     /**
55178      * Returns true if the column width cannot be changed
55179      */
55180     isFixed : function(colIndex){
55181         return this.config[colIndex].fixed;
55182     },
55183
55184     /**
55185      * Returns true if the column can be resized
55186      * @return {Boolean}
55187      */
55188     isResizable : function(colIndex){
55189         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
55190     },
55191     /**
55192      * Sets if a column is hidden.
55193      * @param {Number} colIndex The column index
55194      * @param {Boolean} hidden True if the column is hidden
55195      */
55196     setHidden : function(colIndex, hidden){
55197         this.config[colIndex].hidden = hidden;
55198         this.totalWidth = null;
55199         this.fireEvent("hiddenchange", this, colIndex, hidden);
55200     },
55201
55202     /**
55203      * Sets the editor for a column.
55204      * @param {Number} col The column index
55205      * @param {Object} editor The editor object
55206      */
55207     setEditor : function(col, editor){
55208         this.config[col].editor = editor;
55209     }
55210 });
55211
55212 Roo.grid.ColumnModel.defaultRenderer = function(value){
55213         if(typeof value == "string" && value.length < 1){
55214             return "&#160;";
55215         }
55216         return value;
55217 };
55218
55219 // Alias for backwards compatibility
55220 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
55221 /*
55222  * Based on:
55223  * Ext JS Library 1.1.1
55224  * Copyright(c) 2006-2007, Ext JS, LLC.
55225  *
55226  * Originally Released Under LGPL - original licence link has changed is not relivant.
55227  *
55228  * Fork - LGPL
55229  * <script type="text/javascript">
55230  */
55231
55232 /**
55233  * @class Roo.grid.AbstractSelectionModel
55234  * @extends Roo.util.Observable
55235  * Abstract base class for grid SelectionModels.  It provides the interface that should be
55236  * implemented by descendant classes.  This class should not be directly instantiated.
55237  * @constructor
55238  */
55239 Roo.grid.AbstractSelectionModel = function(){
55240     this.locked = false;
55241     Roo.grid.AbstractSelectionModel.superclass.constructor.call(this);
55242 };
55243
55244 Roo.extend(Roo.grid.AbstractSelectionModel, Roo.util.Observable,  {
55245     /** @ignore Called by the grid automatically. Do not call directly. */
55246     init : function(grid){
55247         this.grid = grid;
55248         this.initEvents();
55249     },
55250
55251     /**
55252      * Locks the selections.
55253      */
55254     lock : function(){
55255         this.locked = true;
55256     },
55257
55258     /**
55259      * Unlocks the selections.
55260      */
55261     unlock : function(){
55262         this.locked = false;
55263     },
55264
55265     /**
55266      * Returns true if the selections are locked.
55267      * @return {Boolean}
55268      */
55269     isLocked : function(){
55270         return this.locked;
55271     }
55272 });/*
55273  * Based on:
55274  * Ext JS Library 1.1.1
55275  * Copyright(c) 2006-2007, Ext JS, LLC.
55276  *
55277  * Originally Released Under LGPL - original licence link has changed is not relivant.
55278  *
55279  * Fork - LGPL
55280  * <script type="text/javascript">
55281  */
55282 /**
55283  * @extends Roo.grid.AbstractSelectionModel
55284  * @class Roo.grid.RowSelectionModel
55285  * The default SelectionModel used by {@link Roo.grid.Grid}.
55286  * It supports multiple selections and keyboard selection/navigation. 
55287  * @constructor
55288  * @param {Object} config
55289  */
55290 Roo.grid.RowSelectionModel = function(config){
55291     Roo.apply(this, config);
55292     this.selections = new Roo.util.MixedCollection(false, function(o){
55293         return o.id;
55294     });
55295
55296     this.last = false;
55297     this.lastActive = false;
55298
55299     this.addEvents({
55300         /**
55301              * @event selectionchange
55302              * Fires when the selection changes
55303              * @param {SelectionModel} this
55304              */
55305             "selectionchange" : true,
55306         /**
55307              * @event afterselectionchange
55308              * Fires after the selection changes (eg. by key press or clicking)
55309              * @param {SelectionModel} this
55310              */
55311             "afterselectionchange" : true,
55312         /**
55313              * @event beforerowselect
55314              * Fires when a row is selected being selected, return false to cancel.
55315              * @param {SelectionModel} this
55316              * @param {Number} rowIndex The selected index
55317              * @param {Boolean} keepExisting False if other selections will be cleared
55318              */
55319             "beforerowselect" : true,
55320         /**
55321              * @event rowselect
55322              * Fires when a row is selected.
55323              * @param {SelectionModel} this
55324              * @param {Number} rowIndex The selected index
55325              * @param {Roo.data.Record} r The record
55326              */
55327             "rowselect" : true,
55328         /**
55329              * @event rowdeselect
55330              * Fires when a row is deselected.
55331              * @param {SelectionModel} this
55332              * @param {Number} rowIndex The selected index
55333              */
55334         "rowdeselect" : true
55335     });
55336     Roo.grid.RowSelectionModel.superclass.constructor.call(this);
55337     this.locked = false;
55338 };
55339
55340 Roo.extend(Roo.grid.RowSelectionModel, Roo.grid.AbstractSelectionModel,  {
55341     /**
55342      * @cfg {Boolean} singleSelect
55343      * True to allow selection of only one row at a time (defaults to false)
55344      */
55345     singleSelect : false,
55346
55347     // private
55348     initEvents : function(){
55349
55350         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
55351             this.grid.on("mousedown", this.handleMouseDown, this);
55352         }else{ // allow click to work like normal
55353             this.grid.on("rowclick", this.handleDragableRowClick, this);
55354         }
55355
55356         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
55357             "up" : function(e){
55358                 if(!e.shiftKey){
55359                     this.selectPrevious(e.shiftKey);
55360                 }else if(this.last !== false && this.lastActive !== false){
55361                     var last = this.last;
55362                     this.selectRange(this.last,  this.lastActive-1);
55363                     this.grid.getView().focusRow(this.lastActive);
55364                     if(last !== false){
55365                         this.last = last;
55366                     }
55367                 }else{
55368                     this.selectFirstRow();
55369                 }
55370                 this.fireEvent("afterselectionchange", this);
55371             },
55372             "down" : function(e){
55373                 if(!e.shiftKey){
55374                     this.selectNext(e.shiftKey);
55375                 }else if(this.last !== false && this.lastActive !== false){
55376                     var last = this.last;
55377                     this.selectRange(this.last,  this.lastActive+1);
55378                     this.grid.getView().focusRow(this.lastActive);
55379                     if(last !== false){
55380                         this.last = last;
55381                     }
55382                 }else{
55383                     this.selectFirstRow();
55384                 }
55385                 this.fireEvent("afterselectionchange", this);
55386             },
55387             scope: this
55388         });
55389
55390         var view = this.grid.view;
55391         view.on("refresh", this.onRefresh, this);
55392         view.on("rowupdated", this.onRowUpdated, this);
55393         view.on("rowremoved", this.onRemove, this);
55394     },
55395
55396     // private
55397     onRefresh : function(){
55398         var ds = this.grid.dataSource, i, v = this.grid.view;
55399         var s = this.selections;
55400         s.each(function(r){
55401             if((i = ds.indexOfId(r.id)) != -1){
55402                 v.onRowSelect(i);
55403             }else{
55404                 s.remove(r);
55405             }
55406         });
55407     },
55408
55409     // private
55410     onRemove : function(v, index, r){
55411         this.selections.remove(r);
55412     },
55413
55414     // private
55415     onRowUpdated : function(v, index, r){
55416         if(this.isSelected(r)){
55417             v.onRowSelect(index);
55418         }
55419     },
55420
55421     /**
55422      * Select records.
55423      * @param {Array} records The records to select
55424      * @param {Boolean} keepExisting (optional) True to keep existing selections
55425      */
55426     selectRecords : function(records, keepExisting){
55427         if(!keepExisting){
55428             this.clearSelections();
55429         }
55430         var ds = this.grid.dataSource;
55431         for(var i = 0, len = records.length; i < len; i++){
55432             this.selectRow(ds.indexOf(records[i]), true);
55433         }
55434     },
55435
55436     /**
55437      * Gets the number of selected rows.
55438      * @return {Number}
55439      */
55440     getCount : function(){
55441         return this.selections.length;
55442     },
55443
55444     /**
55445      * Selects the first row in the grid.
55446      */
55447     selectFirstRow : function(){
55448         this.selectRow(0);
55449     },
55450
55451     /**
55452      * Select the last row.
55453      * @param {Boolean} keepExisting (optional) True to keep existing selections
55454      */
55455     selectLastRow : function(keepExisting){
55456         this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
55457     },
55458
55459     /**
55460      * Selects the row immediately following the last selected row.
55461      * @param {Boolean} keepExisting (optional) True to keep existing selections
55462      */
55463     selectNext : function(keepExisting){
55464         if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
55465             this.selectRow(this.last+1, keepExisting);
55466             this.grid.getView().focusRow(this.last);
55467         }
55468     },
55469
55470     /**
55471      * Selects the row that precedes the last selected row.
55472      * @param {Boolean} keepExisting (optional) True to keep existing selections
55473      */
55474     selectPrevious : function(keepExisting){
55475         if(this.last){
55476             this.selectRow(this.last-1, keepExisting);
55477             this.grid.getView().focusRow(this.last);
55478         }
55479     },
55480
55481     /**
55482      * Returns the selected records
55483      * @return {Array} Array of selected records
55484      */
55485     getSelections : function(){
55486         return [].concat(this.selections.items);
55487     },
55488
55489     /**
55490      * Returns the first selected record.
55491      * @return {Record}
55492      */
55493     getSelected : function(){
55494         return this.selections.itemAt(0);
55495     },
55496
55497
55498     /**
55499      * Clears all selections.
55500      */
55501     clearSelections : function(fast){
55502         if(this.locked) return;
55503         if(fast !== true){
55504             var ds = this.grid.dataSource;
55505             var s = this.selections;
55506             s.each(function(r){
55507                 this.deselectRow(ds.indexOfId(r.id));
55508             }, this);
55509             s.clear();
55510         }else{
55511             this.selections.clear();
55512         }
55513         this.last = false;
55514     },
55515
55516
55517     /**
55518      * Selects all rows.
55519      */
55520     selectAll : function(){
55521         if(this.locked) return;
55522         this.selections.clear();
55523         for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
55524             this.selectRow(i, true);
55525         }
55526     },
55527
55528     /**
55529      * Returns True if there is a selection.
55530      * @return {Boolean}
55531      */
55532     hasSelection : function(){
55533         return this.selections.length > 0;
55534     },
55535
55536     /**
55537      * Returns True if the specified row is selected.
55538      * @param {Number/Record} record The record or index of the record to check
55539      * @return {Boolean}
55540      */
55541     isSelected : function(index){
55542         var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
55543         return (r && this.selections.key(r.id) ? true : false);
55544     },
55545
55546     /**
55547      * Returns True if the specified record id is selected.
55548      * @param {String} id The id of record to check
55549      * @return {Boolean}
55550      */
55551     isIdSelected : function(id){
55552         return (this.selections.key(id) ? true : false);
55553     },
55554
55555     // private
55556     handleMouseDown : function(e, t){
55557         var view = this.grid.getView(), rowIndex;
55558         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
55559             return;
55560         };
55561         if(e.shiftKey && this.last !== false){
55562             var last = this.last;
55563             this.selectRange(last, rowIndex, e.ctrlKey);
55564             this.last = last; // reset the last
55565             view.focusRow(rowIndex);
55566         }else{
55567             var isSelected = this.isSelected(rowIndex);
55568             if(e.button !== 0 && isSelected){
55569                 view.focusRow(rowIndex);
55570             }else if(e.ctrlKey && isSelected){
55571                 this.deselectRow(rowIndex);
55572             }else if(!isSelected){
55573                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
55574                 view.focusRow(rowIndex);
55575             }
55576         }
55577         this.fireEvent("afterselectionchange", this);
55578     },
55579     // private
55580     handleDragableRowClick :  function(grid, rowIndex, e) 
55581     {
55582         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
55583             this.selectRow(rowIndex, false);
55584             grid.view.focusRow(rowIndex);
55585              this.fireEvent("afterselectionchange", this);
55586         }
55587     },
55588     
55589     /**
55590      * Selects multiple rows.
55591      * @param {Array} rows Array of the indexes of the row to select
55592      * @param {Boolean} keepExisting (optional) True to keep existing selections
55593      */
55594     selectRows : function(rows, keepExisting){
55595         if(!keepExisting){
55596             this.clearSelections();
55597         }
55598         for(var i = 0, len = rows.length; i < len; i++){
55599             this.selectRow(rows[i], true);
55600         }
55601     },
55602
55603     /**
55604      * Selects a range of rows. All rows in between startRow and endRow are also selected.
55605      * @param {Number} startRow The index of the first row in the range
55606      * @param {Number} endRow The index of the last row in the range
55607      * @param {Boolean} keepExisting (optional) True to retain existing selections
55608      */
55609     selectRange : function(startRow, endRow, keepExisting){
55610         if(this.locked) return;
55611         if(!keepExisting){
55612             this.clearSelections();
55613         }
55614         if(startRow <= endRow){
55615             for(var i = startRow; i <= endRow; i++){
55616                 this.selectRow(i, true);
55617             }
55618         }else{
55619             for(var i = startRow; i >= endRow; i--){
55620                 this.selectRow(i, true);
55621             }
55622         }
55623     },
55624
55625     /**
55626      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
55627      * @param {Number} startRow The index of the first row in the range
55628      * @param {Number} endRow The index of the last row in the range
55629      */
55630     deselectRange : function(startRow, endRow, preventViewNotify){
55631         if(this.locked) return;
55632         for(var i = startRow; i <= endRow; i++){
55633             this.deselectRow(i, preventViewNotify);
55634         }
55635     },
55636
55637     /**
55638      * Selects a row.
55639      * @param {Number} row The index of the row to select
55640      * @param {Boolean} keepExisting (optional) True to keep existing selections
55641      */
55642     selectRow : function(index, keepExisting, preventViewNotify){
55643         if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) return;
55644         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
55645             if(!keepExisting || this.singleSelect){
55646                 this.clearSelections();
55647             }
55648             var r = this.grid.dataSource.getAt(index);
55649             this.selections.add(r);
55650             this.last = this.lastActive = index;
55651             if(!preventViewNotify){
55652                 this.grid.getView().onRowSelect(index);
55653             }
55654             this.fireEvent("rowselect", this, index, r);
55655             this.fireEvent("selectionchange", this);
55656         }
55657     },
55658
55659     /**
55660      * Deselects a row.
55661      * @param {Number} row The index of the row to deselect
55662      */
55663     deselectRow : function(index, preventViewNotify){
55664         if(this.locked) return;
55665         if(this.last == index){
55666             this.last = false;
55667         }
55668         if(this.lastActive == index){
55669             this.lastActive = false;
55670         }
55671         var r = this.grid.dataSource.getAt(index);
55672         this.selections.remove(r);
55673         if(!preventViewNotify){
55674             this.grid.getView().onRowDeselect(index);
55675         }
55676         this.fireEvent("rowdeselect", this, index);
55677         this.fireEvent("selectionchange", this);
55678     },
55679
55680     // private
55681     restoreLast : function(){
55682         if(this._last){
55683             this.last = this._last;
55684         }
55685     },
55686
55687     // private
55688     acceptsNav : function(row, col, cm){
55689         return !cm.isHidden(col) && cm.isCellEditable(col, row);
55690     },
55691
55692     // private
55693     onEditorKey : function(field, e){
55694         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
55695         if(k == e.TAB){
55696             e.stopEvent();
55697             ed.completeEdit();
55698             if(e.shiftKey){
55699                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
55700             }else{
55701                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
55702             }
55703         }else if(k == e.ENTER && !e.ctrlKey){
55704             e.stopEvent();
55705             ed.completeEdit();
55706             if(e.shiftKey){
55707                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
55708             }else{
55709                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
55710             }
55711         }else if(k == e.ESC){
55712             ed.cancelEdit();
55713         }
55714         if(newCell){
55715             g.startEditing(newCell[0], newCell[1]);
55716         }
55717     }
55718 });/*
55719  * Based on:
55720  * Ext JS Library 1.1.1
55721  * Copyright(c) 2006-2007, Ext JS, LLC.
55722  *
55723  * Originally Released Under LGPL - original licence link has changed is not relivant.
55724  *
55725  * Fork - LGPL
55726  * <script type="text/javascript">
55727  */
55728 /**
55729  * @class Roo.grid.CellSelectionModel
55730  * @extends Roo.grid.AbstractSelectionModel
55731  * This class provides the basic implementation for cell selection in a grid.
55732  * @constructor
55733  * @param {Object} config The object containing the configuration of this model.
55734  * @cfg {Boolean} enter_is_tab Enter behaves the same as tab. (eg. goes to next cell) default: false
55735  */
55736 Roo.grid.CellSelectionModel = function(config){
55737     Roo.apply(this, config);
55738
55739     this.selection = null;
55740
55741     this.addEvents({
55742         /**
55743              * @event beforerowselect
55744              * Fires before a cell is selected.
55745              * @param {SelectionModel} this
55746              * @param {Number} rowIndex The selected row index
55747              * @param {Number} colIndex The selected cell index
55748              */
55749             "beforecellselect" : true,
55750         /**
55751              * @event cellselect
55752              * Fires when a cell is selected.
55753              * @param {SelectionModel} this
55754              * @param {Number} rowIndex The selected row index
55755              * @param {Number} colIndex The selected cell index
55756              */
55757             "cellselect" : true,
55758         /**
55759              * @event selectionchange
55760              * Fires when the active selection changes.
55761              * @param {SelectionModel} this
55762              * @param {Object} selection null for no selection or an object (o) with two properties
55763                 <ul>
55764                 <li>o.record: the record object for the row the selection is in</li>
55765                 <li>o.cell: An array of [rowIndex, columnIndex]</li>
55766                 </ul>
55767              */
55768             "selectionchange" : true,
55769         /**
55770              * @event tabend
55771              * Fires when the tab (or enter) was pressed on the last editable cell
55772              * You can use this to trigger add new row.
55773              * @param {SelectionModel} this
55774              */
55775             "tabend" : true,
55776          /**
55777              * @event beforeeditnext
55778              * Fires before the next editable sell is made active
55779              * You can use this to skip to another cell or fire the tabend
55780              *    if you set cell to false
55781              * @param {Object} eventdata object : { cell : [ row, col ] } 
55782              */
55783             "beforeeditnext" : true
55784     });
55785     Roo.grid.CellSelectionModel.superclass.constructor.call(this);
55786 };
55787
55788 Roo.extend(Roo.grid.CellSelectionModel, Roo.grid.AbstractSelectionModel,  {
55789     
55790     enter_is_tab: false,
55791
55792     /** @ignore */
55793     initEvents : function(){
55794         this.grid.on("mousedown", this.handleMouseDown, this);
55795         this.grid.getGridEl().on(Roo.isIE ? "keydown" : "keypress", this.handleKeyDown, this);
55796         var view = this.grid.view;
55797         view.on("refresh", this.onViewChange, this);
55798         view.on("rowupdated", this.onRowUpdated, this);
55799         view.on("beforerowremoved", this.clearSelections, this);
55800         view.on("beforerowsinserted", this.clearSelections, this);
55801         if(this.grid.isEditor){
55802             this.grid.on("beforeedit", this.beforeEdit,  this);
55803         }
55804     },
55805
55806         //private
55807     beforeEdit : function(e){
55808         this.select(e.row, e.column, false, true, e.record);
55809     },
55810
55811         //private
55812     onRowUpdated : function(v, index, r){
55813         if(this.selection && this.selection.record == r){
55814             v.onCellSelect(index, this.selection.cell[1]);
55815         }
55816     },
55817
55818         //private
55819     onViewChange : function(){
55820         this.clearSelections(true);
55821     },
55822
55823         /**
55824          * Returns the currently selected cell,.
55825          * @return {Array} The selected cell (row, column) or null if none selected.
55826          */
55827     getSelectedCell : function(){
55828         return this.selection ? this.selection.cell : null;
55829     },
55830
55831     /**
55832      * Clears all selections.
55833      * @param {Boolean} true to prevent the gridview from being notified about the change.
55834      */
55835     clearSelections : function(preventNotify){
55836         var s = this.selection;
55837         if(s){
55838             if(preventNotify !== true){
55839                 this.grid.view.onCellDeselect(s.cell[0], s.cell[1]);
55840             }
55841             this.selection = null;
55842             this.fireEvent("selectionchange", this, null);
55843         }
55844     },
55845
55846     /**
55847      * Returns true if there is a selection.
55848      * @return {Boolean}
55849      */
55850     hasSelection : function(){
55851         return this.selection ? true : false;
55852     },
55853
55854     /** @ignore */
55855     handleMouseDown : function(e, t){
55856         var v = this.grid.getView();
55857         if(this.isLocked()){
55858             return;
55859         };
55860         var row = v.findRowIndex(t);
55861         var cell = v.findCellIndex(t);
55862         if(row !== false && cell !== false){
55863             this.select(row, cell);
55864         }
55865     },
55866
55867     /**
55868      * Selects a cell.
55869      * @param {Number} rowIndex
55870      * @param {Number} collIndex
55871      */
55872     select : function(rowIndex, colIndex, preventViewNotify, preventFocus, /*internal*/ r){
55873         if(this.fireEvent("beforecellselect", this, rowIndex, colIndex) !== false){
55874             this.clearSelections();
55875             r = r || this.grid.dataSource.getAt(rowIndex);
55876             this.selection = {
55877                 record : r,
55878                 cell : [rowIndex, colIndex]
55879             };
55880             if(!preventViewNotify){
55881                 var v = this.grid.getView();
55882                 v.onCellSelect(rowIndex, colIndex);
55883                 if(preventFocus !== true){
55884                     v.focusCell(rowIndex, colIndex);
55885                 }
55886             }
55887             this.fireEvent("cellselect", this, rowIndex, colIndex);
55888             this.fireEvent("selectionchange", this, this.selection);
55889         }
55890     },
55891
55892         //private
55893     isSelectable : function(rowIndex, colIndex, cm){
55894         return !cm.isHidden(colIndex);
55895     },
55896
55897     /** @ignore */
55898     handleKeyDown : function(e){
55899         //Roo.log('Cell Sel Model handleKeyDown');
55900         if(!e.isNavKeyPress()){
55901             return;
55902         }
55903         var g = this.grid, s = this.selection;
55904         if(!s){
55905             e.stopEvent();
55906             var cell = g.walkCells(0, 0, 1, this.isSelectable,  this);
55907             if(cell){
55908                 this.select(cell[0], cell[1]);
55909             }
55910             return;
55911         }
55912         var sm = this;
55913         var walk = function(row, col, step){
55914             return g.walkCells(row, col, step, sm.isSelectable,  sm);
55915         };
55916         var k = e.getKey(), r = s.cell[0], c = s.cell[1];
55917         var newCell;
55918
55919       
55920
55921         switch(k){
55922             case e.TAB:
55923                 // handled by onEditorKey
55924                 if (g.isEditor && g.editing) {
55925                     return;
55926                 }
55927                 if(e.shiftKey) {
55928                     newCell = walk(r, c-1, -1);
55929                 } else {
55930                     newCell = walk(r, c+1, 1);
55931                 }
55932                 break;
55933             
55934             case e.DOWN:
55935                newCell = walk(r+1, c, 1);
55936                 break;
55937             
55938             case e.UP:
55939                 newCell = walk(r-1, c, -1);
55940                 break;
55941             
55942             case e.RIGHT:
55943                 newCell = walk(r, c+1, 1);
55944                 break;
55945             
55946             case e.LEFT:
55947                 newCell = walk(r, c-1, -1);
55948                 break;
55949             
55950             case e.ENTER:
55951                 
55952                 if(g.isEditor && !g.editing){
55953                    g.startEditing(r, c);
55954                    e.stopEvent();
55955                    return;
55956                 }
55957                 
55958                 
55959              break;
55960         };
55961         if(newCell){
55962             this.select(newCell[0], newCell[1]);
55963             e.stopEvent();
55964             
55965         }
55966     },
55967
55968     acceptsNav : function(row, col, cm){
55969         return !cm.isHidden(col) && cm.isCellEditable(col, row);
55970     },
55971     /**
55972      * Selects a cell.
55973      * @param {Number} field (not used) - as it's normally used as a listener
55974      * @param {Number} e - event - fake it by using
55975      *
55976      * var e = Roo.EventObjectImpl.prototype;
55977      * e.keyCode = e.TAB
55978      *
55979      * 
55980      */
55981     onEditorKey : function(field, e){
55982         
55983         var k = e.getKey(),
55984             newCell,
55985             g = this.grid,
55986             ed = g.activeEditor,
55987             forward = false;
55988         ///Roo.log('onEditorKey' + k);
55989         
55990         
55991         if (this.enter_is_tab && k == e.ENTER) {
55992             k = e.TAB;
55993         }
55994         
55995         if(k == e.TAB){
55996             if(e.shiftKey){
55997                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
55998             }else{
55999                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
56000                 forward = true;
56001             }
56002             
56003             e.stopEvent();
56004             
56005         } else if(k == e.ENTER &&  !e.ctrlKey){
56006             ed.completeEdit();
56007             e.stopEvent();
56008             newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
56009         
56010                 } else if(k == e.ESC){
56011             ed.cancelEdit();
56012         }
56013                 
56014         if (newCell) {
56015             var ecall = { cell : newCell, forward : forward };
56016             this.fireEvent('beforeeditnext', ecall );
56017             newCell = ecall.cell;
56018                         forward = ecall.forward;
56019         }
56020                 
56021         if(newCell){
56022             //Roo.log('next cell after edit');
56023             g.startEditing.defer(100, g, [newCell[0], newCell[1]]);
56024         } else if (forward) {
56025             // tabbed past last
56026             this.fireEvent.defer(100, this, ['tabend',this]);
56027         }
56028     }
56029 });/*
56030  * Based on:
56031  * Ext JS Library 1.1.1
56032  * Copyright(c) 2006-2007, Ext JS, LLC.
56033  *
56034  * Originally Released Under LGPL - original licence link has changed is not relivant.
56035  *
56036  * Fork - LGPL
56037  * <script type="text/javascript">
56038  */
56039  
56040 /**
56041  * @class Roo.grid.EditorGrid
56042  * @extends Roo.grid.Grid
56043  * Class for creating and editable grid.
56044  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered - 
56045  * The container MUST have some type of size defined for the grid to fill. The container will be 
56046  * automatically set to position relative if it isn't already.
56047  * @param {Object} dataSource The data model to bind to
56048  * @param {Object} colModel The column model with info about this grid's columns
56049  */
56050 Roo.grid.EditorGrid = function(container, config){
56051     Roo.grid.EditorGrid.superclass.constructor.call(this, container, config);
56052     this.getGridEl().addClass("xedit-grid");
56053
56054     if(!this.selModel){
56055         this.selModel = new Roo.grid.CellSelectionModel();
56056     }
56057
56058     this.activeEditor = null;
56059
56060         this.addEvents({
56061             /**
56062              * @event beforeedit
56063              * Fires before cell editing is triggered. The edit event object has the following properties <br />
56064              * <ul style="padding:5px;padding-left:16px;">
56065              * <li>grid - This grid</li>
56066              * <li>record - The record being edited</li>
56067              * <li>field - The field name being edited</li>
56068              * <li>value - The value for the field being edited.</li>
56069              * <li>row - The grid row index</li>
56070              * <li>column - The grid column index</li>
56071              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
56072              * </ul>
56073              * @param {Object} e An edit event (see above for description)
56074              */
56075             "beforeedit" : true,
56076             /**
56077              * @event afteredit
56078              * Fires after a cell is edited. <br />
56079              * <ul style="padding:5px;padding-left:16px;">
56080              * <li>grid - This grid</li>
56081              * <li>record - The record being edited</li>
56082              * <li>field - The field name being edited</li>
56083              * <li>value - The value being set</li>
56084              * <li>originalValue - The original value for the field, before the edit.</li>
56085              * <li>row - The grid row index</li>
56086              * <li>column - The grid column index</li>
56087              * </ul>
56088              * @param {Object} e An edit event (see above for description)
56089              */
56090             "afteredit" : true,
56091             /**
56092              * @event validateedit
56093              * Fires after a cell is edited, but before the value is set in the record. 
56094          * You can use this to modify the value being set in the field, Return false
56095              * to cancel the change. The edit event object has the following properties <br />
56096              * <ul style="padding:5px;padding-left:16px;">
56097          * <li>editor - This editor</li>
56098              * <li>grid - This grid</li>
56099              * <li>record - The record being edited</li>
56100              * <li>field - The field name being edited</li>
56101              * <li>value - The value being set</li>
56102              * <li>originalValue - The original value for the field, before the edit.</li>
56103              * <li>row - The grid row index</li>
56104              * <li>column - The grid column index</li>
56105              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
56106              * </ul>
56107              * @param {Object} e An edit event (see above for description)
56108              */
56109             "validateedit" : true
56110         });
56111     this.on("bodyscroll", this.stopEditing,  this);
56112     this.on(this.clicksToEdit == 1 ? "cellclick" : "celldblclick", this.onCellDblClick,  this);
56113 };
56114
56115 Roo.extend(Roo.grid.EditorGrid, Roo.grid.Grid, {
56116     /**
56117      * @cfg {Number} clicksToEdit
56118      * The number of clicks on a cell required to display the cell's editor (defaults to 2)
56119      */
56120     clicksToEdit: 2,
56121
56122     // private
56123     isEditor : true,
56124     // private
56125     trackMouseOver: false, // causes very odd FF errors
56126
56127     onCellDblClick : function(g, row, col){
56128         this.startEditing(row, col);
56129     },
56130
56131     onEditComplete : function(ed, value, startValue){
56132         this.editing = false;
56133         this.activeEditor = null;
56134         ed.un("specialkey", this.selModel.onEditorKey, this.selModel);
56135         var r = ed.record;
56136         var field = this.colModel.getDataIndex(ed.col);
56137         var e = {
56138             grid: this,
56139             record: r,
56140             field: field,
56141             originalValue: startValue,
56142             value: value,
56143             row: ed.row,
56144             column: ed.col,
56145             cancel:false,
56146             editor: ed
56147         };
56148         var cell = Roo.get(this.view.getCell(ed.row,ed.col))
56149         cell.show();
56150           
56151         if(String(value) !== String(startValue)){
56152             
56153             if(this.fireEvent("validateedit", e) !== false && !e.cancel){
56154                 r.set(field, e.value);
56155                 // if we are dealing with a combo box..
56156                 // then we also set the 'name' colum to be the displayField
56157                 if (ed.field.displayField && ed.field.name) {
56158                     r.set(ed.field.name, ed.field.el.dom.value);
56159                 }
56160                 
56161                 delete e.cancel; //?? why!!!
56162                 this.fireEvent("afteredit", e);
56163             }
56164         } else {
56165             this.fireEvent("afteredit", e); // always fire it!
56166         }
56167         this.view.focusCell(ed.row, ed.col);
56168     },
56169
56170     /**
56171      * Starts editing the specified for the specified row/column
56172      * @param {Number} rowIndex
56173      * @param {Number} colIndex
56174      */
56175     startEditing : function(row, col){
56176         this.stopEditing();
56177         if(this.colModel.isCellEditable(col, row)){
56178             this.view.ensureVisible(row, col, true);
56179           
56180             var r = this.dataSource.getAt(row);
56181             var field = this.colModel.getDataIndex(col);
56182             var cell = Roo.get(this.view.getCell(row,col));
56183             var e = {
56184                 grid: this,
56185                 record: r,
56186                 field: field,
56187                 value: r.data[field],
56188                 row: row,
56189                 column: col,
56190                 cancel:false 
56191             };
56192             if(this.fireEvent("beforeedit", e) !== false && !e.cancel){
56193                 this.editing = true;
56194                 var ed = this.colModel.getCellEditor(col, row);
56195                 
56196                 if (!ed) {
56197                     return;
56198                 }
56199                 if(!ed.rendered){
56200                     ed.render(ed.parentEl || document.body);
56201                 }
56202                 ed.field.reset();
56203                
56204                 cell.hide();
56205                 
56206                 (function(){ // complex but required for focus issues in safari, ie and opera
56207                     ed.row = row;
56208                     ed.col = col;
56209                     ed.record = r;
56210                     ed.on("complete",   this.onEditComplete,        this,       {single: true});
56211                     ed.on("specialkey", this.selModel.onEditorKey,  this.selModel);
56212                     this.activeEditor = ed;
56213                     var v = r.data[field];
56214                     ed.startEdit(this.view.getCell(row, col), v);
56215                     // combo's with 'displayField and name set
56216                     if (ed.field.displayField && ed.field.name) {
56217                         ed.field.el.dom.value = r.data[ed.field.name];
56218                     }
56219                     
56220                     
56221                 }).defer(50, this);
56222             }
56223         }
56224     },
56225         
56226     /**
56227      * Stops any active editing
56228      */
56229     stopEditing : function(){
56230         if(this.activeEditor){
56231             this.activeEditor.completeEdit();
56232         }
56233         this.activeEditor = null;
56234     },
56235         
56236          /**
56237      * Called to get grid's drag proxy text, by default returns this.ddText.
56238      * @return {String}
56239      */
56240     getDragDropText : function(){
56241         var count = this.selModel.getSelectedCell() ? 1 : 0;
56242         return String.format(this.ddText, count, count == 1 ? '' : 's');
56243     }
56244         
56245 });/*
56246  * Based on:
56247  * Ext JS Library 1.1.1
56248  * Copyright(c) 2006-2007, Ext JS, LLC.
56249  *
56250  * Originally Released Under LGPL - original licence link has changed is not relivant.
56251  *
56252  * Fork - LGPL
56253  * <script type="text/javascript">
56254  */
56255
56256 // private - not really -- you end up using it !
56257 // This is a support class used internally by the Grid components
56258
56259 /**
56260  * @class Roo.grid.GridEditor
56261  * @extends Roo.Editor
56262  * Class for creating and editable grid elements.
56263  * @param {Object} config any settings (must include field)
56264  */
56265 Roo.grid.GridEditor = function(field, config){
56266     if (!config && field.field) {
56267         config = field;
56268         field = Roo.factory(config.field, Roo.form);
56269     }
56270     Roo.grid.GridEditor.superclass.constructor.call(this, field, config);
56271     field.monitorTab = false;
56272 };
56273
56274 Roo.extend(Roo.grid.GridEditor, Roo.Editor, {
56275     
56276     /**
56277      * @cfg {Roo.form.Field} field Field to wrap (or xtyped)
56278      */
56279     
56280     alignment: "tl-tl",
56281     autoSize: "width",
56282     hideEl : false,
56283     cls: "x-small-editor x-grid-editor",
56284     shim:false,
56285     shadow:"frame"
56286 });/*
56287  * Based on:
56288  * Ext JS Library 1.1.1
56289  * Copyright(c) 2006-2007, Ext JS, LLC.
56290  *
56291  * Originally Released Under LGPL - original licence link has changed is not relivant.
56292  *
56293  * Fork - LGPL
56294  * <script type="text/javascript">
56295  */
56296   
56297
56298   
56299 Roo.grid.PropertyRecord = Roo.data.Record.create([
56300     {name:'name',type:'string'},  'value'
56301 ]);
56302
56303
56304 Roo.grid.PropertyStore = function(grid, source){
56305     this.grid = grid;
56306     this.store = new Roo.data.Store({
56307         recordType : Roo.grid.PropertyRecord
56308     });
56309     this.store.on('update', this.onUpdate,  this);
56310     if(source){
56311         this.setSource(source);
56312     }
56313     Roo.grid.PropertyStore.superclass.constructor.call(this);
56314 };
56315
56316
56317
56318 Roo.extend(Roo.grid.PropertyStore, Roo.util.Observable, {
56319     setSource : function(o){
56320         this.source = o;
56321         this.store.removeAll();
56322         var data = [];
56323         for(var k in o){
56324             if(this.isEditableValue(o[k])){
56325                 data.push(new Roo.grid.PropertyRecord({name: k, value: o[k]}, k));
56326             }
56327         }
56328         this.store.loadRecords({records: data}, {}, true);
56329     },
56330
56331     onUpdate : function(ds, record, type){
56332         if(type == Roo.data.Record.EDIT){
56333             var v = record.data['value'];
56334             var oldValue = record.modified['value'];
56335             if(this.grid.fireEvent('beforepropertychange', this.source, record.id, v, oldValue) !== false){
56336                 this.source[record.id] = v;
56337                 record.commit();
56338                 this.grid.fireEvent('propertychange', this.source, record.id, v, oldValue);
56339             }else{
56340                 record.reject();
56341             }
56342         }
56343     },
56344
56345     getProperty : function(row){
56346        return this.store.getAt(row);
56347     },
56348
56349     isEditableValue: function(val){
56350         if(val && val instanceof Date){
56351             return true;
56352         }else if(typeof val == 'object' || typeof val == 'function'){
56353             return false;
56354         }
56355         return true;
56356     },
56357
56358     setValue : function(prop, value){
56359         this.source[prop] = value;
56360         this.store.getById(prop).set('value', value);
56361     },
56362
56363     getSource : function(){
56364         return this.source;
56365     }
56366 });
56367
56368 Roo.grid.PropertyColumnModel = function(grid, store){
56369     this.grid = grid;
56370     var g = Roo.grid;
56371     g.PropertyColumnModel.superclass.constructor.call(this, [
56372         {header: this.nameText, sortable: true, dataIndex:'name', id: 'name'},
56373         {header: this.valueText, resizable:false, dataIndex: 'value', id: 'value'}
56374     ]);
56375     this.store = store;
56376     this.bselect = Roo.DomHelper.append(document.body, {
56377         tag: 'select', style:'display:none', cls: 'x-grid-editor', children: [
56378             {tag: 'option', value: 'true', html: 'true'},
56379             {tag: 'option', value: 'false', html: 'false'}
56380         ]
56381     });
56382     Roo.id(this.bselect);
56383     var f = Roo.form;
56384     this.editors = {
56385         'date' : new g.GridEditor(new f.DateField({selectOnFocus:true})),
56386         'string' : new g.GridEditor(new f.TextField({selectOnFocus:true})),
56387         'number' : new g.GridEditor(new f.NumberField({selectOnFocus:true, style:'text-align:left;'})),
56388         'int' : new g.GridEditor(new f.NumberField({selectOnFocus:true, allowDecimals:false, style:'text-align:left;'})),
56389         'boolean' : new g.GridEditor(new f.Field({el:this.bselect,selectOnFocus:true}))
56390     };
56391     this.renderCellDelegate = this.renderCell.createDelegate(this);
56392     this.renderPropDelegate = this.renderProp.createDelegate(this);
56393 };
56394
56395 Roo.extend(Roo.grid.PropertyColumnModel, Roo.grid.ColumnModel, {
56396     
56397     
56398     nameText : 'Name',
56399     valueText : 'Value',
56400     
56401     dateFormat : 'm/j/Y',
56402     
56403     
56404     renderDate : function(dateVal){
56405         return dateVal.dateFormat(this.dateFormat);
56406     },
56407
56408     renderBool : function(bVal){
56409         return bVal ? 'true' : 'false';
56410     },
56411
56412     isCellEditable : function(colIndex, rowIndex){
56413         return colIndex == 1;
56414     },
56415
56416     getRenderer : function(col){
56417         return col == 1 ?
56418             this.renderCellDelegate : this.renderPropDelegate;
56419     },
56420
56421     renderProp : function(v){
56422         return this.getPropertyName(v);
56423     },
56424
56425     renderCell : function(val){
56426         var rv = val;
56427         if(val instanceof Date){
56428             rv = this.renderDate(val);
56429         }else if(typeof val == 'boolean'){
56430             rv = this.renderBool(val);
56431         }
56432         return Roo.util.Format.htmlEncode(rv);
56433     },
56434
56435     getPropertyName : function(name){
56436         var pn = this.grid.propertyNames;
56437         return pn && pn[name] ? pn[name] : name;
56438     },
56439
56440     getCellEditor : function(colIndex, rowIndex){
56441         var p = this.store.getProperty(rowIndex);
56442         var n = p.data['name'], val = p.data['value'];
56443         
56444         if(typeof(this.grid.customEditors[n]) == 'string'){
56445             return this.editors[this.grid.customEditors[n]];
56446         }
56447         if(typeof(this.grid.customEditors[n]) != 'undefined'){
56448             return this.grid.customEditors[n];
56449         }
56450         if(val instanceof Date){
56451             return this.editors['date'];
56452         }else if(typeof val == 'number'){
56453             return this.editors['number'];
56454         }else if(typeof val == 'boolean'){
56455             return this.editors['boolean'];
56456         }else{
56457             return this.editors['string'];
56458         }
56459     }
56460 });
56461
56462 /**
56463  * @class Roo.grid.PropertyGrid
56464  * @extends Roo.grid.EditorGrid
56465  * This class represents the  interface of a component based property grid control.
56466  * <br><br>Usage:<pre><code>
56467  var grid = new Roo.grid.PropertyGrid("my-container-id", {
56468       
56469  });
56470  // set any options
56471  grid.render();
56472  * </code></pre>
56473   
56474  * @constructor
56475  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
56476  * The container MUST have some type of size defined for the grid to fill. The container will be
56477  * automatically set to position relative if it isn't already.
56478  * @param {Object} config A config object that sets properties on this grid.
56479  */
56480 Roo.grid.PropertyGrid = function(container, config){
56481     config = config || {};
56482     var store = new Roo.grid.PropertyStore(this);
56483     this.store = store;
56484     var cm = new Roo.grid.PropertyColumnModel(this, store);
56485     store.store.sort('name', 'ASC');
56486     Roo.grid.PropertyGrid.superclass.constructor.call(this, container, Roo.apply({
56487         ds: store.store,
56488         cm: cm,
56489         enableColLock:false,
56490         enableColumnMove:false,
56491         stripeRows:false,
56492         trackMouseOver: false,
56493         clicksToEdit:1
56494     }, config));
56495     this.getGridEl().addClass('x-props-grid');
56496     this.lastEditRow = null;
56497     this.on('columnresize', this.onColumnResize, this);
56498     this.addEvents({
56499          /**
56500              * @event beforepropertychange
56501              * Fires before a property changes (return false to stop?)
56502              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
56503              * @param {String} id Record Id
56504              * @param {String} newval New Value
56505          * @param {String} oldval Old Value
56506              */
56507         "beforepropertychange": true,
56508         /**
56509              * @event propertychange
56510              * Fires after a property changes
56511              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
56512              * @param {String} id Record Id
56513              * @param {String} newval New Value
56514          * @param {String} oldval Old Value
56515              */
56516         "propertychange": true
56517     });
56518     this.customEditors = this.customEditors || {};
56519 };
56520 Roo.extend(Roo.grid.PropertyGrid, Roo.grid.EditorGrid, {
56521     
56522      /**
56523      * @cfg {Object} customEditors map of colnames=> custom editors.
56524      * the custom editor can be one of the standard ones (date|string|number|int|boolean), or a
56525      * grid editor eg. Roo.grid.GridEditor(new Roo.form.TextArea({selectOnFocus:true})),
56526      * false disables editing of the field.
56527          */
56528     
56529       /**
56530      * @cfg {Object} propertyNames map of property Names to their displayed value
56531          */
56532     
56533     render : function(){
56534         Roo.grid.PropertyGrid.superclass.render.call(this);
56535         this.autoSize.defer(100, this);
56536     },
56537
56538     autoSize : function(){
56539         Roo.grid.PropertyGrid.superclass.autoSize.call(this);
56540         if(this.view){
56541             this.view.fitColumns();
56542         }
56543     },
56544
56545     onColumnResize : function(){
56546         this.colModel.setColumnWidth(1, this.container.getWidth(true)-this.colModel.getColumnWidth(0));
56547         this.autoSize();
56548     },
56549     /**
56550      * Sets the data for the Grid
56551      * accepts a Key => Value object of all the elements avaiable.
56552      * @param {Object} data  to appear in grid.
56553      */
56554     setSource : function(source){
56555         this.store.setSource(source);
56556         //this.autoSize();
56557     },
56558     /**
56559      * Gets all the data from the grid.
56560      * @return {Object} data  data stored in grid
56561      */
56562     getSource : function(){
56563         return this.store.getSource();
56564     }
56565 });/*
56566   
56567  * Licence LGPL
56568  
56569  */
56570  
56571 /**
56572  * @class Roo.grid.Calendar
56573  * @extends Roo.util.Grid
56574  * This class extends the Grid to provide a calendar widget
56575  * <br><br>Usage:<pre><code>
56576  var grid = new Roo.grid.Calendar("my-container-id", {
56577      ds: myDataStore,
56578      cm: myColModel,
56579      selModel: mySelectionModel,
56580      autoSizeColumns: true,
56581      monitorWindowResize: false,
56582      trackMouseOver: true
56583      eventstore : real data store..
56584  });
56585  // set any options
56586  grid.render();
56587   
56588   * @constructor
56589  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
56590  * The container MUST have some type of size defined for the grid to fill. The container will be
56591  * automatically set to position relative if it isn't already.
56592  * @param {Object} config A config object that sets properties on this grid.
56593  */
56594 Roo.grid.Calendar = function(container, config){
56595         // initialize the container
56596         this.container = Roo.get(container);
56597         this.container.update("");
56598         this.container.setStyle("overflow", "hidden");
56599     this.container.addClass('x-grid-container');
56600
56601     this.id = this.container.id;
56602
56603     Roo.apply(this, config);
56604     // check and correct shorthanded configs
56605     
56606     var rows = [];
56607     var d =1;
56608     for (var r = 0;r < 6;r++) {
56609         
56610         rows[r]=[];
56611         for (var c =0;c < 7;c++) {
56612             rows[r][c]= '';
56613         }
56614     }
56615     if (this.eventStore) {
56616         this.eventStore= Roo.factory(this.eventStore, Roo.data);
56617         this.eventStore.on('load',this.onLoad, this);
56618         this.eventStore.on('beforeload',this.clearEvents, this);
56619          
56620     }
56621     
56622     this.dataSource = new Roo.data.Store({
56623             proxy: new Roo.data.MemoryProxy(rows),
56624             reader: new Roo.data.ArrayReader({}, [
56625                    'weekday0', 'weekday1', 'weekday2', 'weekday3', 'weekday4', 'weekday5', 'weekday6' ])
56626     });
56627
56628     this.dataSource.load();
56629     this.ds = this.dataSource;
56630     this.ds.xmodule = this.xmodule || false;
56631     
56632     
56633     var cellRender = function(v,x,r)
56634     {
56635         return String.format(
56636             '<div class="fc-day  fc-widget-content"><div>' +
56637                 '<div class="fc-event-container"></div>' +
56638                 '<div class="fc-day-number">{0}</div>'+
56639                 
56640                 '<div class="fc-day-content"><div style="position:relative"></div></div>' +
56641             '</div></div>', v);
56642     
56643     }
56644     
56645     
56646     this.colModel = new Roo.grid.ColumnModel( [
56647         {
56648             xtype: 'ColumnModel',
56649             xns: Roo.grid,
56650             dataIndex : 'weekday0',
56651             header : 'Sunday',
56652             renderer : cellRender
56653         },
56654         {
56655             xtype: 'ColumnModel',
56656             xns: Roo.grid,
56657             dataIndex : 'weekday1',
56658             header : 'Monday',
56659             renderer : cellRender
56660         },
56661         {
56662             xtype: 'ColumnModel',
56663             xns: Roo.grid,
56664             dataIndex : 'weekday2',
56665             header : 'Tuesday',
56666             renderer : cellRender
56667         },
56668         {
56669             xtype: 'ColumnModel',
56670             xns: Roo.grid,
56671             dataIndex : 'weekday3',
56672             header : 'Wednesday',
56673             renderer : cellRender
56674         },
56675         {
56676             xtype: 'ColumnModel',
56677             xns: Roo.grid,
56678             dataIndex : 'weekday4',
56679             header : 'Thursday',
56680             renderer : cellRender
56681         },
56682         {
56683             xtype: 'ColumnModel',
56684             xns: Roo.grid,
56685             dataIndex : 'weekday5',
56686             header : 'Friday',
56687             renderer : cellRender
56688         },
56689         {
56690             xtype: 'ColumnModel',
56691             xns: Roo.grid,
56692             dataIndex : 'weekday6',
56693             header : 'Saturday',
56694             renderer : cellRender
56695         }
56696     ]);
56697     this.cm = this.colModel;
56698     this.cm.xmodule = this.xmodule || false;
56699  
56700         
56701           
56702     //this.selModel = new Roo.grid.CellSelectionModel();
56703     //this.sm = this.selModel;
56704     //this.selModel.init(this);
56705     
56706     
56707     if(this.width){
56708         this.container.setWidth(this.width);
56709     }
56710
56711     if(this.height){
56712         this.container.setHeight(this.height);
56713     }
56714     /** @private */
56715         this.addEvents({
56716         // raw events
56717         /**
56718          * @event click
56719          * The raw click event for the entire grid.
56720          * @param {Roo.EventObject} e
56721          */
56722         "click" : true,
56723         /**
56724          * @event dblclick
56725          * The raw dblclick event for the entire grid.
56726          * @param {Roo.EventObject} e
56727          */
56728         "dblclick" : true,
56729         /**
56730          * @event contextmenu
56731          * The raw contextmenu event for the entire grid.
56732          * @param {Roo.EventObject} e
56733          */
56734         "contextmenu" : true,
56735         /**
56736          * @event mousedown
56737          * The raw mousedown event for the entire grid.
56738          * @param {Roo.EventObject} e
56739          */
56740         "mousedown" : true,
56741         /**
56742          * @event mouseup
56743          * The raw mouseup event for the entire grid.
56744          * @param {Roo.EventObject} e
56745          */
56746         "mouseup" : true,
56747         /**
56748          * @event mouseover
56749          * The raw mouseover event for the entire grid.
56750          * @param {Roo.EventObject} e
56751          */
56752         "mouseover" : true,
56753         /**
56754          * @event mouseout
56755          * The raw mouseout event for the entire grid.
56756          * @param {Roo.EventObject} e
56757          */
56758         "mouseout" : true,
56759         /**
56760          * @event keypress
56761          * The raw keypress event for the entire grid.
56762          * @param {Roo.EventObject} e
56763          */
56764         "keypress" : true,
56765         /**
56766          * @event keydown
56767          * The raw keydown event for the entire grid.
56768          * @param {Roo.EventObject} e
56769          */
56770         "keydown" : true,
56771
56772         // custom events
56773
56774         /**
56775          * @event cellclick
56776          * Fires when a cell is clicked
56777          * @param {Grid} this
56778          * @param {Number} rowIndex
56779          * @param {Number} columnIndex
56780          * @param {Roo.EventObject} e
56781          */
56782         "cellclick" : true,
56783         /**
56784          * @event celldblclick
56785          * Fires when a cell is double clicked
56786          * @param {Grid} this
56787          * @param {Number} rowIndex
56788          * @param {Number} columnIndex
56789          * @param {Roo.EventObject} e
56790          */
56791         "celldblclick" : true,
56792         /**
56793          * @event rowclick
56794          * Fires when a row is clicked
56795          * @param {Grid} this
56796          * @param {Number} rowIndex
56797          * @param {Roo.EventObject} e
56798          */
56799         "rowclick" : true,
56800         /**
56801          * @event rowdblclick
56802          * Fires when a row is double clicked
56803          * @param {Grid} this
56804          * @param {Number} rowIndex
56805          * @param {Roo.EventObject} e
56806          */
56807         "rowdblclick" : true,
56808         /**
56809          * @event headerclick
56810          * Fires when a header is clicked
56811          * @param {Grid} this
56812          * @param {Number} columnIndex
56813          * @param {Roo.EventObject} e
56814          */
56815         "headerclick" : true,
56816         /**
56817          * @event headerdblclick
56818          * Fires when a header cell is double clicked
56819          * @param {Grid} this
56820          * @param {Number} columnIndex
56821          * @param {Roo.EventObject} e
56822          */
56823         "headerdblclick" : true,
56824         /**
56825          * @event rowcontextmenu
56826          * Fires when a row is right clicked
56827          * @param {Grid} this
56828          * @param {Number} rowIndex
56829          * @param {Roo.EventObject} e
56830          */
56831         "rowcontextmenu" : true,
56832         /**
56833          * @event cellcontextmenu
56834          * Fires when a cell is right clicked
56835          * @param {Grid} this
56836          * @param {Number} rowIndex
56837          * @param {Number} cellIndex
56838          * @param {Roo.EventObject} e
56839          */
56840          "cellcontextmenu" : true,
56841         /**
56842          * @event headercontextmenu
56843          * Fires when a header is right clicked
56844          * @param {Grid} this
56845          * @param {Number} columnIndex
56846          * @param {Roo.EventObject} e
56847          */
56848         "headercontextmenu" : true,
56849         /**
56850          * @event bodyscroll
56851          * Fires when the body element is scrolled
56852          * @param {Number} scrollLeft
56853          * @param {Number} scrollTop
56854          */
56855         "bodyscroll" : true,
56856         /**
56857          * @event columnresize
56858          * Fires when the user resizes a column
56859          * @param {Number} columnIndex
56860          * @param {Number} newSize
56861          */
56862         "columnresize" : true,
56863         /**
56864          * @event columnmove
56865          * Fires when the user moves a column
56866          * @param {Number} oldIndex
56867          * @param {Number} newIndex
56868          */
56869         "columnmove" : true,
56870         /**
56871          * @event startdrag
56872          * Fires when row(s) start being dragged
56873          * @param {Grid} this
56874          * @param {Roo.GridDD} dd The drag drop object
56875          * @param {event} e The raw browser event
56876          */
56877         "startdrag" : true,
56878         /**
56879          * @event enddrag
56880          * Fires when a drag operation is complete
56881          * @param {Grid} this
56882          * @param {Roo.GridDD} dd The drag drop object
56883          * @param {event} e The raw browser event
56884          */
56885         "enddrag" : true,
56886         /**
56887          * @event dragdrop
56888          * Fires when dragged row(s) are dropped on a valid DD target
56889          * @param {Grid} this
56890          * @param {Roo.GridDD} dd The drag drop object
56891          * @param {String} targetId The target drag drop object
56892          * @param {event} e The raw browser event
56893          */
56894         "dragdrop" : true,
56895         /**
56896          * @event dragover
56897          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
56898          * @param {Grid} this
56899          * @param {Roo.GridDD} dd The drag drop object
56900          * @param {String} targetId The target drag drop object
56901          * @param {event} e The raw browser event
56902          */
56903         "dragover" : true,
56904         /**
56905          * @event dragenter
56906          *  Fires when the dragged row(s) first cross another DD target while being dragged
56907          * @param {Grid} this
56908          * @param {Roo.GridDD} dd The drag drop object
56909          * @param {String} targetId The target drag drop object
56910          * @param {event} e The raw browser event
56911          */
56912         "dragenter" : true,
56913         /**
56914          * @event dragout
56915          * Fires when the dragged row(s) leave another DD target while being dragged
56916          * @param {Grid} this
56917          * @param {Roo.GridDD} dd The drag drop object
56918          * @param {String} targetId The target drag drop object
56919          * @param {event} e The raw browser event
56920          */
56921         "dragout" : true,
56922         /**
56923          * @event rowclass
56924          * Fires when a row is rendered, so you can change add a style to it.
56925          * @param {GridView} gridview   The grid view
56926          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
56927          */
56928         'rowclass' : true,
56929
56930         /**
56931          * @event render
56932          * Fires when the grid is rendered
56933          * @param {Grid} grid
56934          */
56935         'render' : true,
56936             /**
56937              * @event select
56938              * Fires when a date is selected
56939              * @param {DatePicker} this
56940              * @param {Date} date The selected date
56941              */
56942         'select': true,
56943         /**
56944              * @event monthchange
56945              * Fires when the displayed month changes 
56946              * @param {DatePicker} this
56947              * @param {Date} date The selected month
56948              */
56949         'monthchange': true,
56950         /**
56951              * @event evententer
56952              * Fires when mouse over an event
56953              * @param {Calendar} this
56954              * @param {event} Event
56955              */
56956         'evententer': true,
56957         /**
56958              * @event eventleave
56959              * Fires when the mouse leaves an
56960              * @param {Calendar} this
56961              * @param {event}
56962              */
56963         'eventleave': true,
56964         /**
56965              * @event eventclick
56966              * Fires when the mouse click an
56967              * @param {Calendar} this
56968              * @param {event}
56969              */
56970         'eventclick': true,
56971         /**
56972              * @event eventrender
56973              * Fires before each cell is rendered, so you can modify the contents, like cls / title / qtip
56974              * @param {Calendar} this
56975              * @param {data} data to be modified
56976              */
56977         'eventrender': true
56978         
56979     });
56980
56981     Roo.grid.Grid.superclass.constructor.call(this);
56982     this.on('render', function() {
56983         this.view.el.addClass('x-grid-cal'); 
56984         
56985         (function() { this.setDate(new Date()); }).defer(100,this); //default today..
56986
56987     },this);
56988     
56989     if (!Roo.grid.Calendar.style) {
56990         Roo.grid.Calendar.style = Roo.util.CSS.createStyleSheet({
56991             
56992             
56993             '.x-grid-cal .x-grid-col' :  {
56994                 height: 'auto !important',
56995                 'vertical-align': 'top'
56996             },
56997             '.x-grid-cal  .fc-event-hori' : {
56998                 height: '14px'
56999             }
57000              
57001             
57002         }, Roo.id());
57003     }
57004
57005     
57006     
57007 };
57008 Roo.extend(Roo.grid.Calendar, Roo.grid.Grid, {
57009     /**
57010      * @cfg {Store} eventStore The store that loads events.
57011      */
57012     eventStore : 25,
57013
57014      
57015     activeDate : false,
57016     startDay : 0,
57017     autoWidth : true,
57018     monitorWindowResize : false,
57019
57020     
57021     resizeColumns : function() {
57022         var col = (this.view.el.getWidth() / 7) - 3;
57023         // loop through cols, and setWidth
57024         for(var i =0 ; i < 7 ; i++){
57025             this.cm.setColumnWidth(i, col);
57026         }
57027     },
57028      setDate :function(date) {
57029         
57030         Roo.log('setDate?');
57031         
57032         this.resizeColumns();
57033         var vd = this.activeDate;
57034         this.activeDate = date;
57035 //        if(vd && this.el){
57036 //            var t = date.getTime();
57037 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
57038 //                Roo.log('using add remove');
57039 //                
57040 //                this.fireEvent('monthchange', this, date);
57041 //                
57042 //                this.cells.removeClass("fc-state-highlight");
57043 //                this.cells.each(function(c){
57044 //                   if(c.dateValue == t){
57045 //                       c.addClass("fc-state-highlight");
57046 //                       setTimeout(function(){
57047 //                            try{c.dom.firstChild.focus();}catch(e){}
57048 //                       }, 50);
57049 //                       return false;
57050 //                   }
57051 //                   return true;
57052 //                });
57053 //                return;
57054 //            }
57055 //        }
57056         
57057         var days = date.getDaysInMonth();
57058         
57059         var firstOfMonth = date.getFirstDateOfMonth();
57060         var startingPos = firstOfMonth.getDay()-this.startDay;
57061         
57062         if(startingPos < this.startDay){
57063             startingPos += 7;
57064         }
57065         
57066         var pm = date.add(Date.MONTH, -1);
57067         var prevStart = pm.getDaysInMonth()-startingPos;
57068 //        
57069         
57070         
57071         this.cells = this.view.el.select('.x-grid-row .x-grid-col',true);
57072         
57073         this.textNodes = this.view.el.query('.x-grid-row .x-grid-col .x-grid-cell-text');
57074         //this.cells.addClassOnOver('fc-state-hover');
57075         
57076         var cells = this.cells.elements;
57077         var textEls = this.textNodes;
57078         
57079         //Roo.each(cells, function(cell){
57080         //    cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
57081         //});
57082         
57083         days += startingPos;
57084
57085         // convert everything to numbers so it's fast
57086         var day = 86400000;
57087         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
57088         //Roo.log(d);
57089         //Roo.log(pm);
57090         //Roo.log(prevStart);
57091         
57092         var today = new Date().clearTime().getTime();
57093         var sel = date.clearTime().getTime();
57094         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
57095         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
57096         var ddMatch = this.disabledDatesRE;
57097         var ddText = this.disabledDatesText;
57098         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
57099         var ddaysText = this.disabledDaysText;
57100         var format = this.format;
57101         
57102         var setCellClass = function(cal, cell){
57103             
57104             //Roo.log('set Cell Class');
57105             cell.title = "";
57106             var t = d.getTime();
57107             
57108             //Roo.log(d);
57109             
57110             
57111             cell.dateValue = t;
57112             if(t == today){
57113                 cell.className += " fc-today";
57114                 cell.className += " fc-state-highlight";
57115                 cell.title = cal.todayText;
57116             }
57117             if(t == sel){
57118                 // disable highlight in other month..
57119                 cell.className += " fc-state-highlight";
57120                 
57121             }
57122             // disabling
57123             if(t < min) {
57124                 //cell.className = " fc-state-disabled";
57125                 cell.title = cal.minText;
57126                 return;
57127             }
57128             if(t > max) {
57129                 //cell.className = " fc-state-disabled";
57130                 cell.title = cal.maxText;
57131                 return;
57132             }
57133             if(ddays){
57134                 if(ddays.indexOf(d.getDay()) != -1){
57135                     // cell.title = ddaysText;
57136                    // cell.className = " fc-state-disabled";
57137                 }
57138             }
57139             if(ddMatch && format){
57140                 var fvalue = d.dateFormat(format);
57141                 if(ddMatch.test(fvalue)){
57142                     cell.title = ddText.replace("%0", fvalue);
57143                    cell.className = " fc-state-disabled";
57144                 }
57145             }
57146             
57147             if (!cell.initialClassName) {
57148                 cell.initialClassName = cell.dom.className;
57149             }
57150             
57151             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
57152         };
57153
57154         var i = 0;
57155         
57156         for(; i < startingPos; i++) {
57157             cells[i].dayName =  (++prevStart);
57158             Roo.log(textEls[i]);
57159             d.setDate(d.getDate()+1);
57160             
57161             //cells[i].className = "fc-past fc-other-month";
57162             setCellClass(this, cells[i]);
57163         }
57164         
57165         var intDay = 0;
57166         
57167         for(; i < days; i++){
57168             intDay = i - startingPos + 1;
57169             cells[i].dayName =  (intDay);
57170             d.setDate(d.getDate()+1);
57171             
57172             cells[i].className = ''; // "x-date-active";
57173             setCellClass(this, cells[i]);
57174         }
57175         var extraDays = 0;
57176         
57177         for(; i < 42; i++) {
57178             //textEls[i].innerHTML = (++extraDays);
57179             
57180             d.setDate(d.getDate()+1);
57181             cells[i].dayName = (++extraDays);
57182             cells[i].className = "fc-future fc-other-month";
57183             setCellClass(this, cells[i]);
57184         }
57185         
57186         //this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
57187         
57188         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
57189         
57190         // this will cause all the cells to mis
57191         var rows= [];
57192         var i =0;
57193         for (var r = 0;r < 6;r++) {
57194             for (var c =0;c < 7;c++) {
57195                 this.ds.getAt(r).set('weekday' + c ,cells[i++].dayName );
57196             }    
57197         }
57198         
57199         this.cells = this.view.el.select('.x-grid-row .x-grid-col',true);
57200         for(i=0;i<cells.length;i++) {
57201             
57202             this.cells.elements[i].dayName = cells[i].dayName ;
57203             this.cells.elements[i].className = cells[i].className;
57204             this.cells.elements[i].initialClassName = cells[i].initialClassName ;
57205             this.cells.elements[i].title = cells[i].title ;
57206             this.cells.elements[i].dateValue = cells[i].dateValue ;
57207         }
57208         
57209         
57210         
57211         
57212         //this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
57213         //this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
57214         
57215         ////if(totalRows != 6){
57216             //this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
57217            // this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
57218        // }
57219         
57220         this.fireEvent('monthchange', this, date);
57221         
57222         
57223     },
57224  /**
57225      * Returns the grid's SelectionModel.
57226      * @return {SelectionModel}
57227      */
57228     getSelectionModel : function(){
57229         if(!this.selModel){
57230             this.selModel = new Roo.grid.CellSelectionModel();
57231         }
57232         return this.selModel;
57233     },
57234
57235     load: function() {
57236         this.eventStore.load()
57237         
57238         
57239         
57240     },
57241     
57242     findCell : function(dt) {
57243         dt = dt.clearTime().getTime();
57244         var ret = false;
57245         this.cells.each(function(c){
57246             //Roo.log("check " +c.dateValue + '?=' + dt);
57247             if(c.dateValue == dt){
57248                 ret = c;
57249                 return false;
57250             }
57251             return true;
57252         });
57253         
57254         return ret;
57255     },
57256     
57257     findCells : function(rec) {
57258         var s = rec.data.start_dt.clone().clearTime().getTime();
57259        // Roo.log(s);
57260         var e= rec.data.end_dt.clone().clearTime().getTime();
57261        // Roo.log(e);
57262         var ret = [];
57263         this.cells.each(function(c){
57264              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
57265             
57266             if(c.dateValue > e){
57267                 return ;
57268             }
57269             if(c.dateValue < s){
57270                 return ;
57271             }
57272             ret.push(c);
57273         });
57274         
57275         return ret;    
57276     },
57277     
57278     findBestRow: function(cells)
57279     {
57280         var ret = 0;
57281         
57282         for (var i =0 ; i < cells.length;i++) {
57283             ret  = Math.max(cells[i].rows || 0,ret);
57284         }
57285         return ret;
57286         
57287     },
57288     
57289     
57290     addItem : function(rec)
57291     {
57292         // look for vertical location slot in
57293         var cells = this.findCells(rec);
57294         
57295         rec.row = this.findBestRow(cells);
57296         
57297         // work out the location.
57298         
57299         var crow = false;
57300         var rows = [];
57301         for(var i =0; i < cells.length; i++) {
57302             if (!crow) {
57303                 crow = {
57304                     start : cells[i],
57305                     end :  cells[i]
57306                 };
57307                 continue;
57308             }
57309             if (crow.start.getY() == cells[i].getY()) {
57310                 // on same row.
57311                 crow.end = cells[i];
57312                 continue;
57313             }
57314             // different row.
57315             rows.push(crow);
57316             crow = {
57317                 start: cells[i],
57318                 end : cells[i]
57319             };
57320             
57321         }
57322         
57323         rows.push(crow);
57324         rec.els = [];
57325         rec.rows = rows;
57326         rec.cells = cells;
57327         for (var i = 0; i < cells.length;i++) {
57328             cells[i].rows = Math.max(cells[i].rows || 0 , rec.row + 1 );
57329             
57330         }
57331         
57332         
57333     },
57334     
57335     clearEvents: function() {
57336         
57337         if (!this.eventStore.getCount()) {
57338             return;
57339         }
57340         // reset number of rows in cells.
57341         Roo.each(this.cells.elements, function(c){
57342             c.rows = 0;
57343         });
57344         
57345         this.eventStore.each(function(e) {
57346             this.clearEvent(e);
57347         },this);
57348         
57349     },
57350     
57351     clearEvent : function(ev)
57352     {
57353         if (ev.els) {
57354             Roo.each(ev.els, function(el) {
57355                 el.un('mouseenter' ,this.onEventEnter, this);
57356                 el.un('mouseleave' ,this.onEventLeave, this);
57357                 el.remove();
57358             },this);
57359             ev.els = [];
57360         }
57361     },
57362     
57363     
57364     renderEvent : function(ev,ctr) {
57365         if (!ctr) {
57366              ctr = this.view.el.select('.fc-event-container',true).first();
57367         }
57368         
57369          
57370         this.clearEvent(ev);
57371             //code
57372        
57373         
57374         
57375         ev.els = [];
57376         var cells = ev.cells;
57377         var rows = ev.rows;
57378         this.fireEvent('eventrender', this, ev);
57379         
57380         for(var i =0; i < rows.length; i++) {
57381             
57382             cls = '';
57383             if (i == 0) {
57384                 cls += ' fc-event-start';
57385             }
57386             if ((i+1) == rows.length) {
57387                 cls += ' fc-event-end';
57388             }
57389             
57390             //Roo.log(ev.data);
57391             // how many rows should it span..
57392             var cg = this.eventTmpl.append(ctr,Roo.apply({
57393                 fccls : cls
57394                 
57395             }, ev.data) , true);
57396             
57397             
57398             cg.on('mouseenter' ,this.onEventEnter, this, ev);
57399             cg.on('mouseleave' ,this.onEventLeave, this, ev);
57400             cg.on('click', this.onEventClick, this, ev);
57401             
57402             ev.els.push(cg);
57403             
57404             var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
57405             var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
57406             //Roo.log(cg);
57407              
57408             cg.setXY([sbox.x +2, sbox.y +(ev.row * 20)]);    
57409             cg.setWidth(ebox.right - sbox.x -2);
57410         }
57411     },
57412     
57413     renderEvents: function()
57414     {   
57415         // first make sure there is enough space..
57416         
57417         if (!this.eventTmpl) {
57418             this.eventTmpl = new Roo.Template(
57419                 '<div class="roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable {fccls} {cls}"  style="position: absolute" unselectable="on">' +
57420                     '<div class="fc-event-inner">' +
57421                         '<span class="fc-event-time">{time}</span>' +
57422                         '<span class="fc-event-title" qtip="{qtip}">{title}</span>' +
57423                     '</div>' +
57424                     '<div class="ui-resizable-heandle ui-resizable-e">&nbsp;&nbsp;&nbsp;</div>' +
57425                 '</div>'
57426             );
57427                 
57428         }
57429                
57430         
57431         
57432         this.cells.each(function(c) {
57433             //Roo.log(c.select('.fc-day-content div',true).first());
57434             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, (c.rows || 1) * 20));
57435         });
57436         
57437         var ctr = this.view.el.select('.fc-event-container',true).first();
57438         
57439         var cls;
57440         this.eventStore.each(function(ev){
57441             
57442             this.renderEvent(ev);
57443              
57444              
57445         }, this);
57446         this.view.layout();
57447         
57448     },
57449     
57450     onEventEnter: function (e, el,event,d) {
57451         this.fireEvent('evententer', this, el, event);
57452     },
57453     
57454     onEventLeave: function (e, el,event,d) {
57455         this.fireEvent('eventleave', this, el, event);
57456     },
57457     
57458     onEventClick: function (e, el,event,d) {
57459         this.fireEvent('eventclick', this, el, event);
57460     },
57461     
57462     onMonthChange: function () {
57463         this.store.load();
57464     },
57465     
57466     onLoad: function () {
57467         
57468         //Roo.log('calendar onload');
57469 //         
57470         if(this.eventStore.getCount() > 0){
57471             
57472            
57473             
57474             this.eventStore.each(function(d){
57475                 
57476                 
57477                 // FIXME..
57478                 var add =   d.data;
57479                 if (typeof(add.end_dt) == 'undefined')  {
57480                     Roo.log("Missing End time in calendar data: ");
57481                     Roo.log(d);
57482                     return;
57483                 }
57484                 if (typeof(add.start_dt) == 'undefined')  {
57485                     Roo.log("Missing Start time in calendar data: ");
57486                     Roo.log(d);
57487                     return;
57488                 }
57489                 add.start_dt = typeof(add.start_dt) == 'string' ? Date.parseDate(add.start_dt,'Y-m-d H:i:s') : add.start_dt,
57490                 add.end_dt = typeof(add.end_dt) == 'string' ? Date.parseDate(add.end_dt,'Y-m-d H:i:s') : add.end_dt,
57491                 add.id = add.id || d.id;
57492                 add.title = add.title || '??';
57493                 
57494                 this.addItem(d);
57495                 
57496              
57497             },this);
57498         }
57499         
57500         this.renderEvents();
57501     }
57502     
57503
57504 });
57505 /*
57506  grid : {
57507                 xtype: 'Grid',
57508                 xns: Roo.grid,
57509                 listeners : {
57510                     render : function ()
57511                     {
57512                         _this.grid = this;
57513                         
57514                         if (!this.view.el.hasClass('course-timesheet')) {
57515                             this.view.el.addClass('course-timesheet');
57516                         }
57517                         if (this.tsStyle) {
57518                             this.ds.load({});
57519                             return; 
57520                         }
57521                         Roo.log('width');
57522                         Roo.log(_this.grid.view.el.getWidth());
57523                         
57524                         
57525                         this.tsStyle =  Roo.util.CSS.createStyleSheet({
57526                             '.course-timesheet .x-grid-row' : {
57527                                 height: '80px'
57528                             },
57529                             '.x-grid-row td' : {
57530                                 'vertical-align' : 0
57531                             },
57532                             '.course-edit-link' : {
57533                                 'color' : 'blue',
57534                                 'text-overflow' : 'ellipsis',
57535                                 'overflow' : 'hidden',
57536                                 'white-space' : 'nowrap',
57537                                 'cursor' : 'pointer'
57538                             },
57539                             '.sub-link' : {
57540                                 'color' : 'green'
57541                             },
57542                             '.de-act-sup-link' : {
57543                                 'color' : 'purple',
57544                                 'text-decoration' : 'line-through'
57545                             },
57546                             '.de-act-link' : {
57547                                 'color' : 'red',
57548                                 'text-decoration' : 'line-through'
57549                             },
57550                             '.course-timesheet .course-highlight' : {
57551                                 'border-top-style': 'dashed !important',
57552                                 'border-bottom-bottom': 'dashed !important'
57553                             },
57554                             '.course-timesheet .course-item' : {
57555                                 'font-family'   : 'tahoma, arial, helvetica',
57556                                 'font-size'     : '11px',
57557                                 'overflow'      : 'hidden',
57558                                 'padding-left'  : '10px',
57559                                 'padding-right' : '10px',
57560                                 'padding-top' : '10px' 
57561                             }
57562                             
57563                         }, Roo.id());
57564                                 this.ds.load({});
57565                     }
57566                 },
57567                 autoWidth : true,
57568                 monitorWindowResize : false,
57569                 cellrenderer : function(v,x,r)
57570                 {
57571                     return v;
57572                 },
57573                 sm : {
57574                     xtype: 'CellSelectionModel',
57575                     xns: Roo.grid
57576                 },
57577                 dataSource : {
57578                     xtype: 'Store',
57579                     xns: Roo.data,
57580                     listeners : {
57581                         beforeload : function (_self, options)
57582                         {
57583                             options.params = options.params || {};
57584                             options.params._month = _this.monthField.getValue();
57585                             options.params.limit = 9999;
57586                             options.params['sort'] = 'when_dt';    
57587                             options.params['dir'] = 'ASC';    
57588                             this.proxy.loadResponse = this.loadResponse;
57589                             Roo.log("load?");
57590                             //this.addColumns();
57591                         },
57592                         load : function (_self, records, options)
57593                         {
57594                             _this.grid.view.el.select('.course-edit-link', true).on('click', function() {
57595                                 // if you click on the translation.. you can edit it...
57596                                 var el = Roo.get(this);
57597                                 var id = el.dom.getAttribute('data-id');
57598                                 var d = el.dom.getAttribute('data-date');
57599                                 var t = el.dom.getAttribute('data-time');
57600                                 //var id = this.child('span').dom.textContent;
57601                                 
57602                                 //Roo.log(this);
57603                                 Pman.Dialog.CourseCalendar.show({
57604                                     id : id,
57605                                     when_d : d,
57606                                     when_t : t,
57607                                     productitem_active : id ? 1 : 0
57608                                 }, function() {
57609                                     _this.grid.ds.load({});
57610                                 });
57611                            
57612                            });
57613                            
57614                            _this.panel.fireEvent('resize', [ '', '' ]);
57615                         }
57616                     },
57617                     loadResponse : function(o, success, response){
57618                             // this is overridden on before load..
57619                             
57620                             Roo.log("our code?");       
57621                             //Roo.log(success);
57622                             //Roo.log(response)
57623                             delete this.activeRequest;
57624                             if(!success){
57625                                 this.fireEvent("loadexception", this, o, response);
57626                                 o.request.callback.call(o.request.scope, null, o.request.arg, false);
57627                                 return;
57628                             }
57629                             var result;
57630                             try {
57631                                 result = o.reader.read(response);
57632                             }catch(e){
57633                                 Roo.log("load exception?");
57634                                 this.fireEvent("loadexception", this, o, response, e);
57635                                 o.request.callback.call(o.request.scope, null, o.request.arg, false);
57636                                 return;
57637                             }
57638                             Roo.log("ready...");        
57639                             // loop through result.records;
57640                             // and set this.tdate[date] = [] << array of records..
57641                             _this.tdata  = {};
57642                             Roo.each(result.records, function(r){
57643                                 //Roo.log(r.data);
57644                                 if(typeof(_this.tdata[r.data.when_dt.format('j')]) == 'undefined'){
57645                                     _this.tdata[r.data.when_dt.format('j')] = [];
57646                                 }
57647                                 _this.tdata[r.data.when_dt.format('j')].push(r.data);
57648                             });
57649                             
57650                             //Roo.log(_this.tdata);
57651                             
57652                             result.records = [];
57653                             result.totalRecords = 6;
57654                     
57655                             // let's generate some duumy records for the rows.
57656                             //var st = _this.dateField.getValue();
57657                             
57658                             // work out monday..
57659                             //st = st.add(Date.DAY, -1 * st.format('w'));
57660                             
57661                             var date = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
57662                             
57663                             var firstOfMonth = date.getFirstDayOfMonth();
57664                             var days = date.getDaysInMonth();
57665                             var d = 1;
57666                             var firstAdded = false;
57667                             for (var i = 0; i < result.totalRecords ; i++) {
57668                                 //var d= st.add(Date.DAY, i);
57669                                 var row = {};
57670                                 var added = 0;
57671                                 for(var w = 0 ; w < 7 ; w++){
57672                                     if(!firstAdded && firstOfMonth != w){
57673                                         continue;
57674                                     }
57675                                     if(d > days){
57676                                         continue;
57677                                     }
57678                                     firstAdded = true;
57679                                     var dd = (d > 0 && d < 10) ? "0"+d : d;
57680                                     row['weekday'+w] = String.format(
57681                                                     '<span style="font-size: 16px;"><b>{0}</b></span>'+
57682                                                     '<span class="course-edit-link" style="color:blue;" data-id="0" data-date="{1}"> Add New</span>',
57683                                                     d,
57684                                                     date.format('Y-m-')+dd
57685                                                 );
57686                                     added++;
57687                                     if(typeof(_this.tdata[d]) != 'undefined'){
57688                                         Roo.each(_this.tdata[d], function(r){
57689                                             var is_sub = '';
57690                                             var deactive = '';
57691                                             var id = r.id;
57692                                             var desc = (r.productitem_id_descrip) ? r.productitem_id_descrip : '';
57693                                             if(r.parent_id*1>0){
57694                                                 is_sub = (r.productitem_id_visible*1 < 1) ? 'de-act-sup-link' :'sub-link';
57695                                                 id = r.parent_id;
57696                                             }
57697                                             if(r.productitem_id_visible*1 < 1 && r.parent_id*1 < 1){
57698                                                 deactive = 'de-act-link';
57699                                             }
57700                                             
57701                                             row['weekday'+w] += String.format(
57702                                                     '<br /><span class="course-edit-link {3} {4}" qtip="{5}" data-id="{0}">{2} - {1}</span>',
57703                                                     id, //0
57704                                                     r.product_id_name, //1
57705                                                     r.when_dt.format('h:ia'), //2
57706                                                     is_sub, //3
57707                                                     deactive, //4
57708                                                     desc // 5
57709                                             );
57710                                         });
57711                                     }
57712                                     d++;
57713                                 }
57714                                 
57715                                 // only do this if something added..
57716                                 if(added > 0){ 
57717                                     result.records.push(_this.grid.dataSource.reader.newRow(row));
57718                                 }
57719                                 
57720                                 
57721                                 // push it twice. (second one with an hour..
57722                                 
57723                             }
57724                             //Roo.log(result);
57725                             this.fireEvent("load", this, o, o.request.arg);
57726                             o.request.callback.call(o.request.scope, result, o.request.arg, true);
57727                         },
57728                     sortInfo : {field: 'when_dt', direction : 'ASC' },
57729                     proxy : {
57730                         xtype: 'HttpProxy',
57731                         xns: Roo.data,
57732                         method : 'GET',
57733                         url : baseURL + '/Roo/Shop_course.php'
57734                     },
57735                     reader : {
57736                         xtype: 'JsonReader',
57737                         xns: Roo.data,
57738                         id : 'id',
57739                         fields : [
57740                             {
57741                                 'name': 'id',
57742                                 'type': 'int'
57743                             },
57744                             {
57745                                 'name': 'when_dt',
57746                                 'type': 'string'
57747                             },
57748                             {
57749                                 'name': 'end_dt',
57750                                 'type': 'string'
57751                             },
57752                             {
57753                                 'name': 'parent_id',
57754                                 'type': 'int'
57755                             },
57756                             {
57757                                 'name': 'product_id',
57758                                 'type': 'int'
57759                             },
57760                             {
57761                                 'name': 'productitem_id',
57762                                 'type': 'int'
57763                             },
57764                             {
57765                                 'name': 'guid',
57766                                 'type': 'int'
57767                             }
57768                         ]
57769                     }
57770                 },
57771                 toolbar : {
57772                     xtype: 'Toolbar',
57773                     xns: Roo,
57774                     items : [
57775                         {
57776                             xtype: 'Button',
57777                             xns: Roo.Toolbar,
57778                             listeners : {
57779                                 click : function (_self, e)
57780                                 {
57781                                     var sd = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
57782                                     sd.setMonth(sd.getMonth()-1);
57783                                     _this.monthField.setValue(sd.format('Y-m-d'));
57784                                     _this.grid.ds.load({});
57785                                 }
57786                             },
57787                             text : "Back"
57788                         },
57789                         {
57790                             xtype: 'Separator',
57791                             xns: Roo.Toolbar
57792                         },
57793                         {
57794                             xtype: 'MonthField',
57795                             xns: Roo.form,
57796                             listeners : {
57797                                 render : function (_self)
57798                                 {
57799                                     _this.monthField = _self;
57800                                    // _this.monthField.set  today
57801                                 },
57802                                 select : function (combo, date)
57803                                 {
57804                                     _this.grid.ds.load({});
57805                                 }
57806                             },
57807                             value : (function() { return new Date(); })()
57808                         },
57809                         {
57810                             xtype: 'Separator',
57811                             xns: Roo.Toolbar
57812                         },
57813                         {
57814                             xtype: 'TextItem',
57815                             xns: Roo.Toolbar,
57816                             text : "Blue: in-active, green: in-active sup-event, red: de-active, purple: de-active sup-event"
57817                         },
57818                         {
57819                             xtype: 'Fill',
57820                             xns: Roo.Toolbar
57821                         },
57822                         {
57823                             xtype: 'Button',
57824                             xns: Roo.Toolbar,
57825                             listeners : {
57826                                 click : function (_self, e)
57827                                 {
57828                                     var sd = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
57829                                     sd.setMonth(sd.getMonth()+1);
57830                                     _this.monthField.setValue(sd.format('Y-m-d'));
57831                                     _this.grid.ds.load({});
57832                                 }
57833                             },
57834                             text : "Next"
57835                         }
57836                     ]
57837                 },
57838                  
57839             }
57840         };
57841         
57842         *//*
57843  * Based on:
57844  * Ext JS Library 1.1.1
57845  * Copyright(c) 2006-2007, Ext JS, LLC.
57846  *
57847  * Originally Released Under LGPL - original licence link has changed is not relivant.
57848  *
57849  * Fork - LGPL
57850  * <script type="text/javascript">
57851  */
57852  
57853 /**
57854  * @class Roo.LoadMask
57855  * A simple utility class for generically masking elements while loading data.  If the element being masked has
57856  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
57857  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
57858  * element's UpdateManager load indicator and will be destroyed after the initial load.
57859  * @constructor
57860  * Create a new LoadMask
57861  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
57862  * @param {Object} config The config object
57863  */
57864 Roo.LoadMask = function(el, config){
57865     this.el = Roo.get(el);
57866     Roo.apply(this, config);
57867     if(this.store){
57868         this.store.on('beforeload', this.onBeforeLoad, this);
57869         this.store.on('load', this.onLoad, this);
57870         this.store.on('loadexception', this.onLoadException, this);
57871         this.removeMask = false;
57872     }else{
57873         var um = this.el.getUpdateManager();
57874         um.showLoadIndicator = false; // disable the default indicator
57875         um.on('beforeupdate', this.onBeforeLoad, this);
57876         um.on('update', this.onLoad, this);
57877         um.on('failure', this.onLoad, this);
57878         this.removeMask = true;
57879     }
57880 };
57881
57882 Roo.LoadMask.prototype = {
57883     /**
57884      * @cfg {Boolean} removeMask
57885      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
57886      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
57887      */
57888     /**
57889      * @cfg {String} msg
57890      * The text to display in a centered loading message box (defaults to 'Loading...')
57891      */
57892     msg : 'Loading...',
57893     /**
57894      * @cfg {String} msgCls
57895      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
57896      */
57897     msgCls : 'x-mask-loading',
57898
57899     /**
57900      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
57901      * @type Boolean
57902      */
57903     disabled: false,
57904
57905     /**
57906      * Disables the mask to prevent it from being displayed
57907      */
57908     disable : function(){
57909        this.disabled = true;
57910     },
57911
57912     /**
57913      * Enables the mask so that it can be displayed
57914      */
57915     enable : function(){
57916         this.disabled = false;
57917     },
57918     
57919     onLoadException : function()
57920     {
57921         Roo.log(arguments);
57922         
57923         if (typeof(arguments[3]) != 'undefined') {
57924             Roo.MessageBox.alert("Error loading",arguments[3]);
57925         } 
57926         /*
57927         try {
57928             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
57929                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
57930             }   
57931         } catch(e) {
57932             
57933         }
57934         */
57935     
57936         
57937         
57938         this.el.unmask(this.removeMask);
57939     },
57940     // private
57941     onLoad : function()
57942     {
57943         this.el.unmask(this.removeMask);
57944     },
57945
57946     // private
57947     onBeforeLoad : function(){
57948         if(!this.disabled){
57949             this.el.mask(this.msg, this.msgCls);
57950         }
57951     },
57952
57953     // private
57954     destroy : function(){
57955         if(this.store){
57956             this.store.un('beforeload', this.onBeforeLoad, this);
57957             this.store.un('load', this.onLoad, this);
57958             this.store.un('loadexception', this.onLoadException, this);
57959         }else{
57960             var um = this.el.getUpdateManager();
57961             um.un('beforeupdate', this.onBeforeLoad, this);
57962             um.un('update', this.onLoad, this);
57963             um.un('failure', this.onLoad, this);
57964         }
57965     }
57966 };/*
57967  * Based on:
57968  * Ext JS Library 1.1.1
57969  * Copyright(c) 2006-2007, Ext JS, LLC.
57970  *
57971  * Originally Released Under LGPL - original licence link has changed is not relivant.
57972  *
57973  * Fork - LGPL
57974  * <script type="text/javascript">
57975  */
57976
57977
57978 /**
57979  * @class Roo.XTemplate
57980  * @extends Roo.Template
57981  * Provides a template that can have nested templates for loops or conditionals. The syntax is:
57982 <pre><code>
57983 var t = new Roo.XTemplate(
57984         '&lt;select name="{name}"&gt;',
57985                 '&lt;tpl for="options"&gt;&lt;option value="{value:trim}"&gt;{text:ellipsis(10)}&lt;/option&gt;&lt;/tpl&gt;',
57986         '&lt;/select&gt;'
57987 );
57988  
57989 // then append, applying the master template values
57990  </code></pre>
57991  *
57992  * Supported features:
57993  *
57994  *  Tags:
57995
57996 <pre><code>
57997       {a_variable} - output encoded.
57998       {a_variable.format:("Y-m-d")} - call a method on the variable
57999       {a_variable:raw} - unencoded output
58000       {a_variable:toFixed(1,2)} - Roo.util.Format."toFixed"
58001       {a_variable:this.method_on_template(...)} - call a method on the template object.
58002  
58003 </code></pre>
58004  *  The tpl tag:
58005 <pre><code>
58006         &lt;tpl for="a_variable or condition.."&gt;&lt;/tpl&gt;
58007         &lt;tpl if="a_variable or condition"&gt;&lt;/tpl&gt;
58008         &lt;tpl exec="some javascript"&gt;&lt;/tpl&gt;
58009         &lt;tpl name="named_template"&gt;&lt;/tpl&gt; (experimental)
58010   
58011         &lt;tpl for="."&gt;&lt;/tpl&gt; - just iterate the property..
58012         &lt;tpl for=".."&gt;&lt;/tpl&gt; - iterates with the parent (probably the template) 
58013 </code></pre>
58014  *      
58015  */
58016 Roo.XTemplate = function()
58017 {
58018     Roo.XTemplate.superclass.constructor.apply(this, arguments);
58019     if (this.html) {
58020         this.compile();
58021     }
58022 };
58023
58024
58025 Roo.extend(Roo.XTemplate, Roo.Template, {
58026
58027     /**
58028      * The various sub templates
58029      */
58030     tpls : false,
58031     /**
58032      *
58033      * basic tag replacing syntax
58034      * WORD:WORD()
58035      *
58036      * // you can fake an object call by doing this
58037      *  x.t:(test,tesT) 
58038      * 
58039      */
58040     re : /\{([\w-\.]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
58041
58042     /**
58043      * compile the template
58044      *
58045      * This is not recursive, so I'm not sure how nested templates are really going to be handled..
58046      *
58047      */
58048     compile: function()
58049     {
58050         var s = this.html;
58051      
58052         s = ['<tpl>', s, '</tpl>'].join('');
58053     
58054         var re     = /<tpl\b[^>]*>((?:(?=([^<]+))\2|<(?!tpl\b[^>]*>))*?)<\/tpl>/,
58055             nameRe = /^<tpl\b[^>]*?for="(.*?)"/,
58056             ifRe   = /^<tpl\b[^>]*?if="(.*?)"/,
58057             execRe = /^<tpl\b[^>]*?exec="(.*?)"/,
58058             namedRe = /^<tpl\b[^>]*?name="(\w+)"/,  // named templates..
58059             m,
58060             id     = 0,
58061             tpls   = [];
58062     
58063         while(true == !!(m = s.match(re))){
58064             var forMatch   = m[0].match(nameRe),
58065                 ifMatch   = m[0].match(ifRe),
58066                 execMatch   = m[0].match(execRe),
58067                 namedMatch   = m[0].match(namedRe),
58068                 
58069                 exp  = null, 
58070                 fn   = null,
58071                 exec = null,
58072                 name = forMatch && forMatch[1] ? forMatch[1] : '';
58073                 
58074             if (ifMatch) {
58075                 // if - puts fn into test..
58076                 exp = ifMatch && ifMatch[1] ? ifMatch[1] : null;
58077                 if(exp){
58078                    fn = new Function('values', 'parent', 'with(values){ return '+(Roo.util.Format.htmlDecode(exp))+'; }');
58079                 }
58080             }
58081             
58082             if (execMatch) {
58083                 // exec - calls a function... returns empty if true is  returned.
58084                 exp = execMatch && execMatch[1] ? execMatch[1] : null;
58085                 if(exp){
58086                    exec = new Function('values', 'parent', 'with(values){ '+(Roo.util.Format.htmlDecode(exp))+'; }');
58087                 }
58088             }
58089             
58090             
58091             if (name) {
58092                 // for = 
58093                 switch(name){
58094                     case '.':  name = new Function('values', 'parent', 'with(values){ return values; }'); break;
58095                     case '..': name = new Function('values', 'parent', 'with(values){ return parent; }'); break;
58096                     default:   name = new Function('values', 'parent', 'with(values){ return '+name+'; }');
58097                 }
58098             }
58099             var uid = namedMatch ? namedMatch[1] : id;
58100             
58101             
58102             tpls.push({
58103                 id:     namedMatch ? namedMatch[1] : id,
58104                 target: name,
58105                 exec:   exec,
58106                 test:   fn,
58107                 body:   m[1] || ''
58108             });
58109             if (namedMatch) {
58110                 s = s.replace(m[0], '');
58111             } else { 
58112                 s = s.replace(m[0], '{xtpl'+ id + '}');
58113             }
58114             ++id;
58115         }
58116         this.tpls = [];
58117         for(var i = tpls.length-1; i >= 0; --i){
58118             this.compileTpl(tpls[i]);
58119             this.tpls[tpls[i].id] = tpls[i];
58120         }
58121         this.master = tpls[tpls.length-1];
58122         return this;
58123     },
58124     /**
58125      * same as applyTemplate, except it's done to one of the subTemplates
58126      * when using named templates, you can do:
58127      *
58128      * var str = pl.applySubTemplate('your-name', values);
58129      *
58130      * 
58131      * @param {Number} id of the template
58132      * @param {Object} values to apply to template
58133      * @param {Object} parent (normaly the instance of this object)
58134      */
58135     applySubTemplate : function(id, values, parent)
58136     {
58137         
58138         
58139         var t = this.tpls[id];
58140         
58141         
58142         try { 
58143             if(t.test && !t.test.call(this, values, parent)){
58144                 return '';
58145             }
58146         } catch(e) {
58147             Roo.log("Xtemplate.applySubTemplate 'test': Exception thrown");
58148             Roo.log(e.toString());
58149             Roo.log(t.test);
58150             return ''
58151         }
58152         try { 
58153             
58154             if(t.exec && t.exec.call(this, values, parent)){
58155                 return '';
58156             }
58157         } catch(e) {
58158             Roo.log("Xtemplate.applySubTemplate 'exec': Exception thrown");
58159             Roo.log(e.toString());
58160             Roo.log(t.exec);
58161             return ''
58162         }
58163         try {
58164             var vs = t.target ? t.target.call(this, values, parent) : values;
58165             parent = t.target ? values : parent;
58166             if(t.target && vs instanceof Array){
58167                 var buf = [];
58168                 for(var i = 0, len = vs.length; i < len; i++){
58169                     buf[buf.length] = t.compiled.call(this, vs[i], parent);
58170                 }
58171                 return buf.join('');
58172             }
58173             return t.compiled.call(this, vs, parent);
58174         } catch (e) {
58175             Roo.log("Xtemplate.applySubTemplate : Exception thrown");
58176             Roo.log(e.toString());
58177             Roo.log(t.compiled);
58178             return '';
58179         }
58180     },
58181
58182     compileTpl : function(tpl)
58183     {
58184         var fm = Roo.util.Format;
58185         var useF = this.disableFormats !== true;
58186         var sep = Roo.isGecko ? "+" : ",";
58187         var undef = function(str) {
58188             Roo.log("Property not found :"  + str);
58189             return '';
58190         };
58191         
58192         var fn = function(m, name, format, args)
58193         {
58194             //Roo.log(arguments);
58195             args = args ? args.replace(/\\'/g,"'") : args;
58196             //["{TEST:(a,b,c)}", "TEST", "", "a,b,c", 0, "{TEST:(a,b,c)}"]
58197             if (typeof(format) == 'undefined') {
58198                 format= 'htmlEncode';
58199             }
58200             if (format == 'raw' ) {
58201                 format = false;
58202             }
58203             
58204             if(name.substr(0, 4) == 'xtpl'){
58205                 return "'"+ sep +'this.applySubTemplate('+name.substr(4)+', values, parent)'+sep+"'";
58206             }
58207             
58208             // build an array of options to determine if value is undefined..
58209             
58210             // basically get 'xxxx.yyyy' then do
58211             // (typeof(xxxx) == 'undefined' || typeof(xxx.yyyy) == 'undefined') ?
58212             //    (function () { Roo.log("Property not found"); return ''; })() :
58213             //    ......
58214             
58215             var udef_ar = [];
58216             var lookfor = '';
58217             Roo.each(name.split('.'), function(st) {
58218                 lookfor += (lookfor.length ? '.': '') + st;
58219                 udef_ar.push(  "(typeof(" + lookfor + ") == 'undefined')"  );
58220             });
58221             
58222             var udef_st = '((' + udef_ar.join(" || ") +") ? undef('" + name + "') : "; // .. needs )
58223             
58224             
58225             if(format && useF){
58226                 
58227                 args = args ? ',' + args : "";
58228                  
58229                 if(format.substr(0, 5) != "this."){
58230                     format = "fm." + format + '(';
58231                 }else{
58232                     format = 'this.call("'+ format.substr(5) + '", ';
58233                     args = ", values";
58234                 }
58235                 
58236                 return "'"+ sep +   udef_st   +    format + name + args + "))"+sep+"'";
58237             }
58238              
58239             if (args.length) {
58240                 // called with xxyx.yuu:(test,test)
58241                 // change to ()
58242                 return "'"+ sep + udef_st  + name + '(' +  args + "))"+sep+"'";
58243             }
58244             // raw.. - :raw modifier..
58245             return "'"+ sep + udef_st  + name + ")"+sep+"'";
58246             
58247         };
58248         var body;
58249         // branched to use + in gecko and [].join() in others
58250         if(Roo.isGecko){
58251             body = "tpl.compiled = function(values, parent){  with(values) { return '" +
58252                    tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
58253                     "';};};";
58254         }else{
58255             body = ["tpl.compiled = function(values, parent){  with (values) { return ['"];
58256             body.push(tpl.body.replace(/(\r\n|\n)/g,
58257                             '\\n').replace(/'/g, "\\'").replace(this.re, fn));
58258             body.push("'].join('');};};");
58259             body = body.join('');
58260         }
58261         
58262         Roo.debug && Roo.log(body.replace(/\\n/,'\n'));
58263        
58264         /** eval:var:tpl eval:var:fm eval:var:useF eval:var:undef  */
58265         eval(body);
58266         
58267         return this;
58268     },
58269
58270     applyTemplate : function(values){
58271         return this.master.compiled.call(this, values, {});
58272         //var s = this.subs;
58273     },
58274
58275     apply : function(){
58276         return this.applyTemplate.apply(this, arguments);
58277     }
58278
58279  });
58280
58281 Roo.XTemplate.from = function(el){
58282     el = Roo.getDom(el);
58283     return new Roo.XTemplate(el.value || el.innerHTML);
58284 };